使用 shell 命令或 globbing 检索源,相应地更新构建

Retrieve sources using shell command or globbing, update build accordingly

如何使用任意 shell 命令获取 CMake 生成的构建系统来检索源文件并正确更新构建?

这样做的动机是从另一个构建系统迁移到 CMake。中间目标是在构建系统之间共享一个源文件列表。源文件列表实际上可能存在于其他构建系统中,但通常假设某些 shell 命令可以检索源文件列表。这个问题与使用 globbing 获取源文件名的问题非常相似,但在这种情况下,手动列出 CMakeLists.txt 文件中的所有源文件并不是一个合理的选择。

即使没有办法让 CMake 自己执行此操作,提供任何自动化解决方案的答案也是可以的(例如,包装生成的构建系统的脚本)。


很简单的问题的具体例子

我们有一个包含两个源文件的应用程序,main.cppfoo.cpp。出于某种原因,希望使用一些 shell 命令获取源文件的名称,而不是将它们列在 CMakeLists.txt 文件中。对于这个例子,文件在 files.txt 中每行列出一个,我们 cat 这个文件。通常,shell 命令是一些以神秘方式检索源文件列表的脚本。

main.cpp(第 1 版)

#include "foo.h"

int main() {
    foo();
}

foo.h

#ifndef FOO_H
#define FOO_H

void foo();

#endif

foo.cpp

#include "foo.h"
#include <iostream>

void foo() {
    std::cout << "foo()" << std::endl;
}

files.txt(第 1 版)

main.cpp
foo.cpp

CMakeLists.txt

cmake_minimum_required(VERSION 2.8.1)
project(SourcesFromCommand)

# run some external command that retrieves our source files
execute_process(COMMAND cat ${CMAKE_CURRENT_SOURCE_DIR}/files.txt OUTPUT_VARIABLE files)
# turn newline separated relative filenames into cmake list of absolute filenames
string(REPLACE "\n" ";${CMAKE_CURRENT_SOURCE_DIR}/" file_list ${files})

# print filenames to make sure the list came out right
foreach(file ${file_list})
    message(${file})
endforeach()

add_executable(main ${file_list})

CMake 为包含上述文件的项目生成一个工作构建系统。后来,我们的应用程序成功并广受欢迎,因此我们决定为版本添加新功能。 2.0。 main.cpp 现在调用位于 bar.hbar.cpp 中的 bar()。我们相应地更新 files.txt

main.cpp(第 2 版)

#include "foo.h"
#include "bar.h"

int main() {
    foo();
    bar();
}

bar.h

#ifndef BAR_H
#define BAR_H

void bar();

#endif

bar.cpp

#include "bar.h"

#include <iostream>

void bar() {
    std::cout << "bar()" << std::endl;
}

files.txt(第 2 版)

main.cpp
foo.cpp
bar.cpp

之前由 CMake 生成的构建系统不再有效——尝试使用它会导致链接器错误,因为它不知道 bar.cpp。这可以通过触摸 CMakeLists.txt 文件或重新运行 cmake 命令来手动解决,但构建系统的目的是让我们从这种艰巨且容易被遗忘的体力劳动中解放出来。如何实现自动化?

您可以使用 touch 包含相关 CMakeLists.txt 文件的 makefile 来解决这个问题。

我将从 OP 的示例继续并添加一个目录 glob_us/,我们希望从中全局匹配所有匹配 *.cpp 的文件名。它包含 baz.cpp 类似于 OP 的 bar.cpp.

相关文件:

  • 生成文件
  • CMakeLists.txt
  • files.txt
  • main.cpp
  • main.h
  • foo.cpp
  • foo.h
  • bar.cpp
  • bar.h
  • glob_us/baz.h
  • glob_us/baz.cpp

CMakeLists.txt的底部变为:

file(GLOB globbed_files glob_us/*.cpp)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/glob_us)

add_executable(main ${file_list} ${globbed_files})

Makefile 包含以下内容:

MAKEFILE_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))

GLOBBED := $(wildcard $(MAKEFILE_DIR)/glob_us/*cpp)

# phony target that depends on whatever files we need to touch
cmake: $(MAKEFILE_DIR)/CMakeLists.txt

$(MAKEFILE_DIR)/CMakeLists.txt: $(MAKEFILE_DIR)/files.txt $(GLOBBED)
    @touch $(MAKEFILE_DIR)/CMakeLists.txt

.PHONY: cmake

生成构建系统:

mkdir build
cd build
cmake -G $MY_FAVORITE_GENERATOR ..

然后构建:

make -f ../Makefile && $MY_FAVORITE_BUILD_SYSTEM_COMMAND

可以将文件添加到 glob_us 或从 glob_us 或在 OP 的示例中添加到 files.txt,而无需手动干预。