CMakeLists.txt文件写法(6):要求CMake根据指定的源文件生成库文件

本文介绍了如何使用 CMake 的 ADD_LIBRARY 命令来添加一个库目标到项目中,并指定了源文件。可以根据需要设置库类型为 SHARED、STATIC 或 MODULE,默认情况下为 BUILD_SHARED_LIBS 的当前值,若未设置则默认为 STATIC。此外,还可以通过 EXCLUDE_FROM_ALL 选项控制该目标是否默认被构建。

ADD_LIBRARY: Add a library to the project using the specified source files.

  •   ADD_LIBRARY(libname [SHARED | STATIC | MODULE] [EXCLUDE_FROM_ALL]
    source1 source2 ... sourceN)

    Adds a library target. SHARED, STATIC or MODULE keywords are used to set the library type. If the keyword MODULE appears, the library type is set to MH_BUNDLE on systems which use dyld. On systems without dyld, MODULE is treated like SHARED. If no keywords appear as the second argument, the type defaults to the current value of BUILD_SHARED_LIBS. If this variable is not set, the type defaults to STATIC.

    If EXCLUDE_FROM_ALL is given the target will not be built by default. It will be built only if the user explicitly builds the target or another target that requires the target depends on it.

     

<think>我们有一个Unity测试项目,需要生成两个可执行文件:一个用于运行测试,另一个可能是应用程序本身。但是,根据错误信息,我们遇到了符号重复定义的问题(如_setUp, _tearDown, _main等)。解决这个问题的关键在于正确地组织项目结构,确保每个可执行文件有自己独立的入口点,并且避免Unity框架的重复包含。 根据之前的解决方案,我们已经知道: 1. 需要将Unity编译为静态库(避免unity.c中的符号重复定义)。 2. 每个可执行文件应该有且仅有一个main函数。 现在,用户的需求是生成两个可执行文件。假设这两个可执行文件分别是: - 一个用于运行测试的可执行文件(test_runner) - 另一个是应用程序本身(app) 但是,在Unity测试框架中,测试运行器通常就是一个可执行文件,它包含main函数并运行所有测试。而应用程序可能是我们要测试的代码,但通常应用程序本身可能也是一个可执行文件(或者库,然后由测试运行器链接测试)。 然而,在Unity的例子中,我们通常构建一个测试运行器可执行文件,而应用程序代码通常编译为静态库或直接链接到测试运行器中(以便测试)。因此,这里生成两个可执行文件可能是指: 1. 一个是被测试的应用程序(如果它本身是一个可执行程序)。 2. 另一个是测试运行器(用于测试该应用程序)。 但请注意,通常被测试的应用程序可能是一个库(而不是可执行文件),这样测试运行器可以链接该库并测试其中的函数。如果应用程序本身需要作为独立的可执行文件运行,那么我们可以同时构建两个目标:应用程序可执行文件和测试运行器可执行文件。 因此,我们假设项目结构如下: - src/ : 存放应用程序源代码(可能有main函数,也可能没有) - test/ : 存放测试代码 我们需要在CMakeLists.txt中定义两个可执行文件目标: 1. 应用程序可执行文件(app) 2. 测试运行器可执行文件(test_runner) 为了避免符号冲突,特别是Unity框架中的符号(如setUp, tearDown等)以及main函数的重复,我们需要: - 将Unity框架编译为静态库(这样每个可执行文件链接自己的Unity库实例,但由于是静态库,每个可执行文件会有独立的副本,不会冲突)。 - 确保每个可执行文件只有一个main函数。 但是,注意:静态库中的符号在链接时,如果多个静态库有相同的符号,则链接器会按照顺序使用第一个找到的符号。然而,我们这里的问题是两个可执行文件需要分别链接,所以它们彼此独立,不会相互影响。 因此,我们可以在同一个CMake项目中构建两个可执行文件,每个可执行文件链接Unity静态库(但注意,Unity静态库在每个可执行文件中都是独立的一份,所以不会冲突)。 然而,我们之前遇到的错误是因为在同一个可执行文件中出现了多个main函数(测试运行器中有多个测试文件都包含了main函数)。所以,我们需要确保每个可执行文件目标只有一个main函数。 具体步骤: 1. 将Unity源码编译为静态库(add_library(Unity STATIC unity.c)),这样setUp、tearDown等函数在库中,每个可执行文件链接时只包含一份。 2. 创建两个可执行文件目标: - 应用程序可执行文件(app):包含应用程序的源文件(如果有main函数,则其中一个文件有main函数)。 - 测试运行器可执行文件(test_runner):包含测试代码和测试运行器的main函数(注意:测试运行器只能有一个main函数)。 3. 注意:测试运行器会链接应用程序的代码(因为要测试其中的函数)和Unity库。 4. 在测试代码中,不要定义main函数,而是由测试运行器的一个单独的源文件(如main_runner.c)提供main函数,并调用所有测试。 5. 应用程序可执行文件则有自己的main函数(在应用程序的某个源文件中)。 示例CMakeLists.txt结构: ```cmake cmake_minimum_required(VERSION 3.10) project(MyProject) # 设置Unity路径 set(UNITY_ROOT_DIR "e:/Unity-master/Unity-master") include_directories(${UNITY_ROOT_DIR}/src) # 编译Unity为静态库 add_library(Unity STATIC ${UNITY_ROOT_DIR}/src/unity.c ) # 添加应用程序源代码(假设在src目录下) file(GLOB APP_SOURCES "src/*.c") # 注意:其中有一个文件包含main函数(例如app_main.c) add_executable(app ${APP_SOURCES}) # 添加测试运行器可执行文件 # 首先,添加应用程序的源代码(测试需要链接这些代码)和测试代码 file(GLOB TEST_SOURCES "test/*.c") # 注意:在test目录下,我们有一个main_runner.c文件,它包含main函数,其他测试文件不应该包含main函数。 add_executable(test_runner ${TEST_SOURCES} ) # 测试运行器需要链接应用程序的代码吗?实际上,测试运行器需要调用应用程序中的函数,所以应该链接应用程序的库。 # 但我们没有将应用程序编译为库,所以我们可以直接将应用程序的源代码链接到测试运行器?或者将应用程序编译为库,然后app和test_runner都链接这个库。 # 更好的做法:将应用程序编译为静态库(这样app可执行文件和test_runner都可以链接它) # 修改:将应用程序的源代码编译为静态库,然后app可执行文件链接这个库(并加上自己的main函数),测试运行器也链接这个库。 # 步骤: # 1. 创建应用程序静态库(不包含main函数) # 假设应用程序的源代码中,除了包含main函数的文件,其他文件编译为静态库。 # 将包含main函数的文件单独拿出来。 # 假设应用程序的源代码结构: # src/ # app_main.c # 包含main函数 # app_func1.c # app_func1.h # app_func2.c # ... # 因此,我们可以将除app_main.c以外的文件编译为静态库。 file(GLOB APP_LIB_SOURCES "src/app_func1.c" "src/app_func2.c" ... ) # 或者排除app_main.c file(GLOB ALL_APP_SOURCES "src/*.c") list(REMOVE_ITEM APP_LIB_SOURCES "src/app_main.c") add_library(app_lib STATIC ${APP_LIB_SOURCES}) # 然后,应用程序可执行文件链接这个库,并包含main函数文件 add_executable(app src/app_main.c) target_link_libraries(app app_lib) # 测试运行器需要包含所有测试文件,并且有一个main函数(在test/main_runner.c中) # 测试运行器链接应用程序库和Unity库 add_executable(test_runner test/main_runner.c test/test_app_func1.c test/test_app_func2.c ... ) target_link_libraries(test_runner app_lib Unity) # 同时,测试运行器需要包含应用程序的头文件和Unity头文件 target_include_directories(test_runner PRIVATE src) ``` 这样,两个可执行文件(app和test_runner)都可以被构建。 但是,注意:测试运行器中的测试文件不应该包含main函数,所以之前出现的main函数重复定义问题就不会发生。 回顾之前的错误,是因为在同一个可执行文件中(可能是test_runner)有多个源文件包含了main函数(每个测试运行器文件都有一个main函数)。现在我们通过一个单独的main_runner.c来提供main函数,其他测试文件只包含测试用例。 因此,总结: 1. 将Unity编译为静态库。 2. 将应用程序的核心代码(不包含main函数)编译为静态库(app_lib)。 3. 创建应用程序可执行文件(app),它由main函数文件(app_main.c)和链接app_lib组成。 4. 创建测试运行器可执行文件(test_runner),它包含测试代码和一个main_runner.c文件(其中包含main函数),并链接app_lib和Unity静态库。 这样,两个可执行文件就可以同时生成,并且没有符号冲突。 注意:在测试运行器中,我们可能会有多个测试文件,每个测试文件包含一个或多个测试用例,但都不包含main函数。在main_runner.c中,我们调用所有测试用例。 例如,main_runner.c的内容: ```c #include "unity.h" // 声明各个测试文件中的测试函数 extern void test_app_func1(void); extern void test_app_func2(void); int main(void) { UNITY_BEGIN(); RUN_TEST(test_app_func1); RUN_TEST(test_app_func2); // ... 其他测试 return UNITY_END(); } ``` 这样,测试运行器只有一个main函数,不会冲突。 因此,在CMakeLists.txt中,我们定义了两个可执行文件目标:`app`和`test_runner`,分别用于应用程序和测试。 构建命令: 在build目录下执行: ``` cmake .. cmake --build . # 这将同时构建两个可执行文件 ``` 或者,也可以只构建其中一个目标: ``` cmake --build . --target app cmake --build . --target test_runner ``` 这样,我们就实现了在一个CMake项目中生成两个可执行文件。 注意:如果测试运行器需要测试应用程序中的函数,那么应用程序的函数应该被包含在app_lib静态库中。 关于Unity的静态库:由于我们将其编译为静态库,并且两个可执行文件都链接了它(虽然在这个例子中,只有test_runner链接了Unity),所以不会出现重复定义问题,因为每个可执行文件都是独立链接的。 但是,如果将来有第三个可执行文件也需要Unity,那么它也会链接这个静态库,同样不会有问题。 所以,上述方法可以解决用户的问题。 最后,针对用户之前遇到的错误,我们通过将Unity编译为静态库,并确保每个可执行文件只有一个main函数,从而避免了链接错误。 如果用户希望保留之前的例子(example_6)的结构,但生成两个可执行文件(例如,一个运行测试,另一个是示例程序),那么我们需要调整该例子的结构。 由于用户没有提供具体的目录结构,我们假设example_6的结构如下: example_6/ src/ - 应用程序代码(可能已经有main函数) test/ - 测试代码(包括测试运行器) 我们可以在example_6CMakeLists.txt中按照上面的方法组织。 注意:如果应用程序本身只是一个库(没有main函数),那么我们可以只生成一个可执行文件(即测试运行器),而另一个可执行文件就不需要了。但根据用户要求,我们需要生成两个可执行文件,所以应用程序必须包含main函数。 因此,我们按照上述CMakeLists.txt写法即可。 如果用户希望看到完整的CMakeLists.txt示例,我们可以提供一个简化版本: 注意:以下是一个示例,需要根据实际项目结构调整。 ```cmake cmake_minimum_required(VERSION 3.10) project(UnityExample) # 设置Unity路径 set(UNITY_ROOT_DIR "e:/Unity-master/Unity-master") # 包含Unity头文件 include_directories(${UNITY_ROOT_DIR}/src) # 将Unity编译为静态库 add_library(Unity STATIC ${UNITY_ROOT_DIR}/src/unity.c) # 应用程序库(不包含main函数) # 假设应用程序源文件在src目录,排除src/main.c file(GLOB APP_SOURCES "src/*.c") list(REMOVE_ITEM APP_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/main.c") # 如果有多个目录,可以使用aux_source_directory或file(GLOB_RECURSE) add_library(app_lib STATIC ${APP_SOURCES}) # 应用程序可执行文件(包含main函数) add_executable(app src/main.c) target_link_libraries(app app_lib) # 测试运行器 # 假设测试代码在test目录下,并且有一个main_runner.c文件作为入口 file(GLOB TEST_SOURCES "test/*.c") # 注意:如果测试代码中也有main函数(除了main_runner.c),需要排除,但我们这里假设只有main_runner.c有main add_executable(test_runner test/main_runner.c ${TEST_SOURCES}) # 注意:这里我们添加了所有测试源文件,包括main_runner.c和其他的测试文件。但注意,其他测试文件不能包含main函数。 # 测试运行器需要链接应用程序库和Unity库 target_link_libraries(test_runner app_lib Unity) # 测试运行器需要包含应用程序的头文件 target_include_directories(test_runner PRIVATE src) ``` 这样,在构建之后,我们就会在构建目录下得到app和test_runner两个可执行文件(在Windows下可能是app.exe和test_runner.exe)。 最后,关于用户提到的Unity例子中的错误,通过这种方式重构后,应该可以解决。 如果用户在实际操作中遇到问题,请检查: - 文件路径是否正确(特别是Unity的路径) - 是否每个可执行文件只有一个main函数 - 是否将应用程序的核心代码编译为库(避免重复编译和符号重复) 希望这个解决方案能帮助用户成功构建两个可执行文件。 相关问题: 1. 如何在CMake中同时生成动态库和可执行文件? 2. 如何让CMake构建的可执行文件输出到指定目录? 3. 如何为不同的可执行文件设置不同的编译选项? 4. 如何在CMake中组织多个测试套件? 5. 如何将Unity测试框架集成到CMake项目中? 注意:如果用户使用的是多目录结构(如引用[3]),则可能需要使用add_subdirectory来组织子目录,但基本原理相同。 </think>通过 CMakeLists.txt 生成多个可执行文件需要合理组织项目结构并正确配置构建规则。以下是标准解决方案,基于引用[1][2][3][5]的 CMake 最佳实践: ### 解决方案:多可执行文件配置 在 CMakeLists.txt 中定义多个独立的 `add_executable` 目标即可生成多个可执行文件: ```cmake cmake_minimum_required(VERSION 3.10) project(MultiExecutableProject) # 可执行文件1:主应用程序 add_executable(app_main src/main_app.cpp src/core_functions.cpp ) # 可执行文件2:单元测试运行器 add_executable(test_runner test/test_main.cpp test/test_cases.cpp ${UNITY_SOURCES} # Unity测试框架文件 ) # 共享配置(可选) target_include_directories(app_main PUBLIC include) target_include_directories(test_runner PUBLIC include) target_compile_options(app_main PRIVATE -Wall) ``` ### 关键配置说明 1. **独立目标声明** 每个 `add_executable()` 定义一个独立可执行文件: ```cmake add_executable(<target_name> [source_files...]) ``` - 目标名需唯一(如 `app_main` 和 `test_runner`) - 源文件列表可包含多个文件(自动关联编译) 2. **共享代码处理**(引用[3]) 若多个可执行文件共享代码,使用静态库封装: ```cmake # 创建共享库 add_library(common_lib STATIC src/shared_code.cpp src/utils.cpp ) # 链接到可执行文件 target_link_libraries(app_main common_lib) target_link_libraries(test_runner common_lib) ``` 3. **目录结构示例**(引用[3]) ```vim project_root/ ├── CMakeLists.txt # 主配置文件 ├── src/ │ ├── main_app.cpp # 主程序入口 │ └── core_functions.cpp # 核心逻辑 ├── test/ │ ├── test_main.cpp # 测试入口 │ └── test_cases.cpp # 测试用例 └── include/ # 共享头文件 ``` 4. **构建与输出**(引用[4]) ```bash mkdir build && cd build cmake .. -G "Unix Makefiles" # 生成构建系统 cmake --build . # 编译所有目标 ``` 生成的可执行文件默认位于 `build/` 目录: - `app_main`(主应用程序) - `test_runner`(测试程序) ### 高级配置技巧 1. **条件编译** 使用 `option()` 控制构建目标: ```cmake option(BUILD_TESTS "Build test executable" ON) if(BUILD_TESTS) add_executable(test_runner test/*.cpp) endif() ``` 2. **输出目录定制** 修改可执行文件输出路径: ```cmake set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) ``` 3. **依赖管理**(引用[5]) ```cmake add_dependencies(test_runner app_main) # 确保app_main先编译 ``` ### 验证结果 ```bash # 查看生成的可执行文件 ls build/bin/ app_main test_runner # 运行程序 ./build/bin/app_main ./build/bin/test_runner ``` > **最佳实践**:当项目包含测试套件时(如 Unity 框架),推荐将测试运行器作为独立可执行文件构建,确保与主程序隔离[^3][^5]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值