首页 > 编程语言 >cmake基础示例:如何编译【跨平台】的动态库和应用程序

cmake基础示例:如何编译【跨平台】的动态库和应用程序

时间:2023-09-09 17:33:05浏览次数:53  
标签:cmake mylib 示例 int 编译 跨平台 build myapp

示例代码

首先看一下测试代码的全貌:

  1. mylib:只有一个源文件,编译输出一个动态库;
  2. myapp:也只有一个源文件,链接 mylib 动态库,编译输出一个可执行程序;

 

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系统的动态导出库,在 Linux 系统中,不是必要的。

补充:在 windows 系统中,编译动态库时会生成 xxx.dll 和 xxx.lib。xxx.dll 中是真正的库文件指令,xxx.lib 中仅仅是符号表。

具体来说:在 Windows 系统中,当编译动态库的时候,打开(定义)宏 MYLIB_EXPORT,下面这个宏生效:

#define MYLIB_API	__declspec(dllexport)

这样的话,两个函数 my_addmy_sub 的符号才可能被导出到 mylib.lib 文件中。

当这个动态库被应用程序(myapp)使用的时候,myapp.c在 include mylib.h 的时,关闭宏 MYLIB_EXPORT,此时下面这个宏就生效:

#define MYLIB_API	__declspec(dllimport)

为了简化宏定义的复杂度,这里就不考虑静态库了。

看完了头文件,再来看看源文件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是用来编译动态库的,因此在Windows平台下,每一个导出符号的前面需要加上 __declspec(dllexport),因此需要打开宏定义:MYLIB_EXPORT。

myapp

应用程序的代码就更简单了,只有两个文件:myapp.c 和 CMakeLists.txt,内容如下:

// myapp/myapp.c 文件

#include <stdio.h>
#include <stdlib.h>

#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})

# 依赖的动态库
TARGET_LINK_LIBRARIES(${PROJECT_NAME} mylib)

最后一行 TARGET_LINK_LIBRARIES(${PROJECT_NAME} mylib) 说明要链接mylib这个动态库。

那么到哪个目录下去查找相应的头文件和库文件呢?

通过这两行来指定查找目录:

# 头文件路径
INCLUDE_DIRECTORIES(./include)

# 库文件路径
LINK_DIRECTORIES(./lib)

这个两个目录暂时还不存在,待会编译的时候我们再手动创建。

可以让 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这两个子文件夹。

【点击链接免费学习c/c++ linux服务器开发相关技术】

C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂​ ke.qq.com/course/417774?flowToken=1013300

需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

Linux 下构建过程

cmake 配置

为了不污染源文件目录,在最外层目录下新建build目录,然后执行cmake指令:

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

此时,在build目录下,产生如下文件:

CMakeCache.txt  CMakeFiles  cmake_install.cmake  Makefile  myapp  mylib

make 编译

我们可以分别进入mylibmyapp目录,执行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 文件,但是在编译可执行程序 myapp 时遇到了错误:找不到 mylib.h 文件!

在刚才介绍myapp/CMakeLists.txt文件时说到:应用程序查找头文件的目录是 myapp/include, 查找库文件的目录是 myapp/lib。

但是这2个目录以及相应的头文件、库文件都不存在!

因此我们需要手动创建,并且把头文件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 目录下生成可执行文件myapp了。

测试、执行

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

完美!

由于我们是在build目录下编译的,编译过程中所有的输出和中间文件,都放在build目录下,一点都没有污染源文件。

Windows 下构建过程

Linux系统中的build文件夹删除,然后把测试代码压缩,复制到Windows系统中继续测试。

Windows下编译,一般就很少使用命令行了,大部分都使用VS或者VSCode来编译。

打开 VSCode,然后打开测试代码文件夹 cmake_demo:

因为需要使用cmake工具来构建,所以需要在VSCode安装 cmake 插件。(如何安装 VSCode 插件就不赘述了)

第一步: cmake 配置

按下键盘 ctrl + shift + p,在命令窗口中选择 Cmake: Configure,如果没看到这个选项,就手动输入前面的几个字符,然后就可以智能匹配到:

在第一次 Configure 的时候,会弹出下面的选项,来选择编译器:

我们这里选择 64 位的 amd64。

配置的结果输出在最下面窗口中的output标签中,如下所示:

这就表明cmake配置成功,正确的执行了每一个文件夹下的 CMakeLists.txt 文件。

这个时候,来看一下资源管理器中有啥变化:自动生成了 build 目录,其中的文件如下:

看来,流程与Linux系统中都是一样的,只不过这里是VSCode主动帮我们做了一些事情。

第二步: 编译

配置之后,下一步就是编译了。

按下 shift + F7,或者单击VSCode底部的 Build 图标:

弹出编译目标列表:

这里选择 ALL_BUILD,也就是编译所有的目标:mylib 和 myapp,输出如下:

来看一下编译的输出文件:

mylib.dll 就是编译得到的动态链接库,mylib.lib是导入符号。

myapp.exe 是编译得到的可执行程序。

第三步: 执行

我们先在命令行窗口中执行一下myapp.exe

提示错误:找不到动态链接库!

手动把mylib.dll拷贝到myuapp.exe同一个目录下,然后再执行一次 myapp.exe:

完美!

但是,既然已经用VSCode来编译了,那就继续在VSCode中进行代码调试吧。

按下调试快捷键 F5,第一次会弹出调试器选择项:

选择 LLDB,然后弹出错误对话框:

因为我们没有提供相应的配置文件来告诉VSCode调试哪一个可执行程序。

单击[OK]之后,VSCode 会自动为我们生成 .vscode/launcher.json 文件,内容如下:

把其中的program项目,改成可执行程序的全路径:

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

然后再次按下F5键,这回终于可以正确执行了:

此时,就可以在mylib.c或者myapp.c中设置断点,然后进行单步调试程序了:

 

编辑于 2023-05-17 20:09・IP 属地湖南

标签:cmake,mylib,示例,int,编译,跨平台,build,myapp
From: https://www.cnblogs.com/jiftle/p/17689879.html

相关文章

  • 重构第一个示例
    《重构改善既有代码的设计》马丁富勒 第一章戏剧演出团原始代码invoices.json[{"customer":"BigCo","performances":[{"playId":"hamlet","audience":55},{"......
  • CMake 备忘录
    CMAKE配置protobufQ:protobuf_generate_cpp是CMake的内置函数么?A:不是的。protobuf_generate_cpp不是CMake的内置函数,而是由FindProtobuf.cmake模块提供的一个函数,用于从.proto文件生成C++代码。FindProtobuf.cmake是Protobuf库的CMake模块。使用这个函......
  • java 支持 超大上G,多附件上传示例解析
    ​ 在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传和下载功能的实现。先说下要求:PC端全平台支持,要求支持Windows,Mac,Linux支持所有浏览器。支持文件批量上传支持文件夹上传,且要求在服务端保留层级结构。文件夹数量要求支持到10W......
  • Python跨平台兼容性解决方案及实现方法
    在如今多样化的网络计算环境下,开发者们面临着将应用程序运行于不同操作系统和硬件平台上的挑战。本文旨在分享一些关键技巧和实际操作步骤,帮助您更好地理解并实现Python中的跨平台兼容性,使您编写出具有广泛适应性、可移植性强以及稳定高效的代码。一、了解目标平台特点......
  • 华为云ES导出数据脚本示例
      #!bash#接口用法#shexport-es-data.sh索引名称css密码cssIPindex=$1password=$2ip=$3value=$(curl-XPOST-uadmin:$2-k"https://${ip}:9200/${index}/_doc/_search?scroll=1m"-H'Content-Type:application/json'-d'{"siz......
  • qt程序调用cuda-11.7,cmake编译时,提示:"CMakeCUDACompilerId.cu" failed. Compiler:
    报错显示:Running/home/wc/software/cmake-3.26.3-linux-x86_64/bin/cmake/home/wc/work/junke_src/missile-sim'-GCodeBlocks-UnixMakefiles'in/home/wc/work/junke_src/build/debug.CMakeErrorat/home/wc/software/cmake-3.26.3-linux-x86_64/share/cmak......
  • Java是如何实现跨平台的
    "Java跨平台"是指Java编程语言的特性,使得开发的应用程序可以在不同的操作系统和硬件平台上运行,而无需进行额外的修改或适配。这意味着使用Java编写的程序可以在Windows、Mac、Linux等不同的操作系统上运行,而不需要针对每个平台进行单独的开发。这种跨平台的能力是由Java虚拟机(Java......
  • 初学者学习Auto.js的示例和教程资源
    1.Auto.js官方文档:您可以前往Auto.js官方网站(https://hyb1996.github.io/AutoJs-Docs/#/)查看官方文档,其中包含了基本使用方法、API参考和示例代码等。2.Auto.js示例脚本:Auto.js官方文档中提供了一些示例脚本,您可以下载并运行这些脚本,以了解其基本用法和功能。3.Auto.js论坛:Auto.......
  • spring中的aop(面向切面编程)需要到导入的包与简单示例
    2023-09-07<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://......
  • Android 调试桥 (adb) 使用教程/示例
    sidebar:autoAndroid调试桥(adb)Android调试桥(adb)是一种功能多样的命令行工具,可让您与设备进行通信。adb命令可用于执行各种设备操作,例如安装和调试应用。adb提供对Unixshell(可用来在设备上运行各种命令)的访问权限。它是一种客户端-服务器程序,包括以下三个组件:客......