立即构建工具,以便以后在相同的CMake运行中使用 [英] Building a tool immediately so it can be used later in same CMake run

查看:133
本文介绍了立即构建工具,以便以后在相同的CMake运行中使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个有趣的鸡和鸡蛋问题和一个潜在的解决方案(见我发布的答案),但该解决方案使用CMake以不寻常的方式。欢迎更好的替代品或评论。

I have an interesting chicken-and-egg problem and a potential solution to it (see my posted answer), but that solution uses CMake in an unusual way. Better alternatives or comments would be welcome.

问题:

版本的问题可以描述为具有以下特征的单个CMake项目:

The simple version of the problem can be described as a single CMake project with the following characteristics:


  1. 其中一个构建目标是命令行可执行文件我将调用 mycomp ,其源位于 mycompdir 中,并且不能对该目录的内容进行任何修改。 / li>
  2. 该项目包含文本文件(我将其称为 foo.my bar.my ),需要运行它们来生成一组C ++源文件和头文件以及一些 CMakeLists.txt 文件

  3. 同一个项目中的其他构建目标需要链接到由生成的 CMakeLists.txt 文件定义的库。这些其他目标还具有 #include 一些生成的标题的来源。

  1. One of the build targets is a command-line executable which I'll call mycomp, the source of which is in a mycompdir and making any modifications to the contents of that directory is not possible.
  2. The project contains text files (I'll call them foo.my and bar.my) which need mycomp run on them to produce a set of C++ sources and headers and some CMakeLists.txt files defining libraries built from those sources.
  3. Other build targets in the same project need to link against the libraries defined by those generated CMakeLists.txt files. These other targets also have sources which #include some of the generated headers.

您可以将 mycomp 看作是一个编译器,而将第2步中的文本文件视为某种源文件。这提出了一个问题,因为CMake在配置时需要 CMakeLists.txt 文件,但在构建时间之前不能使用mycomp ,因此不是第一次运行时可以提前创建 CMakeLists.txt 文件。

You can think of mycomp as being something like a compiler and the text files in step 2 as some sort of source files. This presents a problem, because CMake needs the CMakeLists.txt files at configure time, but mycomp is not available until build time and therefore isn't available on the first run to create the CMakeLists.txt files early enough.

NON-ANSWER:

通常,基于ExternalProject的超级构建安排将是一个潜在的解决方案,但上述是一个相当大的简化我正在处理的实际项目并且我没有将构建分成不同部分或执行其他大规模重组工作的自由。

Normally, an ExternalProject-based superbuild arrangement would be a potential solution to this, but the above is a considerable simplification of the actual project I am dealing with and I don't have the freedom to split the build into different parts or perform other large scale restructuring work.

推荐答案

的问题是需要 mycomp 在CMake运行时可用,以便可以创建生成的 CMakeLists.txt 文件,然后使用 add_subdirectory()。一种可能的方法是使用 execute_process()从主构建中运行嵌套的cmake-and-build。嵌套cmake-and-build将使用与顶级CMake运行完全相同的源和二进制目录(除非交叉编译)。主要顶层 CMakeLists.txt 的一般结构如下:

The crux of the problem is needing mycomp to be available when CMake is run so that the generated CMakeLists.txt files can be created and then pulled in with add_subdirectory(). A possible way to achieve this is to use execute_process() to run a nested cmake-and-build from the main build. That nested cmake-and-build would use the exact same source and binary directories as the top level CMake run (unless cross compiling). The general structure of the main top level CMakeLists.txt would be something like this:

# Usual CMakeLists.txt setup stuff goes here...

if(EARLY_BUILD)
    # This is the nested build and we will only be asked to
    # build the mycomp target (see (c) below)
    add_subdirectory(mycompdir)

    # End immediately, we don't want anything else in the nested build
    return()
endif()

# This is the main build, setup and execute the nested build
# to ensure the mycomp executable exists before continuing

# (a) When cross compiling, we cannot re-use the same binary dir
#     because the host and target are different architectures
if(CMAKE_CROSSCOMPILING)
    set(workdir "${CMAKE_BINARY_DIR}/host")
    execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory "${workdir}")
else()
    set(workdir "${CMAKE_BINARY_DIR}")
endif()

# (b) Nested CMake run. May need more -D... options than shown here.
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}"
                        -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
                        -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}
                        -DEARLY_BUILD=ON
                        ${CMAKE_SOURCE_DIR}
               WORKING_DIRECTORY "${workdir}")

# (c) Build just mycomp in the nested build. Don't specify a --config
#     because we cannot know what config the developer will be using
#     at this point. For non-multi-config generators, we've already
#     specified CMAKE_BUILD_TYPE above in (b).
execute_process(COMMAND ${CMAKE_COMMAND} --build . --target mycomp
                WORKING_DIRECTORY "${workdir}")

# (d) We want everything from mycompdir in our main build,
#     not just the mycomp target
add_subdirectory(mycompdir)

# (e) Run mycomp on the sources to generate a CMakeLists.txt in the
#     ${CMAKE_BINARY_DIR}/foobar directory. Note that because we want
#     to support cross compiling, working out the location of the
#     executable is a bit more tricky. We cannot know whether the user
#     wants debug or release build types for multi-config generators
#     so we have to choose one. We cannot query the target properties
#     because they are only known at generate time, which is after here.
#     Best we can do is hardcode some basic logic.
if(MSVC)
    set(mycompsuffix "Debug/mycomp.exe")
elseif(CMAKE_GENERATOR STREQUAL "Xcode")
    set(mycompsuffix "Debug/mycomp")
else()
    set(mycompsuffix "mycomp")
endif()
set(mycomp_EXECUTABLE "${workdir}/mycompdir/${mycompsuffix}")
execute_process(COMMAND "${mycomp_EXECUTABLE}" -outdir foobar ${CMAKE_SOURCE_DIR}/foo.my ${CMAKE_SOURCE_DIR}/bar.my)

# (f) Now pull that generated CMakeLists.txt into the main build.
#     It will create a CMake library target called foobar.
add_subdirectory(${CMAKE_BINARY_DIR}/foobar ${CMAKE_BINARY_DIR}/foobar-build)

# (g) Another target which links to the foobar library
#     and includes headers from there
add_executable(gumby gumby.cpp)
target_link_libraries(gumby PUBLIC foobar)
target_include_directories(gumby PUBLIC foobar)

如果我们不在(b)和(c)重复使用相同的二进制目录,因为我们使用主构建,我们最终构建 mycomp 两次,我们显然希望避免。对于交叉编译,我们不能避免这种情况,因此在这种情况下,我们在单独的二进制目录中建立 mycomp 工具。

If we don't re-use the same binary directory at (b) and (c) as we use for the main build, we end up building mycomp twice, which we obviously want to avoid. For cross compiling, we cannot avoid that, so in such cases we build the mycomp tool off to the side in a separate binary directory.

我已经尝试了上述方法,确实它似乎在现实世界项目中提示原始问题,至少对于Unix Makefiles,Ninja,Xcode(OS X和iOS)和Visual Studio发电机。这种方法的吸引力的一部分是,它只需要添加一个适量的代码,只添加到顶层 CMakeLists.txt 文件。但是,应该做一些观察:

I've experimented with the above approach and indeed it appears to work in the real world project that prompted the original question, at least for the Unix Makefiles, Ninja, Xcode (OS X and iOS) and Visual Studio generators. Part of the attractiveness of this approach is that it only requires a modest amount of code to be added just to the top level CMakeLists.txt file. Nevertheless, there are some observations that should be made:


  • 如果编译器或链接器命令为 mycomp 并且其源在嵌套构建和主构建之间以任何方式不同,则 mycomp 目标最终在(d)处第二次重建。如果没有差异, mycomp 只有在不交叉编译时才会生成一次,这正是我们想要的。

  • 简单的方法传递完全相同的参数到CMake的嵌套调用(b)作为传递到顶级CMake运行(基本上描述的问题这里)。读取 CMakeCache.txt 不是一个选项,因为它不会存在于第一次调用,它不会给你任何新的或更改的参数从当前运行无论如何。我可以做的最好的是设置那些CMake变量我认为可能会被使用,这可能影响编译器和链接器命令 mycomp 。这可以通过添加更多和更多的变量,通过添加越来越多的变量,我遇到我发现我需要,但这不是理想。

  • 当重新使用相同的二进制目录,我们依靠CMake没有开始写它的任何文件到二进制目录,直到生成阶段(好,至少直到在(c)构建完成之后)。对于测试的生成器,它似乎我们没关系,但我不知道如果所有平台上的所有生成器也遵循这种行为(我不能测试每一个组合找出!)。这是让我最感兴趣的部分。如果任何人可以用推理和/或证据证实这对于所有发电机和平台都是安全的,那么这将是有价值的(如果你想作为单独的答案解决这个问题,则值得一提)。

  • If the compiler or linker commands for mycomp and its sources are different in any way between the nested build and the main build, the mycomp target ends up getting rebuilt a second time at (d). If there are no differences, mycomp only gets built once when not cross compiling, which is exactly what we want.
  • I see no easy way to pass exactly the same arguments to the nested invocation of CMake at (b) as was passed to the top level CMake run (basically the problem described here). Reading CMakeCache.txt isn't an option since it won't exist on the first invocation and it would not give you any new or changed arguments from the current run anyway. The best I can do is to set those CMake variables I think are potentially going to be used and which may influence the compiler and linker commands of mycomp. This can be worked around by adding more and more variables as I encounter ones I discover I need, but that's not ideal.
  • When re-using the same binary directory, we are relying on CMake not starting to write any of its files to the binary directory until the generate stage (well, at least until after the build at (c) completes). For the generators tested, it appears we are okay, but I don't know if all generators on all platforms follow this behaviour too (and I can't test every single combination to find out!). This is the part that gives me the greatest concern. If anyone can confirm with reasoning and/or evidence that this is safe for all generators and platforms, that would be valuable (and worth an upvote if you want to address this as a separate answer).

这篇关于立即构建工具,以便以后在相同的CMake运行中使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆