[cmake-developers] Setting up environment using ExternalProject_Add
James Johnston
johnstonj.public at codenest.com
Tue Aug 11 00:49:58 EDT 2015
Hi,
(Pardon the length; skip to end to see what I am proposing to change. Below
is justification why.)
I have found it annoyingly difficult to set up an environment for a build
when using ExternalProject_Add. This seems to be needed when creating a
superbuild that uses several different compilers. The common workaround
seems to be to use a batch file that kicks off the build, as suggested by
multiple posts I have seen on these mailing lists, StackOverflow, etc. An
example of a nice & neat usage that doesn't need an environment set up.
# For this discussion, suppose this is called from a
# superbuild that has no languages set up, and no build
# environment.
ExternalProject_Add(
<snip>
# Conveniently, VS2010 generator doesn't need VCVarsAll.bat
CMAKE_GENERATOR "Visual Studio 10 2010"
CMAKE_CACHE_ARGS <long list of args>)
But let's say I want to use Ninja to build both VS2010 32-bit/64-bit. Now
things get really messy, especially if I want my logic in a reusable piece
of code (effectively acting as a wrapper around ExternalProject_Add):
# ${setEnv} must be path to a batch file that does this.
# 1. Call VCVarsAll for desired VC version.
# 2. Run command passed via arguments.
<snip code to configure_file and write ${setEnv}>
# ${someScriptFile} has to be made via file(GENERATE).
# Essentially we have to reimplement the CMAKE_CACHE_ARGS option.
# That is made difficult because _ep_command_line_to_initial_cache
# is a private implementation detail of ExternalProject.cmake.
<snip code to file(GENERATE) ${someScriptFile}>
ExternalProject_Add(
<snip>
CONFIGURE_COMMAND ${setEnv} ${CMAKE_COMMAND}
-GNinja -C${someScriptFile})
Basically I find myself copy/pasting _ep_command_line_to_initial_cache into
my own project, amongst other annoyances. :(
What would be nice is if I could directly pass VCVarsAll.bat to
ExternalProject_Add and still be able to use CMAKE_GENERATOR and
CMAKE_CACHE_ARGS. It would also be nice if this mechanism did not interfere
with option CONFIGURE_COMMAND in case the external project is not
CMake-based.
Something like this might be perfect:
ExternalProject_Add(
<snip>
CONFIGURE_ENVIRONMENT_COMMAND <path to VCVarsAll.bat>
CMAKE_GENERATOR Ninja
CMAKE_CACHE_ARGS <long list of args>)
# Offer similar capability for other steps like building.
This is made somewhat difficult by the fact that CMake currently provides no
mechanism for a custom command to do this that I know of, and thus nothing
for ExternalProject_Add to easily build off of. Therefore, I propose
several possible options, and I'd like to know which one(s) I should think
about pursuing:
1. add_custom_command (and friends) could be extended with a new keyword
indicating that command is environment preparation for the next command.
For example:
add_custom_command(<snip> ENVIRONMENT_COMMAND <path to vcvarsall.bat>
COMMAND ${CMAKE_PROGRAM} -GNinja <snip>)
In this case, add_custom_command will run the ENVIRONMENT_COMMAND and
COMMAND in the same shell. The ENVIRONMENT_COMMAND would have to be
source'd / call'd as needed, depending on the shell.
Implementation of #1:
a. A new "cmake -E run_commands <command list file>" option could be
added that runs each command on each line of the input file in a shared
environment (source'ing and call'ing as needed depending on shell). E.g. by
translating the list of input commands to a temporary batch/shell script and
running it. Behind the scenes, add_custom_command and friends would
assemble things into a "cmake -E" invocation that can be passed to the
generators - handling EXE targets as needed.
b. OR: Each CMake generator handles this directly without any special
"cmake -E" option. (This sounds like more work?)
2. Instead of altering add_custom_command, just add the proposed "cmake -E
run_commands" option as described above. ExternalProject_Add could then be
modified to use this new "cmake -E" option directly. (Need to think about
if the command is an EXE target...)
3. Instead of altering any C++ CMake code at all, modify
ExternalProject_Add to implement my original workaround outlined at the top
of this e-mail of generating a wrapper script. Downside: now
ExternalProject.cmake has to learn platform-specific knowledge of the shell.
Also need to think about if target is EXE target...
4. Do nothing; this is too complicated and so are not desired changes to
make to CMake. Or: something else?
The one possible wrench in the works would be if you used a Cygwin version
of CMake (i.e. CMake expects /bin/sh shell) and wanted to prepare to use
Visual C++ and run VCVarsAll.bat (i.e. but you need cmd.exe shell). That
wouldn't work without a special option allowing you to explicitly set the
shell. That's a rare enough case it's probably not worth worrying about in
this initial version, I think.
My personal preference would be option 1, and probably option 1a. In that
case, steps would be: (1) implement "cmake -E run_commands", (2) extend
add_custom_command, (3) extend ExternalProject_Add. What do you think?
Best regards,
James Johnston
More information about the cmake-developers
mailing list