<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          cmake基礎(chǔ)示例:如何編譯【跨平臺(tái)】的動(dòng)態(tài)庫(kù)和應(yīng)用程序

          共 6578字,需瀏覽 14分鐘

           ·

          2021-12-27 22:21

          作 ?者:道哥,10+年嵌入式開發(fā)老兵,專注于:C/C++、嵌入式、Linux。

          關(guān)注下方公眾號(hào),回復(fù)【書籍】,獲取 Linux、嵌入式領(lǐng)域經(jīng)典書籍;回復(fù)【PDF】,獲取所有原創(chuàng)文章( PDF 格式)。

          目錄

          • 示例代碼

            • mylib

            • myapp

          • Linux 下構(gòu)建過程

            • cmake 配置

            • make 編譯

            • 編譯、執(zhí)行

          • Windows 下構(gòu)建過程

            • cmake cofigure

            • build

            • 調(diào)試

          別人的經(jīng)驗(yàn),我們的階梯!

          大家好,我是道哥,今天我為大伙兒解說的技術(shù)知識(shí)點(diǎn)是:【使用 cmake 來構(gòu)建跨平臺(tái)的動(dòng)態(tài)庫(kù)和應(yīng)用程序】

          在很久之前,曾經(jīng)在B站上傳過幾個(gè)小視頻,介紹了在WindowsLinux這兩個(gè)平臺(tái)下,如何通過cmakemake這兩個(gè)構(gòu)建工具,來編譯、鏈接動(dòng)態(tài)庫(kù)、靜態(tài)庫(kù)以及可執(zhí)行程序。

          視頻中的示例代碼是提前寫好的,因此重點(diǎn)就放在構(gòu)建(Build)環(huán)節(jié)了。主要是介紹了動(dòng)態(tài)庫(kù)與動(dòng)態(tài)庫(kù)之間、應(yīng)用程序與動(dòng)態(tài)庫(kù)之間的引用等等。

          對(duì)動(dòng)態(tài)庫(kù)、靜態(tài)庫(kù)比較熟悉的小伙伴,應(yīng)該很容易就能理解其中的內(nèi)容。但是對(duì) C 語(yǔ)言不熟悉的朋友,看起來還是有一點(diǎn)點(diǎn)障礙。

          這篇文章,主要是把視頻中的示例代碼進(jìn)行簡(jiǎn)化,只使用一個(gè)動(dòng)態(tài)庫(kù)和一個(gè)可執(zhí)行文件,使用cmake構(gòu)建工具,演示在 Windows 和 Linux 這兩個(gè)平臺(tái)下的構(gòu)建過程

          本文的內(nèi)容很基礎(chǔ),算是使用 cmake 來構(gòu)建跨平臺(tái)程序的入門教程吧!

          示例代碼

          首先看一下測(cè)試代碼的全貌

          1. mylib:只有一個(gè)源文件,編譯輸出一個(gè)動(dòng)態(tài)庫(kù);

          2. myapp:也只有一個(gè)源文件,鏈接 mylib 動(dòng)態(tài)庫(kù),編譯輸出一個(gè)可執(zhí)行程序;

          mylib

          mylib目錄中,一共有3個(gè)文件:mylib.h, mylib.c 以及 CMakeLists.txt,內(nèi)容分別如下:

          // mylib/mylib.h w文件

          #ifndef _MY_LIB_
          #define _MY_LIB_

          #ifdef MY_LINUX
          #define MYLIB_API extern
          #else
          #ifdef MYLIB_EXPORT
          #define MYLIB_API __declspec(dllexport)
          #else
          #define MYLIB_API __declspec(dllimport)
          #endif
          #endif

          MYLIB_API int my_add(int num1, int num2);
          MYLIB_API int my_sub(int num1, int num2);

          #endif // _MY_LIB_

          以上這個(gè)代碼,主要是用在Windows系統(tǒng)的動(dòng)態(tài)導(dǎo)出庫(kù),在 Linux 系統(tǒng)中,不是必要的

          補(bǔ)充:在 windows 系統(tǒng)中,編譯動(dòng)態(tài)庫(kù)時(shí)會(huì)生成 xxx.dll 和 xxx.lib。xxx.dll 中是真正的庫(kù)文件指令,xxx.lib 中僅僅是符號(hào)表。

          具體來說:在 Windows 系統(tǒng)中,當(dāng)編譯動(dòng)態(tài)庫(kù)的時(shí)候,打開(定義)宏 MYLIB_EXPORT,下面這個(gè)宏生效:

          #define MYLIB_API	__declspec(dllexport)

          這樣的話,兩個(gè)函數(shù) my_addmy_sub 的符號(hào)才可能被導(dǎo)出到 mylib.lib 文件中。

          當(dāng)這個(gè)動(dòng)態(tài)庫(kù)被應(yīng)用程序(myapp)使用的時(shí)候,myapp.c include mylib.h 的時(shí),關(guān)閉MYLIB_EXPORT,此時(shí)下面這個(gè)宏就生效:

          #define MYLIB_API	__declspec(dllimport)

          為了簡(jiǎn)化宏定義的復(fù)雜度,這里就不考慮靜態(tài)庫(kù)了。

          看完了頭文件,再來看看源文件mylib.c

          // mylib/mylib.c 文件

          #include "mylib.h"

          int my_add(int num1, int num2)
          {
          return (num1 + num2);
          }

          int my_sub(int num1, int num2)
          {
          return (num1 - num2);
          }

          最后再來看一下mylib/CMakeLists.txt文件:

          // mylib/CMakeLists.txt 文件

          CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
          PROJECT(mylib VERSION 1.0.0)

          # 自定義宏,代碼中可以使用
          ADD_DEFINITIONS(-DMYLIB_EXPORT)

          # 頭文件
          INCLUDE_DIRECTORIES(./)

          # 源文件
          FILE(GLOB MYLIB_SRCS "*.c")

          # 編譯目標(biāo)
          ADD_LIBRARY(${PROJECT_NAME} SHARED ${MYLIB_SRCS})

          關(guān)于cmake的語(yǔ)法就不多說了,這里只用到了其中很少的一部分。

          注意其中的一點(diǎn):ADD_DEFINITIONS(-DMYLIB_EXPORT),因?yàn)檫@個(gè)CMakeLists.txt是用來編譯動(dòng)態(tài)庫(kù)的,因此在Windows平臺(tái)下,每一個(gè)導(dǎo)出符號(hào)的前面需要加上 __declspec(dllexport),因此需要打開宏定義:MYLIB_EXPORT

          myapp

          應(yīng)用程序的代碼就更簡(jiǎn)單了,只有兩個(gè)文件:myapp.c 和 CMakeLists.txt,內(nèi)容如下:

          // myapp/myapp.c 文件

          #include
          #include

          #include "mylib.h"

          int main(int argc, char *argv[])
          {
          int ret1, ret2;
          int a = 5;
          int b = 2;

          ret1 = my_add(a, b);
          ret2 = my_sub(a, b);
          printf("ret1 = %d \n", ret1);
          printf("ret2 = %d \n", ret2);
          getchar();
          return 0;
          }

          HelloWorld級(jí)別的代碼,不需要多解釋!CMakeLists.txt內(nèi)容如下:

          // myapp/CMakeLists.txt 文件

          CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
          PROJECT(myapp VERSION 1.0.0)

          # 頭文件路徑
          INCLUDE_DIRECTORIES(./include)

          # 庫(kù)文件路徑
          LINK_DIRECTORIES(./lib)

          # 源文件
          FILE(GLOB MYAPP_SRCS "*.c")

          # 編譯目標(biāo)
          ADD_EXECUTABLE(${PROJECT_NAME} ${MYAPP_SRCS})

          # 依賴的動(dòng)態(tài)庫(kù)
          TARGET_LINK_LIBRARIES(${PROJECT_NAME} mylib)

          最后一行 TARGET_LINK_LIBRARIES(${PROJECT_NAME} mylib) 說明要鏈接mylib這個(gè)動(dòng)態(tài)庫(kù)。

          那么到哪個(gè)目錄下去查找相應(yīng)的頭文件和庫(kù)文件呢?

          通過這兩行來指定查找目錄:

          # 頭文件路徑
          INCLUDE_DIRECTORIES(./include)

          # 庫(kù)文件路徑
          LINK_DIRECTORIES(./lib)

          這個(gè)兩個(gè)目錄暫時(shí)還不存在,待會(huì)編譯的時(shí)候我們?cè)偈謩?dòng)創(chuàng)建。

          可以讓 mylib 在編譯時(shí)的輸出文件,自動(dòng)拷貝到指定的目錄。但是為了不把問題復(fù)雜化,某些操作步驟通過手動(dòng)操作來完成,這樣也能更清楚的理解其中的鏈接過程。

          最后就剩下最外層的CMakeLists.txt文件了:

          CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
          PROJECT(cmake_demo VERSION 1.0.0)

          SET(CMAKE_C_STANDARD 99)

          # 自定義宏,代碼中可以使用
          if (CMAKE_HOST_UNIX)
          ADD_DEFINITIONS(-DMY_LINUX)
          else ()
          ADD_DEFINITIONS(-DMY_WINDOWS)
          endif()

          ADD_SUBDIRECTORY(mylib)
          ADD_SUBDIRECTORY(myapp)

          它所做的主要工作就是:根據(jù)不同的平臺(tái),定義相應(yīng)的宏,并且添加了mylibmyapp這兩個(gè)子文件夾。

          Linux 下構(gòu)建過程

          cmake 配置

          為了不污染源文件目錄,在最外層目錄下新建build目錄,然后執(zhí)行cmake指令:

          $ cd ~/tmp/cmake_demo/
          $ mkdir build
          $ cd build/
          $ ls
          $ cmake ..

          此時(shí),在build目錄下,產(chǎn)生如下文件:

          CMakeCache.txt  CMakeFiles  cmake_install.cmake  Makefile  myapp  mylib

          make 編譯

          我們可以分別進(jìn)入mylibmyapp目錄,執(zhí)行make指令來單獨(dú)編譯,也可以直接在build目錄下編譯所有的目標(biāo)。

          現(xiàn)在就直接在build目錄下編譯所有目標(biāo):

          $ cd ~/tmp/cmake_demo/build
          $ make
          Scanning dependencies of target mylib
          [ 25%] Building C object mylib/CMakeFiles/mylib.dir/mylib.c.o
          [ 50%] Linking C shared library libmylib.so
          [ 50%] Built target mylib
          Scanning dependencies of target myapp
          [ 75%] Building C object myapp/CMakeFiles/myapp.dir/myapp.c.o
          ~/tmp/cmake_demo/myapp/myapp.c:4:19: fatal error: mylib.h: 沒有那個(gè)文件或目錄
          #include "mylib.h"
          ^
          compilation terminated.
          myapp/CMakeFiles/myapp.dir/build.make:62: recipe for target 'myapp/CMakeFiles/myapp.dir/myapp.c.o' failed
          make[2]: *** [myapp/CMakeFiles/myapp.dir/myapp.c.o] Error 1
          CMakeFiles/Makefile2:140: recipe for target 'myapp/CMakeFiles/myapp.dir/all' failed
          make[1]: *** [myapp/CMakeFiles/myapp.dir/all] Error 2
          Makefile:83: recipe for target 'all' failed
          make: *** [all] Error 2

          從提示信息中看出:已經(jīng)編譯生成了 ./mylib/libmylib.so 文件,但是在編譯可執(zhí)行程序 myapp 時(shí)遇到了錯(cuò)誤:找不到 mylib.h 文件!

          在剛才介紹myapp/CMakeLists.txt文件時(shí)說到:應(yīng)用程序查找頭文件的目錄是 myapp/include, 查找?guī)煳募哪夸浭?span style="color:LightSeaGreen;"> myapp/lib。

          但是這2個(gè)目錄以及相應(yīng)的頭文件、庫(kù)文件都不存在!

          因此我們需要手動(dòng)創(chuàng)建,并且把頭文件mylib.h和庫(kù)文件libmylib.so拷貝進(jìn)去,操作過程如下:

          $ cd ~/tmp/cmake_demo/myapp/
          $ mkdir include lib
          $ cp ~/tmp/cmake_demo/mylib/mylib.h ./include/
          $ cp ~/tmp/cmake_demo/build/mylib/libmylib.so ./lib/

          注意:剛才編譯生成的庫(kù)文件libmylib.so是在build目錄下。

          準(zhǔn)備好頭文件和庫(kù)文件之后,再次編譯一下:

          $ cd ~/tmp/cmake_demo/build/
          $ make
          [ 50%] Built target mylib
          [ 75%] Building C object myapp/CMakeFiles/myapp.dir/myapp.c.o
          [100%] Linking C executable myapp
          [100%] Built target myapp

          此時(shí),就在 build/myapp 目錄下生成可執(zhí)行文件myapp了。

          測(cè)試、執(zhí)行

          $ cd ~/tmp/cmake_demo/build/myapp
          $ ./myapp
          ret1 = 7
          ret2 = 3

          完美!

          由于我們是在build目錄下編譯的,編譯過程中所有的輸出和中間文件,都放在build目錄下,一點(diǎn)都沒有污染源文件。

          Windows 下構(gòu)建過程

          Linux系統(tǒng)中的build文件夾刪除,然后把測(cè)試代碼壓縮,復(fù)制到Windows系統(tǒng)中繼續(xù)測(cè)試。

          Windows下編譯,一般就很少使用命令行了,大部分都使用VS或者VSCode來編譯。

          打開 VSCode,然后打開測(cè)試代碼文件夾 cmake_demo:

          因?yàn)樾枰褂?code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">cmake工具來構(gòu)建,所以需要在VSCode安裝 cmake 插件。(如何安裝 VSCode 插件就不贅述了)

          第一步: cmake 配置

          按下鍵盤 ctrl + shift + p,在命令窗口中選擇 Cmake: Configure,如果沒看到這個(gè)選項(xiàng),就手動(dòng)輸入前面的幾個(gè)字符,然后就可以智能匹配到:

          第一次 Configure 的時(shí)候,會(huì)彈出下面的選項(xiàng),來選擇編譯器:

          我們這里選擇 64 位的 amd64

          配置的結(jié)果輸出在最下面窗口中的output標(biāo)簽中,如下所示:

          這就表明cmake配置成功,正確的執(zhí)行了每一個(gè)文件夾下的 CMakeLists.txt 文件。

          這個(gè)時(shí)候,來看一下資源管理器中有啥變化:自動(dòng)生成了 build 目錄,其中的文件如下:

          看來,流程與Linux系統(tǒng)中都是一樣的,只不過這里是VSCode主動(dòng)幫我們做了一些事情。

          第二步: 編譯

          配置之后,下一步就是編譯了。

          按下 shift + F7,或者單擊VSCode底部的 Build 圖標(biāo)

          彈出編譯目標(biāo)列表

          這里選擇 ALL_BUILD,也就是編譯所有的目標(biāo):mylib 和 myapp,輸出如下:

          來看一下編譯的輸出文件:

          mylib.dll 就是編譯得到的動(dòng)態(tài)鏈接庫(kù),mylib.lib是導(dǎo)入符號(hào)。

          myapp.exe 是編譯得到的可執(zhí)行程序。

          第三步: 執(zhí)行

          我們先在命令行窗口中執(zhí)行一下myapp.exe

          提示錯(cuò)誤:找不到動(dòng)態(tài)鏈接庫(kù)!

          手動(dòng)把mylib.dll拷貝到myuapp.exe同一個(gè)目錄下,然后再執(zhí)行一次 myapp.exe:

          完美!

          但是,既然已經(jīng)用VSCode來編譯了,那就繼續(xù)在VSCode中進(jìn)行代碼調(diào)試吧。

          按下調(diào)試快捷鍵 F5,第一次會(huì)彈出調(diào)試器選擇項(xiàng):

          選擇 LLDB,然后彈出錯(cuò)誤對(duì)話框:

          因?yàn)槲覀儧]有提供相應(yīng)的配置文件來告訴VSCode調(diào)試哪一個(gè)可執(zhí)行程序。

          單擊[OK]之后,VSCode 會(huì)自動(dòng)為我們生成 .vscode/launcher.json 文件,內(nèi)容如下:

          把其中的program項(xiàng)目,改成可執(zhí)行程序的全路徑:

          "program": "F:/tmp/cmake_demo/build/myapp/Debug/myapp.exe"

          然后再次按下F5鍵,這回終于可以正確執(zhí)行了:

          此時(shí),就可以在mylib.c或者myapp.c中設(shè)置斷點(diǎn),然后進(jìn)行單步調(diào)試程序了:


          ------ End ------

          推薦閱讀

          【1】《Linux 從頭學(xué)》系列文章

          【2】C語(yǔ)言指針-從底層原理到花式技巧,用圖文和代碼幫你講解透徹

          【3】原來gdb的底層調(diào)試原理這么簡(jiǎn)單

          【4】?jī)?nèi)聯(lián)匯編很可怕嗎?看完這篇文章,終結(jié)它!

          其他系列專輯:精選文章、應(yīng)用程序設(shè)計(jì)、物聯(lián)網(wǎng)、 C語(yǔ)言。

          星標(biāo)公眾號(hào),第一時(shí)間看文章!


          瀏覽 90
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人影片黄色A片 | 国产成人电影久久 | 男女啪啪免费网站 | 欧美成人免费观看视频 | 色女孩国产视频 |