關(guān)于CMake,這篇真的很到位!!
來源:txp玩Linux
作者 : txp
整理 :?李肖遙
今天給大家分享的是工程管理工具cmake,沒有先介紹makefile工程管理工具(坦白來說,這兩者都差不多,cmake最終還是會生成Makefile的,只是說cmake語法稍微比較簡單一些,沒有Makefile那么復(fù)雜!)。最近很多讀者在群里問道CMake的問題,與Makefile的差別等等,今天給大家整理了一篇文章,字?jǐn)?shù)較多,希望對大家有幫助。
就自己個人經(jīng)歷,現(xiàn)在一般公司去寫Makefile和cmake的比較少(當(dāng)然去招聘網(wǎng)站上,有的時候還是可以看到有這個技能要求會寫的,所以說能夠自己寫出來是最為完美的!),一般都是直接使用廠家的Makefile或者Cmake;
但作為學(xué)習(xí),還是要認(rèn)真學(xué)習(xí)里面的原理,比如出現(xiàn)了錯誤,你要能夠定位到錯誤并把它解決掉,因為可能錯誤就出現(xiàn)在配置好的Makefile或者Cmake里面,所以你要看的懂里面代碼的意思(也就是說,你知道這個工具是這樣用,但是也要明白它的原理機制,做到之知其然,知其所以個然來!),這樣才能把問題解決掉。
而且就個人見解,在傳統(tǒng)的linux工程管理用Makefile的比較多(Uboot里面也是大量使用Makefile來進行管理工程);在新型領(lǐng)域,比如物聯(lián)網(wǎng)開發(fā)(特別是一些開源項目等),用Cmake的比較多(當(dāng)然也有可能是例外哈!);好了,廢話就不多說了,開始來學(xué)習(xí)了:
一、Cmake學(xué)習(xí)使用:
1、安裝Cmake管理工具:
一般實際嵌入式linux開發(fā),幾乎都是用Ubuntu來開發(fā)的,因為那啥,安裝啥應(yīng)用程序的非常方便,只需一個命令“apt install + 應(yīng)用程序名稱” 大部分都直接搞定,不用再去配置(特殊的,就例外,還要一些其他相關(guān)配置!),安裝Cmake就是一條命令直接搞定:
root@txp-virtual-machine:/home/txp#?apt?install?cmake
Reading?package?lists...?Done
Building?dependency?tree????
2、先從一個簡單示例,來得出一般書寫步驟規(guī)律:
這里先寫一個簡單的代碼工程main.c,然后再使用我們的cmake來管理代碼工程:
#include?
int?main(void)
{
????printf("TXP嵌入式\n");
????return?0;
}
然后開始寫cmake工程管理文件,我在當(dāng)前目錄建立一個CMakeLists.txt文件,然后再往里面開始工程管理代碼
root@txp-virtual-machine:/home/txp/test#?pwd
/home/txp/test
root@txp-virtual-machine:/home/txp/test#?touch?CMakeLists.txt
root@txp-virtual-machine:/home/txp/test#?ls
CMakeLists.txt??main.c
CMakeLists.txt文件里面的內(nèi)容如下:
cmake_minimum_required?(VERSION?2.8)
project?(main)
add_executable(main?main.c)
解釋一下這三條語句分別代表什么意思:
1、表示cmake最低執(zhí)行版本是2.8才有效來管理我們的工程項目。
2、表示整個工程名為main
3、表示最終要生成的elf文件的名字叫main,使用的源文件是main.c
現(xiàn)在我們來實現(xiàn)cmake的功能,在當(dāng)前目錄下,使用命令"cmake ."(.表示當(dāng)前目錄,而..表示上一級目錄),生成makefile等相關(guān)文件;然后再執(zhí)行一下make命令進行編譯工程,就能生成可執(zhí)行文件main了,同時也會生成makefile文件,最后就可以執(zhí)行可執(zhí)行main文件,就能得到我們所要的結(jié)果:
root@txp-virtual-machine:/home/txp/test#?cmake?.
--?The?C?compiler?identification?is?GNU?4.8.4
--?The?CXX?compiler?identification?is?GNU?4.8.4
--?Check?for?working?C?compiler:?/usr/bin/cc
--?Check?for?working?C?compiler:?/usr/bin/cc?--?works
--?Detecting?C?compiler?ABI?info
--?Detecting?C?compiler?ABI?info?-?done
--?Check?for?working?CXX?compiler:?/usr/bin/c++
--?Check?for?working?CXX?compiler:?/usr/bin/c++?--?works
--?Detecting?CXX?compiler?ABI?info
--?Detecting?CXX?compiler?ABI?info?-?done
--?Configuring?done
--?Generating?done
--?Build?files?have?been?written?to:?/home/txp/test
root@txp-virtual-machine:/home/txp/test#?ls
CMakeCache.txt??CMakeFiles??cmake_install.cmake??CMakeLists.txt??main??main.c??Makefile
root@txp-virtual-machine:/home/txp/test#?make
Scanning?dependencies?of?target?main
[100%]?Building?C?object?CMakeFiles/main.dir/main.c.o
Linking?C?executable?main
[100%]?Built?target?main
root@txp-virtual-machine:/home/txp/test#?./main
TXP嵌入式
如果你再好奇一下的話,可以打開看看Makefile里面的內(nèi)容是啥,很容易想到是用makefile的方式來實現(xiàn)對工程main的管理,這里我就不畫蛇添足把源代碼貼出來了。cmake_install.cmake 是一些相關(guān)配置選項:
#?Install?script?for?directory:?/home/txp/test
#?Set?the?install?prefix
IF(NOT?DEFINED?CMAKE_INSTALL_PREFIX)
??SET(CMAKE_INSTALL_PREFIX?"/usr/local")
ENDIF(NOT?DEFINED?CMAKE_INSTALL_PREFIX)
STRING(REGEX?REPLACE?"/$"?""?CMAKE_INSTALL_PREFIX?"${CMAKE_INSTALL_PREFIX}")
#?Set?the?install?configuration?name.
IF(NOT?DEFINED?CMAKE_INSTALL_CONFIG_NAME)
??IF(BUILD_TYPE)
????STRING(REGEX?REPLACE?"^[^A-Za-z0-9_]+"?""
???????????CMAKE_INSTALL_CONFIG_NAME?"${BUILD_TYPE}")
??ELSE(BUILD_TYPE)
????SET(CMAKE_INSTALL_CONFIG_NAME?"")
??ENDIF(BUILD_TYPE)
??MESSAGE(STATUS?"Install?configuration:?\"${CMAKE_INSTALL_CONFIG_NAME}\"")
ENDIF(NOT?DEFINED?CMAKE_INSTALL_CONFIG_NAME)
#?Set?the?component?getting?installed.
IF(NOT?CMAKE_INSTALL_COMPONENT)
??IF(COMPONENT)
????MESSAGE(STATUS?"Install?component:?\"${COMPONENT}\"")
????SET(CMAKE_INSTALL_COMPONENT?"${COMPONENT}")
??ELSE(COMPONENT)
????SET(CMAKE_INSTALL_COMPONENT)
??ENDIF(COMPONENT)
ENDIF(NOT?CMAKE_INSTALL_COMPONENT)
#?Install?shared?libraries?without?execute?permission?
IF(NOT?DEFINED?CMAKE_INSTALL_SO_NO_EXE)
??SET(CMAKE_INSTALL_SO_NO_EXE?"1")
ENDIF(NOT?DEFINED?CMAKE_INSTALL_SO_NO_EXE)
IF(CMAKE_INSTALL_COMPONENT)
??SET(CMAKE_INSTALL_MANIFEST?"install_manifest_${CMAKE_INSTALL_COMPONENT}.txt")
ELSE(CMAKE_INSTALL_COMPONENT)
??SET(CMAKE_INSTALL_MANIFEST?"install_manifest.txt")
ENDIF(CMAKE_INSTALL_COMPONENT)
FILE(WRITE?"/home/txp/test/${CMAKE_INSTALL_MANIFEST}"?"")
FOREACH(file?${CMAKE_INSTALL_MANIFEST_FILES})
??FILE(APPEND?"/home/txp/test/${CMAKE_INSTALL_MANIFEST}"?"${file}\n")
ENDFOREACH(file)
~?????????????????
而CMakeFiles是一個文件夾,里面文件內(nèi)容如下:
root@txp-virtual-machine:/home/txp/test/CMakeFiles#?ls
2.8.12.2??cmake.check_cache??CMakeDirectoryInformation.cmake??
CMakeOutput.log??CMakeTmp??main.dir??Makefile2?
Makefile.cmake??progress.marks??TargetDirectories.txt
1、先建立CMakeLists.txt(這跟我們用makefile來管理工程一樣,都有一個特殊的文件來專門書寫工程管理代碼的,cmake也不例外,使用CMakeLists.txt文本文件來專門書寫代碼管理工程項目)。
2、然后進行往CMakeLists.txt寫配置,具體簡單配置參考上面的形式寫,復(fù)雜的配置,我們會在下面進行演示講解的。
3、接著使用命令"cmake .",這里必須是CMakeLists.txt同級目錄下執(zhí)行這個命令哈,生成makefile等文件
4、最后使用make命令進行工程編譯,進而生成可執(zhí)行文件。
3、同一個目錄有多個源文件:
這里我們在當(dāng)前目錄再添加兩個文件:test1.c和test1.h。test1.c文件內(nèi)容如下:
#include?
#include?"test1.h"
void?func(int?a)
{
????printf("a=%d\n",a);
}
test1.h里面的內(nèi)容如下:
#ifndef?_TEST1_H
#define?_TEST1_H
void?func(int?a);
#endif?/*?_TEST1_H?*/
然后在main。c里面進行調(diào)用func()這個函數(shù):
#include?
#include?"test1.h"
int?main(void)
{
????func(6);
????printf("TXP嵌入式\n");
????return?0;
}
然后這個時候我們的CMakeLists.txt里面書寫形式肯定要改了:
cmake_minimum_required?(VERSION?2.8)
project?(main)
add_executable(main?main.c?test1.c)
最終結(jié)果如下:
root@txp-virtual-machine:/home/txp/test#?cmake?.
--?Configuring?done
--?Generating?done
--?Build?files?have?been?written?to:?/home/txp/test
root@txp-virtual-machine:/home/txp/test#?make
Scanning?dependencies?of?target?main
[?50%]?Building?C?object?CMakeFiles/main.dir/main.c.o
[100%]?Building?C?object?CMakeFiles/main.dir/test1.c.o
Linking?C?executable?main
[100%]?Built?target?main
root@txp-virtual-machine:/home/txp/test#?ls
1??CMakeCache.txt??CMakeFiles??cmake_install.cmake??CMakeLists.txt??main??main.c??Makefile??test1.c??test1.h
root@txp-virtual-machine:/home/txp/test#?./main
a=6
TXP嵌入式
小結(jié):
上面的CMakeLists.txt的寫法,你會注意到add_executable(main main.c test1.c)多了一個源文件(你可以把源碼看作成加工的原材料,在makefile里面也是這樣來理解目標(biāo)文件的生成!),所以如果你還要往當(dāng)前目錄下添加源文件的話,在書寫CMakeLists.txt文本文件時,直接在第三個語句里面添加源文件;不過這種方式有缺陷,比如說,如果當(dāng)前文件下有幾百個源文件,這個時候你不可能一個個去手寫敲吧,不然這樣就又回到了原始社會,就不能體現(xiàn)cmake的優(yōu)越性出來了!更多用法,且聽下回細解!
二、用好 Cmake,高興一整天(甚至...):
1、多個源文件,使用命令 aux_source_directory(dir var):
在上一篇文章最后結(jié)尾的時候,有一個問題,就是在同一目錄下面,有多個源文件的時候,這個時候你不能都往下面第三條命令里面一直手動添加源文件,那工作效率多低啊:
cmake_minimum_required(VERSION?2.8)
project(main)
add_executable(main?main.c?test1.c)
于是乎為了解決這種低效率的操作,在 cmake 里面有一條指令可以完全搞定這個問題;不過為了說明問題,在這之前我又添加了兩個文件:test2.c 和 test2.h:
root@txp-virtual-machine:/home/txp/test#?ls
1???????????????cmake_install.cmake??main.c????test1.h??touch1.c
CMakeCache.txt??CMakeLists.txt???????Makefile??test2.c??touch1.h
CMakeFiles??????main?????????????????test1.c???test2.h
test2.c內(nèi)容如下:
#include?
#include?"test2.h"
void?func1()
{
??printf("i?like?the?cmake\n");
}
test2.h內(nèi)容如下:
#ifndef?_TEST2_H_
#define?_TEST2_H_
void?func1();
#endif
最后main.c里面調(diào)用了func1函數(shù):
#include?
#include?"test1.h"
#include?"test2.h"
int?main(void)
{
????func1();
????func(8);
????printf("TXP嵌入式\n");
????return?0;
}
接下來我們的重點就來了,在cmake里面可以使用aux_source_directory(dir var)就可以搞定上面效率低的問題,接下來我們在CMakeLists.txt這樣操作:
cmake_minimum_required(VERSION?2.8)
project(main)
aux_source_directory(.?SRC_LIST)
add_executable(main?${SRC_LIST})
然后再進行編譯:
root@txp-virtual-machine:/home/txp/test#?cmake?.
--?Configuring?done
--?Generating?done
--?Build?files?have?been?written?to:?/home/txp/test
root@txp-virtual-machine:/home/txp/test#?make
Scanning?dependencies?of?target?main
[?25%]?Building?C?object?CMakeFiles/main.dir/main.c.o
[?50%]?Linking?C?executable?main
root@txp-virtual-machine:/home/txp/test#?./main
i?like?the?cmake
the?b?is?8
TXP嵌入式
說明:
aux_source_directory(. SRC_LIST):表示是把當(dāng)當(dāng)前目錄下的所有源文件都添加到源列表變量里面去,最后用add_executable(main ${SRC_LIST})把所有有用的源文件加工成目標(biāo)文件main。不過這方法也有他的缺點,就是把當(dāng)前目錄下的源文件都添加到變量SRC_LIST,如果我們不需要一些沒有用的文件(只要拿到所需的源文件就行),可以進行這樣操作:
cmake_minimum_required(VERSION?2.8)
project(main)
set(SRC_LIST
????????./main.c
????????./test1.c
????????./test2.c
?????????)
add_executable(main?${SRC_LIST})
這樣是能夠通過編譯的:
root@txp-virtual-machine:/home/txp/test#?cmake?.
--?Configuring?done
--?Generating?done
--?Build?files?have?been?written?to:?/home/txp/test
root@txp-virtual-machine:/home/txp/test#?make
[100%]?Built?target?main
2、在上面的例子中,我們會發(fā)現(xiàn)同一目錄下源文件比較亂,所以在cmake里面有這樣的規(guī)則,可以把相同類型以及相關(guān)的源文件放到同一個目錄,比如說,現(xiàn)在我在test目錄下創(chuàng)建test1和test2兩個目錄文件,并同時把test1.c、test1.h、test2.c、test2.h分別放到這兩個目錄下去:
root@txp-virtual-machine:/home/txp/test#?mkdir?-p?test1?test2
root@txp-virtual-machine:/home/txp/test#?ls
@???????????????CMakeFiles???????????main??????test1????test2
1???????????????cmake_install.cmake??main.c????test1.c??test2.c
CMakeCache.txt??CMakeLists.txt???????Makefile??test1.h??test2.h
然后把相關(guān)文件一到這兩個目錄文件下去:
root@txp-virtual-machine:/home/txp/test#?mv?test1.c?test1.h?test1
root@txp-virtual-machine:/home/txp/test#?mv?test2.c?test2.h?test2
root@txp-virtual-machine:/home/txp/test#?ls
@??CMakeCache.txt??cmake_install.cmake??main????Makefile??test2
1??CMakeFiles??????CMakeLists.txt???????main.c??test1
root@txp-virtual-machine:/home/txp/test#?tree
├──?cmake_install.cmake
├──?CMakeLists.txt
├──?main
├──?main.c
├──?Makefile
├──?test1
│???├──?test1.c
│???└──?test1.h
└──?test2
????├──?test2.c
????└──?test2.h
然后這個時候要修改CMakeLists.txt里面的規(guī)則屬性了:
cmake_minimum_required(VERSION?2.8)
project(main)
include_directories(test1?test2)
aux_source_directory(test1?SRC_LIST)
aux_source_directory(test2?SRC_LIST1)
add_executable(main?main.c??${SRC_LIST}?${SRC_LIST1})
然后編譯輸出,也是能夠通過的:
root@txp-virtual-machine:/home/txp/test#?cmake?.
--?Configuring?done
--?Generating?done
--?Build?files?have?been?written?to:?/home/txp/test
root@txp-virtual-machine:/home/txp/test#?make
Scanning?dependencies?of?target?main
[?25%]?Building?C?object?CMakeFiles/main.dir/main.c.o
[?50%]?Building?C?object?CMakeFiles/main.dir/test1/test1.c.o
[?75%]?Building?C?object?CMakeFiles/main.dir/test2/test2.c.o
[100%]?Linking?C?executable?main
[100%]?Built?target?main
root@txp-virtual-machine:/home/txp/test#?ls
@??CMakeCache.txt??cmake_install.cmake??main????Makefile??test2
1??CMakeFiles??????CMakeLists.txt???????main.c??test1
root@txp-virtual-machine:/home/txp/test#?./main
i?like?the?cmake
the?b?is?8
TXP嵌入式
說明:
這里出現(xiàn)了一個新的命令:include_directories。該命令是用來向工程添加多個指定頭文件的搜索路徑,路徑之間用空格分隔。
其實在實際開發(fā)工程中,一般會把源文件放到src目錄下,把頭文件放入到include文件下,生成的對象文件放入到build目錄下,最終輸出的elf文件會放到bin目錄下,這樣讓人看起來一目了然。
三、Cmake中添加鏈接庫文件:
在上一篇文Cmake文章里面,我們同樣在文章的最后面留了一個問題實現(xiàn),就是把源文件放到src目錄下,把頭文件放到include目錄下去,這樣也比較符合別人和自己日后去配置工程(一看到這兩個目就能知道啥意思了,清晰明了),同時在linux環(huán)境下生成的elf文件放到bin目錄下;不過在文章發(fā)出去了幾天,后面有網(wǎng)友又有提出了一些新的需求:
(如果網(wǎng)友有啥實際需要,可以私聊我,只要在我自身能力之內(nèi),我都可以寫成文章出來分享給大家)熟悉我的網(wǎng)友都知道,我也是小白,會從很基礎(chǔ)的東西開始分享開始,雖然都是比較理論化的東西,但是都是點滴的積累(有的時候,其實你真正在有些項目開發(fā)過程中,學(xué)到的東西不是很多,更多的是依靠平時的基礎(chǔ)積累加以擴展,所以總的來說,平時的折騰還是非常值得!);同時有啥比較實際一點的需求咋也慢慢深入,一步步來,穩(wěn)扎穩(wěn)打,知識性的東西來不得半點虛假和馬虎。好了,開始進入主題分享了:
一、src、include、bin目錄的使用(更加正規(guī)化):
1、先開始創(chuàng)建這三個目錄結(jié)構(gòu),并把相應(yīng)的文件放入進去:
root@txp-virtual-machine:/home/txp/testmy#?mkdir?bin?build?src?include
root@txp-virtual-machine:/home/txp/testmy#?ls
bin??build??include??src
include目錄下文件放入(這里test1.h和test2.h的內(nèi)容是接續(xù)前面的文章里面的內(nèi)容,這里我就不再造輪子了):
root@txp-virtual-machine:/home/txp/testmy/include#?ls
test1.h??test2.h
src目錄下文件放入(這里test1.c和test2.c的內(nèi)容是接續(xù)前面的文章里面的內(nèi)容,這里我就不再造輪子了):
root@txp-virtual-machine:/home/txp/testmy/src#?ls
main.c??test1.c??test2.c
最終我們還要在testmy目錄和src目錄下都創(chuàng)建一個CMakeLists.txt:
/*testmy目錄下的CMakeLists.txt內(nèi)容:*/
cmake_minimum_required(VERSION?2.8)
project(main)
add_subdirectory(src)
/*src目下CMakeLists.txt內(nèi)容:*/
aux_source_directory(.?SRC_LIST)
include_directories(../include)
add_executable(main?${SRC_LIST})
set?(EXECUTABLE_OUTPUT_PATH?${PROJECT_SOURCE_DIR}/bin)
上面第一個CMakeLists.txt里面陌生的語句解釋:
add_subdirectory(src)意思是可以向當(dāng)前工程添加存放源文件的子目錄,并可以指定中間二進制和目標(biāo)二進制的存放位置(subdirectory字母就是子目錄的意思,所以意思是:這里指定src目錄下存放了源文件,當(dāng)執(zhí)行cmake時,就會進入src目錄下去找src目錄下的CMakeLists.txt,所以在src目錄下也建立一個CMakeLists.txt),官方用法是這樣的(不過這里暫時沒去深究):
add_subdirectory
----------------
Add?a?subdirectory?to?the?build.
::
??add_subdirectory(source_dir?[binary_dir]
???????????????????[EXCLUDE_FROM_ALL])
Add?a?subdirectory?to?the?build.??The?source_dir?specifies?the
directory?in?which?the?source?CMakeLists.txt?and?code?files?are
located.??If?it?is?a?relative?path?it?will?be?evaluated?with?respect
to?the?current?directory?(the?typical?usage),?but?it?may?also?be?an
absolute?path.??The?binary_dir?specifies?the?directory?in?which?to
place?the?output?files.??If?it?is?a?relative?path?it?will?be?evaluated
with?respect?to?the?current?output?directory,?but?it?may?also?be?an
absolute?path.??If?binary_dir?is?not?specified,?the?value?of
source_dir,?before?expanding?any?relative?path,?will?be?used?(the
typical?usage).??The?CMakeLists.txt?file?in?the?specified?source
directory?will?be?processed?immediately?by?CMake?before?processing?in
the?current?input?file?continues?beyond?this?command.
If?the?EXCLUDE_FROM_ALL?argument?is?provided?then?targets?in?the
subdirectory?will?not?be?included?in?the?ALL?target?of?the?parent
directory?by?default,?and?will?be?excluded?from?IDE?project?files.
Users?must?explicitly?build?targets?in?the?subdirectory.??This?is
meant?for?use?when?the?subdirectory?contains?a?separate?part?of?the
project?that?is?useful?but?not?necessary,?such?as?a?set?of?examples.
Typically?the?subdirectory?should?contain?its?own?project()?command
invocation?so?that?a?full?build?system?will?be?generated?in?the
subdirectory?(such?as?a?VS?IDE?solution?file).??Note?that?inter-target
dependencies?supercede?this?exclusion.??If?a?target?built?by?the
parent?project?depends?on?a?target?in?the?subdirectory,?the?dependee
target?will?be?included?in?the?parent?project?build?system?to?satisfy
the?dependency.
第二個CMakeLists.txt內(nèi)容分析:
aux_source_directory (. SRC_LIST):把當(dāng)前目錄的源文件:main.c test1.c ?test2.c都放到變量SRC_LIST里面去。
include_directories (../include):把include目錄的頭文件包含進來。
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin):這里面的EXECUTABLE_OUT_PATH和PROJECT_SOURCE_DIR是CMake自帶的預(yù)定義變量,同時他們的作用分別如下:
?EXECUTABLE_OUTPUT_PATH :目標(biāo)二進制可執(zhí)行文件的存放位置
PROJECT_SOURCE_DIR:工程的根目錄
所以最終生成的elf文件(也就是我們的最終可執(zhí)行文件)就會放到bin目錄下,然后我們build目錄下會成一些配置中間文件。
具體步驟過程我寫出來:
root@txp-virtual-machine:/home/txp/testmy#?vim?CMakeLists.txt
root@txp-virtual-machine:/home/txp/testmy#?cd?src
root@txp-virtual-machine:/home/txp/testmy/src#?ls
main.c??test1.c??test2.c
root@txp-virtual-machine:/home/txp/testmy/src#?vim?CMakeLists.txt
最后架構(gòu)如下:
root@txp-virtual-machine:/home/txp/testmy#?tree
.
├──?bin
├──?build
├──?CMakeLists.txt
├──?include
│???├──?test1.h
│???└──?test2.h
└──?src
????├──?CMakeLists.txt
????├──?main.c
????├──?test1.c
????└──?test2.c
2、編譯運行:
root@txp-virtual-machine:/home/txp/testmy#?cd?build
root@txp-virtual-machine:/home/txp/testmy/build#?ls
root@txp-virtual-machine:/home/txp/testmy/build#?cmake?..
--?The?C?compiler?identification?is?GNU?4.8.4
--?The?CXX?compiler?identification?is?GNU?4.8.4
--?Check?for?working?C?compiler:?/usr/bin/cc
--?Check?for?working?C?compiler:?/usr/bin/cc?--?works
--?Detecting?C?compiler?ABI?info
--?Detecting?C?compiler?ABI?info?-?done
--?Check?for?working?CXX?compiler:?/usr/bin/c++
--?Check?for?working?CXX?compiler:?/usr/bin/c++?--?works
--?Detecting?CXX?compiler?ABI?info
--?Detecting?CXX?compiler?ABI?info?-?done
--?Configuring?done
--?Generating?done
--?Build?files?have?been?written?to:?/home/txp/testmy/build
root@txp-virtual-machine:/home/txp/testmy/build#?make
Scanning?dependencies?of?target?main
[?33%]?Building?C?object?src/CMakeFiles/main.dir/test2.c.o
[?66%]?Building?C?object?src/CMakeFiles/main.dir/test1.c.o
[100%]?Building?C?object?src/CMakeFiles/main.dir/main.c.o
Linking?C?executable?../../bin/main
[100%]?Built?target?main
root@txp-virtual-machine:/home/txp/testmy/build#?cd?../bin
root@txp-virtual-machine:/home/txp/testmy/bin#?ls
main
注意這里是切換到build目錄下去執(zhí)行cmake哈。?這里為啥這樣做呢,我們課可以看到在build目錄下執(zhí)行cmake .. 和make命令后,生成的一些配置文件都會在這個目錄下,不會在別的目錄下,就這樣看起來就舒服整潔多了:
root@txp-virtual-machine:/home/txp/testmy/build#?ls
CMakeCache.txt??CMakeFiles??cmake_install.cmake??Makefile??src
現(xiàn)在整個架構(gòu)如下:
root@txp-virtual-machine:/home/txp/testmy#?tree
.
├──?bin
│???└──?main
├──?build
│???├──?CMakeCache.txt
│???├──?CMakeFiles
│???│???├──?2.8.12.2
│???│???│???├──?CMakeCCompiler.cmake
│???│???│???├──?CMakeCXXCompiler.cmake
│???│???│???├──?CMakeDetermineCompilerABI_C.bin
│???│???│???├──?CMakeDetermineCompilerABI_CXX.bin
│???│???│???├──?CMakeSystem.cmake
│???│???│???├──?CompilerIdC
│???│???│???│???├──?a.out
│???│???│???│???└──?CMakeCCompilerId.c
│???│???│???└──?CompilerIdCXX
│???│???│???????├──?a.out
│???│???│???????└──?CMakeCXXCompilerId.cpp
│???│???├──?cmake.check_cache
│???│???├──?CMakeDirectoryInformation.cmake
│???│???├──?CMakeOutput.log
│???│???├──?CMakeTmp
│???│???├──?Makefile2
│???│???├──?Makefile.cmake
│???│???├──?progress.marks
│???│???└──?TargetDirectories.txt
│???├──?cmake_install.cmake
│???├──?Makefile
│???└──?src
│???????├──?CMakeFiles
│???????│???├──?CMakeDirectoryInformation.cmake
│???????│???├──?main.dir
│???????│???│???├──?build.make
│???????│???│???├──?C.includecache
│???????│???│???├──?cmake_clean.cmake
│???????│???│???├──?DependInfo.cmake
│???????│???│???├──?depend.internal
│???????│???│???├──?depend.make
│???????│???│???├──?flags.make
│???????│???│???├──?link.txt
│???????│???│???├──?main.c.o
│???????│???│???├──?progress.make
│???????│???│???├──?test1.c.o
│???????│???│???└──?test2.c.o
│???????│???└──?progress.marks
│???????├──?cmake_install.cmake
│???????└──?Makefile
├──?CMakeLists.txt
├──?include
│???├──?test1.h
│???└──?test2.h
└──?src
????├──?CMakeLists.txt
????├──?main.c
????├──?test1.c
????└──?test2.c
看一下最終執(zhí)行結(jié)果:
root@txp-virtual-machine:/home/txp/testmy/bin#?./main
i?like?the?cmake
a=8
TXP嵌入式
二、動態(tài)庫和靜態(tài)庫的學(xué)習(xí):
這個話題就回到了我們最開始那位網(wǎng)友提出的需求了;不過我們還是從簡單的開始來學(xué)習(xí),然后慢慢深入。有的時候我們只需要編譯出動態(tài)庫、靜態(tài)庫,然后給其他程序來調(diào)用,那么在cmake里面如何實現(xiàn)呢?具體如下:
注我這用到的源文件內(nèi)容(test1.c test2.c test1.h test2.h main.c都是一樣的來測試
1、為了清晰明了,這里我重新創(chuàng)建了一個目錄工程來演示:
root@txp-virtual-machine:/home/txp#?mkdir?testcmake
root@txp-virtual-machine:/home/txp/testcmake#?mkdir?build?lib?lib_test
root@txp-virtual-machine:/home/txp/testcmake#?ls
build??lib??lib_test
然后在lib_test目錄下放我們的源文件test1.c test1.h,并同時在lib_test目錄創(chuàng)建一個CMakeLists.txt;在testcmake目錄也創(chuàng)建一個CMakeLists.txt:
root@txp-virtual-machine:/home/txp/testcmake/lib_test#?ls
CMakeLists.txt??test1.c??test1.h
/*lib_test目錄下的CMakeLists.txt的內(nèi)容:
aux_source_directory(.?SRC_LIST)
add_library(test1_shared?SHARED?${SRC_LIST})
add_library(test1_static?STATIC?${SRC_LIST})
set_target_properties(test1_shared?PROPERTIES?OUTPUT_NAME?"test1")
set_target_properties(test1_static?PROPERTIES?OUTPUT_NAME?"test1")
set?(LIBRARY_OUTPUT_PATH?${PROJECT_SOURCE_DIR}/lib)
解釋說明:
add_library: 生成動態(tài)庫或靜態(tài)庫(第1個參數(shù)指定庫的名字;第2個參數(shù)決定是動態(tài)還是靜態(tài),如果沒有就默認(rèn)靜態(tài);第3個參數(shù)指定生成庫的源文件)。
set_target_properties: 設(shè)置輸出的名稱,還有其它功能,如設(shè)置庫的版本號等等。
LIBRARY_OUTPUT_PATH: 庫文件的默認(rèn)輸出路徑,這里設(shè)置為工程目錄下的lib目錄。
testcmake目錄下的CMakeLists.txt內(nèi)容:
cmake_minimum_required(VERSION?2.8)
project(main)
add_subdirectory(lib_test)
最后架構(gòu)如下:
root@txp-virtual-machine:/home/txp/testcmake#?ls
build??CMakeLists.txt??lib??lib_test
root@txp-virtual-machine:/home/txp/testcmake#?tree
.
├──?build
├──?CMakeLists.txt
├──?lib
└──?lib_test
????├──?CMakeLists.txt
????├──?test1.c
????└──?test1.h
3?directories,?4?files
2、編譯結(jié)果:
root@txp-virtual-machine:/home/txp/testcmake/build#?cmake?..
--?The?C?compiler?identification?is?GNU?4.8.4
--?The?CXX?compiler?identification?is?GNU?4.8.4
--?Check?for?working?C?compiler:?/usr/bin/cc
--?Check?for?working?C?compiler:?/usr/bin/cc?--?works
--?Detecting?C?compiler?ABI?info
--?Detecting?C?compiler?ABI?info?-?done
--?Check?for?working?CXX?compiler:?/usr/bin/c++
--?Check?for?working?CXX?compiler:?/usr/bin/c++?--?works
--?Detecting?CXX?compiler?ABI?info
--?Detecting?CXX?compiler?ABI?info?-?done
--?Configuring?done
--?Generating?done
--?Build?files?have?been?written?to:?/home/txp/testcmake/build
root@txp-virtual-machine:/home/txp/testcmake/build#?make
Scanning?dependencies?of?target?test1_shared
[?50%]?Building?C?object?lib_test/CMakeFiles/test1_shared.dir/test1.c.o
Linking?C?shared?library?../../lib/libtest1.so
[?50%]?Built?target?test1_shared
Scanning?dependencies?of?target?test1_static
[100%]?Building?C?object?lib_test/CMakeFiles/test1_static.dir/test1.c.o
Linking?C?static?library?../../lib/libtest1.a
[100%]?Built?target?test1_static
root@txp-virtual-machine:/home/txp/testcmake/build#?cd?../lib
root@txp-virtual-machine:/home/txp/testcmake/lib#?ls
libtest1.a??libtest1.so
注意編譯規(guī)則上面講過,這里不再重復(fù)
從lib目錄下我們可以看到生成了生成了靜態(tài)庫和動態(tài)庫:libtest1.a ?libtest1.so
2、對庫進行鏈接:
現(xiàn)在我們使用剛才生成的庫了。把build剛才的配置文件清空,并同時在testcmake目錄下創(chuàng)建bin和src目錄(跟剛才上面講的差不多):
root@txp-virtual-machine:/home/txp/testcmake#?mkdir?src?bin
root@txp-virtual-machine:/home/txp/testcmake#?ls
bin??build??CMakeLists.txt??lib??lib_test??src
root@txp-virtual-machine:/home/txp/testcmake#?cd?src
root@txp-virtual-machine:/home/txp/testcmake/src#?vim?main.c
并在testcmake目錄下的CMakeLists.txt內(nèi)容改成:
cmake_minimum_required(VERSION?2.8)
project(main)
add_subdirectory(lib_test)
add_subdirectory(src)
src目錄下要創(chuàng)建一個CMakeLists.txt:
aux_source_directory(.SRC_LIST)
include_directories(../lib_test)
link_directories(${PROJECT_SOURCE_DIR}/lib)
add_executable(main?${SRC_LIST})
target_link_libraries(main?test1)
set(EXECUTABLE_OUTPUT_PATH?${PROJECT_SOURCE_DIR}/bin)
解釋說明:
--link_directories: 添加非標(biāo)準(zhǔn)的共享庫搜索路徑。
--target_link_libraries: 把目標(biāo)文件與庫文件進行鏈接。
3、編譯:
root@txp-virtual-machine:/home/txp/testcmake/build#?cmake?..
--?The?C?compiler?identification?is?GNU?4.8.4
--?The?CXX?compiler?identification?is?GNU?4.8.4
--?Check?for?working?C?compiler:?/usr/bin/cc
--?Check?for?working?C?compiler:?/usr/bin/cc?--?works
--?Detecting?C?compiler?ABI?info
--?Detecting?C?compiler?ABI?info?-?done
--?Check?for?working?CXX?compiler:?/usr/bin/c++
--?Check?for?working?CXX?compiler:?/usr/bin/c++?--?works
--?Detecting?CXX?compiler?ABI?info
--?Detecting?CXX?compiler?ABI?info?-?done
--?Configuring?done
--?Generating?done
--?Build?files?have?been?written?to:?/home/txp/testcmake/build
root@txp-virtual-machine:/home/txp/testcmake/build#?make
Scanning?dependencies?of?target?test1_shared
[?33%]?Building?C?object?lib_test/CMakeFiles/test1_shared.dir/test1.c.o
Linking?C?shared?library?../../lib/libtest1.so
[?33%]?Built?target?test1_shared
Scanning?dependencies?of?target?test1_static
[?66%]?Building?C?object?lib_test/CMakeFiles/test1_static.dir/test1.c.o
Linking?C?static?library?../../lib/libtest1.a
[?66%]?Built?target?test1_static
Scanning?dependencies?of?target?main
[100%]?Building?C?object?src/CMakeFiles/main.dir/main.c.o
Linking?C?executable?../../bin/main
[100%]?Built?target?main
root@txp-virtual-machine:/home/txp/testcmake/build#?cd?../bin
root@txp-virtual-machine:/home/txp/testcmake/bin#?ls
main
root@txp-virtual-machine:/home/txp/testcmake/bin#?./main
a=6
TXP嵌入式
從上面可以執(zhí)行成功。同時注意:
我們lib下面又動態(tài)庫和靜態(tài)庫,而在我們src目錄下的CMakeLists.txt里面的target_link_libraries (main test1)默認(rèn)是使用動態(tài)庫,如果lib目錄下只有靜態(tài)庫,那么這種寫法就會去鏈接靜態(tài)庫。也可以直接指定使用動態(tài)庫還是靜態(tài)庫,寫法是:target_link_libraries (main libtest1.so)或target_link_libraries (main libtest1.a)
同時我們可以查看elf文件到底使用了哪些庫文件:
root@txp-virtual-machine:/home/txp/testcmake/bin#?readelf?-d?./main
Dynamic?section?at?offset?0xe08?contains?26?entries:
??Tag????????Type?????????????????????????Name/Value
?0x0000000000000001?(NEEDED)?????????????Shared?library:?[libtest1.so]
?0x0000000000000001?(NEEDED)?????????????Shared?library:?[libc.so.6]
?0x000000000000000f?(RPATH)??????????????Library?rpath:?[/home/txp/testcmake/lib]
?0x000000000000000c?(INIT)???????????????0x4006a0
?0x000000000000000d?(FINI)???????????????0x400894
?0x0000000000000019?(INIT_ARRAY)?????????0x600df0
?0x000000000000001b?(INIT_ARRAYSZ)???????8?(bytes)
?0x000000000000001a?(FINI_ARRAY)?????????0x600df8
?0x000000000000001c?(FINI_ARRAYSZ)???????8?(bytes)
?0x000000006ffffef5?(GNU_HASH)???????????0x400298
?0x0000000000000005?(STRTAB)?????????????0x4004d8
?0x0000000000000006?(SYMTAB)?????????????0x4002f8
?0x000000000000000a?(STRSZ)??????????????260?(bytes)
?0x000000000000000b?(SYMENT)?????????????24?(bytes)
?0x0000000000000015?(DEBUG)??????????????0x0
?0x0000000000000003?(PLTGOT)?????????????0x601000
?0x0000000000000002?(PLTRELSZ)???????????96?(bytes)
?0x0000000000000014?(PLTREL)?????????????RELA
?0x0000000000000017?(JMPREL)?????????????0x400640
?0x0000000000000007?(RELA)???????????????0x400628
?0x0000000000000008?(RELASZ)?????????????24?(bytes)
?0x0000000000000009?(RELAENT)????????????24?(bytes)
?0x000000006ffffffe?(VERNEED)????????????0x400608
?0x000000006fffffff?(VERNEEDNUM)?????????1
?0x000000006ffffff0?(VERSYM)?????????????0x4005dc
?0x0000000000000000?(NULL)???????????????0x0
三、小結(jié):
這里還有一個網(wǎng)友的提的那個實現(xiàn),現(xiàn)在我們知道了怎么去使用動態(tài)庫和靜態(tài)庫就好辦了;限于篇幅原因,這個測試留在下篇cmake文章里面來做實驗看看到底能不能實現(xiàn)(這里我暫時還沒做實驗,等我做完實驗就會分享出來,這篇文章分享花的時間比較久,大家先暫時消化一下)
推薦閱讀:嵌入式編程專輯 Linux 學(xué)習(xí)專輯 C/C++編程專輯 長按前往圖中包含的公眾號關(guān)注
