Browse Source

cmake: Teach --build mode to support multiple targets

Fixes: #16136
Bartosz Kosiorek 6 years ago
parent
commit
324d18bb34
36 changed files with 403 additions and 328 deletions
  1. 1 1
      Help/command/build_command.rst
  2. 2 2
      Help/manual/cmake.1.rst
  3. 5 0
      Help/release/dev/cmake-build-multiply-targets.rst
  4. 1 1
      Source/CTest/cmCTestBuildAndTestHandler.cxx
  5. 9 8
      Source/cmGlobalBorlandMakefileGenerator.cxx
  6. 6 9
      Source/cmGlobalBorlandMakefileGenerator.h
  7. 56 43
      Source/cmGlobalGenerator.cxx
  8. 9 9
      Source/cmGlobalGenerator.h
  9. 19 11
      Source/cmGlobalGhsMultiGenerator.cxx
  10. 6 9
      Source/cmGlobalGhsMultiGenerator.h
  11. 9 8
      Source/cmGlobalJOMMakefileGenerator.cxx
  12. 6 9
      Source/cmGlobalJOMMakefileGenerator.h
  13. 9 8
      Source/cmGlobalNMakeMakefileGenerator.cxx
  14. 6 9
      Source/cmGlobalNMakeMakefileGenerator.h
  15. 17 11
      Source/cmGlobalNinjaGenerator.cxx
  16. 6 9
      Source/cmGlobalNinjaGenerator.h
  17. 20 14
      Source/cmGlobalUnixMakefileGenerator3.cxx
  18. 6 9
      Source/cmGlobalUnixMakefileGenerator3.h
  19. 66 55
      Source/cmGlobalVisualStudio10Generator.cxx
  20. 6 9
      Source/cmGlobalVisualStudio10Generator.h
  21. 38 20
      Source/cmGlobalVisualStudio7Generator.cxx
  22. 6 9
      Source/cmGlobalVisualStudio7Generator.h
  23. 10 8
      Source/cmGlobalWatcomWMakeGenerator.cxx
  24. 6 9
      Source/cmGlobalWatcomWMakeGenerator.h
  25. 26 14
      Source/cmGlobalXCodeGenerator.cxx
  26. 6 9
      Source/cmGlobalXCodeGenerator.h
  27. 4 4
      Source/cmake.cxx
  28. 2 2
      Source/cmake.h
  29. 28 16
      Source/cmakemain.cxx
  30. 1 0
      Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-jobs-stderr.txt
  31. 0 3
      Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-stderr.txt
  32. 0 0
      Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-with-clean-first-result.txt
  33. 2 0
      Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-with-clean-first-stderr.txt
  34. 1 0
      Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-with-clean-second-result.txt
  35. 2 0
      Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-with-clean-second-stderr.txt
  36. 6 0
      Tests/RunCMake/CommandLine/RunCMakeTest.cmake

+ 1 - 1
Help/command/build_command.rst

@@ -14,7 +14,7 @@ This is mainly intended for internal use by the :module:`CTest` module.
 
 Sets the given ``<variable>`` to a command-line string of the form::
 
- <cmake> --build . [--config <config>] [--target <target>] [-- -i]
+ <cmake> --build . [--config <config>] [--target <target>...] [-- -i]
 
 where ``<cmake>`` is the location of the :manual:`cmake(1)` command-line
 tool, and ``<config>`` and ``<target>`` are the values provided to the

+ 2 - 2
Help/manual/cmake.1.rst

@@ -276,8 +276,8 @@ following options:
   The :envvar:`CMAKE_BUILD_PARALLEL_LEVEL` environment variable, if set,
   specifies a default parallel level when this option is not given.
 
-``--target <tgt>``
-  Build ``<tgt>`` instead of default targets.  May only be specified once.
+``--target <tgt>...``
+  Build ``<tgt>`` instead of default targets.  May be specified multiple times.
 
 ``--config <cfg>``
   For multi-configuration tools, choose configuration ``<cfg>``.

+ 5 - 0
Help/release/dev/cmake-build-multiply-targets.rst

@@ -0,0 +1,5 @@
+cmake-build-multiply-targets
+----------------------------
+
+* The :manual:`cmake(1)` ``--build`` tool ``--target`` parameter gained support for
+  multiple targets, e.g. ``cmake --build . --target Library1 Library2``.

+ 1 - 1
Source/CTest/cmCTestBuildAndTestHandler.cxx

@@ -256,7 +256,7 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring)
     }
     int retVal = cm.GetGlobalGenerator()->Build(
       cmake::NO_BUILD_PARALLEL_LEVEL, this->SourceDir, this->BinaryDir,
-      this->BuildProject, tar, output, this->BuildMakeProgram, config,
+      this->BuildProject, { tar }, output, this->BuildMakeProgram, config,
       !this->BuildNoClean, false, false, remainingTime);
     out << output;
     // if the build failed then return

+ 9 - 8
Source/cmGlobalBorlandMakefileGenerator.cxx

@@ -53,15 +53,16 @@ void cmGlobalBorlandMakefileGenerator::GetDocumentation(
   entry.Brief = "Generates Borland makefiles.";
 }
 
-void cmGlobalBorlandMakefileGenerator::GenerateBuildCommand(
-  GeneratedMakeCommand& makeCommand, const std::string& makeProgram,
-  const std::string& projectName, const std::string& projectDir,
-  const std::string& targetName, const std::string& config, bool fast,
-  int /*jobs*/, bool verbose, std::vector<std::string> const& makeOptions)
+std::vector<cmGlobalGenerator::GeneratedMakeCommand>
+cmGlobalBorlandMakefileGenerator::GenerateBuildCommand(
+  const std::string& makeProgram, const std::string& projectName,
+  const std::string& projectDir, std::vector<std::string> const& targetNames,
+  const std::string& config, bool fast, int /*jobs*/, bool verbose,
+  std::vector<std::string> const& makeOptions)
 {
-  this->cmGlobalUnixMakefileGenerator3::GenerateBuildCommand(
-    makeCommand, makeProgram, projectName, projectDir, targetName, config,
-    fast, cmake::NO_BUILD_PARALLEL_LEVEL, verbose, makeOptions);
+  return this->cmGlobalUnixMakefileGenerator3::GenerateBuildCommand(
+    makeProgram, projectName, projectDir, targetNames, config, fast,
+    cmake::NO_BUILD_PARALLEL_LEVEL, verbose, makeOptions);
 }
 
 void cmGlobalBorlandMakefileGenerator::PrintBuildCommandAdvice(

+ 6 - 9
Source/cmGlobalBorlandMakefileGenerator.h

@@ -46,15 +46,12 @@ public:
   bool AllowDeleteOnError() const override { return false; }
 
 protected:
-  void GenerateBuildCommand(GeneratedMakeCommand& makeCommand,
-                            const std::string& makeProgram,
-                            const std::string& projectName,
-                            const std::string& projectDir,
-                            const std::string& targetName,
-                            const std::string& config, bool fast, int jobs,
-                            bool verbose,
-                            std::vector<std::string> const& makeOptions =
-                              std::vector<std::string>()) override;
+  std::vector<GeneratedMakeCommand> GenerateBuildCommand(
+    const std::string& makeProgram, const std::string& projectName,
+    const std::string& projectDir, std::vector<std::string> const& targetNames,
+    const std::string& config, bool fast, int jobs, bool verbose,
+    std::vector<std::string> const& makeOptions =
+      std::vector<std::string>()) override;
 
   void PrintBuildCommandAdvice(std::ostream& os, int jobs) const override;
 };

+ 56 - 43
Source/cmGlobalGenerator.cxx

@@ -1763,9 +1763,9 @@ int cmGlobalGenerator::TryCompile(int jobs, const std::string& srcdir,
                                         this->FirstTimeProgress);
   }
 
-  std::string newTarget;
+  std::vector<std::string> newTarget = {};
   if (!target.empty()) {
-    newTarget += target;
+    newTarget = { target };
   }
   std::string config =
     mf->GetSafeDefinition("CMAKE_TRY_COMPILE_CONFIGURATION");
@@ -1773,14 +1773,16 @@ int cmGlobalGenerator::TryCompile(int jobs, const std::string& srcdir,
                      config, false, fast, false, this->TryCompileTimeout);
 }
 
-void cmGlobalGenerator::GenerateBuildCommand(
-  GeneratedMakeCommand& makeCommand, const std::string& /*unused*/,
+std::vector<cmGlobalGenerator::GeneratedMakeCommand>
+cmGlobalGenerator::GenerateBuildCommand(
   const std::string& /*unused*/, const std::string& /*unused*/,
-  const std::string& /*unused*/, const std::string& /*unused*/,
-  bool /*unused*/, int /*unused*/, bool /*unused*/,
-  std::vector<std::string> const& /*unused*/)
+  const std::string& /*unused*/, std::vector<std::string> const& /*unused*/,
+  const std::string& /*unused*/, bool /*unused*/, int /*unused*/,
+  bool /*unused*/, std::vector<std::string> const& /*unused*/)
 {
+  GeneratedMakeCommand makeCommand;
   makeCommand.Add("cmGlobalGenerator::GenerateBuildCommand not implemented");
+  return { std::move(makeCommand) };
 }
 
 void cmGlobalGenerator::PrintBuildCommandAdvice(std::ostream& /*os*/,
@@ -1790,15 +1792,13 @@ void cmGlobalGenerator::PrintBuildCommandAdvice(std::ostream& /*os*/,
   // they do not support certain build command line options
 }
 
-int cmGlobalGenerator::Build(int jobs, const std::string& /*unused*/,
-                             const std::string& bindir,
-                             const std::string& projectName,
-                             const std::string& target, std::string& output,
-                             const std::string& makeCommandCSTR,
-                             const std::string& config, bool clean, bool fast,
-                             bool verbose, cmDuration timeout,
-                             cmSystemTools::OutputOption outputflag,
-                             std::vector<std::string> const& nativeOptions)
+int cmGlobalGenerator::Build(
+  int jobs, const std::string& /*unused*/, const std::string& bindir,
+  const std::string& projectName, const std::vector<std::string>& targets,
+  std::string& output, const std::string& makeCommandCSTR,
+  const std::string& config, bool clean, bool fast, bool verbose,
+  cmDuration timeout, cmSystemTools::OutputOption outputflag,
+  std::vector<std::string> const& nativeOptions)
 {
   bool hideconsole = cmSystemTools::GetRunCommandHideConsole();
 
@@ -1819,32 +1819,37 @@ int cmGlobalGenerator::Build(int jobs, const std::string& /*unused*/,
     return 1;
   }
 
-  int retVal;
+  int retVal = 0;
   cmSystemTools::SetRunCommandHideConsole(true);
   std::string outputBuffer;
   std::string* outputPtr = &outputBuffer;
 
-  GeneratedMakeCommand makeCommand;
-  this->GenerateBuildCommand(makeCommand, makeCommandCSTR, projectName, bindir,
-                             target, config, fast, jobs, verbose,
-                             nativeOptions);
+  std::vector<GeneratedMakeCommand> makeCommand =
+    this->GenerateBuildCommand(makeCommandCSTR, projectName, bindir, targets,
+                               config, fast, jobs, verbose, nativeOptions);
 
   // Workaround to convince some commands to produce output.
   if (outputflag == cmSystemTools::OUTPUT_PASSTHROUGH &&
-      makeCommand.RequiresOutputForward) {
+      makeCommand.back().RequiresOutputForward) {
     outputflag = cmSystemTools::OUTPUT_FORWARD;
   }
 
   // should we do a clean first?
   if (clean) {
-    GeneratedMakeCommand cleanCommand;
-    this->GenerateBuildCommand(cleanCommand, makeCommandCSTR, projectName,
-                               bindir, "clean", config, fast, jobs, verbose);
+    std::vector<GeneratedMakeCommand> cleanCommand =
+      this->GenerateBuildCommand(makeCommandCSTR, projectName, bindir,
+                                 { "clean" }, config, fast, jobs, verbose);
     output += "\nRun Clean Command:";
-    output += cleanCommand.Printable();
+    output += cleanCommand.front().Printable();
     output += "\n";
-
-    if (!cmSystemTools::RunSingleCommand(cleanCommand.PrimaryCommand,
+    if (cleanCommand.size() != 1) {
+      this->GetCMakeInstance()->IssueMessage(MessageType::INTERNAL_ERROR,
+                                             "The generator did not produce "
+                                             "exactly one command for the "
+                                             "'clean' target");
+      return 1;
+    }
+    if (!cmSystemTools::RunSingleCommand(cleanCommand.front().PrimaryCommand,
                                          outputPtr, outputPtr, &retVal,
                                          nullptr, outputflag, timeout)) {
       cmSystemTools::SetRunCommandHideConsole(hideconsole);
@@ -1858,25 +1863,33 @@ int cmGlobalGenerator::Build(int jobs, const std::string& /*unused*/,
   }
 
   // now build
-  std::string makeCommandStr = makeCommand.Printable();
+  std::string makeCommandStr;
   output += "\nRun Build Command(s):";
-  output += makeCommandStr;
-  output += "\n";
 
-  if (!cmSystemTools::RunSingleCommand(makeCommand.PrimaryCommand, outputPtr,
-                                       outputPtr, &retVal, nullptr, outputflag,
-                                       timeout)) {
-    cmSystemTools::SetRunCommandHideConsole(hideconsole);
-    cmSystemTools::Error(
-      "Generator: execution of make failed. Make command was: " +
-      makeCommandStr);
-    output += *outputPtr;
-    output += "\nGenerator: execution of make failed. Make command was: " +
-      makeCommandStr + "\n";
+  for (auto command = makeCommand.begin(); command != makeCommand.end();
+       ++command) {
+    makeCommandStr = command->Printable();
+    if (command != makeCommand.end()) {
+      makeCommandStr += " && ";
+    }
 
-    return 1;
+    output += makeCommandStr;
+    if (!cmSystemTools::RunSingleCommand(command->PrimaryCommand, outputPtr,
+                                         outputPtr, &retVal, nullptr,
+                                         outputflag, timeout)) {
+      cmSystemTools::SetRunCommandHideConsole(hideconsole);
+      cmSystemTools::Error(
+        "Generator: execution of make failed. Make command was: " +
+        makeCommandStr);
+      output += *outputPtr;
+      output += "\nGenerator: execution of make failed. Make command was: " +
+        makeCommandStr + "\n";
+
+      return 1;
+    }
+    output += *outputPtr;
   }
-  output += *outputPtr;
+  output += "\n";
   cmSystemTools::SetRunCommandHideConsole(hideconsole);
 
   // The OpenWatcom tools do not return an error code when a link

+ 9 - 9
Source/cmGlobalGenerator.h

@@ -13,6 +13,7 @@
 #include <utility>
 #include <vector>
 
+#include "cmAlgorithms.h"
 #include "cmCustomCommandLines.h"
 #include "cmDuration.h"
 #include "cmExportSetMap.h"
@@ -203,10 +204,10 @@ public:
    */
   int Build(
     int jobs, const std::string& srcdir, const std::string& bindir,
-    const std::string& projectName, const std::string& targetName,
-    std::string& output, const std::string& makeProgram,
-    const std::string& config, bool clean, bool fast, bool verbose,
-    cmDuration timeout,
+    const std::string& projectName,
+    std::vector<std::string> const& targetNames, std::string& output,
+    const std::string& makeProgram, const std::string& config, bool clean,
+    bool fast, bool verbose, cmDuration timeout,
     cmSystemTools::OutputOption outputflag = cmSystemTools::OUTPUT_NONE,
     std::vector<std::string> const& nativeOptions =
       std::vector<std::string>());
@@ -221,11 +222,10 @@ public:
   {
   };
 
-  virtual void GenerateBuildCommand(
-    GeneratedMakeCommand& makeCommand, const std::string& makeProgram,
-    const std::string& projectName, const std::string& projectDir,
-    const std::string& targetName, const std::string& config, bool fast,
-    int jobs, bool verbose,
+  virtual std::vector<GeneratedMakeCommand> GenerateBuildCommand(
+    const std::string& makeProgram, const std::string& projectName,
+    const std::string& projectDir, std::vector<std::string> const& targetNames,
+    const std::string& config, bool fast, int jobs, bool verbose,
     std::vector<std::string> const& makeOptions = std::vector<std::string>());
 
   virtual void PrintBuildCommandAdvice(std::ostream& os, int jobs) const;

+ 19 - 11
Source/cmGlobalGhsMultiGenerator.cxx

@@ -369,12 +369,14 @@ void cmGlobalGhsMultiGenerator::OutputTopLevelProject(
   fout.Close();
 }
 
-void cmGlobalGhsMultiGenerator::GenerateBuildCommand(
-  GeneratedMakeCommand& makeCommand, const std::string& makeProgram,
-  const std::string& projectName, const std::string& projectDir,
-  const std::string& targetName, const std::string& /*config*/, bool /*fast*/,
-  int jobs, bool /*verbose*/, std::vector<std::string> const& makeOptions)
+std::vector<cmGlobalGenerator::GeneratedMakeCommand>
+cmGlobalGhsMultiGenerator::GenerateBuildCommand(
+  const std::string& makeProgram, const std::string& projectName,
+  const std::string& projectDir, std::vector<std::string> const& targetNames,
+  const std::string& /*config*/, bool /*fast*/, int jobs, bool /*verbose*/,
+  std::vector<std::string> const& makeOptions)
 {
+  GeneratedMakeCommand makeCommand = {};
   const char* gbuild =
     this->CMakeInstance->GetCacheDefinition("CMAKE_MAKE_PROGRAM");
   makeCommand.Add(this->SelectMakeProgram(makeProgram, (std::string)gbuild));
@@ -400,17 +402,23 @@ void cmGlobalGhsMultiGenerator::GenerateBuildCommand(
   }
 
   makeCommand.Add("-top", proj);
-  if (!targetName.empty()) {
-    if (targetName == "clean") {
+  if (!targetNames.empty()) {
+    if (std::find(targetNames.begin(), targetNames.end(), "clean") !=
+        targetNames.end()) {
       makeCommand.Add("-clean");
     } else {
-      if (targetName.compare(targetName.size() - 4, 4, ".gpj") == 0) {
-        makeCommand.Add(targetName);
-      } else {
-        makeCommand.Add(targetName + ".gpj");
+      for (const auto& tname : targetNames) {
+        if (!tname.empty()) {
+          if (tname.compare(tname.size() - 4, 4, ".gpj") == 0) {
+            makeCommand.Add(tname);
+          } else {
+            makeCommand.Add(tname + ".gpj");
+          }
+        }
       }
     }
   }
+  return { makeCommand };
 }
 
 void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout)

+ 6 - 9
Source/cmGlobalGhsMultiGenerator.h

@@ -88,15 +88,12 @@ public:
 
 protected:
   void Generate() override;
-  void GenerateBuildCommand(GeneratedMakeCommand& makeCommand,
-                            const std::string& makeProgram,
-                            const std::string& projectName,
-                            const std::string& projectDir,
-                            const std::string& targetName,
-                            const std::string& config, bool fast, int jobs,
-                            bool verbose,
-                            std::vector<std::string> const& makeOptions =
-                              std::vector<std::string>()) override;
+  std::vector<GeneratedMakeCommand> GenerateBuildCommand(
+    const std::string& makeProgram, const std::string& projectName,
+    const std::string& projectDir, std::vector<std::string> const& targetNames,
+    const std::string& config, bool fast, int jobs, bool verbose,
+    std::vector<std::string> const& makeOptions =
+      std::vector<std::string>()) override;
 
 private:
   void GetToolset(cmMakefile* mf, std::string& tsd, const std::string& ts);

+ 9 - 8
Source/cmGlobalJOMMakefileGenerator.cxx

@@ -54,11 +54,12 @@ void cmGlobalJOMMakefileGenerator::PrintCompilerAdvice(
   this->cmGlobalUnixMakefileGenerator3::PrintCompilerAdvice(os, lang, envVar);
 }
 
-void cmGlobalJOMMakefileGenerator::GenerateBuildCommand(
-  GeneratedMakeCommand& makeCommand, const std::string& makeProgram,
-  const std::string& projectName, const std::string& projectDir,
-  const std::string& targetName, const std::string& config, bool fast,
-  int jobs, bool verbose, std::vector<std::string> const& makeOptions)
+std::vector<cmGlobalGenerator::GeneratedMakeCommand>
+cmGlobalJOMMakefileGenerator::GenerateBuildCommand(
+  const std::string& makeProgram, const std::string& projectName,
+  const std::string& projectDir, std::vector<std::string> const& targetNames,
+  const std::string& config, bool fast, int jobs, bool verbose,
+  std::vector<std::string> const& makeOptions)
 {
   std::vector<std::string> jomMakeOptions;
 
@@ -75,7 +76,7 @@ void cmGlobalJOMMakefileGenerator::GenerateBuildCommand(
     jobs = cmake::NO_BUILD_PARALLEL_LEVEL;
   }
 
-  cmGlobalUnixMakefileGenerator3::GenerateBuildCommand(
-    makeCommand, makeProgram, projectName, projectDir, targetName, config,
-    fast, jobs, verbose, jomMakeOptions);
+  return cmGlobalUnixMakefileGenerator3::GenerateBuildCommand(
+    makeProgram, projectName, projectDir, targetNames, config, fast, jobs,
+    verbose, jomMakeOptions);
 }

+ 6 - 9
Source/cmGlobalJOMMakefileGenerator.h

@@ -40,15 +40,12 @@ public:
                       bool optional) override;
 
 protected:
-  void GenerateBuildCommand(GeneratedMakeCommand& makeCommand,
-                            const std::string& makeProgram,
-                            const std::string& projectName,
-                            const std::string& projectDir,
-                            const std::string& targetName,
-                            const std::string& config, bool fast, int jobs,
-                            bool verbose,
-                            std::vector<std::string> const& makeOptions =
-                              std::vector<std::string>()) override;
+  std::vector<GeneratedMakeCommand> GenerateBuildCommand(
+    const std::string& makeProgram, const std::string& projectName,
+    const std::string& projectDir, std::vector<std::string> const& targetNames,
+    const std::string& config, bool fast, int jobs, bool verbose,
+    std::vector<std::string> const& makeOptions =
+      std::vector<std::string>()) override;
 
 private:
   void PrintCompilerAdvice(std::ostream& os, std::string const& lang,

+ 9 - 8
Source/cmGlobalNMakeMakefileGenerator.cxx

@@ -54,11 +54,12 @@ void cmGlobalNMakeMakefileGenerator::PrintCompilerAdvice(
   this->cmGlobalUnixMakefileGenerator3::PrintCompilerAdvice(os, lang, envVar);
 }
 
-void cmGlobalNMakeMakefileGenerator::GenerateBuildCommand(
-  GeneratedMakeCommand& makeCommand, const std::string& makeProgram,
-  const std::string& projectName, const std::string& projectDir,
-  const std::string& targetName, const std::string& config, bool fast,
-  int /*jobs*/, bool verbose, std::vector<std::string> const& makeOptions)
+std::vector<cmGlobalGenerator::GeneratedMakeCommand>
+cmGlobalNMakeMakefileGenerator::GenerateBuildCommand(
+  const std::string& makeProgram, const std::string& projectName,
+  const std::string& projectDir, std::vector<std::string> const& targetNames,
+  const std::string& config, bool fast, int /*jobs*/, bool verbose,
+  std::vector<std::string> const& makeOptions)
 {
   std::vector<std::string> nmakeMakeOptions;
 
@@ -68,9 +69,9 @@ void cmGlobalNMakeMakefileGenerator::GenerateBuildCommand(
   nmakeMakeOptions.insert(nmakeMakeOptions.end(), makeOptions.begin(),
                           makeOptions.end());
 
-  this->cmGlobalUnixMakefileGenerator3::GenerateBuildCommand(
-    makeCommand, makeProgram, projectName, projectDir, targetName, config,
-    fast, cmake::NO_BUILD_PARALLEL_LEVEL, verbose, nmakeMakeOptions);
+  return this->cmGlobalUnixMakefileGenerator3::GenerateBuildCommand(
+    makeProgram, projectName, projectDir, targetNames, config, fast,
+    cmake::NO_BUILD_PARALLEL_LEVEL, verbose, nmakeMakeOptions);
 }
 
 void cmGlobalNMakeMakefileGenerator::PrintBuildCommandAdvice(std::ostream& os,

+ 6 - 9
Source/cmGlobalNMakeMakefileGenerator.h

@@ -45,15 +45,12 @@ public:
                       bool optional) override;
 
 protected:
-  void GenerateBuildCommand(GeneratedMakeCommand& makeCommand,
-                            const std::string& makeProgram,
-                            const std::string& projectName,
-                            const std::string& projectDir,
-                            const std::string& targetName,
-                            const std::string& config, bool fast, int jobs,
-                            bool verbose,
-                            std::vector<std::string> const& makeOptions =
-                              std::vector<std::string>()) override;
+  std::vector<GeneratedMakeCommand> GenerateBuildCommand(
+    const std::string& makeProgram, const std::string& projectName,
+    const std::string& projectDir, std::vector<std::string> const& targetNames,
+    const std::string& config, bool fast, int jobs, bool verbose,
+    std::vector<std::string> const& makeOptions =
+      std::vector<std::string>()) override;
 
   void PrintBuildCommandAdvice(std::ostream& os, int jobs) const override;
 

+ 17 - 11
Source/cmGlobalNinjaGenerator.cxx

@@ -677,12 +677,15 @@ void cmGlobalNinjaGenerator::EnableLanguage(
 //   cmGlobalXCodeGenerator
 // Called by:
 //   cmGlobalGenerator::Build()
-void cmGlobalNinjaGenerator::GenerateBuildCommand(
-  GeneratedMakeCommand& makeCommand, const std::string& makeProgram,
-  const std::string& /*projectName*/, const std::string& /*projectDir*/,
-  const std::string& targetName, const std::string& /*config*/, bool /*fast*/,
-  int jobs, bool verbose, std::vector<std::string> const& makeOptions)
-{
+std::vector<cmGlobalGenerator::GeneratedMakeCommand>
+cmGlobalNinjaGenerator::GenerateBuildCommand(
+  const std::string& makeProgram, const std::string& /*projectName*/,
+  const std::string& /*projectDir*/,
+  std::vector<std::string> const& targetNames, const std::string& /*config*/,
+  bool /*fast*/, int jobs, bool verbose,
+  std::vector<std::string> const& makeOptions)
+{
+  GeneratedMakeCommand makeCommand;
   makeCommand.Add(this->SelectMakeProgram(makeProgram));
 
   if (verbose) {
@@ -695,13 +698,16 @@ void cmGlobalNinjaGenerator::GenerateBuildCommand(
   }
 
   makeCommand.Add(makeOptions.begin(), makeOptions.end());
-  if (!targetName.empty()) {
-    if (targetName == "clean") {
-      makeCommand.Add("-t", "clean");
-    } else {
-      makeCommand.Add(targetName);
+  for (const auto& tname : targetNames) {
+    if (!tname.empty()) {
+      if (tname == "clean") {
+        makeCommand.Add("-t", "clean");
+      } else {
+        makeCommand.Add(tname);
+      }
     }
   }
+  return { std::move(makeCommand) };
 }
 
 // Non-virtual public methods.

+ 6 - 9
Source/cmGlobalNinjaGenerator.h

@@ -200,15 +200,12 @@ public:
   void EnableLanguage(std::vector<std::string> const& languages,
                       cmMakefile* mf, bool optional) override;
 
-  void GenerateBuildCommand(GeneratedMakeCommand& makeCommand,
-                            const std::string& makeProgram,
-                            const std::string& projectName,
-                            const std::string& projectDir,
-                            const std::string& targetName,
-                            const std::string& config, bool fast, int jobs,
-                            bool verbose,
-                            std::vector<std::string> const& makeOptions =
-                              std::vector<std::string>()) override;
+  std::vector<GeneratedMakeCommand> GenerateBuildCommand(
+    const std::string& makeProgram, const std::string& projectName,
+    const std::string& projectDir, std::vector<std::string> const& targetNames,
+    const std::string& config, bool fast, int jobs, bool verbose,
+    std::vector<std::string> const& makeOptions =
+      std::vector<std::string>()) override;
 
   // Setup target names
   const char* GetAllTargetName() const override { return "all"; }

+ 20 - 14
Source/cmGlobalUnixMakefileGenerator3.cxx

@@ -494,11 +494,13 @@ void cmGlobalUnixMakefileGenerator3::WriteDirectoryRules2(
   this->WriteDirectoryRule2(ruleFileStream, lg, "preinstall", true, true);
 }
 
-void cmGlobalUnixMakefileGenerator3::GenerateBuildCommand(
-  GeneratedMakeCommand& makeCommand, const std::string& makeProgram,
-  const std::string& /*projectName*/, const std::string& /*projectDir*/,
-  const std::string& targetName, const std::string& /*config*/, bool fast,
-  int jobs, bool verbose, std::vector<std::string> const& makeOptions)
+std::vector<cmGlobalGenerator::GeneratedMakeCommand>
+cmGlobalUnixMakefileGenerator3::GenerateBuildCommand(
+  const std::string& makeProgram, const std::string& /*projectName*/,
+  const std::string& /*projectDir*/,
+  std::vector<std::string> const& targetNames, const std::string& /*config*/,
+  bool fast, int jobs, bool verbose,
+  std::vector<std::string> const& makeOptions)
 {
   std::unique_ptr<cmMakefile> mfu;
   cmMakefile* mf;
@@ -515,6 +517,8 @@ void cmGlobalUnixMakefileGenerator3::GenerateBuildCommand(
     mf = mfu.get();
   }
 
+  GeneratedMakeCommand makeCommand;
+
   // Make it possible to set verbosity also from command line
   if (verbose) {
     makeCommand.Add(cmSystemTools::GetCMakeCommand());
@@ -532,17 +536,19 @@ void cmGlobalUnixMakefileGenerator3::GenerateBuildCommand(
   }
 
   makeCommand.Add(makeOptions.begin(), makeOptions.end());
-  if (!targetName.empty()) {
-    std::string tname = targetName;
-    if (fast) {
-      tname += "/fast";
+  for (auto tname : targetNames) {
+    if (!tname.empty()) {
+      if (fast) {
+        tname += "/fast";
+      }
+      tname =
+        mf->GetStateSnapshot().GetDirectory().ConvertToRelPathIfNotContained(
+          mf->GetState()->GetBinaryDirectory(), tname);
+      cmSystemTools::ConvertToOutputSlashes(tname);
+      makeCommand.Add(std::move(tname));
     }
-    tname =
-      mf->GetStateSnapshot().GetDirectory().ConvertToRelPathIfNotContained(
-        mf->GetState()->GetBinaryDirectory(), tname);
-    cmSystemTools::ConvertToOutputSlashes(tname);
-    makeCommand.Add(std::move(tname));
   }
+  return { std::move(makeCommand) };
 }
 
 void cmGlobalUnixMakefileGenerator3::WriteConvenienceRules(

+ 6 - 9
Source/cmGlobalUnixMakefileGenerator3.h

@@ -127,15 +127,12 @@ public:
   std::string GetEmptyRuleHackDepends() { return this->EmptyRuleHackDepends; }
 
   // change the build command for speed
-  void GenerateBuildCommand(GeneratedMakeCommand& makeCommand,
-                            const std::string& makeProgram,
-                            const std::string& projectName,
-                            const std::string& projectDir,
-                            const std::string& targetName,
-                            const std::string& config, bool fast, int jobs,
-                            bool verbose,
-                            std::vector<std::string> const& makeOptions =
-                              std::vector<std::string>()) override;
+  std::vector<GeneratedMakeCommand> GenerateBuildCommand(
+    const std::string& makeProgram, const std::string& projectName,
+    const std::string& projectDir, std::vector<std::string> const& targetNames,
+    const std::string& config, bool fast, int jobs, bool verbose,
+    std::vector<std::string> const& makeOptions =
+      std::vector<std::string>()) override;
 
   /** Record per-target progress information.  */
   void RecordTargetProgress(cmMakefileTargetGenerator* tg);

+ 66 - 55
Source/cmGlobalVisualStudio10Generator.cxx

@@ -878,12 +878,14 @@ bool cmGlobalVisualStudio10Generator::FindVCTargetsPath(cmMakefile* mf)
   return true;
 }
 
-void cmGlobalVisualStudio10Generator::GenerateBuildCommand(
-  GeneratedMakeCommand& makeCommand, const std::string& makeProgram,
-  const std::string& projectName, const std::string& projectDir,
-  const std::string& targetName, const std::string& config, bool fast,
-  int jobs, bool verbose, std::vector<std::string> const& makeOptions)
+std::vector<cmGlobalGenerator::GeneratedMakeCommand>
+cmGlobalVisualStudio10Generator::GenerateBuildCommand(
+  const std::string& makeProgram, const std::string& projectName,
+  const std::string& projectDir, std::vector<std::string> const& targetNames,
+  const std::string& config, bool fast, int jobs, bool verbose,
+  std::vector<std::string> const& makeOptions)
 {
+  std::vector<GeneratedMakeCommand> makeCommands;
   // Select the caller- or user-preferred make program, else MSBuild.
   std::string makeProgramSelected =
     this->SelectMakeProgram(makeProgram, this->GetMSBuildCommand());
@@ -895,7 +897,7 @@ void cmGlobalVisualStudio10Generator::GenerateBuildCommand(
                     makeProgramLower.find("vcexpress") != std::string::npos);
 
   // Workaround to convince VCExpress.exe to produce output.
-  makeCommand.RequiresOutputForward =
+  const bool requiresOutputForward =
     (makeProgramLower.find("vcexpress") != std::string::npos);
 
   // MSBuild is preferred (and required for VS Express), but if the .sln has
@@ -926,62 +928,71 @@ void cmGlobalVisualStudio10Generator::GenerateBuildCommand(
   }
   if (useDevEnv) {
     // Use devenv to build solutions containing Intel Fortran projects.
-    cmGlobalVisualStudio7Generator::GenerateBuildCommand(
-      makeCommand, makeProgram, projectName, projectDir, targetName, config,
-      fast, jobs, verbose, makeOptions);
-    return;
-  }
+    return cmGlobalVisualStudio7Generator::GenerateBuildCommand(
+      makeProgram, projectName, projectDir, targetNames, config, fast, jobs,
+      verbose, makeOptions);
+  }
+
+  std::vector<std::string> realTargetNames = targetNames;
+  if (targetNames.empty() ||
+      ((targetNames.size() == 1) && targetNames.front().empty())) {
+    realTargetNames = { "ALL_BUILD" };
+  }
+  for (const auto& tname : realTargetNames) {
+    // msbuild.exe CxxOnly.sln /t:Build /p:Configuration=Debug
+    // /target:ALL_BUILD
+    //                         /m
+    if (tname.empty()) {
+      continue;
+    }
 
-  makeCommand.Add(makeProgramSelected);
+    GeneratedMakeCommand makeCommand;
+    makeCommand.RequiresOutputForward = requiresOutputForward;
+    makeCommand.Add(makeProgramSelected);
 
-  std::string realTarget = targetName;
-  // msbuild.exe CxxOnly.sln /t:Build /p:Configuration=Debug /target:ALL_BUILD
-  //                         /m
-  if (realTarget.empty()) {
-    realTarget = "ALL_BUILD";
-  }
-  if (realTarget == "clean") {
-    makeCommand.Add(std::string(projectName) + ".sln");
-    makeCommand.Add("/t:Clean");
-  } else {
-    std::string targetProject(realTarget);
-    targetProject += ".vcxproj";
-    if (targetProject.find('/') == std::string::npos) {
-      // it might be in a subdir
-      if (cmSlnProjectEntry const* proj =
-            slnData.GetProjectByName(realTarget)) {
-        targetProject = proj->GetRelativePath();
-        cmSystemTools::ConvertToUnixSlashes(targetProject);
+    if (tname == "clean") {
+      makeCommand.Add(std::string(projectName) + ".sln");
+      makeCommand.Add("/t:Clean");
+    } else {
+      std::string targetProject(tname);
+      targetProject += ".vcxproj";
+      if (targetProject.find('/') == std::string::npos) {
+        // it might be in a subdir
+        if (cmSlnProjectEntry const* proj = slnData.GetProjectByName(tname)) {
+          targetProject = proj->GetRelativePath();
+          cmSystemTools::ConvertToUnixSlashes(targetProject);
+        }
       }
+      makeCommand.Add(std::move(targetProject));
     }
-    makeCommand.Add(std::move(targetProject));
-  }
-  std::string configArg = "/p:Configuration=";
-  if (!config.empty()) {
-    configArg += config;
-  } else {
-    configArg += "Debug";
-  }
-  makeCommand.Add(configArg);
-  makeCommand.Add(std::string("/p:Platform=") + this->GetPlatformName());
-  makeCommand.Add(std::string("/p:VisualStudioVersion=") +
-                  this->GetIDEVersion());
-
-  if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) {
-    if (jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL) {
-      makeCommand.Add("/m");
+    std::string configArg = "/p:Configuration=";
+    if (!config.empty()) {
+      configArg += config;
     } else {
-      makeCommand.Add(std::string("/m:") + std::to_string(jobs));
+      configArg += "Debug";
+    }
+    makeCommand.Add(configArg);
+    makeCommand.Add(std::string("/p:Platform=") + this->GetPlatformName());
+    makeCommand.Add(std::string("/p:VisualStudioVersion=") +
+                    this->GetIDEVersion());
+
+    if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) {
+      if (jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL) {
+        makeCommand.Add("/m");
+      } else {
+        makeCommand.Add(std::string("/m:") + std::to_string(jobs));
+      }
+      // Having msbuild.exe and cl.exe using multiple jobs is discouraged
+      makeCommand.Add("/p:CL_MPCount=1");
     }
-    // Having msbuild.exe and cl.exe using multiple jobs is discouraged
-    makeCommand.Add("/p:CL_MPCount=1");
-  }
-
-  // Respect the verbosity: 'n' normal will show build commands
-  //                        'm' minimal only the build step's title
-  makeCommand.Add(std::string("/v:") + ((verbose) ? "n" : "m"));
 
-  makeCommand.Add(makeOptions.begin(), makeOptions.end());
+    // Respect the verbosity: 'n' normal will show build commands
+    //                        'm' minimal only the build step's title
+    makeCommand.Add(std::string("/v:") + ((verbose) ? "n" : "m"));
+    makeCommand.Add(makeOptions.begin(), makeOptions.end());
+    makeCommands.emplace_back(std::move(makeCommand));
+  }
+  return makeCommands;
 }
 
 bool cmGlobalVisualStudio10Generator::Find64BitTools(cmMakefile* mf)

+ 6 - 9
Source/cmGlobalVisualStudio10Generator.h

@@ -22,15 +22,12 @@ public:
   bool SetGeneratorPlatform(std::string const& p, cmMakefile* mf) override;
   bool SetGeneratorToolset(std::string const& ts, cmMakefile* mf) override;
 
-  void GenerateBuildCommand(GeneratedMakeCommand& makeCommand,
-                            const std::string& makeProgram,
-                            const std::string& projectName,
-                            const std::string& projectDir,
-                            const std::string& targetName,
-                            const std::string& config, bool fast, int jobs,
-                            bool verbose,
-                            std::vector<std::string> const& makeOptions =
-                              std::vector<std::string>()) override;
+  std::vector<GeneratedMakeCommand> GenerateBuildCommand(
+    const std::string& makeProgram, const std::string& projectName,
+    const std::string& projectDir, std::vector<std::string> const& targetNames,
+    const std::string& config, bool fast, int jobs, bool verbose,
+    std::vector<std::string> const& makeOptions =
+      std::vector<std::string>()) override;
 
   ///! create the correct local generator
   cmLocalGenerator* CreateLocalGenerator(cmMakefile* mf) override;

+ 38 - 20
Source/cmGlobalVisualStudio7Generator.cxx

@@ -190,11 +190,14 @@ const char* cmGlobalVisualStudio7Generator::ExternalProjectType(
   }
   return "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942";
 }
-void cmGlobalVisualStudio7Generator::GenerateBuildCommand(
-  GeneratedMakeCommand& makeCommand, const std::string& makeProgram,
-  const std::string& projectName, const std::string& /*projectDir*/,
-  const std::string& targetName, const std::string& config, bool /*fast*/,
-  int /*jobs*/, bool /*verbose*/, std::vector<std::string> const& makeOptions)
+
+std::vector<cmGlobalGenerator::GeneratedMakeCommand>
+cmGlobalVisualStudio7Generator::GenerateBuildCommand(
+  const std::string& makeProgram, const std::string& projectName,
+  const std::string& /*projectDir*/,
+  std::vector<std::string> const& targetNames, const std::string& config,
+  bool /*fast*/, int /*jobs*/, bool /*verbose*/,
+  std::vector<std::string> const& makeOptions)
 {
   // Select the caller- or user-preferred make program, else devenv.
   std::string makeProgramSelected =
@@ -210,24 +213,39 @@ void cmGlobalVisualStudio7Generator::GenerateBuildCommand(
   }
 
   // Workaround to convince VCExpress.exe to produce output.
-  makeCommand.RequiresOutputForward =
+  const bool requiresOutputForward =
     (makeProgramLower.find("vcexpress") != std::string::npos);
+  std::vector<GeneratedMakeCommand> makeCommands;
 
-  makeCommand.Add(makeProgramSelected);
-
-  makeCommand.Add(std::string(projectName) + ".sln");
-  std::string realTarget = targetName;
-  bool clean = false;
-  if (realTarget == "clean") {
-    clean = true;
-    realTarget = "ALL_BUILD";
+  std::vector<std::string> realTargetNames = targetNames;
+  if (targetNames.empty() ||
+      ((targetNames.size() == 1) && targetNames.front().empty())) {
+    realTargetNames = { "ALL_BUILD" };
   }
-
-  makeCommand.Add((clean ? "/clean" : "/build"));
-  makeCommand.Add((config.empty() ? "Debug" : config));
-  makeCommand.Add("/project");
-  makeCommand.Add((realTarget.empty() ? "ALL_BUILD" : realTarget));
-  makeCommand.Add(makeOptions.begin(), makeOptions.end());
+  for (const auto& tname : realTargetNames) {
+    std::string realTarget;
+    if (!tname.empty()) {
+      realTarget = tname;
+    } else {
+      continue;
+    }
+    bool clean = false;
+    if (realTarget == "clean") {
+      clean = true;
+      realTarget = "ALL_BUILD";
+    }
+    GeneratedMakeCommand makeCommand;
+    makeCommand.RequiresOutputForward = requiresOutputForward;
+    makeCommand.Add(makeProgramSelected);
+    makeCommand.Add(std::string(projectName) + ".sln");
+    makeCommand.Add((clean ? "/clean" : "/build"));
+    makeCommand.Add((config.empty() ? "Debug" : config));
+    makeCommand.Add("/project");
+    makeCommand.Add(realTarget);
+    makeCommand.Add(makeOptions.begin(), makeOptions.end());
+    makeCommands.emplace_back(std::move(makeCommand));
+  }
+  return makeCommands;
 }
 
 ///! Create a local generator appropriate to this Global Generator

+ 6 - 9
Source/cmGlobalVisualStudio7Generator.h

@@ -52,15 +52,12 @@ public:
    * Try running cmake and building a file. This is used for dynamically
    * loaded commands, not as part of the usual build process.
    */
-  void GenerateBuildCommand(GeneratedMakeCommand& makeCommand,
-                            const std::string& makeProgram,
-                            const std::string& projectName,
-                            const std::string& projectDir,
-                            const std::string& targetName,
-                            const std::string& config, bool fast, int jobs,
-                            bool verbose,
-                            std::vector<std::string> const& makeOptions =
-                              std::vector<std::string>()) override;
+  std::vector<GeneratedMakeCommand> GenerateBuildCommand(
+    const std::string& makeProgram, const std::string& projectName,
+    const std::string& projectDir, std::vector<std::string> const& targetNames,
+    const std::string& config, bool fast, int jobs, bool verbose,
+    std::vector<std::string> const& makeOptions =
+      std::vector<std::string>()) override;
 
   /**
    * Generate the DSW workspace file.

+ 10 - 8
Source/cmGlobalWatcomWMakeGenerator.cxx

@@ -3,6 +3,7 @@
 #include "cmGlobalWatcomWMakeGenerator.h"
 
 #include "cmDocumentationEntry.h"
+#include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmState.h"
 #include "cmake.h"
@@ -50,15 +51,16 @@ void cmGlobalWatcomWMakeGenerator::GetDocumentation(
   entry.Brief = "Generates Watcom WMake makefiles.";
 }
 
-void cmGlobalWatcomWMakeGenerator::GenerateBuildCommand(
-  GeneratedMakeCommand& makeCommand, const std::string& makeProgram,
-  const std::string& projectName, const std::string& projectDir,
-  const std::string& targetName, const std::string& config, bool fast,
-  int /*jobs*/, bool verbose, std::vector<std::string> const& makeOptions)
+std::vector<cmGlobalGenerator::GeneratedMakeCommand>
+cmGlobalWatcomWMakeGenerator::GenerateBuildCommand(
+  const std::string& makeProgram, const std::string& projectName,
+  const std::string& projectDir, std::vector<std::string> const& targetNames,
+  const std::string& config, bool fast, int /*jobs*/, bool verbose,
+  std::vector<std::string> const& makeOptions)
 {
-  this->cmGlobalUnixMakefileGenerator3::GenerateBuildCommand(
-    makeCommand, makeProgram, projectName, projectDir, targetName, config,
-    fast, cmake::NO_BUILD_PARALLEL_LEVEL, verbose, makeOptions);
+  return this->cmGlobalUnixMakefileGenerator3::GenerateBuildCommand(
+    makeProgram, projectName, projectDir, targetNames, config, fast,
+    cmake::NO_BUILD_PARALLEL_LEVEL, verbose, makeOptions);
 }
 
 void cmGlobalWatcomWMakeGenerator::PrintBuildCommandAdvice(std::ostream& os,

+ 6 - 9
Source/cmGlobalWatcomWMakeGenerator.h

@@ -50,15 +50,12 @@ public:
   bool AllowDeleteOnError() const override { return false; }
 
 protected:
-  void GenerateBuildCommand(GeneratedMakeCommand& makeCommand,
-                            const std::string& makeProgram,
-                            const std::string& projectName,
-                            const std::string& projectDir,
-                            const std::string& targetName,
-                            const std::string& config, bool fast, int jobs,
-                            bool verbose,
-                            std::vector<std::string> const& makeOptions =
-                              std::vector<std::string>()) override;
+  std::vector<GeneratedMakeCommand> GenerateBuildCommand(
+    const std::string& makeProgram, const std::string& projectName,
+    const std::string& projectDir, std::vector<std::string> const& targetNames,
+    const std::string& config, bool fast, int jobs, bool verbose,
+    std::vector<std::string> const& makeOptions =
+      std::vector<std::string>()) override;
 
   void PrintBuildCommandAdvice(std::ostream& os, int jobs) const override;
 };

+ 26 - 14
Source/cmGlobalXCodeGenerator.cxx

@@ -334,12 +334,15 @@ bool cmGlobalXCodeGenerator::Open(const std::string& bindir,
   return ret;
 }
 
-void cmGlobalXCodeGenerator::GenerateBuildCommand(
-  GeneratedMakeCommand& makeCommand, const std::string& makeProgram,
-  const std::string& projectName, const std::string& /*projectDir*/,
-  const std::string& targetName, const std::string& config, bool /*fast*/,
-  int jobs, bool /*verbose*/, std::vector<std::string> const& makeOptions)
-{
+std::vector<cmGlobalGenerator::GeneratedMakeCommand>
+cmGlobalXCodeGenerator::GenerateBuildCommand(
+  const std::string& makeProgram, const std::string& projectName,
+  const std::string& /*projectDir*/,
+  std::vector<std::string> const& targetNames, const std::string& config,
+  bool /*fast*/, int jobs, bool /*verbose*/,
+  std::vector<std::string> const& makeOptions)
+{
+  GeneratedMakeCommand makeCommand;
   // now build the test
   makeCommand.Add(
     this->SelectMakeProgram(makeProgram, this->GetXcodeBuildCommand()));
@@ -351,16 +354,24 @@ void cmGlobalXCodeGenerator::GenerateBuildCommand(
     projectArg += "proj";
     makeCommand.Add(projectArg);
   }
-
-  bool clean = false;
-  std::string realTarget = targetName;
-  if (realTarget == "clean") {
-    clean = true;
-    realTarget = "ALL_BUILD";
+  if (std::find(targetNames.begin(), targetNames.end(), "clean") !=
+      targetNames.end()) {
+    makeCommand.Add("clean");
+    makeCommand.Add("-target", "ALL_BUILD");
+  } else {
+    makeCommand.Add("build");
+    if (targetNames.empty() ||
+        ((targetNames.size() == 1) && targetNames.front().empty())) {
+      makeCommand.Add("-target", "ALL_BUILD");
+    } else {
+      for (const auto& tname : targetNames) {
+        if (!tname.empty()) {
+          makeCommand.Add("-target", tname);
+        }
+      }
+    }
   }
 
-  makeCommand.Add((clean ? "clean" : "build"));
-  makeCommand.Add("-target", (realTarget.empty() ? "ALL_BUILD" : realTarget));
   makeCommand.Add("-configuration", (config.empty() ? "Debug" : config));
 
   if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) {
@@ -374,6 +385,7 @@ void cmGlobalXCodeGenerator::GenerateBuildCommand(
     makeCommand.Add("-hideShellScriptEnvironment");
   }
   makeCommand.Add(makeOptions.begin(), makeOptions.end());
+  return { std::move(makeCommand) };
 }
 
 ///! Create a local generator appropriate to this Global Generator

+ 6 - 9
Source/cmGlobalXCodeGenerator.h

@@ -66,15 +66,12 @@ public:
    * Try running cmake and building a file. This is used for dynalically
    * loaded commands, not as part of the usual build process.
    */
-  void GenerateBuildCommand(GeneratedMakeCommand& makeCommand,
-                            const std::string& makeProgram,
-                            const std::string& projectName,
-                            const std::string& projectDir,
-                            const std::string& targetName,
-                            const std::string& config, bool fast, int jobs,
-                            bool verbose,
-                            std::vector<std::string> const& makeOptions =
-                              std::vector<std::string>()) override;
+  std::vector<GeneratedMakeCommand> GenerateBuildCommand(
+    const std::string& makeProgram, const std::string& projectName,
+    const std::string& projectDir, std::vector<std::string> const& targetNames,
+    const std::string& config, bool fast, int jobs, bool verbose,
+    std::vector<std::string> const& makeOptions =
+      std::vector<std::string>()) override;
 
   /** Append the subdirectory for the given configuration.  */
   void AppendDirectoryForConfig(const std::string& prefix,

+ 4 - 4
Source/cmake.cxx

@@ -2529,7 +2529,8 @@ cmMessenger* cmake::GetMessenger() const
   return this->Messenger;
 }
 
-int cmake::Build(int jobs, const std::string& dir, const std::string& target,
+int cmake::Build(int jobs, const std::string& dir,
+                 const std::vector<std::string>& targets,
                  const std::string& config,
                  const std::vector<std::string>& nativeOptions, bool clean,
                  bool verbose)
@@ -2648,9 +2649,8 @@ int cmake::Build(int jobs, const std::string& dir, const std::string& target,
 #endif
 
   gen->PrintBuildCommandAdvice(std::cerr, jobs);
-
-  return gen->Build(jobs, "", dir, projName, target, output, "", config, clean,
-                    false, verbose, cmDuration::zero(),
+  return gen->Build(jobs, "", dir, projName, targets, output, "", config,
+                    clean, false, verbose, cmDuration::zero(),
                     cmSystemTools::OUTPUT_PASSTHROUGH, nativeOptions);
 }
 

+ 2 - 2
Source/cmake.h

@@ -424,8 +424,8 @@ public:
     cmListFileBacktrace const& backtrace = cmListFileBacktrace()) const;
 
   ///! run the --build option
-  int Build(int jobs, const std::string& dir, const std::string& target,
-            const std::string& config,
+  int Build(int jobs, const std::string& dir,
+            const std::vector<std::string>& targets, const std::string& config,
             const std::vector<std::string>& nativeOptions, bool clean,
             bool verbose);
 

+ 28 - 16
Source/cmakemain.cxx

@@ -63,7 +63,7 @@ static const char* cmDocumentationUsageNote[][2] = {
     "option\n"                                                                \
     "                   is not given.\n"                                      \
     "  --target <tgt> = Build <tgt> instead of default targets.\n"            \
-    "                   May only be specified once.\n"                        \
+    "                   May be specified multiple times.\n"                   \
     "  --config <cfg> = For multi-configuration tools, choose <cfg>.\n"       \
     "  --clean-first  = Build target 'clean' first, then build.\n"            \
     "                   (To clean only, use --target 'clean'.)\n"             \
@@ -394,13 +394,14 @@ static int do_build(int ac, char const* const* av)
   return -1;
 #else
   int jobs = cmake::NO_BUILD_PARALLEL_LEVEL;
-  std::string target;
+  std::vector<std::string> targets;
   std::string config = "Debug";
   std::string dir;
   std::vector<std::string> nativeOptions;
-  bool clean = false;
+  bool cleanFirst = false;
+  bool foundClean = false;
+  bool foundNonClean = false;
   bool verbose = cmSystemTools::HasEnv("VERBOSE");
-  bool hasTarget = false;
 
   enum Doing
   {
@@ -420,25 +421,20 @@ static int do_build(int ac, char const* const* av)
       if (jobs < 0) {
         dir.clear();
       }
+      doing = DoingNone;
     } else if (cmHasLiteralPrefix(av[i], "--parallel")) {
       const char* nextArg = ((i + 1 < ac) ? av[i + 1] : nullptr);
       jobs = extract_job_number(i, av[i], nextArg, sizeof("--parallel") - 1);
       if (jobs < 0) {
         dir.clear();
       }
+      doing = DoingNone;
     } else if (strcmp(av[i], "--target") == 0) {
-      if (!hasTarget) {
-        doing = DoingTarget;
-        hasTarget = true;
-      } else {
-        std::cerr << "'--target' may not be specified more than once.\n\n";
-        dir.clear();
-        break;
-      }
+      doing = DoingTarget;
     } else if (strcmp(av[i], "--config") == 0) {
       doing = DoingConfig;
     } else if (strcmp(av[i], "--clean-first") == 0) {
-      clean = true;
+      cleanFirst = true;
       doing = DoingNone;
     } else if ((strcmp(av[i], "--verbose") == 0) ||
                (strcmp(av[i], "-v") == 0)) {
@@ -455,8 +451,23 @@ static int do_build(int ac, char const* const* av)
           doing = DoingNone;
           break;
         case DoingTarget:
-          target = av[i];
-          doing = DoingNone;
+          if (strlen(av[i]) == 0) {
+            std::cerr << "Warning: Argument number " << i
+                      << " after --target option is empty." << std::endl;
+          } else {
+            targets.emplace_back(av[i]);
+            if (strcmp(av[i], "clean") == 0) {
+              foundClean = true;
+            } else {
+              foundNonClean = true;
+            }
+          }
+          if (foundClean && foundNonClean) {
+            std::cerr << "Error: Building 'clean' and other targets together "
+                         "is not supported."
+                      << std::endl;
+            dir.clear();
+          }
           break;
         case DoingConfig:
           config = av[i];
@@ -507,7 +518,8 @@ static int do_build(int ac, char const* const* av)
   cm.SetProgressCallback([&cm](const std::string& msg, float prog) {
     cmakemainProgressCallback(msg, prog, &cm);
   });
-  return cm.Build(jobs, dir, target, config, nativeOptions, clean, verbose);
+  return cm.Build(jobs, dir, targets, config, nativeOptions, cleanFirst,
+                  verbose);
 #endif
 }
 

+ 1 - 0
Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-jobs-stderr.txt

@@ -0,0 +1 @@
+(^$|^Warning: .* does not support parallel builds\. Ignoring parallel build command line option\.)

+ 0 - 3
Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-stderr.txt

@@ -1,3 +0,0 @@
-^'--target' may not be specified more than once\.
-+
-Usage: cmake --build <dir> \[options\] \[-- \[native-options\]\]

+ 0 - 0
Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-result.txt → Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-with-clean-first-result.txt


+ 2 - 0
Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-with-clean-first-stderr.txt

@@ -0,0 +1,2 @@
+^Error: Building 'clean' and other targets together is not supported\.
+Usage: cmake --build <dir> \[options\] \[-- \[native-options\]\]

+ 1 - 0
Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-with-clean-second-result.txt

@@ -0,0 +1 @@
+1

+ 2 - 0
Tests/RunCMake/CommandLine/BuildDir--build-multiple-targets-with-clean-second-stderr.txt

@@ -0,0 +1,2 @@
+^Error: Building 'clean' and other targets together is not supported\.
+Usage: cmake --build <dir> \[options\] \[-- \[native-options\]\]

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

@@ -107,6 +107,12 @@ function(run_BuildDir)
     ${CMAKE_COMMAND} --build BuildDir-build --target CustomTarget)
   run_cmake_command(BuildDir--build-multiple-targets ${CMAKE_COMMAND} -E chdir ..
     ${CMAKE_COMMAND} --build BuildDir-build --target CustomTarget2 --target CustomTarget3)
+  run_cmake_command(BuildDir--build-multiple-targets-jobs ${CMAKE_COMMAND} -E chdir ..
+    ${CMAKE_COMMAND} --build BuildDir-build --target CustomTarget CustomTarget2 -j2 --target CustomTarget3)
+  run_cmake_command(BuildDir--build-multiple-targets-with-clean-first ${CMAKE_COMMAND} -E chdir ..
+    ${CMAKE_COMMAND} --build BuildDir-build --target clean CustomTarget)
+  run_cmake_command(BuildDir--build-multiple-targets-with-clean-second ${CMAKE_COMMAND} -E chdir ..
+    ${CMAKE_COMMAND} --build BuildDir-build --target CustomTarget clean)
   run_cmake_command(BuildDir--build-jobs-bad-number ${CMAKE_COMMAND} -E chdir ..
     ${CMAKE_COMMAND} --build BuildDir-build -j 12ab)
   run_cmake_command(BuildDir--build-jobs-good-number ${CMAKE_COMMAND} -E chdir ..