1. 当要在windows平台下使用MinGW作为cmake使用的make平台时,需要确保cmake能够在系统环境变量PATH中找到MinGW的bin目录,如果PATH中没有MinGW的话可以在CMakeLists文件中手动声明,但是必须在project的声明之前,比如:

    cmake_minimum_required(VERSION 3.10)
    if(WIN32)
    set(MINGW_HOME $ENV{MINGW_HOME}) #获取系统的环境变量MINGW_HOME,前提是系统环境变量中有这个变量,不然还是得手动添加绝对路径
    set(ENV{PATH} $ENV{MINGW_HOME}\\bin) #由于cmake过程中只需要编译链接的工具,所以就在作用域内直接把环境变量PATH设置为MinGW的bin目录
    endif()
    project("test") #声明完PATH后再声明project
  2. 当需要配置以下变量时,需要在project声明之后,否则cmake时会发生错误:

    CMAKE_MAKE_PROGRAM
    CMAKE_C_COMPILER
    CMAKE_CXX_COMPILER

    例如:

    project("test")
    set(MINGW_HOME $ENV{MINGW_HOME})
    set(CMAKE_MAKE_PROGRAM ${MINGW_HOME}/bin/make.exe)
    set(CMAKE_C_COMPILER ${MINGW_HOME}/bin/clang.exe)
    set(CMAKE_CXX_COMPILER ${MINGW_HOME}/bin/clang++.exe)
    #这几个变量的使用主要还是指定使用的编译器,平常也不会使用到
  3. 生成库或者可执行文件时,实际上是可以自定义输出文件名的,因为有时候我们需要同时产生静态库文件和动态库文件,但是生成的目标名却不能一样,比如说:

    #以下代码在MinGW和Linux Gnu下有效,可能因为Linux Gnu和MinGW都是Gnu的缘故
    add_library(testlib_shared SHARED ${CMAKE_SOURCE_DIR}/src/test.c ) #添加生成动态库的目标名称
    add_library(testlib_static STATIC ${CMAKE_SOURCE_DIR}/src/test.c ) #添加生成静态库的目标名称
    #上述的生成静态库和动态库的目标名称不一样,不会冲突
    set_target_properties(testlib_shared PROPERTIES OUTPUT_NAME test) #设置生成动态库的名称为test(最终产生libtest.so/libtest.dll+libtest.dll.a)
    set_target_properties(testlib_static PROPERTIES OUTPUT_NAME test) #设置生成静态库的名称为test(最终产生libtest.a) #这样就同时产生libtest.a和libtest.so/libtest.dll+libtest.dll.a
  4. 以3作为例子,当进行库链接时,如果能够获取库的目录的话,就能够直接链接库的目标名称,而不是库的文件名,这样链接的是静态库还是动态库就一目了然:

    add_executable(main ${CMAKE_SOURCE_DIR}/src/main.c)
    target_link_libraries(main testlib_shared) #main链接动态库
    target_link_libraries(main testlib_static) #main链接静态库
  5. 生成动态库时,不同平台以及不同编译器生成的文件不一样,需要进行的行为也不一样,

    windows平台上使用VS作为编译器的话,就会直接产生VS项目,有需要的话直接使用VS配置编译即可;

    windows平台上使用MinGW作为编译器,会产生.dll文件和.dll.a文件,这个.a文件就是引导库文件,链接动态库的时候需要链接的是引导库文件;

    Linux平台上安装build-essential,然后是使用GNU作为编译器,会产生.so文件,链接动态库的时候就直接链接.so动态库文件;

    #如果是使用MinGW或者GNU进行cmake的话,生成目标需要进行的动态库链接和静态库链接是一样的,只要找到动态库所在的路径,然后target_link_libraries就可以,例如:
    add_library(testlib_shared SHARED ${CMAKE_SOURCE_DIR}/src/test.c ) #添加生成动态库
    target_link_libraries(main testlib_shared) #这里Linux会直接链接so动态库,MinGW会直接链接引导库dll.a
  6. 不同的make工具产生的库文件名称格式不一样:

    #linux平台上使用GNU进行编译产生的动态库或者静态库会在名称前面加上lib,例如:
    add_library(test SHARED ${CMAKE_SOURCE_DIR}/src/test.c ) #库名为test,而生成的动态库文件名为libtest.so
    #windows平台上使用MinGW进行编译产生的动态库或者静态库也会在文件的名称前面加上lib,以上面的生成库代码来看,库名为test,生成的动态库文件名为libtest.dll,另外还会生成一个libtest.dll.a的引导库文件用于其他生成目标可以链接动态库
    #windows平台上使用VS作为编译器的话,会直接产生VS项目,以上面生成库的代码来看,库名为test,生成动态库文件名为test.dll,没有生成引导库文件,而VS项目无法直接链接dll

    从4和6两个例子可以看出使用什么操作系统来进行编译需要进行判断固然重要,但是使用什么编译器来进行编译的判断更为重要。

  7. 由6的结论来进行编译器的判断

    在百度上找到了${CMAKE_COMPILER_IS_GNUCC}这个变量,可以判断使用的C编译器是不是GNU,

    然后顺藤摸瓜在cmake官网上找到了${CMAKE_<LANG>_COMPILER_ID}这个变量,<LANG>部分代入C或者CXX,变量的值就是编译器的标识,比如说GNU或者MSVC

    综合考虑了一下,决定使用${CMAKE_C_COMPILER_ID}这个变量作为对编译器的判断,扩展性比较高

    需要注意的是${CMAKE_C_COMPILER_ID}这个变量只有在project声明之后才会生效

    add_library(testlib_shared SHARED ${CMAKE_SOURCE_DIR}/src/test.c ) #添加生成动态库
    if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
    set_target_properties(testlib_shared PROPERTIES OUTPUT_NAME test) #GNU编译产生的库名前面会自己加上lib
    #如果是使用windows下的MinGW进行编译的话,动态库生成dll的同时还会产生一个dll.a的引导库文件
    elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
    set_target_properties(testlib_shared PROPERTIES OUTPUT_NAME libtest) #MSVC编译产生的库名就是原来指定的目标名
    endif()
  8. 对于3的补充,即便可以指定输出的库的名称,库的目标名称不会重复,但是在VS2019中编译cmake项目时仍然会报重复库的名称重复的错误,也就是说set_target_properties没办法在VS中逃过这个问题,对于这个问题,就保持静态库和动态库名称不一样应该是ok的,直接显式表示静态库或者动态库,如果觉得这样太傻可以像lua一样,静态库就叫liblua,动态库叫lua54,原因就是静态库不打算暴露出去,而动态库具有共享的性质故标明版本,这样使用的人也方便知道使用的库的版本信息。

    if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
    set_target_properties(testlib_shared PROPERTIES OUTPUT_NAME test10) #MSVC编译产生的库名就是原来的库名
    set_target_properties(testlib_static PROPERTIES OUTPUT_NAME libtest)
    endif()
  9. 经测试,在MSVC下确实只有导出接口能够使动态库产生引导库文件,所以这部分只能在动态库的头文件中添加接口

    #ifndef _WIN32 //如果没有_WIN32的宏,说明不是MSVC项目,自然就不用导出接口
    #define TEST_DLL_API
    #endif #ifndef TEST_DLL_API
    #define TEST_DLL_API __declspec(dllexport)
    #endif #include <stdio.h> TEST_DLL_API int hello();
  10. MSVC和第1点是有冲突的,当设置了PATH之后,MSVC会找不到自己的编译器,然后cmake发生错误,但是对PATH的设置只能在project的声明前,判断编译器类型只能在project声明后,所以冲突是有的,如果项目在MSVC上跑的话,请注释掉第一个if分支中set(ENV{PATH}..)一行。

  11. 虚位以待

最后的最后,附上完整的模板CMakeLists.txt文件,该文件在VS2019、MinGW、Linux GNU三种编译器下编译通过:

cmake_minimum_required(VERSION 3.10)
if(WIN32) #如果是MSVC项目的话,请把set(ENV{PATH}..)那一行注释掉,以免干扰MSVC找编译器
set(MINGW_HOME $ENV{MINGW_HOME}) #获取系统的环境变量MINGW_HOME,前提是系统环境变量中有这个变量,不然还是得手动添加绝对路径
set(ENV{PATH} $ENV{MINGW_HOME}\\bin) #由于cmake过程中只需要编译链接的工具,所以就在作用域内直接把环境变量PATH设置为MinGW的bin目录
endif()
project("test")
message(STATUS ${CMAKE_C_COMPILER_ID}) #w为了方便起见,一进来就打印出编译器 include_directories(${CMAKE_SOURCE_DIR}/include )
#aux_source_directory(${CMAKE_SOURCE_DIR}/src SRCFILES) add_library(testlib_shared SHARED ${CMAKE_SOURCE_DIR}/src/test.c ) #添加生成动态库
add_library(testlib_static STATIC ${CMAKE_SOURCE_DIR}/src/test.c ) #添加生成静态库 if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
set_target_properties(testlib_shared PROPERTIES OUTPUT_NAME test) #GNU编译产生的库名前面会自己加上lib
set_target_properties(testlib_static PROPERTIES OUTPUT_NAME test)
#如果是使用windows下的MinGW进行编译的话,动态库生成dll的同时还会产生一个dll.a的引导库文件
elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
set_target_properties(testlib_shared PROPERTIES OUTPUT_NAME test10) #MSVC编译产生的库名就是原来的库名
set_target_properties(testlib_static PROPERTIES OUTPUT_NAME libtest)
else()
#其他情况下没试过,手头没有MacOS设备,半夜三更的也懒得装个黑苹果试试
endif() add_executable(main ${CMAKE_SOURCE_DIR}/src/main.c) #target_link_libraries(main testlib_static) #静态库链接就直接链接上,不是很麻烦 if(CMAKE_C_COMPILER_ID STREQUAL "GNU") #GNU时
target_link_libraries(main testlib_shared) #直接链接生成库的目标名,Linux下会自动链接到so库,而windows下会自动链接引导库文件
elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
target_link_libraries(main testlib_shared) #实测VS项目也只能在头文件中加导出接口了,详情见test.h头文件
else()
#其他情况下没试过,手头没有MacOS设备,半夜三更的也懒得装个黑苹果试试
endif()

说明:test.h头文件包括一个函数声明,test.c文件中包含了头文件中声明函数的定义,main.c文件中包含了test.h头文件并调用了该函数。

CMake配置跨平台项目踩的坑的更多相关文章

  1. 配置thinkphp项目遇到的坑

    坑一: nginx配置必须改成伪静态配置 否则出现nginx 403 forbiddem错误 坑2: 缓存目录权限必须开放 坑3:服务器权限准备: 坑4:防火墙关闭 systemctl stop fi ...

  2. cmake配置MFC项目属性

    MFC的使用 使用下面的代码设置为: # 设置MFC的使用 SET(CMAKE_MFC_FLAG 2) 这里的 2 代表: 在共享 DLL 中使用 MFC, 1代表在静态库中使用 MFC 设置字符集 ...

  3. cmake配置项目引用动态库

    note 本文将介绍使用FIND_PACKAGE配置项目动态库的方法 cmake version: 3.18 platform: win10 20H2 概述 创建了一个动态库,再由主项目调用该动态库. ...

  4. html2canvas在Vue项目踩坑-生成图片偏移不完整

    背景 最近做一个Vue项目需求是用户长按保存图片,页面的数据是根据不同id动态生成的,页面渲染完生成内容图片让用户长按保存的时候,把整个页面都保存起来. 在项目遇到的坑是图片能生成,可是生成的图片总是 ...

  5. 用CMAKE编译配置的项目进行调试的方法

    在Linux 下用CMAKE编译的项目进行Debug 需进行设置: 1.在未设置之前 进行调试可能会出现错误报告:No source available for ...等一系列错误,这些错误可能就是你 ...

  6. Android.mk走读与Cmake配置

    Android.mk认识: 在上一次[https://www.cnblogs.com/webor2006/p/9946061.html]中学会了用NDK提供的交叉编译工程编译成Android能运行的可 ...

  7. 使用VSCode和CMake构建跨平台的C/C++开发环境

    日前在学习制作LearnOpenGL教程的实战项目Breakout游戏时,希望能将这个小游戏开发成跨平台的,支持在多个平台运行.工欲善其事必先利其器,首先需要做的自然是搭建一个舒服的跨平台C/C++开 ...

  8. 使用CMake编译跨平台静态库

    在开始介绍如何使用CMake编译跨平台的静态库之前,先讲讲我在没有使用CMake之前所趟过的坑.因为很多开源的程序,比如png,都是自带编译脚本的.我们可以使用下列脚本来进行编译:   . / con ...

  9. 从零开始学 Java - Spring 支持 CORS 请求踩的坑

    谁没掉进过几个大坑 记得好久之前,总能时不时在某个地方看到一些标语,往往都是上面一个伟人的头像,然后不管是不是他说的话,下面总是有看起来很政治正确且没卵用的屁话,我活到目前为止,最令我笑的肚子痛得是下 ...

  10. Asp.Net Core中使用Swagger,你不得不踩的坑

    很久不来写blog了,换了新工作后很累,很忙.每天常态化加班到21点,偶尔还会到凌晨,加班很累,但这段时间,也确实学到了不少知识,今天这篇文章和大家分享一下:Asp.Net Core中使用Swagge ...

随机推荐

  1. Nacos服务调用(基于Openfeign)

    在<<Nacos服务注册>>这篇文章里,我搭建了一个nacos服务中心,并且注册了一个服务,下面我们来看在上一篇文章的基础上,怎样用Openfeign来调用这个服务. 0.同上 ...

  2. 题解 [SCOI2008] 奖励关

    为了这道题我学了期望 dp(? 为什么会有人期望 dp 入门是这道题啊歪(#`O′) wtx 一眼秒杀了这题,我们一起来膜拜他! 其实这题很水但是我之前没学过期望 dp 我是什么 nt. 显然我们以每 ...

  3. 题解 [SCOI2007]压缩

    好题. 显然区间 dp,令 \(f_{l, r}\) 为 \([l, r]\) 之间的最短的长度.如果我们要压缩,那么就要考虑 M 与 R 的位置.由于我们大体是从左往右来转移的,所以显然我们只需要记 ...

  4. js根据指定字段数据去重

    //根据id去重 function unique(arr1) { const res = new Map(); return arr1.filter((a) => !res.has(a.id) ...

  5. Spring MVC + Webapp 项目显示 404 错误

    这是因为 Tomcat 版本,把 Tomcat10 换成 Tomcat9 就可以解决这个问题了.下面是我正在做的一个 Spring MVC 入门案例,因为 Tomcat 10,DispatcherSe ...

  6. 一些关于STL的笔记

    c++ std中set与unordered_set区别及map与unordered_map区别: map/set基于红黑树实现,红黑树具有自动排序的功能,因此map/set内部所有的数据,在任何时候, ...

  7. Sptring cloud Alibaba Sentinel 实现熔断与限流

    随着微服务的流行,服务和服务之间的稳定性变得越来越重要. Sentinel 以流量为切入点,从流量控制.熔断降级.系统负载保护等多个维度保护服务的稳定性. 作用: 服务雪崩 服务降级 服务熔断 服务限 ...

  8. linus->查看文件及文件夹大小相关命令

    背景: 经常会遇到服务器服务突然停了,去服务器一看服务正常运行. 然后在排查服务器容量,发现100%使用. 那么记下来一些常用命令是有必要的. 相关命令: df -hl   查看占用情况. du -s ...

  9. loj2839

    除了 L 神 txdy 我还能说啥呢.(L 神把这题搬模拟赛了...) 即把每个 x 换成 ( 或 ),问是否能通过不多于一次区间反转(( 与 ) 交换)后合法. 考虑怎样的括号串是合法的. 假设左括 ...

  10. wibu软授权(五)

    本篇将尝试根据RAU的asn1定义手动生成一份自定义的RAU文件,看看能不能通过wibu软授权系统的验证 准备工具 python3 asn1tools Ubuntu 18.04 CodeMeter R ...