我正在使用 CMake 开始一个新的 C 项目,因此我创建了一个与我在 Python(我的“主要”语言)中使用的目录结构非常相似的目录结构。尽管编译正确,但我不确定我是否以正确的方式进行编译。这是当前的结构:
.
├── CMakeLists.txt
├── dist
│ └── # project will be built here, 'cmake ..'
├── extras
│ ├── CMakeLists.txt
│ ├── extra1
│ │ ├── CMakeLists.txt
│ │ ├── extra1.h
│ │ └── extra1.c
│ └── extra2
│ ├── CMakeLists.txt
│ ├── extra2.h
│ └── extra2.c
├── src
│ ├── CMakeLists.txt
│ ├── main.c
│ ├── module1.h
│ ├── module1.c
│ ├── module2.h
│ └── module2.c
└── test
├── CMakeLists.txt
├── test_module1.c
└── test_module2.c
由于所有文件都分布在多个目录中,我必须找到一种方法来找到 extras
中存在的库以及我需要在 src
中测试的库。所以,这些是我的 CMakeLists:
./CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(MyProject)
add_definitions(-Wall -std=c99)
# I don't know really why I need this
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/dist)
add_subdirectory(src)
add_subdirectory(test)
add_subdirectory(extras)
enable_testing()
add_test(NAME DoTestModule1 COMMAND TestModule1)
add_test(NAME DoTestModule2 COMMAND TestModule2)
./src/CMakeLists.txt
macro(make_library target source)
add_library(${target} ${source})
target_include_directories(${target} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
endmacro(make_library)
make_library(Module1.o module1.c)
make_library(Module2.o module2.c)
./test/CMakeLists.txt
macro(make_test target source library)
add_executable(${target} ${source})
target_link_libraries(${target} Libtap.o ${library})
target_include_directories(${target} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
endmacro(make_test)
make_test(TestModule1 test_module1.c Module1.o)
make_test(TestModule2 test_module2.c Module2.o)
./extras/CMakeLists.txt
# Hopefully you'll never need to change this file
foreach(subdir ${SUBDIRS})
add_subdirectory(${subdir})
endforeach()
(最后)./extras/libtap/CMakeLists.txt
add_library(Libtap.o tap.c)
target_include_directories(Libtap.o PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
那么,现在的问题是:我担心的原因是这个设置会为我正在使用的每个文件创建一个“公共(public)”库,包括额外的库(这并不意味着分布)。如果我在 src
中有 10 个库,在 extras
中有 4 个依赖项(包括我用来测试的 libtap)和至少相同数量的测试文件,我'最终会得到 24 个编译工件。
- 有没有更好的方法来公开链接库?
- 我还没有编译“main”,什么是正确的配置?
add_definitions
是向编译器添加标志的正确方法吗?- 如何让这个结构更DRY?
最佳答案
Is there any better way to expose libraries to linking?
不,这看起来不错。
但是,您可能想要重新考虑创建静态库的粒度。例如,如果除测试之外的所有应用程序都只会组合使用 Module1
和 Module2
,您可能希望将它们合并到一个库目标中。当然,测试将链接到他们不使用的组件部分,但这是为降低构建复杂性付出的小代价。
I'm not compiling "main" yet, what would be the right configuration for that?
将它添加到 src/CMakeLists.txt
也没有错:
add_executable(my_main main.c)
target_link_libraries(my_main Module1.o Module2.o)
is add_definitions the right way to add flags to the compiler?
它可以用于该目的,但可能并不理想。
较新的 CMake 脚本应该更喜欢 target_compile_options
用于此目的的命令。这里唯一的缺点是,如果您想为项目中的所有目标重用相同的编译选项,您还必须为每个目标执行相同的 target_compile_options
调用。有关如何解决该问题的提示,请参阅下文。
How can I make this structure more DRY?
首先,与大多数程序代码不同,冗余在构建系统代码中通常不是什么大问题。这里需要注意的值得注意的事情是妨碍可维护性的东西。回到之前的常用编译器选项:如果您将来想要更改这些标志,您很可能想要为每个目标更改它们。在这里集中有关选项的知识是有意义的:要么引入一个 function
在顶层为给定目标设置选项,或将选项存储到全局变量。
无论哪种情况,您都必须为每个目标编写一行以获得该选项,但此后不会产生任何维护开销。作为一个额外的好处,如果您将来实际上只需要更改一个目标的选项,您仍然可以灵活地这样做。
不过,请注意不要过度设计。构建系统首先应该把事情做好。
如果最简单的设置方法是复制/粘贴大量内容,那就去做吧!如果在以后的维护过程中发现您有一些真正不必要的冗余,您可以随时进行重构。
您越早接受您的 CMake 脚本永远不会像您的程序代码那样漂亮的事实越好 ;)
最后一个小问题:避免给目标名称扩展名。也就是说,而不是
add_library(Libtap.o tap.c)
考虑
add_library(Libtap tap.c)
无论如何,CMake 都会根据目标平台自动附加正确的文件结尾。
关于c - 有没有更好的方法来使用 cmake 构建 C 项目?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25790330/