Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CMake add_custom_target depending on whole project being built

Tags:

cmake

I want to add a test target that is dependant on the whole of a project being built successfully, without re-specifying dependencies on all the libraries or executables.

I would write this in make as:

all: foo bar

foo: ...
bar: ...

test: all
    test.sh

test.sh implicitly uses foo and bar and wants them to be up to date.

This is how I would expect to specify this in cmake.

add_library(foo ...)
add_executable(bar ...)

add_custom_target(test test.sh
              DEPENDS all
)

However this does not work as there is no all target.

Is there a way to specify this? Or is there a variable that expands to all the targets I am trying to build?

like image 798
Russell Gallop Avatar asked Jun 16 '11 12:06

Russell Gallop


2 Answers

As of version 2.8 CMake does not provide a variable that holds a list of all targets. The best you can do is to override the built-in commands add_library and add_executable with custom macros that call the built-in ones and keep track of all defined targets in a variable.

You can even use the same names for your custom macros. That way you do not have to make changes to all the existing add_library and add_executable calls. The original built-in commands are prefixed with an underscore if you override any of them:

set (_allTargets "")

macro(add_library _target)
    _add_library (${_target} ${ARGN})
    list (APPEND _allTargets ${_target})
endmacro()

macro(add_executable _target)
    _add_executable (${_target} ${ARGN})
    list (APPEND _allTargets ${_target})
endmacro()

add_library(liba STATIC liba.cpp)
add_executable(main liba main.cpp)

add_custom_target(test "${CMAKE_CURRENT_SOURCE_DIR}/test.sh")

add_dependencies(test ${_allTargets})

Also note that you cannot add a target dependency to a custom target with the DEPENDS option. DEPENDS may only reference existing files or files generated with add_custom_command(...) in the same directory. To add a target dependency use the add_dependencies command instead.

like image 153
sakra Avatar answered Oct 18 '22 12:10

sakra


I have not enough reputation to comment on Sakra answer...

One problem I see with that solution is that if you use any subdirectory the changes you do to the variable _allTargets inside the subdirectory will not be propagated to the parent scope.

Digging more, list(append ...) cannot be used in this case:

Similar to the SET command, the LIST command creates new variable values in the current scope, even if the list itself is actually defined in a parent scope. To propagate the results of these operations upwards, use SET with PARENT_SCOPE, SET with CACHE INTERNAL, or some other means of value propagation.

http://www.cmake.org/cmake/help/v2.8.11/cmake.html#command:set :

If PARENT_SCOPE is present, the variable will be set in the scope above the current scope. Each new directory or function creates a new scope. This command will set the value of a variable into the parent directory or calling function (whichever is applicable to the case at hand).

(note for myself: a macro is not a function)

I don't see a general solution (e.g. independent from using add_subdirectory) when using PARENT_SCOPE. However, here there seem to be a solution using CACHE INTERNAL.

Quoting from: http://www.cmake.org/pipermail/cmake/2007-November/018109.html

# A macro for passing lists between different directories
# through an internal cache variable.
MACRO (APPEND_INTERNAL_LIST  LIST_NAME  VALUE)

   # If the list in not in the cache, create it.
   IF (${LIST_NAME})
      SET (${LIST_NAME} "${${LIST_NAME}};${VALUE}" CACHE INTERNAL "Internal
variable")
   ELSE (${LIST_NAME})
      SET (${LIST_NAME} "${VALUE}" CACHE INTERNAL "Internal variable")
   ENDIF (${LIST_NAME})

ENDMACRO (APPEND_INTERNAL_LIST)

# A macro for passing lists between different directories
# through an internal cache variable.
# This function empties the variable (usually because of older runs)
MACRO (INITIALIZE_INTERNAL_LIST  LIST_NAME)
   SET (${LIST_NAME} "" CACHE INTERNAL "Internal variable")
ENDMACRO (INITIALIZE_INTERNAL_LIST)
like image 36
Antonio Avatar answered Oct 18 '22 11:10

Antonio