Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/cmake/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
立即构建工具,以便以后可以在同一个CMake运行中使用_Cmake - Fatal编程技术网

立即构建工具,以便以后可以在同一个CMake运行中使用

立即构建工具,以便以后可以在同一个CMake运行中使用,cmake,Cmake,我有一个有趣的鸡和蛋的问题和一个潜在的解决方案(见我贴出的答案),但这个解决方案以一种不同寻常的方式使用CMake。欢迎提出更好的备选方案或意见 问题: 问题的简单版本可以描述为具有以下特征的单个CMake项目: 构建目标之一是一个命令行可执行文件,我将其称为mycop,其源代码位于mycopdir中,不可能对该目录的内容进行任何修改 该项目包含文本文件(我将称之为代码> Fo..和代码> Bar。我的 >,它们需要MyCOMP在它们上运行,以产生一组C++源和头,以及一些 CMAKLISTH.

我有一个有趣的鸡和蛋的问题和一个潜在的解决方案(见我贴出的答案),但这个解决方案以一种不同寻常的方式使用CMake。欢迎提出更好的备选方案或意见

问题:

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

  • 构建目标之一是一个命令行可执行文件,我将其称为mycop,其源代码位于
    mycopdir
    中,不可能对该目录的内容进行任何修改
  • 该项目包含文本文件(我将称之为代码> Fo..<代码>和代码> Bar。我的<代码> >,它们需要MyCOMP在它们上运行,以产生一组C++源和头,以及一些<代码> CMAKLISTH.TXT < /Cord>文件,这些文件定义了从这些源构建的库。
  • 同一项目中的其他构建目标需要与生成的
    CMakeLists.txt
    文件定义的库相链接。这些其他目标也有
    #包括
    一些生成的头的源
  • 您可以将mycomp看作类似于编译器的东西,将步骤2中的文本文件看作某种源文件。这就产生了一个问题,因为CMake在配置时需要
    CMakeLists.txt
    文件,但mycomp在构建时才可用,因此在第一次运行时无法尽早创建
    CMakeLists.txt
    文件

    无应答:


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

    问题的关键是需要在运行CMake时提供mycomp,以便生成的
    CMakeLists.txt
    文件可以使用创建,然后使用
    添加子目录()
    拉入。实现这一点的一种可能方法是使用
    execute\u process()
    从主构建运行嵌套的cmake和构建。嵌套的cmake和构建将使用与顶级cmake运行完全相同的源目录和二进制目录(除非交叉编译)。主要顶级
    CMakeLists.txt
    的一般结构如下:

    # 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)处重复使用与主构建相同的二进制目录,我们最终将构建
    mycop
    两次,这显然是我们想要避免的。对于交叉编译,我们无法避免这种情况,因此在这种情况下,我们在单独的二进制目录中构建
    mycomp
    工具

    我已经尝试过上述方法,而且它在现实世界项目中确实有效,至少对于Unix Makefiles、Ninja、Xcode(OSX和iOS)和VisualStudio生成器来说是如此。这种方法的一部分吸引力在于,它只需要在顶级
    CMakeLists.txt
    文件中添加少量代码。尽管如此,仍应提出一些意见:

    • 如果用于
      mycomp
      的编译器或链接器命令及其源在嵌套构建和主构建之间存在任何不同,则
      mycomp
      目标将在(d)处再次重建。如果没有差异,
      mycop
      在不交叉编译时只生成一次,这正是我们想要的
    • 我认为没有一种简单的方法可以将完全相同的参数传递给(b)处的CMake嵌套调用,就像传递给顶级CMake运行一样(基本上就是描述的问题)。读取
      cmakcache.txt
      不是一个选项,因为它在第一次调用时不存在,并且不会从当前运行中提供任何新的或更改的参数。我能做的最好的事情就是设置那些我认为可能会被使用的CMake变量,这些变量可能会影响
      mycomp
      的编译器和链接器命令。这可以通过在遇到我发现需要的变量时添加越来越多的变量来解决,但这并不理想
    • 当重新使用相同的二进制目录时,我们依赖于CMake在生成阶段之前(至少在(c)处的构建完成之后)不会开始将其任何文件写入二进制目录。对于测试过的生成器,看起来我们还可以,但我不知道所有平台上的所有生成器是否也遵循这种行为(我无法测试每种组合来找出答案!)。这是我最关心的部分。如果有人能够通过推理和/或证据证实这对所有发电机和平台都是安全的,那么这将是非常有价值的(如果你想单独回答这个问题,那么值得投票)

    更新:在对CMake熟悉程度不同的工作人员的许多实际项目中使用上述策略后,可以进行一些观察

    • 让嵌套的生成重复使用与主生成相同的生成目录有时会导致问题。具体来说,如果用户在嵌套生成完成后但在主生成完成之前终止了CMake运行,则
      CMakeCache.txt
      文件将保留
      EARLY\u build
      设置为
      ON
      。这使得所有后续的CMake运行都像一个嵌套的构建一样,因此在手动删除
      CMakeCache.txt
      文件之前,主构建基本上是丢失的。项目的
      CMakeLists.txt
      文件中的某个错误也可能导致类似情况(未确认)。在它自己单独的构建目录中执行嵌套的构建,虽然没有这样的问题,但效果非常好

    • 嵌套的构建可能应该是发布的,而不是调试的。如果没有,请重新使用相同的生成目录