[CMake] Generating C++ code from Idl language with variable number of files as output

Michael Hertling mhertling at online.de
Thu Jul 28 23:19:00 EDT 2011


On 07/20/2011 03:00 PM, Clifford Yapp wrote:
> On Mon, Jul 18, 2011 at 8:42 AM, Valentin-Daniel Boca
> <dboca at eservicios.indracompany.com> wrote:
> 
>> gen_code xxx.idl -> xxxIdl.h, xxxIdl.cc, xWrappers.h and xxxWData.h
>>
>> So for every "keyword struct" there is one more file generated. The first
>> three files are always the same.
>>
>> Given this, I can't know the list of output files to put it in the list of
>> add_custom_command output section.
>>
>> I need somehow to be able to tell to add_custom_command that the content of
>> OUTPUT files should be somehow taken from the result of running the COMMAND
>> (which I know it's impossible).
> 
> What about this: a two stage process, where the first stage runs a
> stand-alone CMake script that runs all of the idl commands ahead of
> time, outputting the contents into a custom directory.  For each idl
> file, do the following steps in the stand-alone script:
> 
> 1) run the idl gen_code on the file
> 2) use CMakes glob routines to capture what files were generated in
> the temporary directory as a result of that command
> 3) write the results of that glob to an xxx_idl.out file
> 4) clear out the generated idl files in the temporary directory.
> 
> Once you have all the idl.out files, in the main CMake logic you can
> write a macro for doing the add_custom_command routine that
> incorporates reading in the xxx_idl.out file contents into a
> variable/list.  You might need to generate per-idl-file .cmake files
> to run, based on a template and using configure_file
> 
> Look into commands like:
> 
>    execute_process(COMMAND ${CMAKE_COMMAND} ...)
>    FILE(GLOB IDL_OUTFILES tmpdir/*)
>    FILE(WRITE ${xxx}_idl.out ${IDL_OUTFILES}
> 
> Once you have your xxx_idl.out files, you can incorporate them into a
> wrapper macro in the main file and use that macro to add your idl
> files:
> 
> MACRO(add_idl xxx)
>    FILE(READ ${xxx}_idl.out IDL_OUTFILES)
>    add_custom_command(
>         OUTPUT ${IDL_OUTFILES}
>         COMMAND gen_code xxx.idl
>         DEPENDS ${xxx}_idl.out ${xxx}.idl
>    )
>    add_custom_target(${xxx}_idl ALL DEPENDS ${IDL_OUTFILES})
> ENDMACRO(add_idl)
> 
> This achieves the effect of per-file custom OUTPUT files in principle.
>   This is a more or less "off the cuff" response, so it may not be
> quite the approach you will need, but it's how I would start
> experimenting in such a case.  I should note that the drawback is any
> change to the idl files will require re-running CMake, not just
> running make - I'm not sure if there is a way to enforce that that
> actually happens when an idl file changes but I'm also not sure if
> there's any way around it, since you're essentially faking dynamic
> OUTPUT lists in custom commands and that actually does require
> re-generation of the Makefiles (or whatever) whenever the idl contents
> change (as far as I know).
> 
> CMake devs, is there any way to tell CMake that it needs to re-run
> itself when a user-specified file changes?  (e.g. trigger the same
> behavior for idl files that gets triggered when a CMakeLists.txt file
> is updated?)

Look at the following exemplary project:

# CMakeLists.txt:
CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR)
PROJECT(VARFILENUM C)
SET(CMAKE_VERBOSE_MAKEFILE ON)
CONFIGURE_FILE(gencustomcommand.cmake.in gencustomcommand.cmake @ONLY)
FILE(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/gen)
IF(NOT EXISTS ${CMAKE_BINARY_DIR}/customcommand.cmake)
    FILE(WRITE ${CMAKE_BINARY_DIR}/customcommand.cmake "")
ENDIF()
ADD_CUSTOM_TARGET(gen ${CMAKE_COMMAND}
    -DSRCDIR=${CMAKE_SOURCE_DIR}
    -DBINDIR=${CMAKE_BINARY_DIR}
    -DGENDIR=${CMAKE_BINARY_DIR}/gen
    -P ${CMAKE_BINARY_DIR}/gencustomcommand.cmake
    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/gen)
INCLUDE(${CMAKE_BINARY_DIR}/customcommand.cmake)
FILE(WRITE ${CMAKE_BINARY_DIR}/main.c "int main(void){return 0;}\n")
ADD_EXECUTABLE(main0 EXCLUDE_FROM_ALL main.c ${OUTPUTFILES})
ADD_CUSTOM_TARGET(main ALL ${CMAKE_COMMAND}
    --build ${CMAKE_BINARY_DIR}
    --target main0)
ADD_DEPENDENCIES(main gen)

# gencustomcommand.cmake.in:
EXECUTE_PROCESS(COMMAND sh ${SRCDIR}/genfiles.sh ${SRCDIR}/files.txt)
FILE(GLOB OUTPUTFILES RELATIVE ${GENDIR} *)
CONFIGURE_FILE(${SRCDIR}/customcommand.cmake.in
               ${BINDIR}/customcommand.cmake @ONLY)
FILE(REMOVE ${OUTPUTFILES})

# customcommand.cmake.in:
ADD_CUSTOM_COMMAND(
    OUTPUT @OUTPUTFILES@
    COMMAND sh @SRCDIR@/genfiles.sh @SRCDIR@/files.txt
    DEPENDS @SRCDIR@/files.txt)
SET(OUTPUTFILES @OUTPUTFILES@)

# genfiles.sh:
for i in $(cat $1); do touch $i; done

The files.txt file simply reads "f1.c f2.c f3.c".

When the target "main" is built, the target "gen" is built before which
uses the template-generated gencustomcommand.cmake script to generate a
customcommand.cmake file containing the custom command which will, in
turn, generate the files mentioned in files.txt using the genfiles.sh
shell script. Subsequently, the target "main0" - the actual one - is
built by CMake's --build facility. As customcommand.cmake is read by
INCLUDE(), any change in files.txt triggers a reconfiguration before
building main0 takes place, so main0 is always built from the files
mentioned in files.txt. Here, the crucial point is the intermediate
step between main and main0. Perhaps, this approach can be adapted
to the OP's concern.

Regards,

Michael


More information about the CMake mailing list