源码编译好处-1cmake编译和链接依赖

关键词

cmake编译顺序 cmake源码编译依赖 cmake链接依赖 target_link_libraries 和 add_dependency0 前导码

在实际项目中,我们的项目通常比较大,可能会依赖第三方源码库。 为了控制源码的引入以及多平台的兼容性,我们大多数人通常都会将源码直接放在我们的项目中进行编译。

0.1 引发问题

有些第三方源码库比较大,当编译时间比较长时,我们可能会遇到一些平台项目编译成功,而一些项目编译失败。 为什么是这样?

1cmake编译和链接依赖于1.1项目结构

首先介绍一下我们测试的项目结构以及目录下的文件内容。

源码编译好处_源码作用_源码编译什么意思

.
├── 3rdparty
│   ├── CMakeLists.txt
│   └── jsoncpp
├── CMakeLists.txt
├── build.sh
└── src
    ├── CMakeLists.txt
    └── main.cc

jsoncpp的内容就不列出来了,是从github拉取的,地址为:jsoncpp,其他文件内容如下:

cmake_minimum_required(VERSION 3.5)
project(learn2)
set(CMAKE_CXX_STANDARD 11)
add_subdirectory(3rdparty)
add_subdirectory(src)

#!/bin/bash
[ ! -d build ] && mkdir build
INSTALL_DIR=${PWD}/output
[ ! -d ${INSTALL_DIR} ] && mkdir ${INSTALL_DIR}
cd build
CMAKE_ARGS="${CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}"
# 并行编译任务数量
cores=8
if which nproc &> /dev/null; then
  cores=$(nproc)
fi
cores=$((cores-1))
[ $cores -gt 32 ] && cores=32
[ $cores -lt 0 ] && cores=2
echo "cores is $cores"
cmake .. ${CMAKE_ARGS} && make -j${cores} install

cmake_minimum_required(VERSION 3.5)
project(cmake_test)
include_directories(
  ${CMAKE_SOURCE_DIR}/3rdparty/jsoncpp/include
)
link_directories(
  ${CMAKE_BINARY_DIR}/lib
)
add_executable(${PROJECT_NAME}
  main.cc
)
target_link_libraries(${PROJECT_NAME}
  jsoncpp
)
install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin)

#include 
#include "json/json.h"
int main() {
  Json::Value root;
  root["value"] = 987654321;
  root["ts"] = 123456789.089;
  Json::StreamWriterBuilder builder;
  builder["commentStyle"] = "None";
  builder["indentation"] = "";
  builder["precisionType"] = "decimal";
  builder["precision"] = 1;
  std::string writer_info = Json::writeString(builder, root);
  std::cout << writer_info << std::endl;
  return 0;
}

源码作用_源码编译好处_源码编译什么意思

1.2 编译顺序

cmake的编译顺序与add_subdirectory的顺序有关(前提:在没有任何编译依赖的情况下,这个会在前面介绍),例如上述项目在个别环境中仍然可以编译。并且如果内容CMakeLists.txt 修改为以下内容,无法编译

cmake_minimum_required(VERSION 3.5)
project(learn2)
set(CMAKE_CXX_STANDARD 11)
add_subdirectory(src)
add_subdirectory(3rdparty)

1.3 编译异常

里面的工程,在某些环境下,会编译失败。 失败原因是链接时找不到jsoncpp库,如下:

main.cc:(.text+0x2ea): undefined reference to `Json::Value::operator=(Json::Value&&)'
collect2: error: ld returned 1 exit status

您是否有疑问:您已经使用target_link_libraries来依赖jsoncpp,为什么在jsoncpp尚未编译时就开始链接?

虽然不难找到答案,只要看看官方文档对target_link_libraries的解释就可以了

链接给定目标和/或其依赖项时指定库或标志。 来自链接库目标的使用要求将被传播。 目标依赖项的使用要求会影响所拥有源的编译。

一般含义是目标在链接时使用的一些库或一些符号。 这里解决的问题主要是告诉编译器链接时去哪里找符号表,并没有解决编译顺序的问题。 比如源码编译好处,即使先编译了依赖的jsoncpp,由于编译时间较长,在编译完成之前cmake_test就已经开始链接了,所以报错。

有2种方法可以解决这个问题:

单核编译:我们已经指定了编译顺序。 如果是单核编译的话,不会出现链接时未准备好的问题。 明确指定依赖关系

重大解决方案1,在我们实际开发中,不满足要求,这将大大提高我们的编译和建立效率。 为此,我们就只剩下选项2了

1.4 编译依赖

cmake提供了编译依赖相关的功能,add_dependencies,官方介绍:

添加顶级目标之间的依赖关系。

添加依赖项([]...)

Makesa 顶级目标依赖于其他顶级目标,以确保它们先于其他目标构建。

从介绍可以看出,这是为了解决依赖目标尚未编译源码编译好处,依赖目标开始链接的问题。 为此,我们项目中的一个文件已更改如下:

src/CMakeLists.txt

cmake_minimum_required(VERSION 3.5)
project(cmake_test)
include_directories(
  ${CMAKE_SOURCE_DIR}/3rdparty/jsoncpp/include
)
link_directories(
  ${CMAKE_BINARY_DIR}/lib
)
add_executable(${PROJECT_NAME}
  main.cc
)
target_link_libraries(${PROJECT_NAME}
  jsoncpp
)
add_dependencies(${PROJECT_NAME}
  jsoncpp_lib
)
install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin)

至此,在不同平台、环境下编译就OK了。 这里请注意:

add_dependency的参数是targets,也就是ad​​d_library的第一个参数

99 其他

CMAKE工具使用过程中的一些变量介绍

CMAKE_CURRENT_SOURCE_DIR : 当前CMakeLists.txt文件所在目录
CMAKE_SOURCE_DIR: 顶层目录
CMAKE_BINARY_DIR: build目录
PROJECT_NAME: 当前工程名称
CMAKE_INSTALL_PREFIX: 最终安装目录
CMAKE_LIBRARY_ARCHITECTURE:编译目标架构 x86_64-linux-gnu, aarch64-linux-gnu
CMAKE_CXX_FLAGS : c++的编译参数
CMAKE_C_FLAGS: c编译参数
CMAKE_VERSION: cmake的版本