4

(道歉:从 CMake 邮件列表中交叉发布)

我正在尝试了解 CMake 的正则表达式实现;我有一个包含 4 个文件夹和 2 个文本文件的文件夹,如下所示:

build/  
projectA/  
CMakeLists.txt  
extrafiles/  
README  
temp/

一行 CMakeLists.txt 是:

set(CPACK_SOURCE_IGNORE_FILES "[^projectA]$")

在随后生成的我的源包中,build/存在和 ,但不存在projectA/2个文本文件。我正试图达到一个阶段,正则表达式将忽略文件夹中除,和之外的所有内容,但目前无法弄清楚我提供的正则表达式如何给出这些结果。extrafilestemp/projectA/READMECMakeLists.txt

我想这归结为如何使用正则表达式匹配整个字符串。我意识到文档说Matches any character(s) not inside the brackets我猜我哪里出错了......

进一步探索

在试图理解 CMake 的正则表达式实现时,我想我会从第一条原则开始,做一些简单的事情。

如果我做

set(CPACK_SOURCE_IGNORE_FILES projectA)

然后该文件夹projectA不会出现在我的源包中(如预期的那样);但是,如果我这样做

set(CPACK_SOURCE_IGNORE_FILES ^projectA$)

或者

set(CPACK_SOURCE_IGNORE_FILES ^/projectA/$)

然后projectA 确实出现了。我不理解的^(行首)和(行尾)是什么?$

很明显,projectA这实际上不是我的项目的名称,但是当我将项目文件夹物理重命名为projectA. 但是,当我更换

set(CPACK_SOURCE_IGNORE_FILES projectA)

set(CPACK_SOURCE_IGNORE_FILES <name of my project>)

并将我的实际项目文件夹重命名projectA为其实际名称,我最终得到一个的压缩包!啊!我完全知道 CMake 对我玩了什么奇怪的把戏,但我只想哭。

任何见解将不胜感激!

自包含示例

根据弗雷泽的要求,一个自包含的示例显示了我描述的 2 个“功能”。但是,我确实知道我正在以一种稍微不标准的方式运行 CMake,以便将所有与单个构建有关的事情放在一起,所以如果有任何证据以更标准的方式运行 CMake 可以消除这些问题,我会有兴趣看看他们。

第一步:创建文件

创建树:

cd ~
mkdir 
cd projectA
mkdir projectA    

创建 C 文件,并将其保存为~/projectA/projectA/helloworld.c

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    printf("!!!Hello World!!!\n"); /* prints !!!Hello World!!! */
    printf("!!!Hello CMake!!!\n"); /* prints !!!Hello CMake!!! */
    return 0;
}

创建一个不需要编译的文件,并将其保存为~/projectA/test.sh

#A non compiled program
echo "Hello world!"

创建~/projectA/CMakeLists.txt

cmake_minimum_required (VERSION 2.6)
project (HelloWorld) 

set(CMAKE_INSTALL_PREFIX "$ENV{HOME}/projectAinstall")

add_executable(helloworld projectA/helloworld.c)
install(TARGETS helloworld DESTINATION .)

include(InstallRequiredSystemLibraries)
set(CPACK_GENERATOR "TGZ")
set(CPACK_SOURCE_GENERATOR "TGZ")

include(CPack)

第二步:编译

~/projectA中,运行:

chris@chris:~/projectA$ cmake -H. -Bbuild

然后:

make -C build && make -C build package && make -C build package_source

这会在文件build夹中生成 2 个 tarball。将它们移动到其他地方并解压缩它们会显示helloworld在二进制 tarball 中(如预期的那样),以及源 tarball 中的所有内容~/projectA/projectA,包括test.sh哪些不会被编译(Fraser 似乎对此感到惊讶)

第 3 步:随机测试

修改CMakeLists.txt以包含

set(CPACK_SOURCE_IGNORE_FILES "projectA")

并重新运行上面的 CMake / Make 命令会产生一个空的源 tarball,但使用与上面相同的二进制 tarball。我现在已经意识到,更改目录树以使顶级目录testproject(与其子文件夹不同)不会导致空的源 tarball,并且只会删除中列出的文件CPACK_SOURCE_IGNORE_FILES

4

1 回答 1

2

我不认为你可以达到你使用后的目标CPACK_SOURCE_IGNORE_FILES(虽然我不确定)。正如您正确指出的那样,CMake 的正则表达式处理允许排除字符组,但我认为它不允许否定整个模式。[请参阅编辑末尾的更新答案。]

话虽如此,我想您可以列出您希望在install命令中排除的所有文件夹。不如排除除“projectA”之外的所有内容那么强大,但仍然是语法:

install(DIRECTORY .
        DESTINATION the_install_subdir
        REGEX "build|extrafiles|temp+" EXCLUDE)

关于空的压缩包,我想您可能<name of my project>同时拥有项目的根目录和子目录?因此,在您的示例中,如果您将项目称为“projectA”,那么您将拥有“projectA/build”、“projectA/projectA”等。

如果是这样,正则表达式将在完整路径上工作,因此项目中的所有文件都将包含projectA/在它们的路径中。

至于哭声……好吧,我只能劝你抓紧自己,振作起来!:-)


编辑:作为对评论的回应,这里有一个使用install命令来实现目标的快速示例:

install(DIRECTORY projectA
        DESTINATION the_install_subdir)
install(FILES CMakeLists.txt README DESTINATION the_install_subdir)

进一步编辑:

好的,你的例子很有帮助——我确实误解了你在做什么。我没有发现您实际上是在制作 2 个不同的目标(“package”和“package_source”)。我曾以为您正在通过执行类似的操作来创建二进制包

cpack -G DEB

并且您正在通过执行创建另一个包

cpack -G TGZ

它们都构建了二进制包。我的错误——我应该多加注意。对不起!


至于你的具体问题:

问题 1

在我看来,安装未编译但与包含所有已编译文件(即 bin)的文件夹处于同一级别的文件/目录,然后使用 CPACK_SOURCE_IGNORE_FILES 忽略 bin 文件夹会导致一个空的 tarball - 这是正确的?

我认为这意味着:“应该做set(CPACK_SOURCE_IGNORE_FILES "${CMAKE_BINARY_DIR}")一个空的tarball吗?” 答案很可能不是。

因为CPACK_SOURCE_IGNORE_FILES代表一个正则表达式,我确信在某些情况下,生成的正则表达式可以匹配项目中的每个文件,这会导致一个空的 tarball。但是我认为这不太可能。

如果不是通过变量使用 bin 目录的完整路径,而是${CMAKE_BINARY_DIR}只给出文件夹名称,那么空 tarball 的可能性就会大得多。假设您将 bin 目录称为“build”并拥有set(CPACK_SOURCE_IGNORE_FILES "build"). 如果您的项目位于 say 中~/test_builds/projectA,那么正则表达式“build”将匹配项目中的每个文件,因为每个文件都包含“test_builds”;导致一个空的压缩包。

我认为这是每次生成空 tarball 时问题的症结所在。无论正则表达式试图实现什么,它实际上最终都会匹配并排除所有文件。


问题2

似乎CMAKE_SOURCE_DIR未“安装”的文件最终不会出现在二进制压缩包中,但会出现在源压缩包中

是的,“package_source”确实是二进制包的不同目标。默认情况下,它包含 中的所有文件${CMAKE_SOURCE_DIR},而“包”目标仅包含通过install命令添加的项目。在这里,术语“源文件”可能有点用词不当,因为它表示源树中的所有文件——不仅仅是 .c、.cc、.cxx 等。


原始问题

我认为毕竟有一种相当安全的方法可以实现您的原始目标!如果您使用file(GLOB ...)生成根目录中所有文件/文件夹的非递归列表,然后删除您希望保留在源包中的那些,您应该能够使用剩余的列表作为正则表达式值CPACK_SOURCE_IGNORE_FILES

file(GLOB SourceIgnoreFiles "${CMAKE_SOURCE_DIR}/*")
set(SourceKeepFiles "${CMAKE_SOURCE_DIR}/projectA"
                    "${CMAKE_SOURCE_DIR}/CMakeLists.txt"
                    "${CMAKE_SOURCE_DIR}/README")
list(REMOVE_ITEM SourceIgnoreFiles ${SourceKeepFiles})
# Escape any '.' characters
string(REPLACE "." "\\\\." SourceIgnoreFiles "${SourceIgnoreFiles}")
set(CPACK_SOURCE_IGNORE_FILES "${SourceIgnoreFiles}")


希望这现在对您有用。再次抱歉误导。

于 2013-07-30T21:14:51.803 回答