[CMake] Trying to generate a source that depends on a library target's object files.

Michael Hertling mhertling at online.de
Thu Jul 7 11:21:10 EDT 2011


On 07/07/2011 02:43 PM, Luke Dalessandro wrote:
> Hi Everyone,
> 
> I have the following build dependencies that I'm trying to implement in cmake.
> 
> The ultimate target of the build is an archive library, libme.a.
> 
> libme.a is composed of a number of static source files, (s1.cpp, ..., sn.cpp), and one generated source file, (g.cpp).
> 
> g.cpp is built using a perl script that examines the object files associated with S. For instance maybe it contains one string array with the names of all of the symbols in the objects obtained with nm or objdump.
> 
> So traditionally, we have a build cycle that looks like:
> 
> compile s1.cpp -> s1.o
> ...
> compile sn.cpp -> sn.o
> perl getSymbols.pl s1.o ... sn.o -> g.cpp
> compile g.cpp -> g.o
> archive s1.o ... sn.o  g.o ->libme.a
> 
> I'm trying to emulate this with CMake. The only platform we have support for the getSymbols.pl stuff is on Unix-based systems, in Windows we disable this particular configuration.
> 
> My current plan is to have a library target libme.a that has the (s1.cpp, ..., sn.cpp) as its sources, and a second library, libtemp.a, that has g.cpp as a source. g.cpp is the output of a custom_command that depends on (s1.cpp, ..., sn.cpp) and is built using getSymbols.pl (modified to account for the fact that its input is a .a rather than the set of .os). Then I will add a POST_BUILD custom_command for libtemp.a that extracts the object from libtemp.a and adds it to the libme.a archive.
> 
> This has the advantage that g.cpp is compiled with all of the same flags as the rest of the project---I'm not sure that there's a way to do this without the libtemp intermediate target. I don't know how to add a PRE_LINK command to libme.a that generates and compiles g.cpp in exactly the way that everything else is compiled, and I can't add g.cpp as a generated source for libme.a because I can't depend on the (s1.o, ..., sn.o) object files produced in its compilation.
> 
> I'm worried that the dependency on the custom g.cpp isn't quite right because I really depend on their object files. I could depend on libme.a, but that seems like it will introduce a circular dependency since I update libme.a in a POST_BUILD command for libtemp.a. I could also just say that target_link_libraries(me temp) but I don't really want to expose the two different libraries to users.
> 
> Does anyone know of a cleaner way to do this?
> 
> Luke

Look at the following exemplary project:

# CMakeLists.txt:
CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR)
PROJECT(OBJDEP C)
SET(CMAKE_VERBOSE_MAKEFILE ON)
FILE(WRITE ${CMAKE_BINARY_DIR}/s1.c "void s1(void){}\n")
FILE(WRITE ${CMAKE_BINARY_DIR}/s2.c "void s2(void){}\n")
FILE(WRITE ${CMAKE_BINARY_DIR}/s3.c "void s3(void){}\n")
FILE(WRITE ${CMAKE_BINARY_DIR}/g.c "")  # Initially.
ADD_LIBRARY(me STATIC s1.c s2.c s3.c g.c)
SET_TARGET_PROPERTIES(me PROPERTIES
    RULE_LAUNCH_LINK "sh ${CMAKE_SOURCE_DIR}/objects.sh <OBJECTS> --")
ADD_CUSTOM_COMMAND(TARGET me POST_BUILD
    COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/g.cmake
    COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target me)

# objects.sh:
if [ "$1" != "--" ]; then
    OBJECTS=""
    while [ "$1" != "--" ]; do OBJECTS="${OBJECTS}$1\n"; shift; done
    echo -ne "${OBJECTS}" > objects.txt
fi
shift
exec $@

# g.cmake:
FILE(STRINGS objects.txt OBJECTS)
FILE(WRITE g.c.in "/*\n")
FOREACH(i IN LISTS OBJECTS)
    IF(NOT i MATCHES "^.*/g\\.c\\.[^/]*\$")
        FILE(APPEND g.c.in "${i}\n")
    ENDIF()
ENDFOREACH()
FILE(APPEND g.c.in "*/\n")
# Actually, use getSymbols.pl here.
CONFIGURE_FILE(g.c.in g.c COPYONLY)

First of all, the "me" target uses the objects.sh shell script as a
launcher at link time to collect the target's object files reliably;
since you said that your concern is limited to *nix, this shouldn't
be a serious restriction and is also not strictly needed. The check
for $1 equaling "--" is necessary since the script is called twice,
once for ar with object files and another time for ranlib without.

The g.cmake script is run as a POST_BUILD custom command for the "me"
target and uses the objects.txt file left by objects.sh to generate a
g.c.in template which is finally transferred to the g.c source file by
CONFIGURE_FILE(... COPYONLY). This is crucial because g.c must not be
touched if the list of object files has not changed, see below.

In the end, the POST_BUILD custom command rebuilds the "me" target via
CMake's --build switch. This means that the newly (re)generated g.c is
compiled and the "me" target relinked. Now, if the custom command does
not touch the g.c file - there's no reason to do so since nothing has
changed in the meantime - the "me" target's subsequent re-invocation
does nothing because all of the target's source and object files are
up-to-date; in particular, it doesn't invoke the custom command, so
the recursion terminates.

The downside of this approach is that the "me" target is sometimes
generated more than once - typically twice when the list of object
files, i.e. the g.c file, has changed - but this should be bearable.

'hope that helps.

Regards,

Michael


More information about the CMake mailing list