Don't Provide `-DCMAKE_TOOLCHAIN_FILE` in emcmake Unless Configuring with CMake.
#14,546 创建于 2021年6月28日
仓库指标
- Star
- (27,361 star)
- PR 合并指标
- (平均合并 19天 10小时) (30 天内合并 147 个 PR)
描述
Background
CMake provides numerous commands in addition to merely configuring projects, which can be useful for portability when using different generators. Examples of commands include:
--build--install--open-E(run a command)--find-package
Unfortunately, none of these accept -DCMAKE_TOOLCHAIN_FILE as a valid argument.
Example
A sample workflow would be:
emcmake cmake ..
emcmake cmake --build . --config Release
The second command fails, with the following error output:
emcmake cmake --build . --config Release
configure: cmake --build . --config Release -DCMAKE_TOOLCHAIN_FILE=/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_CROSSCOMPILING_EMULATOR=/emsdk/node/14.15.5_64bit/bin/node
Unknown argument -DCMAKE_TOOLCHAIN_FILE=/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake
Unknown argument -DCMAKE_CROSSCOMPILING_EMULATOR=/emsdk/node/14.15.5_64bit/bin/node
Usage: cmake --build <dir> [options] [-- [native-options]]
Options:
<dir> = Project binary directory to be built.
--parallel [<jobs>], -j [<jobs>]
= Build in parallel using the given number of jobs.
If <jobs> is omitted the native build tool's
default number is used.
The CMAKE_BUILD_PARALLEL_LEVEL environment variable
specifies a default parallel level when this option
is not given.
--target <tgt>..., -t <tgt>...
= Build <tgt> instead of default targets.
--config <cfg> = For multi-configuration tools, choose <cfg>.
--clean-first = Build target 'clean' first, then build.
(To clean only, use --target 'clean'.)
--verbose, -v = Enable verbose output - if supported - including
the build commands to be executed.
-- = Pass remaining options to the native tool.
emcmake: error: 'cmake --build . --config Release -DCMAKE_TOOLCHAIN_FILE=/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_CROSSCOMPILING_EMULATOR=/emsdk/node/14.15.5_64bit/bin/node' failed (returned 1)
The lines creating the issue are here: https://github.com/emscripten-core/emscripten/blob/660dcc00598495f5cf353771995d77a13735c0c9/emcmake.py#L35-L36
Solution
Luckily, all of the commands besides -P (run a script) only accept the command as the next argument. It must be cmake --build . --config Release, it cannot be cmake --config Release --build ., which simplifies the process dramatically.
There's then 3 scenarios:
- cmake is followed directly by a command not compatible with
-DCMAKE_TOOLCHAIN_FILE=... - cmake is following by
-D...defines, and then-P, making it incompatible with-DCMAKE_TOOLCHAIN_FILE=... - everything else, which works out-of-the-box
For example, cmake --build . works but cmake -DX=1 --build . does not.
Solution
In Python pseudocode, we can therefore fix this logic as follows:
import sys
# Get all arguments after the initial command, emcmake.
argv = sys.argv[1:]
# Do the normal logic to verify `cmake` is the first argument
# ...
def should_add_toolchain(args):
'''Check if we should add a toolchain file.'''
unsupported = {
'--build',
'--install',
'--open',
'-E',
'--find-package',
'--help',
}
if not args:
# called as `cmake`, does nothing, do not add a toolchain.
return False
if args[0] in unsupported:
# called where a toolchain is unsupported, do not add it.
return False
# skip all arguments leading with `-D`, to see if the command
# is a script argument.
index = 0
while index < len(args) and args[index].startswith('-D'):
index += 1
if index < len(args) and args[index].startswith('-P'):
return False
# this is a configuration case, and can be called as:
# cmake ... <source>
# cmake ... <build>
# cmake ... -S <source> -B <build>
# can safely as a toolchain to the end if not provided.
return not any(i.startswith('-DCMAKE_TOOLCHAIN_FILE=') for i in argv)
if should_add_toolchain(argv[1:]):
argv.append('-DCMAKE_TOOLCHAIN_FILE=...')
shared.check_call(argv, ...)
Although calling cmake without emcmake works as expected after configuration, considering that emccmake is meant to be a helper for cmake, it would be nice to have it work as expected even if called in other contexts.