有没有更好的方法来使用cmake构建C项目?

Luc*_*aio 5 c cmake

我正在使用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
Run Code Online (Sandbox Code Playgroud)

由于所有文件都分布在多个目录中,因此我必须找到一种方法来查找存在的库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)
Run Code Online (Sandbox Code Playgroud)

./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)
Run Code Online (Sandbox Code Playgroud)

./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)
Run Code Online (Sandbox Code Playgroud)

./extras/CMakeLists.txt

# Hopefully you'll never need to change this file
foreach(subdir ${SUBDIRS})
    add_subdirectory(${subdir})
endforeach()
Run Code Online (Sandbox Code Playgroud)

(最后)./extras/libtap/CMakeLists.txt

add_library(Libtap.o tap.c)
target_include_directories(Libtap.o PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
Run Code Online (Sandbox Code Playgroud)

所以,现在的问题是:我担心的原因是这个设置会为我正在使用的每个文件创建一个"公共"库,包括额外的库(不应该分发).如果我有10个库src,4个依赖项extras(包括libtap,我用来测试)和至少相同数量的测试文件,我最终会得到24个编译工件.

  • 有没有更好的方法来公开库链接?
  • 我还没有编译"主要",那将是什么样的正确配置?
  • add_definitions向编译器添加标志的正确方法吗?
  • 如何让这个结构更

Com*_*sMS 1

有没有更好的方法来公开库以进行链接?

不,这看起来不错。

然而,您可能需要重新考虑创建静态库的粒度。例如,如果除测试之外的所有应用程序都只会组合使用Module1Module2,您可能希望将它们合并到单个库目标中。当然,测试将链接到他们不使用的组件部分,但这对于降低构建复杂性来说只是一个很小的代价。

我还没有编译“main”,正确的配置是什么?

src/CMakeLists.txt将其添加到以下也没有问题:

add_executable(my_main main.c)
target_link_libraries(my_main Module1.o Module2.o)
Run Code Online (Sandbox Code Playgroud)

add_definitions 是向编译器添加标志的正确方法吗?

它可以用于此目的,但可能并不理想。

较新的 CMake 脚本应该更喜欢使用该target_compile_options命令来实现此目的。这里唯一的缺点是,如果您想为项目中的所有目标重用相同的编译选项,则还必须target_compile_options对每个目标进行相同的调用。有关如何解决此问题的提示,请参阅下文。

我怎样才能使这个结构更加干燥?

首先,与大多数程序代码不同,冗余在构建系统代码中通常不是一个大问题。这里值得注意的是那些妨碍可维护性的东西。回到之前的常见编译器选项:如果您将来想更改这些标志,则可能需要为每个目标更改它们。在这里,集中有关选项的知识是有意义的:要么function在顶层引入为给定目标设置选项,要么将选项存储到全局变量。

在任何一种情况下,您都必须为每个目标编写一行才能获得该选项,但此后不会产生任何维护开销。作为额外的好处,如果您将来实际上只需要更改一个目标的选项,您仍然可以灵活地这样做。

不过,请注意不要过度设计。构建系统首先应该把事情做好

如果最简单的设置方法意味着您需要进行大量复制/粘贴,那么就采用它吧!如果在以后的维护过程中发现确实存在一些不必要的冗余,那么您始终可以进行重构。

您越早接受这样一个事实,即您的 CMake 脚本永远不会像您的程序代码一样漂亮,就越好;)

最后有一点挑剔:避免给你的目标名称提供扩展名。也就是说,而不是

add_library(Libtap.o tap.c)

考虑

add_library(Libtap tap.c)

无论如何,CMake 都会根据目标平台自动附加正确的文件结尾。