浏览代码

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 年之前
父节点
当前提交
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.
 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)
 {
   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::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 {
     cmNinjaDeps outs;
     cmTargetDependSet const& targetDeps = this->GetTargetDirectDepends(target);

+ 1 - 12
Source/cmNinjaTargetGenerator.cxx

@@ -38,19 +38,8 @@ cmNinjaTargetGenerator* cmNinjaTargetGenerator::New(cmGeneratorTarget* target)
       return new cmNinjaNormalTargetGenerator(target);
 
     case cmState::UTILITY:
+    case cmState::GLOBAL_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:
       return CM_NULLPTR;

+ 12 - 5
Source/cmNinjaUtilityTargetGenerator.cxx

@@ -31,10 +31,12 @@ cmNinjaUtilityTargetGenerator::~cmNinjaUtilityTargetGenerator()
 
 void cmNinjaUtilityTargetGenerator::Generate()
 {
-  std::string utilCommandName = cmake::GetCMakeFilesDirectoryPostSlash();
+  std::string utilCommandName =
+    this->GetLocalGenerator()->GetCurrentBinaryDirectory();
+  utilCommandName += cmake::GetCMakeFilesDirectory();
+  utilCommandName += "/";
   utilCommandName += this->GetTargetName() + ".util";
-  utilCommandName =
-    this->GetGlobalGenerator()->NinjaOutputPath(utilCommandName);
+  utilCommandName = this->ConvertToNinjaPath(utilCommandName);
 
   std::vector<std::string> commands;
   cmNinjaDeps deps, outputs, util_outputs(1, utilCommandName);
@@ -144,6 +146,11 @@ void cmNinjaUtilityTargetGenerator::Generate()
       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)
   if(WIN32)
     set(SubDir_all [[SubDir\all]])
+    set(SubDir_test [[SubDir\test]])
+    set(SubDir_install [[SubDir\install]])
   else()
     set(SubDir_all [[SubDir/all]])
+    set(SubDir_test [[SubDir/test]])
+    set(SubDir_install [[SubDir/install]])
   endif()
   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()
 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_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(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")
+]])