如何编译跨平台的动态库和应用程序?

嵌入式大杂烩 2022-01-10 21:53

在很久之前,曾经在B站上传过几个小视频,介绍了在WindowsLinux这两个平台下,如何通过cmakemake这两个构建工具,来编译、链接动态库、静态库以及可执行程序

视频中的示例代码是提前写好的,因此重点就放在构建(Build)环节了。主要是介绍了动态库与动态库之间、应用程序与动态库之间的引用等等。

对动态库、静态库比较熟悉的小伙伴,应该很容易就能理解其中的内容。但是对 C 语言不熟悉的朋友,看起来还是有一点点障碍。

这篇文章,主要是把视频中的示例代码进行简化,只使用一个动态库和一个可执行文件,使用cmake构建工具,演示在 Windows 和 Linux 这两个平台下的构建过程

本文的内容很基础,算是使用 cmake 来构建跨平台程序的入门教程吧!

示例代码

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

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

# 依赖的动态库
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这两个子文件夹。

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中设置断点,然后进行单步调试程序了:


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

往期推荐



分享一个小巧的嵌入式日志模块(附代码)

嵌入式中如何正确使用动态内存?

Shell编程必备简明基础知识!


嵌入式大杂烩 专注于嵌入式技术,包括但不限于C/C++、嵌入式、物联网、Linux等编程学习笔记,同时,内包含大量的学习资源。欢迎关注,一同交流学习,共同进步!
评论
  • 本文介绍瑞芯微RK3588主板/开发板Android12系统下,APK签名文件生成方法。触觉智能EVB3588开发板演示,搭载了瑞芯微RK3588芯片,该开发板是核心板加底板设计,音视频接口、通信接口等各类接口一应俱全,可帮助企业提高产品开发效率,缩短上市时间,降低成本和设计风险。工具准备下载Keytool-ImportKeyPair工具在源码:build/target/product/security/系统初始签名文件目录中,将以下三个文件拷贝出来:platform.pem;platform.
    Industio_触觉智能 2024-12-12 10:27 115浏览
  • RK3506 是瑞芯微推出的MPU产品,芯片制程为22nm,定位于轻量级、低成本解决方案。该MPU具有低功耗、外设接口丰富、实时性高的特点,适合用多种工商业场景。本文将基于RK3506的设计特点,为大家分析其应用场景。RK3506核心板主要分为三个型号,各型号间的区别如下图:​图 1  RK3506核心板处理器型号场景1:显示HMIRK3506核心板显示接口支持RGB、MIPI、QSPI输出,且支持2D图形加速,轻松运行QT、LVGL等GUI,最快3S内开
    万象奥科 2024-12-11 15:42 123浏览
  • 铁氧体芯片是一种基于铁氧体磁性材料制成的芯片,在通信、传感器、储能等领域有着广泛的应用。铁氧体磁性材料能够通过外加磁场调控其导电性质和反射性质,因此在信号处理和传感器技术方面有着独特的优势。以下是对半导体划片机在铁氧体划切领域应用的详细阐述: 一、半导体划片机的工作原理与特点半导体划片机是一种使用刀片或通过激光等方式高精度切割被加工物的装置,是半导体后道封测中晶圆切割和WLP切割环节的关键设备。它结合了水气电、空气静压高速主轴、精密机械传动、传感器及自动化控制等先进技术,具有高精度、高
    博捷芯划片机 2024-12-12 09:16 111浏览
  • 应用环境与极具挑战性的测试需求在服务器制造领域里,系统整合测试(System Integration Test;SIT)是确保产品质量和性能的关键步骤。随着服务器系统的复杂性不断提升,包括:多种硬件组件、操作系统、虚拟化平台以及各种应用程序和服务的整合,服务器制造商面临着更有挑战性的测试需求。这些挑战主要体现在以下五个方面:1. 硬件和软件的高度整合:现代服务器通常包括多个处理器、内存模块、储存设备和网络接口。这些硬件组件必须与操作系统及应用软件无缝整合。SIT测试可以帮助制造商确保这些不同组件
    百佳泰测试实验室 2024-12-12 17:45 113浏览
  • 时源芯微——RE超标整机定位与解决详细流程一、 初步测量与问题确认使用专业的电磁辐射测量设备,对整机的辐射发射进行精确测量。确认是否存在RE超标问题,并记录超标频段和幅度。二、电缆检查与处理若存在信号电缆:步骤一:拔掉所有信号电缆,仅保留电源线,再次测量整机的辐射发射。若测量合格:判定问题出在信号电缆上,可能是电缆的共模电流导致。逐一连接信号电缆,每次连接后测量,定位具体哪根电缆或接口导致超标。对问题电缆进行处理,如加共模扼流圈、滤波器,或优化电缆布局和屏蔽。重新连接所有电缆,再次测量
    时源芯微 2024-12-11 17:11 141浏览
  • 一、SAE J1939协议概述SAE J1939协议是由美国汽车工程师协会(SAE,Society of Automotive Engineers)定义的一种用于重型车辆和工业设备中的通信协议,主要应用于车辆和设备之间的实时数据交换。J1939基于CAN(Controller Area Network)总线技术,使用29bit的扩展标识符和扩展数据帧,CAN通信速率为250Kbps,用于车载电子控制单元(ECU)之间的通信和控制。小北同学在之前也对J1939协议做过扫盲科普【科普系列】SAE J
    北汇信息 2024-12-11 15:45 140浏览
  • 习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记
    youyeye 2024-12-11 17:58 108浏览
  • 习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记
    youyeye 2024-12-12 10:13 79浏览
  • 天问Block和Mixly是两个不同的编程工具,分别在单片机开发和教育编程领域有各自的应用。以下是对它们的详细比较: 基本定义 天问Block:天问Block是一个基于区块链技术的数字身份验证和数据交换平台。它的目标是为用户提供一个安全、去中心化、可信任的数字身份验证和数据交换解决方案。 Mixly:Mixly是一款由北京师范大学教育学部创客教育实验室开发的图形化编程软件,旨在为初学者提供一个易于学习和使用的Arduino编程环境。 主要功能 天问Block:支持STC全系列8位单片机,32位
    丙丁先生 2024-12-11 13:15 86浏览
  • 全球知名半导体制造商ROHM Co., Ltd.(以下简称“罗姆”)宣布与Taiwan Semiconductor Manufacturing Company Limited(以下简称“台积公司”)就车载氮化镓功率器件的开发和量产事宜建立战略合作伙伴关系。通过该合作关系,双方将致力于将罗姆的氮化镓器件开发技术与台积公司业界先进的GaN-on-Silicon工艺技术优势结合起来,满足市场对高耐压和高频特性优异的功率元器件日益增长的需求。氮化镓功率器件目前主要被用于AC适配器和服务器电源等消费电子和
    电子资讯报 2024-12-10 17:09 114浏览
  • 在智能化技术快速发展当下,图像数据的采集与处理逐渐成为自动驾驶、工业等领域的一项关键技术。高质量的图像数据采集与算法集成测试都是确保系统性能和可靠性的关键。随着技术的不断进步,对于图像数据的采集、处理和分析的需求日益增长,这不仅要求我们拥有高性能的相机硬件,还要求我们能够高效地集成和测试各种算法。我们探索了一种多源相机数据采集与算法集成测试方案,能够满足不同应用场景下对图像采集和算法测试的多样化需求,确保数据的准确性和算法的有效性。一、相机组成相机一般由镜头(Lens),图像传感器(Image
    康谋 2024-12-12 09:45 117浏览
  • 首先在gitee上打个广告:ad5d2f3b647444a88b6f7f9555fd681f.mp4 · 丙丁先生/香河英茂工作室中国 - Gitee.com丙丁先生 (mr-bingding) - Gitee.com2024年对我来说是充满挑战和机遇的一年。在这一年里,我不仅进行了多个开发板的测评,还尝试了多种不同的项目和技术。今天,我想分享一下这一年的故事,希望能给大家带来一些启发和乐趣。 年初的时候,我开始对各种开发板进行测评。从STM32WBA55CG到瑞萨、平头哥和平海的开发板,我都
    丙丁先生 2024-12-11 20:14 94浏览
  • 近日,搭载紫光展锐W517芯片平台的INMO GO2由影目科技正式推出。作为全球首款专为商务场景设计的智能翻译眼镜,INMO GO2 以“快、准、稳”三大核心优势,突破传统翻译产品局限,为全球商务人士带来高效、自然、稳定的跨语言交流体验。 INMO GO2内置的W517芯片,是紫光展锐4G旗舰级智能穿戴平台,采用四核处理器,具有高性能、低功耗的优势,内置超微高集成技术,采用先进工艺,计算能力相比同档位竞品提升4倍,强大的性能提供更加多样化的应用场景。【视频见P盘链接】 依托“
    紫光展锐 2024-12-11 11:50 97浏览
  • 全球智能电视时代来临这年头若是消费者想随意地从各个通路中选购电视时,不难发现目前市场上的产品都已是具有智能联网功能的智能电视了,可以宣告智能电视的普及时代已到临!Google从2021年开始大力推广Google TV(即原Android TV的升级版),其他各大品牌商也都跟进推出搭载Google TV操作系统的机种,除了Google TV外,LG、Samsung、Panasonic等大厂牌也开发出自家的智能电视平台,可以看出各家业者都一致地看好这块大饼。智能电视的Wi-Fi连线怎么消失了?智能电
    百佳泰测试实验室 2024-12-12 17:33 119浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦