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. .Net6 微服务之Ocelot+IdentityServer4入门看这篇就够了

    前言 .Net 6 使用 Consul 实现服务注册与发现 看这篇就够了.Net6 使用 Ocelot + Consul 看这篇就够了.Net6 微服务之Polly入门看这篇就够了 书接上文,本文将继 ...

  2. 06#Web 实战:可滑动的标签页

    实现效果图 本随笔只是记录一下大概的实现思路,如果感兴趣的小伙伴可以通过代码和本随笔的说明去理解实现过程.我的 Gitee 和 GitHub 地址.注意哦:这个只是 PC 上的标签页,手机端的没用,因 ...

  3. StatefulWidget 组件的参数时(widget.xxx)报 Invalid Constant Value

    一个 Flutter 组件(Widget)在很多情况下都需要接收一些参数.Flutter 插件通常提示使用 const 关键字包裹某 Widget(很多人接受建议且执行),导致通过 widget.xx ...

  4. C#判断useragent是pc还是手机端

    public static bool CheckAgent(string userAgent) { bool flag = false; string[] keywords = { "And ...

  5. Linux新手渣渣上路史

    Linux新手渣渣上路史 时至2022年,IT行业的迅速发展大家也有目共睹,IT行业在社会的发展中起着举足轻重的作用.其中一角Linux系统,从诞生到开源,再到现在受大众的欢迎,是一个很好的例子.Li ...

  6. Kotlin学习-类(嵌套类,内部类,数据类,静态类)及类扩展

    一般类形式: class Person(var name: String){//构造函数放在类头部 var age = 1 var fulName: String var address = &quo ...

  7. 06 RDD编程

    总共有多少学生?map(), distinct(), count() 开设了多少门课程? 每个学生选修了多少门课?map(), countByKey() 每门课程有多少个学生选?map(), coun ...

  8. django搭建简易blog

    目录 下载安装django 创建一个django项目 创建一个django应用 models.py urls.py views.py admin.py 配置应用到项目下 路由设置urls.py set ...

  9. java通过反射将对象A得属性值赋值给对象B

    java通过反射将对象A得属性值赋值给对象B //测试类1public class Test1 { private String name; private String sex; private i ...

  10. vue 清空element表单数据

    resetFields是用于针对整个表单的 Form Methods(整个表单) resetField 是针对表单项的 Form-Item Methods(表单的某一个项) this.$refs['e ...