c++ - CMake:编译生成可执行和可链接库
问题描述
我真的不知道如何准确地表达这个问题,但这是我最好的尝试......
我最近开始从事一个大型项目,我想把它分解成几个组成部分。
以下是其中两个组件的工作原理:
- 第一个组件:本身是一个可执行程序。由 CMake 制作。获取一些库代码的源文件,并与文件进行编译和链接
main.cpp
。 - 第二个组件:应该使用在第一个组件中编译的相同库,但
main.cpp
文件不同,加上一些新库。
在这种情况下,“库”只是包含一些类的头文件和源文件,这些类本身依赖于其他一些库。(确切地说,是 SDL。)
我想要做的是以某种方式让 CMake 编译“第一个组件”并生成一个或多个目标文件,我可以在“第二个组件”中链接到(再次使用 CMake)。
我希望这是有道理的,但它可能并不明显,所以这里有一些进一步的细节:
第一个组件使用 SDL 将文本呈现和绘制到 SDL 窗口。我编写的各种类做了很多事情,包括从文件加载字体、在系统上查找字体以及管理 SDL 对象(例如窗口和渲染的文本对象)。该
main.cpp
文件仅用于测试目的。它允许编译一个可执行文件,证明一切都按预期工作。如果您愿意,可以将其视为测试驱动的开发文件,尽管它不包含任何正式测试。第二个组件取决于 gcc/g++ 通常会从第一个组件的编译中生成的目标文件。换句话说,我希望能够从第一个组件中获取一些目标文件并使用它们来编译第二个组件。我可以将源文件从第一个组件复制并粘贴到第二个组件,然后从头开始编译所有内容,但这会弄乱我的 git 存储库,而且效率也不高。
我确信必须有可能让 cmake 从第一个组件生成一些目标文件,然后在编译第二个组件时链接这些文件。当然,它可能需要两个单独的 cmake 进程,但这很好。
请注意; 我不想编译共享对象。我假设我想要的是静态链接我的目标文件,但是我假设默认情况下 cmake 将调用动态链接过程。换句话说,SDL 库仍然应该是动态链接的。我自己的库不应该动态链接,因为它们在 linux 系统上不可用,它们只在我自己的 linux 系统上可用。
最小的工作示例?
这是 MWE 有点毫无意义的事情,但这里仍然是一种尝试。
第一个目录是这样设置的
first/
main.cpp
first.h
first.cpp
CMakeLists.txt
build [d]
第二个目录只包含一个 main.cpp 文件。
CMakeLists.txt:
cmake_minimum_required(VERSION 3.7)
project(testproject)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED True)
find_package(SDL2 REQUIRED)
include_directories(testproject ${SDL2_INCLUDE_DIRS})
set(SOURCE_FILES main.cpp first.cpp)
add_executable(testproject ${SOURCE_FILES})
target_link_libraries(testproject ${SDL2_LIBRARIES})
main.cpp:
#include "first.h"
int main()
{
SDL_Init(SDL_INIT_VIDEO);
// using first library in main function
SDL_Window *window = GetWindow();
// doesn't draw anything, but you can
// press the window X to quit
for(bool quit = false; quit == false; )
{
SDL_Event event;
while(SDL_PollEvent(&event) != 0)
{
if(event.type == SDL_QUIT)
{
quit = true;
}
}
}
FreeWindow(window);
SDL_Quit();
return 0;
}
first.h:
#ifndef FIRST_H
#define FIRST_H
#include <SDL2/SDL.h>
SDL_Window* GetWindow();
void FreeWindow(SDL_Window* &window);
#endif // FIRST_H
first.cpp
#include "first.h"
SDL_Window* GetWindow()
{
SDL_Window *window =
SDL_CreateWindow(
"a test window",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
800, 600, SDL_WINDOW_SHOWN);
return window;
}
void FreeWindow(SDL_Window *&window)
{
SDL_DestroyWindow(window);
window = nullptr;
}
这是一个有点奇怪的例子。我试图包含一些非常小的函数,它们会生成一个目标文件以及使用外部 SDL 库。在这种情况下,第二个文件夹实际上可能只是 main.cpp 的副本,头文件的副本,或者CMakeLists.txt
告诉 make 系统在哪里找到头文件的行,然后它应该有一些方法可以找到对象文件,目前不存在,因为第一个CMakeLists.txt
还没有产生它们。
我不知道该怎么做:
- 首先告诉目录中的 cmake 生成一个包含(理想情况下是单个)目标文件的目录,该目标文件包含库代码,但不包含用于
main.cpp
- 告诉第二个目录中的 cmake 头文件在哪里
- 告诉目录中的cmake,首先由目录生成的库代码的目标文件在哪里,然后如何将它链接到第二个新的main.cpp文件
希望这足够清楚,但这大概是一件不平凡的事情。
解决方案
第一个组件:本身是一个可执行程序。由 CMake 制作。获取库代码的一些源文件,并使用 main.cpp 文件进行编译和链接。
第二个组件:应该使用在第一个组件中编译的相同库,但不同的 main.cpp 文件,加上一些新的库
所以就:
find_library(SDL2 REQUIRED)
add_library(common_stuff some_source_files_for_library_code)
target_link_libraries(common_stuff PUBLIC
SDL2::SDL2 # prefer newer INTERFACE libraries **if available**
)
target_include_directories(common_stuff PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
add_executable(first_component main.cpp)
target_link_libraries(first_component PRIVATE common_stuff)
add_library(second_component different_main.cpp)
target_link_libraries(second_component PUBLIC common_stuff)
笔记:
- 最好不要使用
set(CMAKE_... ...)
。例如更target_compile_features
喜欢set(CMAKE_CXX_STANDARD 14)
. common_stuff
可能是一个OBJECT
库(请参阅add_library
文档),但我认为没有理由这样做。
推荐阅读
- javascript - 在 JavaScript 或 TypeScript 中重命名时,阻止 VS Code 在解构赋值中添加“as”或别名
- python-3.x - 在 Pandas 中获取旋转数据框的值的比率
- react-native - 如何在本机反应中在弹出堆栈上插入新场景?
- java - 为什么我的 REST 服务器中的响应状态为 204 No content?
- python - Python pandas 将质心数据合并回数据框
- python - “TypeError:无法将 'list' 对象隐式转换为 str” 谁能看到什么可以修复我的代码?
- amazon-cloudformation - 将 HostedZone NameServers 指定为 CloudFormation 输出
- mongodb - Mongodb更新子文档字段以更好的方法比较更新
- sql-server - SQL Server 加入查询 DateCompleted > 3 年前
- r - 使用带有 x 轴的基 R 的堆叠/闪避条形图是数值数据