[CMake] shared directory with subdirs and custom command

Michael Hertling mhertling at online.de
Fri Jun 4 18:45:34 EDT 2010


On 06/02/2010 04:23 PM, Doug Reiland wrote:
> I am porting a library over to cmake.
> This library is built both shared and static AND has several composite
> objects that get linked in.
> 
> For example,
> 
> subdir-a had makefile that compiled and linked a1.c a2.c into ../a.o
> 
> top directory linked in a.o into it's libs (shared and static)

Usually, source files are supposed to be compiled with different flags
targeting a shared or a static library; thus, linking the same object
file into both types of the library is a rather bad approach, IMO.

> I have converted most of this over to cmake by:
> 
> subdir-a's CMakeList.txt
>     -  make a shared lib and top directory shared lib link in via
> target_link_libraries

So, you have an additional shared library to deal with; to me,
this seems to be an avoidable complication of things.

>     -  add it's list of source files to a global property. top
> directory CMakeList.txt looks for that property and adds those files
> to its list for it's static library

Add subdir-a/a{1,2}.c to a list that contains all source files your
library is made from and build the shared and static variants with:

ADD_LIBRARY(mylib-shared SHARED ${SOURCES})
ADD_LIBRARY(mylib-static STATIC ${SOURCES})

Finally, use the target property OUTPUT_NAME for proper naming.

> This is cumbersome, but it is working ok.
> However, I am now running to other stuff the subdirs are doing, like
> custom compile flags and dependencies.
> 
> Question 1) Is the best method to handle these to get add more unique
> (by source file name) properties in the global scope and have top
> level CMakeList.txt look for them?

No, I wouldn't say so. Use source file properties to impose compile
flags where necessary and ADD_CUSTOM_COMMAND(OUTPUT ...) to define
dependencies on generated files.

> Question 2) How to handle custom targets at the subdir-level for the
> static library? For example, I have:
> a1.c which includes aa.c and ab.c
> aa.c and ab.c are generated by a perl script

Don't name them aa.c and ab.c but, say, aa.gen and ab.gen and modify
a1.c accordingly, i.e. #include "aa.gen" and #include "ab.gen". In
the parent directory's CMakeLists.txt,

ADD_CUSTOM_COMMAND(OUTPUT aa.gen ...)
ADD_CUSTOM_COMMAND(OUTPUT ab.gen ...)

and add a{a,b}.gen to the source file list mentioned above. Note that
such ADD_CUSTOM_COMMAND()s must reside in the same CMakeLists.txt as
the ADD_LIBRARY() et al. processing the output files; otherwise, they
will not be triggered when the library is rebuilt, i.e., you can't have
the ADD_CUSTOM_COMMAND()s in subdir-a/CMakeLists.txt while their OUTPUT
is mentioned in ADD_LIBRARY() in the parent directory's CMakeLists.txt.
The renaming of a{a,b}.c to a{a,b}.gen is necessary to prevent CMake
from compiling them twice, one time as direct prerequisites of the
library and another time when compiling a1.c as included files. So,
they're just an anchor for CMake's dependency tracking to trigger
ADD_CUSTOM_COMMAND().

> In subdir's CMakeList.txt I have, note add_sources, and
> add_file_dependencies are not included here, but add the absolute
> filenames to the global property for top-level library and
> add_file_source_properties(). I have already managed to extended
> add_file_dependencies() to add per-file global property where
> top-level Makefile to look to see if it needs to set
> source_file_properties, but I have no idea how to handle this
> custom_command. The shared library build works fine.
> 
> set(name my-subdir-lib)
> set(build-dir ${CMAKE_CURRENT_BINARY_DIR})
> set(source-dir ${CMAKE_CURRENT_SOURCE_DIR})
> set(generated-files aa.c ab.c)
> 
> foreach (g ${generated-files})
>         add_custom_command(
>                 OUTPUT  ${build-dir}/${g}
>                 DEPENDS ${source-dir}/my_perl_script.pl
>                         ${source-dir}/some_header_file.h
>                 COMMAND ${source-dir}/my_perl_script.pl
>                 ARGS    -i ${source-dir}/some_header_file.h -o
>                         ${build-dir}/${g}
>         )
>         set(generated-results ${generated-results} ${build-dir}/${g})
> endforeach()
> 
> 
> set(sources
>         a1.c a2.c
>         a3.c
> )
> 
> add_file_dependencies(a3.c ${generated-results})
> add_sources(super ${sources})
> 
> include_directories(
>         ${source-dir}
>         ${build-dir}
> )
> 
> add_library(${name} SHARED ${sources})

My advice would be - in the parent directory's CMakeLists.txt:

set(name my-lib)
set(build-dir ${CMAKE_CURRENT_BINARY_DIR})
set(source-dir ${CMAKE_CURRENT_SOURCE_DIR})
set(generated-files aa.gen ab.gen)

foreach (g ${generated-files})
        add_custom_command(
                OUTPUT  ${build-dir}/${g}
                DEPENDS ${source-dir}/subdir-a/my_perl_script.pl
                        ${source-dir}/subdir-a/some_header_file.h
                COMMAND ${source-dir}/subdir-a/my_perl_script.pl
                ARGS    -i ${source-dir}/subdir-a/some_header_file.h
                        -o ${build-dir}/${g}
        )
        list(APPEND generated-results ${build-dir}/${g})
endforeach()

set(sources
        subdir-a/a1.c subdir-a/a2.c
        subdir-a/a3.c
        ${generated-results}
)

# add_file_dependencies(a3.c ${generated-results})
# add_sources(super ${sources})

set_source_files_properties(...)

include_directories(
        ${source-dir}/subdir-a
        ${build-dir}
)

add_library(${name}-shared SHARED ${sources})
add_library(${name}-static STATIC ${sources})

set_target_properties(${name}-shared PROPERTIES OUTPUT_NAME ${name})
set_target_properties(${name}-static PROPERTIES OUTPUT_NAME ${name})

Btw, if you absolutely want to use properties in the manner you've
presented here consider to create custom target and source file
properties instead global ones since they obviously relate to
targets and source files and are not of global concern.

Regards,

Michael


More information about the CMake mailing list