CMake自定义构建问题

时间:2015-05-07 21:49:02

标签: build cmake emscripten

我正在尝试使用CMake构建一个自定义项目,该项目涉及使用emscripten为我的C ++库提供javascript绑定。

这就是我希望我的CMakeLists.txt文件实现

  1. 指定文件的来源位置(完成)

  2. 设置要使用的相关编译器以及编译器标志等(完成)

  3. 使用自定义构建生成新的cpp文件(详细步骤如下)

    • 使用自定义工具(python脚本)生成interface/glue.cpp
    • 创建新的EMPTY文件interface/glue_wrapper.cpp
    • 对于f中的每个标头文件${my_header_files},将#include "f"附加到文件interface/glue_wrapper.cpp
    • interface/glue_wrapper.cpp中的最终条目应为#include "glue.cpp"
  4. 使用自定义构建通过使用以下逻辑生成我的javascript文件:

    • 创建一个变量$ {ALL_SOURCES},其中包含上面步骤1中列出的所有来源以及上面步骤3中的interface/glue_wrapper.cpp
    • 使用评估为:${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS} ${ALL_SOURCES} interface/glue_wrapper.cpp --post-js glue.js -o output.js
    • 的COMMAND进行编译
  5. 我在第3步和第4步花了最后7个小时 - 没有成功。

    这是我到目前为止(与上述步骤3和4有关)

    # Build Interface
    ADD_CUSTOM_COMMAND(
                    OUTPUT interface/glue.cpp
                    COMMAND cd interface
                    COMMAND python ${PLATFORM_PREFIX}/tools/webidl_binder.py ${myclasses_INTERFACE} glue
                    # Need to loop through list and generate include statements ...
                    #COMMAND echo "#include <glue.cpp>" > glue_wrapper.cpp
                     )
    
    # Build JS library
    ADD_CUSTOM_COMMAND(
                    OUTPUT ${PROJECT_JS_DIR}/${PROJECT}.js
                    COMMAND ${CMAKE_CXX_COMPILER} # Nothing seems to work anyway .... giving up finally :(
                    )
    

    我正在使用cmake 3.2.1,并在Ubuntu 14.0.4上构建。我正在尝试创建Unix MakeFiles。

    我的问题是:

    如何修改上面的代码段,以实现步骤3和4中指定的所需功能?

1 个答案:

答案 0 :(得分:3)

似乎glue_wrapper.cpp的内容根本不依赖于构建时间值,它们完全基于CMake时间可用的信息(my_header_files的内容变量)。因此,您可以使用简单的file()命令在CMake时创建文件:

file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/interface/glue_wrapper.cpp)  # erase file if it exists
foreach(header IN LISTS my_header_files ITEMS glue.cpp)
  file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/interface/glue_wrapper.cpp "#include \"${header}\"\n")
endforeach()

用于创建.js库的自定义命令应该正常工作:

add_custom_command(
  OUTPUT ${PROJECT_JS_DIR}/${PROJECT}.js
  COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS} ${ALL_SOURCES} interface/glue_wrapper.cpp --post-js glue.js -o output.js
  DEPENDS ${ALL_SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/interface/glue_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/interface/glue.cpp
  COMMENT "Building ${PROJECT}.js"
  VERBATIM
)

与任何CMake自定义命令一样,只有在某些内容取决于其输出时才会包含在构建中(我怀疑这就是您的方法失败的原因)。所以你应该添加一个自定义目标来驱动命令:

add_custom_target(
  JsLibrary ALL
  DEPENDS ${PROJECT_JS_DIR}/${PROJECT}.js
  COMMENT "Building JsLibrary"
)

这应该是所有必要的。

作为旁注,请注意add_custom_command有一个WORKING_DIRECTORY参数,您应该使用该参数代替COMMAND cd

自定义命令和自定义目标之间的CMake关系可能需要一些时间才能完全解决,因此我将尝试解释上述代码中发生的情况。

自定义命令

命令add_custom_command(OUTPUT x ...)创建一个生成输出的构建规则。基本上,这告诉CMake:

  

如果有人需要文件x,请按以下步骤创建。

该命令本身不会向生成的构建系统添加任何内容。它仅向CMake提供有关如何创建文件的信息。

调用的各个组件是:

add_custom_command(
  OUTPUT ${PROJECT_JS_DIR}/${PROJECT}.js

此自定义命令生成的文件。它说:&#34;自定义命令生成这些文件。&#34;

  COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS} ${ALL_SOURCES} interface/glue_wrapper.cpp --post-js glue.js -o output.js

COMMAND参数引入了要执行的命令的命令行。它说:&#34;这是你必须做的,以生成OUTPUT中列出的文件。&#34;

  DEPENDS ${ALL_SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/interface/glue_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/interface/glue.cpp

DEPENDS部分介绍了命令的依赖关系(先决条件)。其后面的每个项目都是一个文件,它是命令的依赖项。它说:&#34;如果缺少这些文件中的任何一个,或者这些文件中的任何比输出文件的任何更新,则必须重新执行此命令运行&#34;

特别注意对${CMAKE_CURRENT_BINARY_DIR}/interface/glue.cpp的依赖,我稍后会再回过头来看。

  COMMENT "Building ${PROJECT}.js"

这纯粹是文档 - 它将在执行自定义命令(= built)时打印。

  VERBATIM
)

这告诉CMake正确转义COMMAND部分中将执行命令的shell的任何特殊字符。基本上,除非你确定你有理由不这样做,否则总是把它放到自定义命令中。

自定义目标

正如我上面提到的,如果某些内容请求输出,CMake只会向构建系统添加自定义命令。正常目标(即库或可执行文件)可以通过在其源文件中列出输出文件来实现。这在自定义命令生成C ++源文件的情况下是典型的,例如来自IDL定义。

自定义命令还可以在其DEPENDS部分列出另一个自定义命令的输出,从而创建所需的依赖关系。但是,如果&#34; master&#34;的输出将再次包括在内。在某处请求命令。

如果生成的文件实际上是最终产品而不仅仅是普通目标的源文件,则必须在某处指定显式依赖它,以确保生成它。自定义目标所在的位置。它是一个目标(就像可执行文件或库一样),因此它将始终存在于构建系统中。使用基于makefile的生成器时,自定义目标只是一个额外的规则。让我们分析一下我在上面的答案中所提出的那个:

add_custom_target(
  JsLibrary

JsLibrary只是目标的符号名称。它可以是你想要的任何东西。它是您在命令行上键入的名称,用于构建.js文件:> make JsLibrary

ALL

默认情况下,自定义目标all调用的make all目标的一部分;你必须明确make他们。添加ALL参数会使自定义目标成为make all的一部分,我假设您在这里需要它。

DEPENDS ${PROJECT_JS_DIR}/${PROJECT}.js

这是关键路线以及我们首先创建自定义目标的原因。这告诉CMake自定义目标取决于生成的文件。现在,CMake认为该文件是构建系统的一部分所需要的(即通过自定义命令JsLibrary),并查看它是否知道如何创建这样的文件。它找到自定义命令,并确保将正确的规则添加到生成的构建系统中。

  COMMENT "Building JsLibrary"
)

这又是纯文档 - 每次创建目标时都会打印它(即使它的所有依赖项都是最新的,因此也不会进行进一步的处理)。

总结

JsLibrary将包含在构建系统中,因为它是自定义目标,并且始终包含自定义目标。它将成为make all的一部分,因为我们在创建它时指定了ALL

JsLibrary取决于${PROJECT_JS_DIR}/${PROJECT}.js,因此自定义命令创建的规则将包含在构建系统中,并且每次构建JsLibrary时都会进行检查。如果过期,它将被执行。

${PROJECT_JS_DIR}/${PROJECT}.js又取决于${CMAKE_CURRENT_BINARY_DIR}/interface/glue.cpp,因为这是在创建DEPENDS的自定义命令的.js部分中指定的。因此,创建glue.cpp的自定义命令中描述的规则也将包含在构建系统中,并且一切都按预期工作。