Bruce Adams
Bruce Adams

Reputation: 5591

cmake: special targets like "all" and "test" having the same name in multiple places in the build tree?

cmake does not allow you to create multiple targets having the same name at different places in the build tree using add_custom_target(). However, it does provide special targets with that property notably:

Each of these when run within a sub-tree will apply to just that sub-tree. Is there a way achieve the same for user targets?

Here is a simplified example:

toplevel CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)

add_custom_target(thing)

add_subdirectory(sub1)
add_subdirectory(sub2)

sub1/CMakeLists.txt:

add_executable(foo foo.cpp)

add_custom_target(thing.sub1 DEPENDS foo)
add_dependencies(thing thing.sub1)

sub2/CMakeLists.txt:

add_executable(bar bar.cpp)

add_custom_target(thing.sub2 DEPENDS bar)
add_dependencies(thing thing.sub2)

I can get almost the behaviour I want in that:

What I would like is to be able to just run make thing and have it apply to the current sub-directory like the all target does.

Is this possible? If not is it a missing feature or would it be a misfeature and if so why?


Solution (almost)

@zuafi's direction led me to a solution.

It is currently possible but only using deprecated behaviour from policy CMP0002. There is effectively a missing feature for adding local targets which is covered by this open ticket.

My workaround is to implement add_local_target() for myself is as follows:

# determine whether local targets should be legal
if("${CMAKE_GENERATOR}" MATCHES "Make")
  cmake_policy(SET CMP0002 OLD)
  # property seems to be deprecated
  set_property(GLOBAL PROPERTY ALLOW_DUPLICATE_CUSTOM_TARGETS 1)
  set(ALLOW_LOCAL_TARGETS 1)
  message("using Makefile generator")
else()
  cmake_policy(SET CMP0002 NEW)
  message("Generator=${CMAKE_GENERATOR}")
endif()

# add a custom target with the same name as another (posssibly global) target iff permitted
# see http://stackoverflow.com/questions/35489093/cmake-special-targets-like-all-and-test-having-the-same-name-in-multiple-pl
function(add_local_target target)
  if(ALLOW_LOCAL_TARGETS)   
    set(dependencies "${ARGN}")
    foreach(dependency IN LISTS dependencies)
       if(TARGET ${dependency})
          list(APPEND targets ${dependency})
       else()
          message("${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt: warning: dependency ${dependency} for add_local_target(${target}) is NOT a target")
       endif()
    endforeach()
    add_custom_target(${target} DEPENDS ${targets})
  endif()
endfunction()

This combines well with target paths like target.subdir which can be used as a fall-back if using a non Makefile generator. I just use:

add_local_target(thing DEPENDS thing.sub1)

Update

I think this solution may be subtly broken. If you attempt to build a 'local' target it may build some other 'local' targets as well. However, it will build at minimum what it is supposed to build. It will also build less than a global project wide target with the same name would build. I haven't managed to track this down yet as the makefiles generated by cmake are somewhat gnarly.

Upvotes: 0

Views: 2072

Answers (1)

zaufi
zaufi

Reputation: 7129

Yes, it is possible... but not with all generators. Particularly it is Ok w/ UNIX Makefiles (see CMP0002 description). And as for me I want to have all unit tests in my project tree to have the same name (I have a bash completion for that name :), so I use the following macro helping me to declare executables:

macro(set_unit_test_target_name OUTPUT_VARIABLE DEFAULT_VALUE)
    cmake_policy(GET CMP0002 _may_use_same_name)
    if(_may_use_same_name STREQUAL "OLD")
        set(${OUTPUT_VARIABLE} "unit_tests")
    else()
        set(${OUTPUT_VARIABLE} ${DEFAULT_VALUE})
    endif()
endmacro()

# usage example
set_unit_test_target_name(UNIT_TESTS someTestExecutable)
add_executable(${UNIT_TESTS} ... )

so for *NIX builds all executables would be named unit_tests, and have unique names for other generators…

Upvotes: 1

Related Questions