[CMake] static library from several subdirectories

Michael Wild themiwi at gmail.com
Mon Mar 22 11:09:42 EDT 2010


On 22. Mar, 2010, at 15:22 , Verweij, Arjen wrote:

> Hi,
> 
>> From: Michael Wild [mailto:themiwi at gmail.com]
> 
>>>> You have to call ADD_SOURCES for all your sources BEFORE you do the
>>>> ADD_LIBRARY call.
>>> 
>>> I am already doing this, but since the files I was adding to the
>> library didn't exist, the call failed. I somehow expected cmake to start
>> executing the custom command since there was a previously defined
>> dependency, but apparently cmake wasn't set up for the way I'm
>> mistreating it :)
>>> 
>>> What triggers a custom command to be executed? With your help I have
>> arrived at:
>> 
>> 
>> You should add a "DEPENDS <input_file1> <input_file2> ..." option to
>> your ADD_CUSTOM_COMMAND calls, then CMake will know when to invoke it,
>> otherwise it can't know anything about the dependency.
> 
> Alright. I have spent a considerable amount of time experimenting, rewriting and testing. I have reduced my problem to hello world inside a hello.cr file. add_sources() has been molested and reduced to a mere shadow of its former glory. With or without DEPENDS doesn't change matters, although I'm sure without is bad form. Layout:
> 
> CMakeLists.txt (code below)
> h -- CMakeLists.txt (it just calls add_sources() -- without any arguments)
> | -- hello.cr (your typical hello world)
> 
> So, when I call add_sources() directly from the top level CMakeLists.txt file, I obtain my executable "test". If I comment out the add_sources() call, and use add_subdirectory(h) instead, and call add_sources() from there, it fails. The complaint is that it cannot find source file "hello.c" Apparently the problem is that add_custom_command() calls are local to the directory they are called in, and since the executable is assembled from a list on a higher level, it expects to find the source file on disc already :(
> 
> PROJECT(Test)
> enable_language( C )
> cmake_minimum_required(VERSION 2.8)
> 
> function (add_sources)
>    define_property(GLOBAL PROPERTY list BRIEF_DOCS "brief" FULL_DOCS "full")
> 
>    SET ( out_file "/mnt/usr3/people/verweija/cmake2/build/h/hello.c" )
>    SET ( in_file "/mnt/usr3/people/verweija/cmake2/h/hello.cr" )
> 
>    ADD_CUSTOM_COMMAND(
>        OUTPUT ${out_file}
>        COMMAND ${CMAKE_COMMAND} -E copy ${in_file} ${out_file}
>        DEPENDS "/mnt/usr3/people/verweija/cmake2/h/hello.cr"
>        )
> 
>    LIST( APPEND mylist ${out_file}  )
> 
>    set_property(GLOBAL APPEND PROPERTY "list" "${mylist}")
> endfunction(add_sources)
> 
> add_sources()
> #add_subdirectory( h )
> 
> get_property(list GLOBAL PROPERTY list)
> message ( STATUS list=${list} )
> ADD_EXECUTABLE( "test" ${list} )
> 
> I have found some threads about this, and I will be reading them shortly :)
> 
> Regards,
> Arjen

Problem is the following:

Dependencies between stuff in different directories only work for top-level targets (add_library, add_executable and add_custom_target). So, you would need to add a ADD_CUSTOM_TARGET call with a unique target name in your ADD_SOURCES function, add that name to a global list (as you do with the sources) and then after your ADD_EXECUTABLE call you have to do an ADD_DEPENDENCIES. Further, you need to tell CMake at the top-level that the sources are generated by setting the source file property GENERATED to TRUE.

All very complicated and error prone. So I suggest, you move the whole logic of your preprocessing to your top-level CMakeLists.txt file:


cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
project(example)

# function to collect all the sources from sub-directories
# into a single list
function(add_sources)
  get_property(is_defined GLOBAL PROPERTY SRCS_LIST DEFINED)
  if(NOT is_defined)
    define_property(GLOBAL PROPERTY SRCS_LIST
      BRIEF_DOCS "List of source files"
      FULL_DOCS "List of source files to be compiled in one library")
  endif()
  # make absolute paths
  set(SRCS)
  foreach(s IN LISTS ARGN)
    if(NOT IS_ABSOLUTE "${s}")
      get_filename_component(s "${s}" ABSOLUTE)
    endif()
    list(APPEND SRCS "${s}")
  endforeach()
  # append to global list
  set_property(GLOBAL APPEND PROPERTY SRCS_LIST "${SRCS}")
endfunction(add_sources)

# add subdirectories
add_subdirectory(src)

# preprocess sources
set(PREP_SRCS)
get_property(SRCS GLOBAL PROPERTY SRCS_LIST)
foreach(s IN LISTS SRCS)
  file(RELATIVE_PATH rs "${CMAKE_CURRENT_SOURCE_DIR}" "${s}")
  string(REGEX REPLACE "r$" "" o "${CMAKE_CURRENT_BINARY_DIR}/${rs}")
  add_custom_command(
    OUTPUT "${o}"
    COMMAND ${CMAKE_COMMAND} -E copy "${s}" "${o}"
    DEPENDS "${s}"
    COMMENT "Creating ${o}"
    VERBATIM
    )
  list(APPEND PREP_SRCS "${o}")
endforeach()

# add executable
add_executable(example ${PREP_SRCS})


HTH

Michael



More information about the CMake mailing list