[CMake] static library from several subdirectories

Verweij, Arjen VerweijA at tass-safe.com
Fri Mar 19 11:27:48 EDT 2010


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

>>    if(NOT IS_ABSOLUTE "${src}")
>>      get_filename_component(path_to_src "${src}" ABSOLUTE )
>>      get_filename_component(path_to_src "${path_to_src}" PATH )
>>      get_filename_component(src "${src}" ABSOLUTE)
>>    endif()
>
>This is dangerous, because if src is originally absolute, path_to_src
>will be undefined! Change it to this:
>
>if(NOT IS_ABSOLUTE "${src}")
>  get_filename_component(src "${src}" ABSOLUTE)
>endif()
>get_filename_component(path_to_src "${src}" PATH)

OK, that would be bad. Thanks for the pointer. I was trying to obtain a handle to a file in the same directory as my unprocessed source, so I could add it there. I will look into it using your next pointer.

>You should NEVER write to the source directory. Don't even think about
>it. And output-redirection doesn't work. So for your test-purposes, you
>could try:
>
>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}"
>  WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
>  COMMENT "Creating ${out_file}"
>  VERBATIM
>  )

Understood. I was told this before by someone else. Thanks for reminding me.

>>
>>    #add_custom_command (
>>    #  OUTPUT ${path_to_src}/${BASECR}.c
>>    #  COMMAND cat ${path_to_src}/${BASECR}.cr >
>${path_to_src}/${BASECR}.c
>>    #  DEPENDS ${path_to_src}/${BASECR}.cr
>>    #  )
>>    list(APPEND SRCS "${path_to_src}/${BASECR}.c")
>
>Of course, change this then to
>
>list(APPEND SRCS "${out_file}")
>
>>  endforeach()
>>  # append to global property
>>  set_property(GLOBAL APPEND PROPERTY "${target}_SRCS" "${SRCS}")
>> endfunction()
>>

OK.

>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:

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



More information about the CMake mailing list