1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        cmake基礎示例:如何編譯【跨平臺】的動態(tài)庫和應用程序

        共 6578字,需瀏覽 14分鐘

         ·

        2021-12-27 22:21

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

        關注下方公眾號,回復【書籍】,獲取 Linux、嵌入式領域經典書籍;回復【PDF】,獲取所有原創(chuàng)文章( PDF 格式)。

        目錄

        • 示例代碼

          • mylib

          • myapp

        • Linux 下構建過程

          • cmake 配置

          • make 編譯

          • 編譯、執(zhí)行

        • Windows 下構建過程

          • cmake cofigure

          • build

          • 調試

        別人的經驗,我們的階梯!

        大家好,我是道哥,今天我為大伙兒解說的技術知識點是:【使用 cmake 來構建跨平臺的動態(tài)庫和應用程序】

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

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

        對動態(tài)庫、靜態(tài)庫比較熟悉的小伙伴,應該很容易就能理解其中的內容。但是對 C 語言不熟悉的朋友,看起來還是有一點點障礙。

        這篇文章,主要是把視頻中的示例代碼進行簡化,只使用一個動態(tài)庫和一個可執(zhí)行文件,使用cmake構建工具,演示在 Windows 和 Linux 這兩個平臺下的構建過程

        本文的內容很基礎,算是使用 cmake 來構建跨平臺程序的入門教程吧!

        示例代碼

        首先看一下測試代碼的全貌

        1. mylib:只有一個源文件,編譯輸出一個動態(tài)庫;

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

        mylib

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

        // 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_

        以上這個代碼,主要是用在Windows系統(tǒng)的動態(tài)導出庫,在 Linux 系統(tǒng)中,不是必要的。

        補充:在 windows 系統(tǒng)中,編譯動態(tài)庫時會生成 xxx.dll 和 xxx.lib。xxx.dll 中是真正的庫文件指令,xxx.lib 中僅僅是符號表。

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

        #define MYLIB_API	__declspec(dllexport)

        這樣的話,兩個函數 my_addmy_sub 的符號才可能被導出到 mylib.lib 文件中

        當這個動態(tài)庫被應用程序(myapp)使用的時候,myapp.c include mylib.h 的時,關閉MYLIB_EXPORT,此時下面這個宏就生效:

        #define MYLIB_API	__declspec(dllimport)

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

        看完了頭文件,再來看看源文件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")

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

        關于cmake的語法就不多說了,這里只用到了其中很少的一部分。

        注意其中的一點:ADD_DEFINITIONS(-DMYLIB_EXPORT),因為這個CMakeLists.txt是用來編譯動態(tài)庫的,因此在Windows平臺下,每一個導出符號的前面需要加上 __declspec(dllexport),因此需要打開宏定義:MYLIB_EXPORT。

        myapp

        應用程序的代碼就更簡單了,只有兩個文件:myapp.c 和 CMakeLists.txt,內容如下:

        // 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級別的代碼,不需要多解釋!CMakeLists.txt內容如下:

        // myapp/CMakeLists.txt 文件

        CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
        PROJECT(myapp VERSION 1.0.0)

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

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

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

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

        # 依賴的動態(tài)庫
        TARGET_LINK_LIBRARIES(${PROJECT_NAME} mylib)

        最后一行 TARGET_LINK_LIBRARIES(${PROJECT_NAME} mylib) 說明要鏈接mylib這個動態(tài)庫。

        那么到哪個目錄下去查找相應的頭文件和庫文件呢?

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

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

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

        這個兩個目錄暫時還不存在,待會編譯的時候我們再手動創(chuàng)建。

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

        最后就剩下最外層的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)

        它所做的主要工作就是:根據不同的平臺,定義相應的宏,并且添加了mylibmyapp這兩個子文件夾。

        Linux 下構建過程

        cmake 配置

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

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

        此時,在build目錄下,產生如下文件:

        CMakeCache.txt  CMakeFiles  cmake_install.cmake  Makefile  myapp  mylib

        make 編譯

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

        現在就直接在build目錄下編譯所有目標:

        $ 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: 沒有那個文件或目錄
        #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

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

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

        但是這2個目錄以及相應的頭文件、庫文件都不存在

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

        $ 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/

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

        準備好頭文件和庫文件之后,再次編譯一下:

        $ 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

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

        測試、執(zhí)行

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

        完美!

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

        Windows 下構建過程

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

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

        打開 VSCode,然后打開測試代碼文件夾 cmake_demo:

        因為需要使用cmake工具來構建,所以需要在VSCode安裝 cmake 插件。(如何安裝 VSCode 插件就不贅述了)

        第一步: cmake 配置

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

        第一次 Configure 的時候,會彈出下面的選項,來選擇編譯器:

        我們這里選擇 64 位的 amd64。

        配置的結果輸出在最下面窗口中的output標簽中,如下所示:

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

        這個時候,來看一下資源管理器中有啥變化:自動生成了 build 目錄,其中的文件如下:

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

        第二步: 編譯

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

        按下 shift + F7,或者單擊VSCode底部的 Build 圖標

        彈出編譯目標列表

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

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

        mylib.dll 就是編譯得到的動態(tài)鏈接庫,mylib.lib是導入符號。

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

        第三步: 執(zhí)行

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

        提示錯誤:找不到動態(tài)鏈接庫!

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

        完美!

        但是,既然已經用VSCode來編譯了,那就繼續(xù)在VSCode中進行代碼調試吧。

        按下調試快捷鍵 F5,第一次會彈出調試器選擇項:

        選擇 LLDB,然后彈出錯誤對話框:

        因為我們沒有提供相應的配置文件來告訴VSCode調試哪一個可執(zhí)行程序。

        單擊[OK]之后,VSCode 會自動為我們生成 .vscode/launcher.json 文件,內容如下:

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

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

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

        此時,就可以在mylib.c或者myapp.c中設置斷點,然后進行單步調試程序了:


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

        推薦閱讀

        【1】《Linux 從頭學》系列文章

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

        【3】原來gdb的底層調試原理這么簡單

        【4】內聯(lián)匯編很可怕嗎?看完這篇文章,終結它!

        其他系列專輯:精選文章應用程序設計、物聯(lián)網、 C語言。

        星標公眾號,第一時間看文章!


        瀏覽 90
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            免费的性视频越黄越好 | 77777_亚洲午夜久久多人 | 久久久久久国产精品 | 黄色片小说 | 亚洲午夜毛片 | 性欧美 | 无码精品一区二区免费 | 国产精品久久亚洲7777 | 粗长灌满h双龙h双性男男动漫 | 好吊操视颎|