[CMake] static library from several subdirectories

Michael Wild themiwi at gmail.com
Fri Mar 19 12:19:45 EDT 2010


On 19. Mar, 2010, at 16:27 , Verweij, Arjen wrote:

> Hi,
> 
> This is a bit length, sorry.
> 
>> -----Original Message-----
>> From: Michael Wild [mailto:themiwi at gmail.com]
> 
> 
>>> What am I missing? I'm trying again with my simple a/liba.c and
>> b/b/libb.c setup, but this time I start out with liba.cr and libb.cr
>> that need to be converted to liba.c and libb.c.
>>> 
>>> function(add_sources target)
>>> # define the <target>_SRCS properties if necessary
>>> get_property(prop_defined GLOBAL PROPERTY ${target}_SRCS DEFINED)
>>> if(NOT prop_defined)
>>>   define_property(GLOBAL PROPERTY ${target}_SRCS
>>>     BRIEF_DOCS "Sources for the ${target} target"
>>>     FULL_DOCS "List of source files for the ${target} target")
>>> endif()
>>> # create list of sources (absolute paths)
>>> set(SRCS)
>>> foreach(src IN LISTS ARGN)
>> 
>> I know I used this syntax, but it is relatively new. Please check that
>> it is supported by the CMake version you use...
> 
> I'm using cmake 2.8.0 on linux. But eventually it should run on a variety of platforms, including windows, altix, ibm and hpux itanium2.
> 
>>>   string (REGEX REPLACE "^(.+)\\.cr$" "\\1" BASECR ${src} )
>>>   string (COMPARE EQUAL ${src} ${BASECR}.cr FILE_IS_C)
>> 
>> You're removing .cr from the file and then add it back again and compare
>> it with the original name? that should always be TRUE, right?
> 
> Yes. But only if the file in ${src} is of the extension .cr. It is a crude way of setting up an identifier for a filetype, then matching if that filetype is set and do a custom command based on that, e.g.:
> 
>    foreach (FILE ${FILE_LIST})
>    string (REGEX REPLACE "^(.+)\\.fr$" "\\1" BASEFR ${FILE})
>    string (REGEX REPLACE "^(.+)\\.f90r$" "\\1" BASEF90R ${FILE})
>    string (REGEX REPLACE "^(.+)\\.cr$" "\\1" BASECR ${FILE})
>    string (REGEX REPLACE "^(.+)\\.cppr$" "\\1" BASECPPR ${FILE})
>    string (REGEX REPLACE "^(.+)\\.hr$" "\\1" BASEHR ${FILE})
>    string (COMPARE EQUAL ${FILE} ${BASEFR}.fr FILE_IS_FORTRAN)
>    string (COMPARE EQUAL ${FILE} ${BASEF90R}.f90r FILE_IS_FORTRAN90)
>    string (COMPARE EQUAL ${FILE} ${BASECR}.cr FILE_IS_C)
>    string (COMPARE EQUAL ${FILE} ${BASECPPR}.cppr FILE_IS_CPP)
>    string (COMPARE EQUAL ${FILE} ${BASEHR}.hr FILE_IS_HEADER)
>    if ($FILE_IS_...)
>      add_custom_command( ... )
>      # append to file list
>      # some macro
>    Elseif ( ... )
> ...
>    Else ( ... )
>    #foreach

Probably simpler:

# don't use FILE, that is a command...
foreach(fname ${FILE_LIST})
  foreach(type f f90 c cpp h) 
    if(fname MATCHES "^(.+)\\.${type}r$")
      string(REGEX REPLACE "r$" "" fbase "${fname}")
      if(type STREQUAL f)
        add_custom_command(...)
        # apend to file list
        # some macro
      elseif(type STREQUAL f90)
        add_custom_command(...)
         # ...
      # ...
      endif()
      break()
    endif()
  endforeach()
endforeach()
    
> [...]
> 
>> 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.

> 
> project(superDuper Fortran)
> enable_language( C )
> cmake_minimum_required(VERSION 2.8)
> #- Add sources for a target
> #
> #  ADD_SOURCES(<target> <source1> [<source2> ...])
> #
> function(add_sources target)
>  # define the <target>_SRCS properties if necessary
>  get_property(prop_defined GLOBAL PROPERTY ${target}_SRCS DEFINED)
>  if(NOT prop_defined)
>    define_property(GLOBAL PROPERTY ${target}_SRCS
>      BRIEF_DOCS "Sources for the ${target} target"
>      FULL_DOCS "List of source files for the ${target} target")
>  endif()
>  # create list of sources (absolute paths)
>  set(SRCS)
>  foreach(src IN LISTS ARGN)
>    string (REGEX REPLACE "^(.+)\\.cr$" "\\1" BASECR ${src} )
>    string (COMPARE EQUAL ${src} ${BASECR}.cr FILE_IS_C)
>    message ( STATUS src=${src} )
>    message ( STATUS basecr=${BASECR} )
>    if(NOT IS_ABSOLUTE "${src}")
>      get_filename_component(src "${src}" ABSOLUTE)
>    endif()
>    get_filename_component(path_to_src "${src}" PATH)    file(RELATIVE_PATH rel_path_to_src "${CMAKE_CURRENT_SOURCE_DIR}" "${path_to_src}")
>    set(out_file "${CMAKE_CURRENT_BINARY_DIR}/${rel_path_to_src}/${BASECR}.c")
>    add_custom_command(
>      OUTPUT "${out_file}"
>      COMMAND ${CMAKE_COMMAND} -E copy "${src}" "${out_file}"
>      message ( STATUS src="${src}" )
>      message ( STATUS out_file="${out_file}" )
>      WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
>      COMMENT "Creating ${out_file}"
>      VERBATIM
>      )
>    list(APPEND SRCS "${out_file}")
>  endforeach()  set_property(GLOBAL APPEND PROPERTY "${target}_SRCS" "${SRCS}")
> endfunction()
> 
> # descend into sub-directories
> add_subdirectory(a)
> add_subdirectory(b)
> 
> get_property(super_SRCS GLOBAL PROPERTY super_SRCS)
> message ( STATUS srcs=${super_SRCS})
> add_library(super STATIC ${super_SRCS})
> 
> 
> The output is:
> 
> sx085_260: cmake ../
> -- src=liba.cr
> -- basecr=liba
> -- src=libb.cr
> -- basecr=libb
> -- srcs=/mnt/usr3/people/verweija/cmake/build/a//liba.c/mnt/usr3/people/verweija/cmake/build/b/b//libb.c
> -- Configuring done
> CMake Error in CMakeLists.txt:
>  Cannot find source file "liba.c".  Tried extensions .c .C .c++ .cc .cpp
>  .cxx .m .M .mm .h .hh .h++ .hm .hpp .hxx .in .txx
> 
> 
> -- Build files have been written to: /mnt/usr3/people/verweija/cmake/build
> 
> So, the messages inside the custom command don't surface which leads me to believe that section is not executed at all. I am starting to worry after rereading the relevant section in the 4th edition of the book, custom commands just add extra lines to the resulting Makefile, but don't get executed while generating them :|
> 
> Regards,
> Arjen

As said above, the missing DEPENDS option is the problem.

HTH

Michael



More information about the CMake mailing list