gpt4 book ai didi

c++ - CMake:默认情况下如何使 add_custom_command 输出保持最新?

转载 作者:行者123 更新时间:2023-12-01 13:49:10 25 4
gpt4 key购买 nike

我有一个基于 CMake 3.11.4 和 Python 3.7 的软件环境。

我的库/程序有一个 config.txt以我指定的格式描述它们的依赖关系的文件。然后,我有一个 Python 脚本( scripts/configure.py )生成 CMakeLists.txt即时调用 CMake 以生成可由 Visual Studio 2015 构建的解决方案。

我希望 Python 在 config.txt 时自动再次运行由用户编辑。

所以我让我的 Python 脚本在生成的 CMakeLists.txt 中添加了一个自定义命令语句.以下是名为“myproject”的项目的外观,其中包括两个库“lib1”和“lib2”。
${SDE_ROOT_DIR}/build/myproject/CMakeLists.txt包含:

# Automatically re-run configure project when an input file changes:
set( PROJECT_DEPENDENCIES )
list( APPEND PROJECT_DEPENDENCIES "${SDE_ROOT_DIR}/lib/lib1/config.txt" )
list( APPEND PROJECT_DEPENDENCIES "${SDE_ROOT_DIR}/lib/lib2/config.txt" )
ADD_CUSTOM_COMMAND( OUTPUT ${SDE_ROOT_DIR}/build/myproject/CMakeLists.txt COMMAND tools/python/Python370/python.exe scripts/configure.py myproject WORKING_DIRECTORY ${SDE_ROOT_DIR} DEPENDS ${PROJECT_DEPENDENCIES} )

这是我所做的:
  • 我运行我的脚本( scripts/configure.py myproject )以获得 CMakeLists.txt以及生成的 Visual Studio 解决方案。
  • 然后我打开解决方案
  • 我第一次构建时,它报告Generating CMakeLists.txt我看到我的脚本 scripts/configure.py被调用。 这不是预期的!
  • 我第二次构建时,什么也没有发生。还行吧。
  • 如果我编辑 config.txt , 下次构建时看到 Generating CMakeLists.txt我看到我的脚本 scripts/configure.py被调用。那挺好的。

  • 除了第一次编译项目时我的脚本运行之外,这几乎是我的预期。如 CMakeLists.txt刚刚生成,绝对比 config.txt 新,我不明白为什么它需要生成 CMakeLists.txt再次。

    知道我可能做错了什么吗?是否有任何额外的命令我应该添加到 CMakeLists.txt使这个自定义命令的输出默认为“最新”?

    这是一个 MCVE( config.txt 被替换为 prgname.txt ):
    prg/main.cpp :
    #include <iostream>

    int main( int argc, char* argv[] )
    {
    std::cout << "Hello World!" << std::endl;
    return 0;
    }
    prg/prgname.txt :
    myprogram
    scripts/configure.py :
    import sys
    import subprocess
    import argparse
    import os
    from contextlib import contextmanager

    @contextmanager
    def pushd(newDir):
    previousDir = os.getcwd()
    os.chdir(newDir)
    yield
    os.chdir(previousDir)

    def configure_project():

    # check configuration args
    parser = argparse.ArgumentParser(description="CMakeLists generator.")
    parser.add_argument('project', metavar='project', type=str, help='project name')
    args = parser.parse_args()

    working_directory = os.getcwd()

    project = args.project

    buildfolder = os.path.normpath(os.path.join( os.path.dirname(os.path.abspath(__file__)), os.pardir, "build", project ))

    if not os.path.isdir(buildfolder):
    os.makedirs(buildfolder)

    prgsourcefolder = os.path.normpath(os.path.join( os.path.dirname(os.path.abspath(__file__)), os.pardir, "prg" ))
    prgbuildfolder = os.path.join( buildfolder, "prg" )
    if not os.path.isdir(prgbuildfolder):
    os.makedirs(prgbuildfolder)

    prgnamepath = os.path.join( prgsourcefolder, "prgname.txt" )
    with open( prgnamepath, "r" ) as prgnamefile:
    prgname = prgnamefile.read()

    with open( os.path.join( prgbuildfolder, "CMakeLists.txt" ), "w" ) as cmakelists:
    cmakelists.write( "add_executable(" + prgname + " " + os.path.join(prgsourcefolder,"main.cpp").replace("\\","/") + ")\n" )

    cmakelistspath = os.path.join( buildfolder, "CMakeLists.txt" )
    with open( cmakelistspath, "w" ) as maincmakelists:
    maincmakelists.write( "cmake_minimum_required(VERSION 3.11)\n" )
    maincmakelists.write( "project(" + project + ")\n" )
    maincmakelists.write( "add_subdirectory(prg)\n" )

    maincmakelists.write( "add_custom_command( OUTPUT " + cmakelistspath.replace("\\","/") + " COMMAND python " + " ".join( [ x.replace("\\","/") for x in sys.argv] ) + " WORKING_DIRECTORY " + working_directory.replace("\\","/") + " DEPENDS " + prgnamepath.replace("\\","/") + ")\n" )

    # Run CMake:
    with pushd( buildfolder ):
    cmd = ['cmake.exe', '-G', 'Visual Studio 14 2015 Win64', buildfolder]
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    while True:
    out = proc.stdout.read(1)
    if proc.poll() != None:
    break
    sys.stdout.write(out.decode())
    sys.stdout.flush()
    proc.wait()

    if __name__ == "__main__":
    import sys
    sys.exit( configure_project() )
  • 将 CMake 和 Python 添加到您的 PATH
  • 从脚本文件夹运行 python configure.py myproject
  • 开通 build/myproject/myproject.sln
  • 点击“全部编译”,你会看到意想不到的消息 Generating CMakeLists.txt在日志中
  • 最佳答案

    这是一个“解决方法”,而不是“解决方案”。

    我只是添加一个状态文件来告诉何时应该跳过从 VS 调用的 generate ,因为我知道解决方案是最新的:

    向解析器添加新参数:

    parser.add_argument('--from_vs', action='store_true', help='identify that configure is ran from VS to prevent useless regeneration, don't set this manually please')

    从脚本本身正确维护此文件:
    vs_force_up_to_date_file = os.path.join( buildfolder, "vs_force_up_to_date" )
    if args.from_vs:
    is_up_to_date = False

    if os.path.isfile( vs_force_up_to_date_file ):
    with open( vs_force_up_to_date_file, "r" ) as file:
    content = file.readlines()[0]
    is_up_to_date = ( content == "True" )

    if is_up_to_date:
    # It's the first time VS runs this script, we know it is up-to-date, so let's not regenerate
    # See https://stackoverflow.com/questions/59861101/cmake-how-can-i-make-add-custom-command-output-up-to-date-by-default
    print( "First time calling generate from VS, project is likely up-to-date, let's not reconfigure!" )
    # Make next generate from VS be skipped
    with open( vs_force_up_to_date_file, "w" ) as file:
    file.write( str(False) )
    exit(0)
    else:
    # need to generate, let's continue!
    pass
    else:
    # Generating from console, let's make VS believe it is up to date for first time it will generate
    if os.path.isfile( vs_force_up_to_date_file ):
    # Let the file as it is, "True" if VS generate never ran, else False
    pass
    else:
    # Create the file to prevent first VS generate to rerun this script while it does not need to be ran
    with open( vs_force_up_to_date_file, "w" ) as file:
    file.write( str(True) )

    从 VS 运行时设置这个新参数:
    run_args = " ".join( [ x.replace("\\","/") for x in sys.argv] )
    if not args.from_vs:
    run_args += " --from_vs"
    maincmakelists.write( "add_custom_command( OUTPUT " + cmakelistspath.replace("\\","/") + " COMMAND python " + run_args + " WORKING_DIRECTORY " + working_directory.replace("\\","/") + " DEPENDS " + prgnamepath.replace("\\","/") + ")\n" )

    这使得 VS 无用的调用配置第一次生成请求被跳过和信,所需的调用将按预期工作。

    编辑

    实际上,这并不像预期的那样有效。因为 VS 每次第一次都会为每个配置运行脚本。因此,在 Release 模式下构建一次后,切换到 Debug 最终将再次生成 CMakeLists.txt 而它不应该因为第一代在 vs_force_up_to_date 文件中写了 False。这是一个太幼稚的解决方案。

    相反,我最终采用的解决方案是将所有输入文件 (prgname.txt) 和输出文件 (CMakeLists.txt) 的路径传递给脚本,并让它检查所有输出是否比所有输入更新,跳过生成。然后,无论对脚本 VS 的意外调用可能会做什么,它们都会由脚本本身正确处理。

    关于c++ - CMake:默认情况下如何使 add_custom_command 输出保持最新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59861101/

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