1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include "cmCTestBuildCommand.h"
4 
5 #include <sstream>
6 
7 #include <cmext/string_view>
8 
9 #include "cmCTest.h"
10 #include "cmCTestBuildHandler.h"
11 #include "cmGlobalGenerator.h"
12 #include "cmMakefile.h"
13 #include "cmMessageType.h"
14 #include "cmStringAlgorithms.h"
15 #include "cmSystemTools.h"
16 #include "cmValue.h"
17 #include "cmake.h"
18 
19 class cmExecutionStatus;
20 
BindArguments()21 void cmCTestBuildCommand::BindArguments()
22 {
23   this->cmCTestHandlerCommand::BindArguments();
24   this->Bind("NUMBER_ERRORS"_s, this->NumberErrors);
25   this->Bind("NUMBER_WARNINGS"_s, this->NumberWarnings);
26   this->Bind("TARGET"_s, this->Target);
27   this->Bind("CONFIGURATION"_s, this->Configuration);
28   this->Bind("FLAGS"_s, this->Flags);
29   this->Bind("PROJECT_NAME"_s, this->ProjectName);
30   this->Bind("PARALLEL_LEVEL"_s, this->ParallelLevel);
31 }
32 
33 cmCTestBuildCommand::~cmCTestBuildCommand() = default;
34 
InitializeHandler()35 cmCTestGenericHandler* cmCTestBuildCommand::InitializeHandler()
36 {
37   cmCTestBuildHandler* handler = this->CTest->GetBuildHandler();
38   handler->Initialize();
39 
40   this->Handler = handler;
41 
42   cmValue ctestBuildCommand =
43     this->Makefile->GetDefinition("CTEST_BUILD_COMMAND");
44   if (cmNonempty(ctestBuildCommand)) {
45     this->CTest->SetCTestConfiguration("MakeCommand", *ctestBuildCommand,
46                                        this->Quiet);
47   } else {
48     cmValue cmakeGeneratorName =
49       this->Makefile->GetDefinition("CTEST_CMAKE_GENERATOR");
50 
51     // Build configuration is determined by: CONFIGURATION argument,
52     // or CTEST_BUILD_CONFIGURATION script variable, or
53     // CTEST_CONFIGURATION_TYPE script variable, or ctest -C command
54     // line argument... in that order.
55     //
56     cmValue ctestBuildConfiguration =
57       this->Makefile->GetDefinition("CTEST_BUILD_CONFIGURATION");
58     std::string cmakeBuildConfiguration = cmNonempty(this->Configuration)
59       ? this->Configuration
60       : cmNonempty(ctestBuildConfiguration) ? *ctestBuildConfiguration
61                                             : this->CTest->GetConfigType();
62 
63     const std::string& cmakeBuildAdditionalFlags = cmNonempty(this->Flags)
64       ? this->Flags
65       : this->Makefile->GetSafeDefinition("CTEST_BUILD_FLAGS");
66     const std::string& cmakeBuildTarget = cmNonempty(this->Target)
67       ? this->Target
68       : this->Makefile->GetSafeDefinition("CTEST_BUILD_TARGET");
69 
70     if (cmNonempty(cmakeGeneratorName)) {
71       if (cmakeBuildConfiguration.empty()) {
72         cmakeBuildConfiguration = "Release";
73       }
74       if (this->GlobalGenerator) {
75         if (this->GlobalGenerator->GetName() != *cmakeGeneratorName) {
76           this->GlobalGenerator.reset();
77         }
78       }
79       if (!this->GlobalGenerator) {
80         this->GlobalGenerator =
81           this->Makefile->GetCMakeInstance()->CreateGlobalGenerator(
82             *cmakeGeneratorName);
83         if (!this->GlobalGenerator) {
84           std::string e = cmStrCat("could not create generator named \"",
85                                    *cmakeGeneratorName, '"');
86           this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
87           cmSystemTools::SetFatalErrorOccured();
88           return nullptr;
89         }
90       }
91       if (cmakeBuildConfiguration.empty()) {
92 #ifdef CMAKE_INTDIR
93         cmakeBuildConfiguration = CMAKE_INTDIR;
94 #else
95         cmakeBuildConfiguration = "Debug";
96 #endif
97       }
98 
99       std::string dir = this->CTest->GetCTestConfiguration("BuildDirectory");
100       std::string buildCommand =
101         this->GlobalGenerator->GenerateCMakeBuildCommand(
102           cmakeBuildTarget, cmakeBuildConfiguration, this->ParallelLevel,
103           cmakeBuildAdditionalFlags, this->Makefile->IgnoreErrorsCMP0061());
104       cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
105                          "SetMakeCommand:" << buildCommand << "\n",
106                          this->Quiet);
107       this->CTest->SetCTestConfiguration("MakeCommand", buildCommand,
108                                          this->Quiet);
109     } else {
110       std::ostringstream ostr;
111       /* clang-format off */
112       ostr << "has no project to build. If this is a "
113         "\"built with CMake\" project, verify that CTEST_CMAKE_GENERATOR "
114         "is set. Otherwise, set CTEST_BUILD_COMMAND to build the project "
115         "with a custom command line.";
116       /* clang-format on */
117       this->SetError(ostr.str());
118       return nullptr;
119     }
120   }
121 
122   if (cmValue useLaunchers =
123         this->Makefile->GetDefinition("CTEST_USE_LAUNCHERS")) {
124     this->CTest->SetCTestConfiguration("UseLaunchers", *useLaunchers,
125                                        this->Quiet);
126   }
127 
128   if (cmValue labelsForSubprojects =
129         this->Makefile->GetDefinition("CTEST_LABELS_FOR_SUBPROJECTS")) {
130     this->CTest->SetCTestConfiguration("LabelsForSubprojects",
131                                        *labelsForSubprojects, this->Quiet);
132   }
133 
134   handler->SetQuiet(this->Quiet);
135   return handler;
136 }
137 
InitialPass(std::vector<std::string> const & args,cmExecutionStatus & status)138 bool cmCTestBuildCommand::InitialPass(std::vector<std::string> const& args,
139                                       cmExecutionStatus& status)
140 {
141   bool ret = this->cmCTestHandlerCommand::InitialPass(args, status);
142   if (!this->NumberErrors.empty()) {
143     this->Makefile->AddDefinition(
144       this->NumberErrors, std::to_string(this->Handler->GetTotalErrors()));
145   }
146   if (!this->NumberWarnings.empty()) {
147     this->Makefile->AddDefinition(
148       this->NumberWarnings, std::to_string(this->Handler->GetTotalWarnings()));
149   }
150   return ret;
151 }
152