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

标签 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

由于所有文件都分布在多个目录中,我必须找到一种方法来找到 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?

不,这看起来不错。

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

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/

相关文章:

opencv - CMake OpenCV 3.0.0 和在 Ubuntu 中构建可执行文件和库

c - errno: 38 (Function not implemented) 在调用更改 sysctl 值时

c - 初始化结构体数组 - C

cmake 字符串不带引号和带引号

cmake - 如何配置 cmake 以链接到预构建的共享库?

c++ - 链接 boost - 对 `boost::serialization::singleton_module::get_lock()' 的 undefined reference

C 将 uint32 类型转换为 uint16

C - 使用 .h 文件

c++ - 简单的音频处理库?

cmake:CUDA 目标的目标特定预处理器定义似乎不起作用