Browse Source

Ninja: Add `$subdir/{test,install,package}` targets

With the Makefile generator one can use `cd $subdir; make install` to build and
install targets associated with a given subdirectory.  This is not possible to
do with the Ninja generator since there is only one `build.ninja` file at the
top of the build tree.  However, we can approximate it by allowing one to run
`ninja $subdir/install` at the top of the tree to build the targets in the
corresponding subdirectory and install them.

This also makes sense for `test`, `package`, and other GLOBAL_TARGET targets.
It was already done for `all` by commit v3.6.0-rc1~240^2~2 (Ninja: Add
`$subdir/all` targets, 2016-03-11).
Brad King 9 years ago
parent
commit
0278989405

+ 14 - 3
Help/generator/Ninja.rst

@@ -7,6 +7,17 @@ A build.ninja file is generated into the build tree.  Recent versions
 of the ninja program can build the project through the "all" target.
 of the ninja program can build the project through the "all" target.
 An "install" target is also provided.
 An "install" target is also provided.
 
 
-For each subdirectory ``sub/dir`` of the project an additional target
-named ``sub/dir/all`` is generated that depends on all targets required
-by that subdirectory.
+For each subdirectory ``sub/dir`` of the project, additional targets
+are generated:
+
+``sub/dir/all``
+  Depends on all targets required by the subdirectory.
+
+``sub/dir/install``
+  Runs the install step in the subdirectory, if any.
+
+``sub/dir/test``
+  Runs the test step in the subdirectory, if any.
+
+``sub/dir/package``
+  Runs the package step in the subdirectory, if any.

+ 8 - 0
Help/release/dev/ninja-directory-targets.rst

@@ -0,0 +1,8 @@
+ninja-directory-targets
+-----------------------
+
+* The :generator:`Ninja` generator learned to produce phony targets
+  of the form ``sub/dir/{test,install,package}`` to drive the build
+  of a subdirectory installation, test or packaging target.
+  This is equivalent to ``cd sub/dir; make {test,install,package}``
+  with :ref:`Makefile Generators`.

+ 8 - 3
Source/cmGlobalNinjaGenerator.cxx

@@ -885,10 +885,15 @@ void cmGlobalNinjaGenerator::AppendTargetDepends(
   cmGeneratorTarget const* target, cmNinjaDeps& outputs)
   cmGeneratorTarget const* target, cmNinjaDeps& outputs)
 {
 {
   if (target->GetType() == cmState::GLOBAL_TARGET) {
   if (target->GetType() == cmState::GLOBAL_TARGET) {
-    // Global targets only depend on other utilities, which may not appear in
-    // the TargetDepends set (e.g. "all").
+    // These depend only on other CMake-provided targets, e.g. "all".
     std::set<std::string> const& utils = target->GetUtilities();
     std::set<std::string> const& utils = target->GetUtilities();
-    std::copy(utils.begin(), utils.end(), std::back_inserter(outputs));
+    for (std::set<std::string>::const_iterator i = utils.begin();
+         i != utils.end(); ++i) {
+      std::string d =
+        target->GetLocalGenerator()->GetCurrentBinaryDirectory() +
+        std::string("/") + *i;
+      outputs.push_back(this->ConvertToNinjaPath(d));
+    }
   } else {
   } else {
     cmNinjaDeps outs;
     cmNinjaDeps outs;
     cmTargetDependSet const& targetDeps = this->GetTargetDirectDepends(target);
     cmTargetDependSet const& targetDeps = this->GetTargetDirectDepends(target);

+ 1 - 12
Source/cmNinjaTargetGenerator.cxx

@@ -38,19 +38,8 @@ cmNinjaTargetGenerator* cmNinjaTargetGenerator::New(cmGeneratorTarget* target)
       return new cmNinjaNormalTargetGenerator(target);
       return new cmNinjaNormalTargetGenerator(target);
 
 
     case cmState::UTILITY:
     case cmState::UTILITY:
+    case cmState::GLOBAL_TARGET:
       return new cmNinjaUtilityTargetGenerator(target);
       return new cmNinjaUtilityTargetGenerator(target);
-      ;
-
-    case cmState::GLOBAL_TARGET: {
-      // We only want to process global targets that live in the home
-      // (i.e. top-level) directory.  CMake creates copies of these targets
-      // in every directory, which we don't need.
-      if (strcmp(target->GetLocalGenerator()->GetCurrentSourceDirectory(),
-                 target->GetLocalGenerator()->GetSourceDirectory()) == 0) {
-        return new cmNinjaUtilityTargetGenerator(target);
-      }
-      // else fallthrough
-    }
 
 
     default:
     default:
       return CM_NULLPTR;
       return CM_NULLPTR;

+ 12 - 5
Source/cmNinjaUtilityTargetGenerator.cxx

@@ -31,10 +31,12 @@ cmNinjaUtilityTargetGenerator::~cmNinjaUtilityTargetGenerator()
 
 
 void cmNinjaUtilityTargetGenerator::Generate()
 void cmNinjaUtilityTargetGenerator::Generate()
 {
 {
-  std::string utilCommandName = cmake::GetCMakeFilesDirectoryPostSlash();
+  std::string utilCommandName =
+    this->GetLocalGenerator()->GetCurrentBinaryDirectory();
+  utilCommandName += cmake::GetCMakeFilesDirectory();
+  utilCommandName += "/";
   utilCommandName += this->GetTargetName() + ".util";
   utilCommandName += this->GetTargetName() + ".util";
-  utilCommandName =
-    this->GetGlobalGenerator()->NinjaOutputPath(utilCommandName);
+  utilCommandName = this->ConvertToNinjaPath(utilCommandName);
 
 
   std::vector<std::string> commands;
   std::vector<std::string> commands;
   cmNinjaDeps deps, outputs, util_outputs(1, utilCommandName);
   cmNinjaDeps deps, outputs, util_outputs(1, utilCommandName);
@@ -144,6 +146,11 @@ void cmNinjaUtilityTargetGenerator::Generate()
       cmNinjaDeps(1, utilCommandName));
       cmNinjaDeps(1, utilCommandName));
   }
   }
 
 
-  this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(),
-                                             this->GetGeneratorTarget());
+  // Add an alias for the logical target name regardless of what directory
+  // contains it.  Skip this for GLOBAL_TARGET because they are meant to
+  // be per-directory and have one at the top-level anyway.
+  if (this->GetGeneratorTarget()->GetType() != cmState::GLOBAL_TARGET) {
+    this->GetGlobalGenerator()->AddTargetAlias(this->GetTargetName(),
+                                               this->GetGeneratorTarget());
+  }
 }
 }

+ 6 - 0
Tests/RunCMake/Ninja/RunCMakeTest.cmake

@@ -41,10 +41,16 @@ function(run_SubDir)
   run_cmake(SubDir)
   run_cmake(SubDir)
   if(WIN32)
   if(WIN32)
     set(SubDir_all [[SubDir\all]])
     set(SubDir_all [[SubDir\all]])
+    set(SubDir_test [[SubDir\test]])
+    set(SubDir_install [[SubDir\install]])
   else()
   else()
     set(SubDir_all [[SubDir/all]])
     set(SubDir_all [[SubDir/all]])
+    set(SubDir_test [[SubDir/test]])
+    set(SubDir_install [[SubDir/install]])
   endif()
   endif()
   run_cmake_command(SubDir-build ${CMAKE_COMMAND} --build . --target ${SubDir_all})
   run_cmake_command(SubDir-build ${CMAKE_COMMAND} --build . --target ${SubDir_all})
+  run_cmake_command(SubDir-test ${CMAKE_COMMAND} --build . --target ${SubDir_test})
+  run_cmake_command(SubDir-install ${CMAKE_COMMAND} --build . --target ${SubDir_install})
 endfunction()
 endfunction()
 run_SubDir()
 run_SubDir()
 
 

+ 1 - 0
Tests/RunCMake/Ninja/SubDir-install-stdout.txt

@@ -0,0 +1 @@
+-- Installing SubDir

+ 1 - 0
Tests/RunCMake/Ninja/SubDir-test-stdout.txt

@@ -0,0 +1 @@
+1/1 Test #1: SubDirTest

+ 5 - 0
Tests/RunCMake/Ninja/SubDir.cmake

@@ -1,2 +1,7 @@
+include(CTest)
 add_subdirectory(SubDir)
 add_subdirectory(SubDir)
 add_custom_target(TopFail ALL COMMAND does_not_exist)
 add_custom_target(TopFail ALL COMMAND does_not_exist)
+add_test(NAME TopTest COMMAND ${CMAKE_COMMAND} -E echo "Running TopTest")
+install(CODE [[
+  message(FATAL_ERROR "Installing Top")
+]])

+ 4 - 0
Tests/RunCMake/Ninja/SubDir/CMakeLists.txt

@@ -1,2 +1,6 @@
 add_custom_target(SubFail COMMAND does_not_exist)
 add_custom_target(SubFail COMMAND does_not_exist)
 add_custom_target(InAll ALL COMMAND ${CMAKE_COMMAND} -E echo "Building InAll")
 add_custom_target(InAll ALL COMMAND ${CMAKE_COMMAND} -E echo "Building InAll")
+add_test(NAME SubDirTest COMMAND ${CMAKE_COMMAND} -E echo "Running SubDirTest")
+install(CODE [[
+  message(STATUS "Installing SubDir")
+]])