[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