CMake 自定义构建问题
CMake custom builds issues
我正在尝试使用 CMake 构建自定义项目,其中涉及使用 emscripten 为我的 C++ 库提供 javascript 绑定。
这就是我希望我的 CMakeLists.txt 文件实现的目标
为我的文件指定源位置(完成)
设置要使用的相关编译器以及编译器标志等(完成)
使用自定义构建生成新的cpp文件(下面有详细步骤)
- 使用自定义工具(python 脚本)生成
interface/glue.cpp
- 创建一个新的空文件
interface/glue_wrapper.cpp
- 对于
${my_header_files}
中的每个头文件 f
将 #include "f"
附加到文件 interface/glue_wrapper.cpp
interface/glue_wrapper.cpp
中的最后一个条目应该是 #include "glue.cpp"
使用自定义构建通过以下逻辑生成我的 javascript 文件:
- 创建一个变量 ${ALL_SOURCES} 包含上面第 1 步中列出的所有源和上面第 3 步中的
interface/glue_wrapper.cpp
- 使用计算结果为的 COMMAND 进行编译:
${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS} ${ALL_SOURCES} interface/glue_wrapper.cpp --post-js glue.js -o output.js
我在第 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 中指定的所需功能?
似乎 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:
If somebody ever needs the file x
, here is how you create it.
该命令本身不会向生成的构建系统添加任何内容。它仅向 CMake 提供有关如何创建文件的信息。
调用的各个组成部分是:
add_custom_command(
OUTPUT ${PROJECT_JS_DIR}/${PROJECT}.js
此自定义命令生成的一个或多个文件。它说:"the custom command produces these files."
COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS} ${ALL_SOURCES} interface/glue_wrapper.cpp --post-js glue.js -o output.js
COMMAND
参数引入了要执行的命令的命令行。它说:"this is what you must do to produce the files listed in OUTPUT
."
DEPENDS ${ALL_SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/interface/glue_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/interface/glue.cpp
DEPENDS
部分介绍了命令的依赖关系(先决条件)。它后面的每一项都是一个文件,它是命令的依赖项。它说:"if any of these files is missing, or if any of these files is newer than any of the output files, this command must be re-run."
特别注意对${CMAKE_CURRENT_BINARY_DIR}/interface/glue.cpp
的依赖,我稍后再回来。
COMMENT "Building ${PROJECT}.js"
这纯粹是文档——它将在自定义命令执行(=构建)时打印出来。
VERBATIM
)
这告诉 CMake 正确转义 COMMAND
部分中的任何特殊字符,以便 shell 将执行命令。基本上,除非您确定有理由不这样做,否则请始终将其放入自定义命令中。
自定义目标
正如我在上面提到的,CMake 仅在某些内容请求其输出时才将自定义命令添加到构建系统。普通目标(即库或可执行文件)可以通过在其源文件中列出输出文件来做到这一点。这在自定义命令生成 C++ 源文件(例如来自 IDL 定义)的情况下很典型。
自定义命令还可以在其 DEPENDS
部分中列出另一个自定义命令的输出,这会创建所需的依赖项。但是,只有在某处请求 "master" 命令的输出时,才会再次包含两者。
如果生成的文件实际上是最终产品而不只是普通目标的源文件,则必须在某处指定对它的显式依赖以确保生成它.这就是自定义目标的用武之地。它是一个目标(就像可执行文件或库),因此它将始终存在于构建系统中。当使用基于 makefile 的生成器时,自定义目标只是一个额外的规则。下面分析一下我在上面的回答里放的那个:
add_custom_target(
JsLibrary
JsLibrary
只是目标的符号名称。它可以是任何你想要的。这是您将在命令行中键入以构建 .js
文件的名称:> make JsLibrary
.
ALL
默认情况下,自定义目标 不是 由 make all
调用的 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
,因为这是在创建 .js
的自定义命令的 DEPENDS
部分中指定的。因此,创建 glue.cpp
的自定义命令中描述的规则也将包含在构建系统中,并且一切都按预期工作。
我正在尝试使用 CMake 构建自定义项目,其中涉及使用 emscripten 为我的 C++ 库提供 javascript 绑定。
这就是我希望我的 CMakeLists.txt 文件实现的目标
为我的文件指定源位置(完成)
设置要使用的相关编译器以及编译器标志等(完成)
使用自定义构建生成新的cpp文件(下面有详细步骤)
- 使用自定义工具(python 脚本)生成
interface/glue.cpp
- 创建一个新的空文件
interface/glue_wrapper.cpp
- 对于
${my_header_files}
中的每个头文件f
将#include "f"
附加到文件interface/glue_wrapper.cpp
interface/glue_wrapper.cpp
中的最后一个条目应该是#include "glue.cpp"
- 使用自定义工具(python 脚本)生成
使用自定义构建通过以下逻辑生成我的 javascript 文件:
- 创建一个变量 ${ALL_SOURCES} 包含上面第 1 步中列出的所有源和上面第 3 步中的
interface/glue_wrapper.cpp
- 使用计算结果为的 COMMAND 进行编译:
${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS} ${ALL_SOURCES} interface/glue_wrapper.cpp --post-js glue.js -o output.js
- 创建一个变量 ${ALL_SOURCES} 包含上面第 1 步中列出的所有源和上面第 3 步中的
我在第 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 中指定的所需功能?
似乎 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:
If somebody ever needs the file
x
, here is how you create it.
该命令本身不会向生成的构建系统添加任何内容。它仅向 CMake 提供有关如何创建文件的信息。
调用的各个组成部分是:
add_custom_command(
OUTPUT ${PROJECT_JS_DIR}/${PROJECT}.js
此自定义命令生成的一个或多个文件。它说:"the custom command produces these files."
COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS} ${ALL_SOURCES} interface/glue_wrapper.cpp --post-js glue.js -o output.js
COMMAND
参数引入了要执行的命令的命令行。它说:"this is what you must do to produce the files listed in OUTPUT
."
DEPENDS ${ALL_SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/interface/glue_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/interface/glue.cpp
DEPENDS
部分介绍了命令的依赖关系(先决条件)。它后面的每一项都是一个文件,它是命令的依赖项。它说:"if any of these files is missing, or if any of these files is newer than any of the output files, this command must be re-run."
特别注意对${CMAKE_CURRENT_BINARY_DIR}/interface/glue.cpp
的依赖,我稍后再回来。
COMMENT "Building ${PROJECT}.js"
这纯粹是文档——它将在自定义命令执行(=构建)时打印出来。
VERBATIM
)
这告诉 CMake 正确转义 COMMAND
部分中的任何特殊字符,以便 shell 将执行命令。基本上,除非您确定有理由不这样做,否则请始终将其放入自定义命令中。
自定义目标
正如我在上面提到的,CMake 仅在某些内容请求其输出时才将自定义命令添加到构建系统。普通目标(即库或可执行文件)可以通过在其源文件中列出输出文件来做到这一点。这在自定义命令生成 C++ 源文件(例如来自 IDL 定义)的情况下很典型。
自定义命令还可以在其 DEPENDS
部分中列出另一个自定义命令的输出,这会创建所需的依赖项。但是,只有在某处请求 "master" 命令的输出时,才会再次包含两者。
如果生成的文件实际上是最终产品而不只是普通目标的源文件,则必须在某处指定对它的显式依赖以确保生成它.这就是自定义目标的用武之地。它是一个目标(就像可执行文件或库),因此它将始终存在于构建系统中。当使用基于 makefile 的生成器时,自定义目标只是一个额外的规则。下面分析一下我在上面的回答里放的那个:
add_custom_target(
JsLibrary
JsLibrary
只是目标的符号名称。它可以是任何你想要的。这是您将在命令行中键入以构建 .js
文件的名称:> make JsLibrary
.
ALL
默认情况下,自定义目标 不是 由 make all
调用的 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
,因为这是在创建 .js
的自定义命令的 DEPENDS
部分中指定的。因此,创建 glue.cpp
的自定义命令中描述的规则也将包含在构建系统中,并且一切都按预期工作。