3

My Mercurial repository repo1 has a custom target named foo; never mind what it does. I also have another repository repo2, which which I want to use as a subrepo of repo1. repo2 is developed in a similar fashion to repo1, and also has a custom target named foo, doing about the same thing (but just for the repo2 directory of course).

If I try to run CMake for repo1 with add_subdirectory(relative/path/to/repo2) in the CMakeLists.txt, I get:

CMake Error at CMakeLists.txt:123 (add_custom_target):
  add_custom_target cannot create target "foo" because another target with
  the same name already exists.  The existing target is a custom target
  created in source directory

I suppose I could just prefix the custom target names with the repository names, but that seems like a crude solution to this problem; and I kind of like the fact that make foo does the same thing, conceptually, both in repo1 and in repo2. So is there anything smarter I can do here?

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • `I kind of like the fact that 'make foo' does the same thing, conceptually, both in repo1 and in repo2.` - And what behavior of `make foo` in repo1 you expect? Building `foo` in both repo1 and repo2? Or building only `foo` in repo1? If you want the last case, instead of `add_subdirectory` use `ExternalProject_Add`. – Tsyvarev Mar 09 '17 at 10:33
  • @Tsyvarev: I don't mind very much which option it is, actually (also, in some cases this might be the same thing, e.g. if `foo` is something like running `cloc` or `ctags`). But other people reading this question may have either preference. Can you make your suggestion into an answer? – einpoklum Mar 09 '17 at 10:34

1 Answers1

1

Approach depends on what you expect from

make foo
  1. Building target only for current project. That is, being run from directory of project1, make foo should build target for this project. Same for project2.

    In that case, use ExternalProject_Add instead of add_subdirectory for bind projects together.

  2. Building targets for both projects.

    Usually such targets are "project-wide actions", like make uninstall or make test.

    In that case, before adding the target into the project, you need to check whether target exists and take appropriate actions:

    if(NOT TARGET foo)
        <create target foo>
    endif()
    <append-new-actions-to-foo>
    

    Steps "create" and "append" depend on target type.

    E.g., classic uninstall target automatically process all subprojects via reading install_manifest.txt file:

    if(NOT TARGET uninstall)
        add_custom_target(uninstall ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
    endif()
    

    For general case, you may create per-project targets and attach them via add_dependencies to the "shared" target:

    if(NOT TARGET foo)
        add_custom_target(foo)
    endif()
    add_custom_target(foo_${CMAKE_PROJECT_NAME} <do-something>)
    add_dependencies(foo foo_${CMAKE_PROJECT_NAME})
    
Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
  • I like your second option a lot. The first option could use a link or some more explanation but this is enough for me to accept. – einpoklum Mar 09 '17 at 13:00