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

时间:2014-09-11 14:37:25

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

./测试/的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)

./额外/的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})

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

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

1 个答案:

答案 0 :(得分:1)

  

有没有更好的方法将库公开给链接?

不,这似乎很好。

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

  

我还没有编译“主要”,什么是正确的配置   那个?

将它添加到src/CMakeLists.txt也没有错:

add_executable(my_main main.c)
target_link_libraries(my_main Module1.o Module2.o)
  

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

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

较新的CMake脚本为此需要首选target_compile_options命令。这里唯一的缺点是,如果要为项目中的所有目标重用相同的编译选项,则还必须对每个目标执行相同的target_compile_options调用。请参阅以下有关如何解决此问题的提示。

  

如何让这个结构更干?

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

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

不过,请注意不要过度工程。构建系统首先应该完成任务

如果设置它的最简单方法意味着你复制/粘贴很多,那就去吧!如果在维护期间发现你有一些真正不必要的冗余,你可以随时重构。

你越早接受你的CMake脚本永远不会像程序代码一样漂亮的事实,那就更好了;)

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

add_library(Libtap.o tap.c)

考虑

add_library(Libtap tap.c)

CMake会根据目标平台自动附加正确的文件。