Skip to content

Instantly share code, notes, and snippets.

@jcfr
Last active August 9, 2018 19:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcfr/7e342d8ff786b82ff81873a96f66a292 to your computer and use it in GitHub Desktop.
Save jcfr/7e342d8ff786b82ff81873a96f66a292 to your computer and use it in GitHub Desktop.
Project created while working on Slicer issue "4595: multiple rules generate slicer (ninja error)". See https://issues.slicer.org/view.php?id=4595
cmake_minimum_required(VERSION 3.12)
project(Test LANGUAGES CXX)
# launcher executable
set(CTKAppLauncher_EXECUTABLE ${CMAKE_CURRENT_BINARY_DIR}/fake_launcher_executable)
file(WRITE ${CTKAppLauncher_EXECUTABLE} "")
# application and executable name
set(application_target "AwesomeApp")
set(application_name "Awesome")
set(executable_name "Awesome")
#if(NOT APPLE
# set(executable_name ${executable_name}App-real) # Setting a different filename "workaround" the problem
#endif()
#
# target type
#
set(target_type)
if(APPLE)
set(target_type MACOSX_BUNDLE)
endif()
#
# target built in "<root>/bin"
#
add_executable(${application_target} ${target_type} main.cpp)
set_target_properties(${application_target} PROPERTIES
OUTPUT_NAME ${executable_name}
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin
)
message(STATUS "Setting ${application_name} executable name to '${executable_name}${CMAKE_EXECUTABLE_SUFFIX}'")
#
# install target
#
if(NOT APPLE)
set(install_destination_args RUNTIME DESTINATION "bin")
else()
set(install_destination_args BUNDLE DESTINATION ".")
endif()
install(
TARGETS ${application_target}
${install_destination_args}
COMPONENT Runtime
)
#
# configured launcher in "<root>"
#
set(configured_launcher_executable "${CMAKE_CURRENT_BINARY_DIR}/${application_name}")
set(configure_launcher_command
${CMAKE_COMMAND} -E copy ${CTKAppLauncher_EXECUTABLE} ${configured_launcher_executable}
)
set(configure_launcher_comment
"Configuring application launcher: ${application_name}"
)
# Create command to copy the launcher
if(CMAKE_VERSION VERSION_GREATER 3.12.0)
message(STATUS "Adding Configure${application_name}Launcher command as regular regular build rule")
add_custom_command(
DEPENDS
${CTKAppLauncher_EXECUTABLE}
OUTPUT
${configured_launcher_executable}
COMMAND
${configure_launcher_command}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT
${configure_launcher_comment}
)
add_custom_target(Configure${application_name}Launcher ALL
DEPENDS
${application_target}
${CTKAppLauncher_EXECUTABLE}
${configured_launcher_executable}
)
else()
# Workaround issue https://issues.slicer.org/view.php?id=4595
message(STATUS "Adding Configure${application_name}Launcher command as a POST_BUILD event")
add_custom_target(Configure${application_name}Launcher ALL
DEPENDS
${CTKAppLauncher_EXECUTABLE}
${application_target}
COMMAND
${configure_launcher_command}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT
${configure_launcher_comment}
)
endif()
#
# side-effect: When using Ninja generator, it automatically adds an alias target for executables.
# This should only happen when there are no conflicting target,
# Proposed fix: https://gitlab.kitware.com/jcfr/cmake/commits/ninja-generator-do-not-add-alias-matching-custom-command-output
#
#
# Display path of the "real" application
#
get_target_property(application_output_dir ${application_target} RUNTIME_OUTPUT_DIRECTORY)
set(application_build_subdir "")
if(APPLE)
get_target_property(_is_bundle ${application_target} MACOSX_BUNDLE)
if(_is_bundle)
set(application_build_subdir "${application_name}.app/Contents/MacOS/")
endif()
endif()
set(real_executable "${application_output_dir}/${application_build_subdir}${executable_name}${CMAKE_EXECUTABLE_SUFFIX}")
message(STATUS "Setting ${application_name} real_executable to '${real_executable}'")
#include <iostream>
int main(int, char*[])
{
std::cout << "Hello" << std::endl;
return 0;
}
@jcfr
Copy link
Author

jcfr commented Aug 8, 2018

The issue originally occurred when building Slicer using Ninja generator on macOS.

This small project allows to reproduce the issue linux.

In a nutshell, the project:

  • adds an executable target named AwesomeApp that creates the executable <root>/bin/Awesome
  • adds a custom target and associated command to configure the launcher <root>/Awesome

failure using ninja

Configuring the project using kitware/ninja currently returns:

$ git clone git://gist.github.com/7e342d8ff786b82ff81873a96f66a292.git test-ninja && \
   mkdir test-ninja-build && \
   cd test-ninja-build && \
   cmake -G Ninja ../test-ninja && \
   ninja
ninja: error: build.ninja:103: multiple rules generate Awesome [-w dupbuild=err]

details of build.ninja

[...]

# Custom command for Awesome

build Awesome: CUSTOM_COMMAND CTKAppLauncher-0.1.24-linux-amd64/bin/CTKAppLauncher
  COMMAND = cd /tmp/test-ninja-build && /home/jcfr/Software/cmake-3.12.0-Linux-x86_64/bin/cmake -E copy /tmp/test-ninja-build/CTKAppLauncher-0.1.24-linux-amd64/bin/CTKAppLauncher /tmp/test-ninja-build/Awesome
  DESC = Configuring application launcher: Awesome
  restat = 1
# =============================================================================
# Target aliases.

build Awesome: phony bin/Awesome
build AwesomeApp: phony bin/Awesome

[...]

This phony rule is problematic:

build Awesome: phony bin/Awesome

workaround

On linux, we can easily address the problem setting the name of the executable to be <root>/bin/AwesomeApp-real instead of <root>/bin/Awesome

That said, this workaround is less preferable on macOS where the executable is of type MACOSX_BUNDLE and generated as <root>/bin/Awesome.app/Contents/MacOS/Awesome. Indeed, in that case we really want the name Awesome.app (instead of AwesomeApp-real.app) because the application will be fixed up and no launcher is provided to the user.

success using makefile

$ git clone git://gist.github.com/7e342d8ff786b82ff81873a96f66a292.git test-cmake && \
   mkdir test-cmake-build && \
   cd test-cmake-build && \
   cmake ../test-cmake && \
   make

@ihnorton
Copy link

ihnorton commented Aug 8, 2018

Another work-around?

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2c71dc2..03114d2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,5 @@

-cmake_minimum_required(VERSION 3.12)
+cmake_minimum_required(VERSION 3.11)

 project(Test LANGUAGES CXX)

@@ -37,19 +37,6 @@ message(STATUS "Setting ${application_name} executable name to '${executable_nam
 #
 set(configured_launcher_executable "${CMAKE_CURRENT_BINARY_DIR}/${application_name}")

-# Create command to copy the launcher
-add_custom_command(
-  DEPENDS
-    ${CTKAppLauncher_EXECUTABLE}
-  OUTPUT
-    ${configured_launcher_executable}
-  COMMAND
-    ${CMAKE_COMMAND} -E copy ${CTKAppLauncher_EXECUTABLE} ${configured_launcher_executable}
-  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
-  COMMENT
-    "Configuring application launcher: ${application_name}"
-  )
-
 #
 # side-effect: When using Ninja generator, it automatically adds an alias target if one of the
 #              dependency has a filename matching the name of an existing target.
@@ -60,5 +47,15 @@ add_custom_command(
 add_custom_target(Configure${application_name}Launcher ALL
   DEPENDS
     ${CTKAppLauncher_EXECUTABLE}
-    ${configured_launcher_executable}
+  )
+
+# Create command to copy the launcher
+add_custom_command(TARGET Configure${application_name}Launcher POST_BUILD
+  DEPENDS
+    ${CTKAppLauncher_EXECUTABLE}
+  COMMAND
+    ${CMAKE_COMMAND} -E copy ${CTKAppLauncher_EXECUTABLE} ${configured_launcher_executable}
+  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+  COMMENT
+    "Configuring application launcher: ${application_name}"
   )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment