makefile
編譯c++
原理
將原始檔案變成執行檔,以下把這個過程分為二個步驟。
首先將個別原始檔(.cpp)編譯成個別目的檔(object file),目的檔的內容是可被電腦執行的機器語言指令。
第二步便是鏈結(linking),將上一步好幾個原始檔案編譯完後有多個目的檔,由於原始檔案中會用到c++的函式庫指令,如std::cout,將多個目的檔與函式庫機器語言指令全部鏈結一起,就形成可執行檔。
gcc與g++分別
gcc編譯c
g++編譯c++
g++語法
g++ -o 執行檔名 原始檔案1.cpp 原始檔案2.cpp 原始檔案3.cpp 原始檔案N.cpp
常用選項:
-o 設定"執行檔名"
-std=c++11 支援c++11
-c 只編譯,不鏈結
產生執行檔
建立檔案
$ vi hello.cpp
將以下的內容貼上
1
2
3
4
5
#include <iostream>
using namespace std;
int main() {
cout << "Hello world!\n";
}
產生執行檔
$ g++ -o demo hello.cpp
執行
$ ./demo
Hello world!
沒有-o 執行檔名
沒有-o 執行檔名,會產生a.out的檔案
$ g++ hello.cpp
$ ls
a.out
使用2個cpp產生執行檔
$ vi public.h
1
2
#include <iostream>
void func();
$ vi public.cpp
1
2
3
4
5
#include "public.h"
using namespace std;
void func() {
cout << "call func()" << endl;
}
$ vi hello.cpp
1
2
3
4
5
6
7
#include <iostream>
#include "public.h"
using namespace std;
int main() {
cout << "Hello world!\n";
func();
}
$ g++ -o demo hello.cpp public.cpp
$ ./demo
Hello world!
call func()
靜態庫
將cpp檔案整理成一個程式庫,產生執行檔可以指定庫名.a,就不用寫很多原始檔案1.cpp 原始檔案2.cpp 原始檔案3.cpp 原始檔案N.cpp
多個process(行程)用到同一個靜態庫中的函式或類別,每個process(行程)拷貝一份,多個process(行程)就有多個拷貝。
靜態庫是二進制檔案(機器語言指令)
主程式在鏈結時會把靜態庫加入,產生執行檔。
若某個靜態庫更新了,所有使用它的程式都需要重新編譯。
語法
g++ -c -o lib庫名.a 原始檔案1.cpp 原始檔案2.cpp 原始檔案3.cpp 原始檔案N.cpp
-c
-c 只編譯,不鏈結
lib庫名.a
.a代表靜態庫
lib是固定
庫名可以自由變換
檔案放置路徑
cici@cici-vm:~/test$ tree
.
├── app
│ └── hello.cpp
└── tools
├── public.cpp
└── public.h
3 directories, 3 files
將以下檔案依上方目錄結構放置。
public.h
1
2
3
4
5
6
#include <iostream>
void func();
class Student {
public:
void print();
};
public.cpp
1
2
3
4
5
6
7
8
#include "public.h"
using namespace std;
void func() {
cout << "call func()" << endl;
}
void Student::print() {
cout << "msg...." << endl;
}
hello.cpp
1
2
3
4
5
6
7
8
9
#include <iostream>
#include "../tools/public.h"
using namespace std;
int main() {
cout << "Hello world!\n";
func();
Student student;
student.print();
}
產生靜態庫
進入tools目錄
$ cd tools
產生靜態庫
$ g++ -c -o libpublic.a public.cpp
使用靜態庫
法1
回到上層
$ cd..
產生demo01執行檔,原始檔案有libpublic.a的靜態庫
$ g++ -o demo01 app/hello.cpp tools/libpublic.a
執行
$ ./demo01
法2
語法
g++ -o 執行檔名 原始檔案1.cpp -L 靜態庫目錄位址 -l 庫名
$ g++ -o demo01 app/hello.cpp -L tools -l public
動態庫
語法
g++ -fPIC -shared -o lib庫名.so 來源檔案1.cpp
-fPIC -shared 這二句代表製作動態庫
-o 產生檔案
lib庫名.so lib與.so是固定寫法,庫名可以自已取名。
$ cd tools
$ g++ -fPIC -shared -o libpublic.so public.cpp
使用動態庫
將cpp檔案整理成一個庫,產生執行檔可以指定庫名.so,就不用寫很多原始檔案1.cpp 原始檔案2.cpp 原始檔案3.cpp 原始檔案N.cpp
動態庫是二進制檔案
多個procee用到同一個動態庫中的函式或類別,在記憶體中只有一份,多個process共享相同程式碼,也稱共享庫,因為只有一份程式碼,不會占用記憶體。
主程式在執行時,才把動態庫程式碼載入到記憶體。
優點,動態庫更新,有用到動態庫的程式不用重新編譯,只需要更新動態庫。
動態庫與靜態庫同時存在,優先使用動態庫。
法1
$ g++ -o demo01 app/hello.cpp tools/libpublic.so
$ ./demo01
法2
先增加LD_LIBRARY_PATH環境變數,設定放置動態庫的目錄
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/cici/test/tools
$ g++ -o demo01 app/hello.cpp -L tools -l public
$ ./demo01
-L 動態庫目錄位址 -l 庫名
模擬更新動態庫
將public.cpp的程式碼更新。
增加update的字
public.cpp
1
2
3
4
5
6
7
8
#include "public.h"
using namespace std;
void func() {
cout << "update all func()" << endl;
}
void Student::print() {
cout << "update msg...." << endl;
}
重新建立動態庫
$ cd tools
$ g++ -fPIC -shared -o libpublic.so public.cpp
執行主程式
$ ./demo01
Hello world!
update call func()
update msg....
總結
以上的過程,都不必重新編譯主程式。
makefile
建立makefile
$ cd tools
$ vi makefile
產生靜態庫,動態庫
用空格分開
all: libpublic.a libpublic.so
換行用\
注意!第二行是用tab,不是空格
all: libpublic.a \
libpublic.so
動態庫或靜態庫中的cpp更新,重新編譯
編譯靜態庫libpublic.a,需要依賴public.h和public.cpp
若public.h或public.cpp更新,需要重新編譯
注意!第二行是用tab,不是空格
libpublic.a:public.h public.cpp
g++ -c -o libpublic.a public.cpp
編譯動態庫libpublic.so,需要依賴public.h和public.cpp
若public.h或public.cpp更新,需要重新編譯
注意!第二行是用tab,不是空格
libpublic.so:public.h public.cpp
g++ -fPIC -shared -o libpublic.so public.cpp
clean刪除編譯完的檔案
clean:
rm -f libpublic.a libpublic.so
使用makefile
清理
語法
make clean
cici@cici-vm:~/test/tools$ ls
libpublic.a libpublic.so makefile public.cpp public.h
cici@cici-vm:~/test/tools$ make clean
rm -f libpublic.a libpublic.so
cici@cici-vm:~/test/tools$ ls
makefile public.cpp public.h
產生靜態庫,動態庫
語法
make
cici@cici-vm:~/test/tools$ make
g++ -c -o libpublic.a public.cpp
g++ -fPIC -shared -o libpublic.so public.cpp
cici@cici-vm:~/test/tools$ ls
libpublic.a libpublic.so makefile public.cpp public.h
若沒有修改任何.cpp或.h,再次make不會進行動作
cici@cici-vm:~/test/tools$ make
make: 對「all」無需做任何事。
修改public.cpp
將"update"改成"update2"
public.cpp
1
2
3
4
5
6
7
8
#include "public.h"
using namespace std;
void func() {
cout << "update2 all func()" << endl;
}
void Student::print() {
cout << "update2 msg...." << endl;
}
若有更改.cpp,make就會重新編譯
cici@cici-vm:~/test/tools$ vi public.cpp
cici@cici-vm:~/test/tools$ make
g++ -c -o libpublic.a public.cpp
g++ -fPIC -shared -o libpublic.so public.cpp
若刪除靜態檔,再make,就會重新產生靜態檔
cici@cici-vm:~/test/tools$ rm libpublic.a
cici@cici-vm:~/test/tools$ make
g++ -c -o libpublic.a public.cpp
cici@cici-vm:~/test/tools$ ls
libpublic.a libpublic.so makefile public.cpp public.h
多動態庫使用
建立myapi庫
回到最上層目錄,建立api的目錄,並在api目錄中,建立myapi.h
$ mkdir api
$ cd api
$ vi myapi.h
路徑如下
cici@cici-vm:~/test$ tree
.
├── api
│ ├── makefile
│ ├── myapi.cpp
│ └── myapi.h
├── app
│ ├── demo01
│ ├── hello
│ ├── hello.cpp
│ └── makefile
├── makefile
└── tools
├── libpublic.a
├── libpublic.so
├── makefile
├── public.cpp
└── public.h
myapi.h
1
2
3
4
5
6
#include <iostream>
void func1();
class Teacher {
public:
void print();
};
myapi.cpp
1
2
3
4
5
6
7
8
#include "myapi.h"
using namespace std;
void func1() {
cout << "call func1()" << endl;
}
void Teacher::print() {
cout << "teacher ... msg ..." << endl;
}
進入api目錄,並建立makefile
1
2
3
4
5
6
7
all:libmyapi.a libmyapi.so
libmyapi.a:myapi.h myapi.cpp
g++ -c -o libmyapi.a myapi.cpp
libmyapi.so:myapi.h myapi.cpp
g++ -fPIC -shared -o libmyapi.so myapi.cpp
clean:
rm -f libmyapi.a libmyapi.so
cici@cici-vm:~/test/api$ make
g++ -c -o libmyapi.a myapi.cpp
g++ -fPIC -shared -o libmyapi.so myapi.cpp
app目錄下的hello.cpp修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include "../tools/public.h"
#include "../api/myapi.h"
using namespace std;
int main() {
cout << "Hello world!\n";
func();
Student student;
student.print();
//新增修改
func1();
Teacher teacher;
teacher.print();
}
api目錄加到LD_LIBRARY_PATH
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/cici/test/api
回到test目錄下,執行以下語句,設定myapi動態檔放置的目錄(-L)與庫名(-l)
$ g++ -o demo01 app/hello.cpp -L tools -l public -L api -l myapi
$ ./demo01
Hello world!
update2 call func()
update2 msg....
call func1()
teacher ... msg ...
-I
修改hello.cpp的include public.h與myapi.h路徑。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include "public.h"
#include "myapi.h"
using namespace std;
int main() {
cout << "Hello world!\n";
func();
Student student;
student.print();
//新增修改
func1();
Teacher teacher;
teacher.print();
}
使用-I,指定.h檔案的路徑
cici@cici-vm:~/test$ g++ -o demo01 app/hello.cpp -L tools -l public -L api -l myapi -I /home/cici/test/tools -I /home/cici/test/api
cici@cici-vm:~/test$ ./demo01
Hello world!
update2 call func()
update2 msg....
call func1()
teacher ... msg ...
為demo01寫makefile
路徑如下
cici@cici-vm:~/test$ tree
.
├── api
│ ├── libmyapi.a
│ ├── libmyapi.so
│ ├── makefile
│ ├── myapi.cpp
│ └── myapi.h
├── app
│ ├── demo01
│ ├── hello
│ ├── hello.cpp
│ └── makefile
├── makefile
└── tools
├── libpublic.a
├── libpublic.so
├── makefile
├── public.cpp
└── public.h
在/home/cici/test目錄下撰寫的makefile:
1
2
3
4
5
all:demo01
demo01:app/hello.cpp
g++ -o demo01 app/hello.cpp -L /home/cici/test/tools -l public -L /home/cici/test/api -l myapi -I /home/cici/test/tools -I /home/cici/test/api
clean:
rm -f demo01
在/home/cici/test/app目錄下撰寫的makefile:
1
2
3
4
5
all:demo01
demo01:hello.cpp
g++ -o demo01 hello.cpp -L /home/cici/test/tools -l public -L /home/cici/test/api -l myapi -I /home/cici/test/tools -I /home/cici/test/api
clean:
rm -f demo01
多個檔案
在makefile定義變數
INCLUDEDIR=-I /home/cici/test/tools -I /home/cici/test/api
LIBDIR=-L /home/cici/test/tools -L /home/cici/test/api
使用變數
$(變數)
多個檔案
1
2
3
4
5
6
7
8
9
10
11
INCLUDEDIR=-I /home/cici/test/tools -I /home/cici/test/api
LIBDIR=-L /home/cici/test/tools -L /home/cici/test/api
all:demo01 demo02 demo03
demo01:hello.cpp
g++ -o demo01 hello.cpp $(INCLUDEDIR) $(LIBDIR) -l public -l myapi
demo02:hello.cpp
g++ -o demo02 hello.cpp $(INCLUDEDIR) $(LIBDIR) -l public -l myapi
demo03:hello.cpp
g++ -o demo03 hello.cpp $(INCLUDEDIR) $(LIBDIR) -l public -l myapi
clean:
rm -f demo01 demo02 demo03
$ vi makefile
$ make
g++ -o demo02 hello.cpp -I /home/cici/test/tools -I /home/cici/test/api -L /home/cici/test/tools -L /home/cici/test/api -l public -l myapi
g++ -o demo03 hello.cpp -I /home/cici/test/tools -I /home/cici/test/api -L /home/cici/test/tools -L /home/cici/test/api -l public -l myapi
$ ls
demo01 demo02 demo03 hello hello.cpp makefile
$ vi makefile
$ ./demo01
Hello world!
update2 call func()
update2 msg....
call func1()
teacher ... msg ...
$ ./demo02
Hello world!
update2 call func()
update2 msg....
call func1()
teacher ... msg ...
$ ./demo03
Hello world!
update2 call func()
update2 msg....
call func1()
teacher ... msg ...
$ make clean
rm -f demo01 demo02 demo03
$ ls
hello hello.cpp makefile