gpt4 book ai didi

Copy file from source directory to binary directory using CMake(使用CMake将文件从源目录复制到二进制目录)

转载 作者:bug小助手 更新时间:2023-10-25 21:14:02 33 4
gpt4 key购买 nike



I'm trying to create a simple project on CLion. It uses CMake to generate Makefiles to build project (or some sort of it)

我正试着在克利安上创建一个简单的项目。它使用CMake生成Makefile来构建项目(或某种类型的项目)


All I need to is transfer some non-project file (some sort of resource file) to binary directory each time when I run the my code.

每次运行我的代码时,我所需要的就是将一些非项目文件(某种资源文件)传输到二进制目录。


That file contains test data and application open it to read them. I tried several ways to do so:

该文件包含测试数据,应用程序打开该文件进行读取。我尝试了几种方法来做到这一点:



  • Via file(COPY ...

    通过文件(复制...


    file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/input.txt
    DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/input.txt

    Looking good but it work just once and not recopy file after next run.

    看起来不错,但它只工作一次,下一次运行后不能重新复制文件。



  • Via add_custom_command

    通过添加自定义命令



    • OUTPUT version

      输出版本


      add_custom_command(
      OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/input.txt
      COMMAND ${CMAKE_COMMAND} -E copy
      ${CMAKE_CURRENT_SOURCE_DIR}/input.txt
      ${CMAKE_CURRENT_BINARY_DIR}/input.txt)


    • TARGET version

      目标版本


      add_custom_target(foo)
      add_custom_command(
      TARGET foo
      COMMAND ${CMAKE_COMMAND} copy
      ${CMAKE_CURRENT_BINARY_DIR}/test/input.txt
      ${CMAKE_SOURCE_DIR})



    But no one of it work.

    但都不管用。




What am I doing wrong?

我做错了什么?


更多回答
优秀答案推荐

You may consider using configure_file with the COPYONLY option:

您可以考虑将CONFIGURE_FILE与COPYONLY选项一起使用:



configure_file(<input> <output> COPYONLY)


Unlike file(COPY ...) it creates a file-level dependency between input and output, that is:

不同于文件(复制...)它在输入和输出之间创建了文件级依赖关系,即:




If the input file is modified the build system will re-run CMake to re-configure the file and generate the build system again.




Both option are valid and targeting two different steps of your build:

这两个选项都是有效的,并且针对构建的两个不同步骤:



  1. file(COPY ... copies the file during the configuration step and only in this step. When you rebuild your project without having changed your cmake configuration, this command won't be executed.

  2. add_custom_command is the preferred choice when you want to copy the file around on each build step.


The right version for your task would be:

适合您的任务的版本应该是:


add_custom_command(
TARGET foo POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_SOURCE_DIR}/test/input.txt
${CMAKE_CURRENT_BINARY_DIR}/input.txt)

you can choose between PRE_BUILD, PRE_LINK, POST_BUILD
best is you read the documentation of add_custom_command

你可以在PRE_BUILD、PRE_LINK、POST_BUILD之间选择,最好是阅读add_custom_command的文档。


An example on how to use the first version can be found here: Use CMake add_custom_command to generate source for another target

有关如何使用第一个版本的示例可在此处找到:使用CMake添加自定义命令为另一个目标生成源



The first of option you tried doesn't work for two reasons.

您尝试的第一个选项不起作用,原因有两个。



First, you forgot to close the parenthesis.

首先,你忘了把括号括起来。



Second, the DESTINATION should be a directory, not a file name. Assuming that you closed the parenthesis, the file would end up in a folder called input.txt.

其次,目标应该是目录,而不是文件名。假设您结束了括号,文件将最终保存在名为input.txt的文件夹中。



To make it work, just change it to

要使其工作,只需将其更改为



file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/input.txt
DESTINATION ${CMAKE_CURRENT_BINARY_DIR})


I would suggest TARGET_FILE_DIR if you want the file to be copied to the same folder as your .exe file.

如果您希望将文件复制到与.exe文件相同的文件夹中,我建议使用TARGET_FILE_DIR。




$
Directory of main file (.exe, .so.1.2, .a).




add_custom_command(
TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/input.txt
$<TARGET_FILE_DIR:${PROJECT_NAME}>)


In VS, this cmake script will copy input.txt to the same file as your final exe, no matter it's debug or release.

在VS中,此cmake脚本会将input.txt复制到与最终exe相同的文件中,无论它是调试还是发布。



The suggested configure_file is probably the easiest solution. However, it will not rerun the copy command to if you manually deleted the file from the build directory. To also handle this case, the following works for me:

建议的CONFIGURE_FILE可能是最简单的解决方案。但是,如果您手动从生成目录中删除该文件,则不会重新运行复制命令。要同时处理此案,以下方法对我有效:



add_custom_target(copy-test-makefile ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/input.txt)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/input.txt
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/input.txt
${CMAKE_CURRENT_BINARY_DIR}/input.txt
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/input.txt)


if you want to copy folder from currant directory to binary (build folder) folder

如果要将文件夹从Currant目录复制到二进制(Build文件夹)文件夹



file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/yourFolder/ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/yourFolder/)


then the syntexe is :

那么syntexe是:



file(COPY pathSource DESTINATION pathDistination)


This is what I used to copy some resource files:
the copy-files is an empty target to ignore errors

这是我用来复制一些资源文件的方法:复制文件是一个空目标,用于忽略错误



 add_custom_target(copy-files ALL
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_BINARY_DIR}/SOURCEDIRECTORY
${CMAKE_BINARY_DIR}/DESTINATIONDIRECTORY
)


Single File


First add a rule for the copying. OUTPUT specifies the produced file and DEPENDS creates a dependency, so when the input changes this rule is run again.

首先添加一条复制规则。OUTPUT指定生成的文件,Dependent创建依赖项,因此当输入更改时,此规则将再次运行。


set(MY_RESOURCE_FILE input.txt)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${MY_RESOURCE_FILE}
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/${MY_RESOURCE_FILE}
${CMAKE_CURRENT_BINARY_DIR}/${MY_RESOURCE_FILE}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${MY_RESOURCE_FILE}
)

Then add the 'result file' as a dependency to the target that needs it.

然后将“结果文件”作为依赖项添加到需要它的目标。


add_executable(main_executable
${CMAKE_CURRENT_BINARY_DIR}/${MY_RESOURCE_FILE}

src/main.cpp
)

This is a modern and clean CMake solution, as all dependencies are defined target based, and are propagated up-wards the dependency tree as needed. We are not changing any global state. Also tracks the files itself, so it is only copied when its out-of-date. This makes it also a robust and efficient solution.

这是一个现代而干净的CMake解决方案,因为所有依赖项都是基于目标定义的,并根据需要向上传播到依赖项树。我们不会改变任何全球国家。还会跟踪文件本身,因此只有在文件过期时才会进行复制。这也使其成为一种强大而高效的解决方案。


In case its needed by multiple targets, add them to all of them. This is good practice, as dependencies are explicitly specified, exactly where they are needed.

如果多个目标需要,则将它们添加到所有目标中。这是一种很好的实践,因为依赖关系是明确指定的,并且正好在需要它们的地方。


Multiple Files


As some people here asked for copying multiple files, I am adding this here as well, as a few more things need to be considered.

由于这里的一些人要求复制多个文件,我在这里也添加了这一点,因为需要考虑更多的事情。


Defining the files one has 2 possibilities, one can manually list them, similar to above. Or we can create a recursive glob rule, that discovers them automatically. The first is very straight forward, using relative paths to CMAKE_CURRENT_SOURCE_DIR.

定义文件有两种可能,一种是手动列出,类似于上面。或者我们可以创建一个递归全局规则,自动发现它们。第一种方法非常简单,使用CMAKE_CURRENT_SOURCE_DIR的相对路径。


set(MY_RESOURCE_FILES 
resources/font.ttf
resources/icon.svg
)

I am also showing the second glob case, at requires a bit more consideration.

我还展示了第二个GLOB案例,需要更多考虑。


file(GLOB_RECURSE
MY_RESOURCE_FILES
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
CONFIGURE_DEPENDS

${CMAKE_CURRENT_SOURCE_DIR}/resources/**.ttf # adapt these to your needs
${CMAKE_CURRENT_SOURCE_DIR}/resources/**.svg
)

Specifying CONFIGURE_DEPENDS re-runs the glob at the beginning the build when any target is out-of date. And RELATIVE gives us relative path to our source directory. And using two stars **.svg recursively searches all sub-directories as well.

当任何目标过期时,指定CONFIGURE_Dependers将在构建开始时重新运行GLOB。而Relative为我们提供了源目录的相对路径。并使用两个星号**.svg递归搜索所有子目录。


Now we create again a separate rule to make each resource file.

现在,我们再次创建一个单独的规则来创建每个资源文件。


FOREACH(MY_RESOURCE_FILE ${MY_RESOURCE_FILES})
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${MY_RESOURCE_FILE}
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/${MY_RESOURCE_FILE}
${CMAKE_CURRENT_BINARY_DIR}/${MY_RESOURCE_FILE}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${MY_RESOURCE_FILE}
)
ENDFOREACH()

This will also make it that only the resources missing or changed are copied.

这还将使其仅复制丢失或更改的资源。


And finally we add all of them to our executable. For that we just need to adjust the paths to the binary dir.

最后,我们将它们全部添加到我们的可执行文件中。为此,我们只需调整指向二进制目录的路径。


list(TRANSFORM MY_RESOURCE_FILES PREPEND ${CMAKE_CURRENT_BINARY_DIR}/)

add_executable(${MAIN_TARGET}
${MY_RESOURCE_FILES}

src/main.cpp
)


If you don't expect modifications to be made to the file in the binary directory, you could just save the complication of writing custom commands / targets for re-copying upon file changes and just use symlinks. CMake supports creating symlinks (or pretty much equivalent things on Windows). It has the file(CREATE_LINK <original> <linkname> [RESULT <result>] [COPY_ON_ERROR] [SYMBOLIC]) command, and the create_symlink <old> <new> commandline command.

如果您不希望对二进制目录中的文件进行修改,则只需省去编写定制命令/目标以在文件更改时重新复制的复杂性,而只需使用符号链接即可。CMake支持创建符号链接(或者在Windows上创建几乎等同的东西)。它具有FILE(CREATE_LINK<原始> [RESULT ][COPY_ON_ERROR][符号])命令和create_symlink 命令行命令。


If you don't expect the copy from the source directory to ever change / or are okay with re-configuring manually every time it changes, you can use the file(COPY_FILE <oldname> <newname> [RESULT <result>] [ONLY_IF_DIFFERENT] [INPUT_MAY_BE_RECENT]) or the file(COPY [...]) commands.

如果您不希望源目录中的副本发生更改,或者可以在每次更改时手动重新配置,则可以使用文件(Copy_FILE [Result>][Only_If_Different][Input_May_Be_Recent])或文件(Copy[...])命令。



If you want to put the content of example into install folder after build:

如果您想在构建后将示例内容放入Install文件夹:


code/
src/
example/
CMakeLists.txt

try add the following to your CMakeLists.txt:

尝试将以下内容添加到您的CMakeLists.txt:


install(DIRECTORY example/ DESTINATION example)

更多回答

Please note that configure_file isn't going to work with sub-directories, even if you use GLOB to create a list of files.

请注意,CONFIGURE_FILE不适用于子目录,即使您使用GLOB创建文件列表也是如此。

Also note that this isn't a "file-level dependency" between input and output: it's a dependency between the input and the cmake configure step. So instead of a regular build dependency where changing a source file rebuilds only the corresponding object file, this causes the whole of cmake to reconfigure, which may have other side effects.

还要注意,这不是输入和输出之间的“文件级依赖关系”:它是输入和cmake配置步骤之间的依赖关系。因此,与更改源文件仅重新构建相应的目标文件的常规构建依赖关系不同,这会导致整个cmake重新配置,这可能会产生其他副作用。

how we use this command to copy recursive directory files?

我们如何使用这个命令来复制递归目录文件?

Is it CMAKE_SOURCE_DIR or CMAKE_CURRENT_SOURCE_DIR?

是CMAKE_SOURCE_DIR还是CMAKE_CURRENT_SOURCE_DIR?

@SyaifulNizamYahya use CMAKE_CURRENT_SOURCE_DIR if the test/input.txt file is relative to the current CMakeLists.txt file. If it's relative to the root CMakeLists.txt, then use CMAKE_SOURCE_DIR.

如果测试/input.txt文件相对于当前的CMakeLists.txt文件,@SyaifulNizamYahya使用CMAKE_CURRENT_SOURCE_DIR。如果它相对于根CMakeLists.txt,则使用CMAKE_SOURCE_DIR。

Is there a way to get the targets filename in cmake? without hardcoding the filename? e.g. I'm building a lib file, does cmake have a variable to get the targte filename? like libSomething.so?

有没有办法在cmake中获得目标文件名?而不对文件名进行硬编码?例如,我正在构建一个lib文件,cmake是否有一个变量来获取targte文件名?就像libSomething。那又怎样?

Note: to copy a directory recursively instead of a file, use ${CMAKE_COMMAND} -E copy_directory

注意:要递归复制目录而不是文件,请使用${CMAKE_COMMAND}-E COPY_DIRECTORY

@jorisv92 how would you force copy or copy if different using copy_directory or some other recursive operation?

@jorisv92如果使用COPY_DIRECTORY或其他递归操作,您将如何强制复制或复制?

This is great, but only guaranteed to work with POST_BUILD. Reference: gitlab.kitware.com/cmake/cmake/-/issues/21364#note_849331

这很好,但只能保证与POST_Build一起使用。参考文献:gitlab.kitware.com/cmake/cmake/-/issues/21364#note_849331

This and only this worked for me. I am migrating a legacy library from autoconf to CMake. This setup works when using both gcc and msvc.

这个,而且只有这个对我有效。我正在将旧库从Autoconf迁移到CMake。当同时使用GCC和MSVC时,此设置可以正常工作。

IMO this is the only correct solution here. The first command tells cmake what files you need to exist and the second command tells cmake how to create them.

我想这是这里唯一正确的解决方案。第一个命令告诉cmake需要存在哪些文件,第二个命令告诉cmake如何创建这些文件。

I would also add add_dependencies(MainTarget copy-files) to make it run automatically when I build MainTarget

我还会添加添加依赖项(MainTarget复制文件),使其在构建MainTarget时自动运行

This seems like the best answer (+Herrgott's comment) as it actually ensures that the current version of the source is always in the destination post build. For small copy jobs this works well, thanks. Putting add_dependencies(MainTarget copy-files) in the root CMakeLists.txt file means that this can be used throughout the project.

这似乎是最好的答案(+Herrgot的评论),因为它实际上确保了源代码的当前版本始终在目标构建后。对于小型复印作业,此功能运行良好,谢谢。将Add_Dependency(MainTarget复制文件)放在根CMakeLists.txt文件中意味着可以在整个项目中使用它。

But the 'install' command has a side effect: the installed files will appear in the cpack package . . .

但是‘Install’命令有一个副作用:已安装的文件将出现在cpack包中。。。

This is a correct answer to a different question. :) Build (cmake --build ...) is a separate stage from install (cmake --install...). In particular, VSCode normally runs only the build command and debugs the program directly from CMAKE_BINARY_DIR. The question is kinda mushy at which stage, configure or build, the file needs to be copied, but the install phase is certainly too late. Yes the files will require separate install description with the install() command, best using target properties in the Modern CMake framework, but that's out of scope.

这是对另一个问题的正确回答。:)构建(cmake--构建...)是一个独立于安装的阶段(cmake--Install...)。特别是,VSCode通常只运行BUILD命令,并直接从CMAKE_BINARY_DIR调试程序。问题有点模糊,在哪个阶段,无论是配置还是构建,需要复制文件,但安装阶段肯定太晚了。是的,这些文件将需要使用Install()命令进行单独的安装描述,最好使用现代CMake框架中的目标属性,但这超出了讨论范围。

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com