makefile 篇 gcc
https://github.com/wrzfeijianshen/Courseware
課件在Courseware/csdn/裏面
makefile 是一個命令工具,可以自動化編譯,一般命名爲makefile或者Makefile.
現在咱針對c/c++語言進行對makefile的學習和認知
gcc 工作流程是編譯c和c++的基礎,有必要學習如何生成.o,可執行程序,以及靜態庫和動態庫,以及相關的知識等.
準備環境
- ubuntu16.04 或其他linux系統
- vscode
https://blog.csdn.net/wrzfeijianshen/article/details/102657549
重點: 顯示當前的tab 格式符號
“editor.insertSpaces”:false
“editor.detectIndentation”: false,
“editor.renderControlCharacters”: true,
“editor.renderWhitespace”: “all”,
遠程修改文件用 remote-ssh
gcc 工作流程
將.c編譯生成可執行程序.
hello.c -->預處理階段(gcc -E) 比如去掉註釋,頭文件展開,宏替換等 --> 彙編文件hello.s–>gcc -c 彙編器–>hello.o 二進制文件–>鏈接器 ld --> a.out
這樣一步步的進行生成,這隻有一個hello.c文件,通過生成.o文件,最終gcc 生成可執行程序
gcc -E hello.c -o hello.i
gcc -S hello.i -o hello.s
gcc -c hello.s -o hello.o
gcc hello.o -o hello
- 01/01_01 :
源文件 hello.c
#include "stdio.h"
#define VERSION "1.0"
int main()
{
// 打印輸出
printf("hello %s ...\n",VERSION);
return 0;
}
file 可執行程序名 查看當前程序是64位還是32等信息
ldd 可執行程序名,查看當前依賴哪些庫名
root@fjs:~/code/fjs/makefile/01/01_1# file hello
hello: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=9f25f325f85140831606a9f6eab2f0bde3711e3b, not stripped
root@fjs:~/code/fjs/makefile/01/01_1# ldd hello
linux-vdso.so.1 (0x00007ffeac3c6000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3951941000)
/lib64/ld-linux-x86-64.so.2 (0x00007f3951f34000)
gcc 常用參數
gcc -c 只編譯生成.o文件,稱爲目標文件
gcc -o 生成的目標文件的名字
gcc -I 指定頭文件路徑
gcc -L 指定庫文件所在的路徑
gcc -l 指定庫的名字
gcc -g 調試信息 debug
gcc -M 查看當前依賴哪些頭文件(系統頭文件)
gcc -MM 查看當前依賴哪些本地文件,不包含系統頭文件
hello.o依賴 hello.c 和stdio.h 等等
root@fjs:~/code/fjs/makefile/01/01_1# gcc -M hello.c
hello.o: hello.c /usr/include/stdc-predef.h /usr/include/stdio.h \
/usr/include/x86_64-linux-gnu/bits/libc-header-start.h \
/usr/include/features.h /usr/include/x86_64-linux-gnu/sys/cdefs.h \
/usr/include/x86_64-linux-gnu/bits/wordsize.h \
/usr/include/x86_64-linux-gnu/bits/long-double.h \
/...
hello.c 僅僅依賴hello.c文件
root@fjs:~/code/fjs/makefile/01/01_1# gcc -MM hello.c
hello.o: hello.c
- 01/01_02
把宏名移動到.h頭文件中,gcc hello.c 自動會生成a.out可執行程序.
root@fjs:~/code/fjs/makefile/01/01_2# gcc hello.c
root@fjs:~/code/fjs/makefile/01/01_2# ls
a.out hello.c hello.h
root@fjs:~/code/fjs/makefile/01/01_2# ./a.out
hello 1.0 ...
hello.c 依賴 hello.c hello.h 這兩個文件
root@fjs:~/code/fjs/makefile/01/01_2# gcc -MM hello.c
hello.o: hello.c hello.h
靜態庫和動態庫
靜態庫 static library : 一些目標目標代碼的集合,簡單講,就是一堆.o文件的集合,
在linux下一般是.a後綴名
靜態庫 一般供別人進行調用.
優點:函數庫最終被打包到應用程序中,運行速度快.移植方便,不依賴其他文件
缺點:更新發布帶來麻煩,耗費內存.
格式:
- 前綴lib
- 庫名字:test
- 後綴:.a
如libtest.a
生成步驟:
-
.c --> .o文件 格式: gcc -c xxx.c -o xxx.o
-
打包工具ar,把.o文件打包爲.a文件, ar res(r更新,c創建,s建立索引)
命令: ar rcs libxxx.a .o文件集合
靜態庫 使用: gcc hello.c -o hello.out -L./ -I./ -ltest
- -L 指定鏈接的庫目錄
- -l 指定需要鏈接的靜態庫.去前綴和後綴
- -I 指定頭文件所在的路徑
動態庫(shared library )共享庫
程序在運行時纔會被載入.增量更新.以.so爲後綴
優點:
- 節省內存
- 升級更新方便,替換庫即可
缺點: - 加載速度比靜態庫慢
- 移植性差
組成: libtest.so
- 前綴 lib
- 庫名稱 :test
- 後綴 .so
動態庫的生成:
- 生成目標文件.o ,此時需要添加編譯選項 -fPIC
(-fpic 創建與地址無關的編譯程序,目的是爲了能夠在多個應用程序之間共享) - 生成共享庫 -shared
gcc -shared *.o列表
生成可執行程序:同靜態庫
使用: gcc hello.c -o hello.out -L./ -I./ -ltest
- -L 指定鏈接的庫目錄
- -l 指定需要鏈接的靜態庫.去前綴和後綴
- -I 指定頭文件所在的路徑
執行時找不到該庫: ldd file
如何讓程序找到共享庫:
- 把.so文件 拷貝到/lib 或者/usr/lib
例子1:生成靜態庫
正常編譯文件步驟
第1步: 把每個.c文件生成.o文件
gcc -c test1.c -o test1.o
gcc -c main.c -o main.o
第2步: 生成可執行程序hello
gcc -o hello main.o test1.o
- 01/01_03: 例子,先正常編譯,test1裏面有void GetTest1Version() 函數,
咱們在main中調用GetTest1Version函數的示例
root@fjs:~/code/fjs/makefile/01/01_03# ls
main.c test1.c test1.h
root@fjs:~/code/fjs/makefile/01/01_03# gcc -c test1.c -o test1.o
root@fjs:~/code/fjs/makefile/01/01_03# ls
main.c test1.c test1.h test1.o
root@fjs:~/code/fjs/makefile/01/01_03# gcc -c main.c -o main.o
root@fjs:~/code/fjs/makefile/01/01_03# ls
main.c main.o test1.c test1.h test1.o
root@fjs:~/code/fjs/makefile/01/01_03# gcc -o hello main.o test1.o
root@fjs:~/code/fjs/makefile/01/01_03# ls
hello main.c main.o test1.c test1.h test1.o
root@fjs:~/code/fjs/makefile/01/01_03# ./hello
當前test1 版本號[1.1]
main run end
上面的例子,生成.a文件
咱們把test1.h和test1.c 生成的.o文件 封裝成靜態庫.然後供main函數進行調用
第1步: 把每個.c文件生成.o文件
gcc -c test1.c -o test1.o
gcc -c main.c -o main.o
第2步: 生成靜態庫libtest1.a
ar rcs libtest1.a test1.o
第3步: 生成可執行程序hello
gcc main.o -o hello -L./ -I./ -ltest1
01_04 :操作如下
root@fjs:~/code/fjs/makefile/01/01_04# ls
main.c test1.c test1.h
root@fjs:~/code/fjs/makefile/01/01_04# gcc -c test1.c -o test1.o
root@fjs:~/code/fjs/makefile/01/01_04# gcc -c main.c -o main.o
root@fjs:~/code/fjs/makefile/01/01_04# ls
main.c main.o test1.c test1.h test1.o
root@fjs:~/code/fjs/makefile/01/01_04# ar rcs libtest1.a test1.o
root@fjs:~/code/fjs/makefile/01/01_04# ls
libtest1.a main.c main.o test1.c test1.h test1.o
root@fjs:~/code/fjs/makefile/01/01_04# gcc main.o -o hello -L./ -I./ -ltest1
root@fjs:~/code/fjs/makefile/01/01_04# ls
hello libtest1.a main.c main.o test1.c test1.h test1.o
root@fjs:~/code/fjs/makefile/01/01_04# ./hello
當前test1 版本號[1.1]
main run end
例子2:生成動態庫
同樣是上面的三個文件
編譯流程:
gcc -fpic -c *.c
gcc -shared *.o -o libxxx.so
gcc main.c -I./ -L./ -lxxxx -o hello
第1步: 編譯出庫.o
gcc -fpic -c test1.c -o test1.o
第2步: 生成.so文件
gcc -shared test1.o -o libtest1.so
第3步: 生成其他.o
gcc -c main.c -o main.o
第4步:生成可執行程序
gcc main.o -I./ -L./ -ltest1 -o hello
ldd hello
01/01_05
root@fjs:~/code/fjs/makefile/01/01_05# gcc -fpic -c test1.c -o test1.o
root@fjs:~/code/fjs/makefile/01/01_05# gcc -shared test1.o -o libtest1.so
root@fjs:~/code/fjs/makefile/01/01_05# gcc -c main.c -o main.o
root@fjs:~/code/fjs/makefile/01/01_05# ls
libtest1.so main.c main.o test1.c test1.h test1.o
root@fjs:~/code/fjs/makefile/01/01_05# gcc main.o -I./ -L./ -ltest1 -o hello
root@fjs:~/code/fjs/makefile/01/01_05# ls
hello libtest1.so main.c main.o test1.c test1.h test1.o
root@fjs:~/code/fjs/makefile/01/01_05# ldd hello
linux-vdso.so.1 (0x00007ffd22791000)
libtest1.so (0x00007fefde37b000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fefddf8a000)
/lib64/ld-linux-x86-64.so.2 (0x00007fefde77f000)
root@fjs:~/code/fjs/makefile/01/01_05# ./hello
當前test1 版本號[1.2]
main run end