CMake Nightly Date Stamp
[kiteware-cmake.git] / Source / cmLocalVisualStudioGenerator.cxx
blob34b8ae342378e5a1865ecd42b041827668804b56
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 "cmLocalVisualStudioGenerator.h"
5 #include <utility>
7 #include <cm/memory>
8 #include <cmext/string_view>
10 #include "windows.h"
12 #include "cmCustomCommand.h"
13 #include "cmCustomCommandGenerator.h"
14 #include "cmCustomCommandLines.h"
15 #include "cmGeneratorTarget.h"
16 #include "cmGlobalGenerator.h"
17 #include "cmMakefile.h"
18 #include "cmOutputConverter.h"
19 #include "cmSourceFile.h"
20 #include "cmStateTypes.h"
21 #include "cmSystemTools.h"
22 #include "cmValue.h"
24 cmLocalVisualStudioGenerator::cmLocalVisualStudioGenerator(
25 cmGlobalGenerator* gg, cmMakefile* mf)
26 : cmLocalGenerator(gg, mf)
30 cmLocalVisualStudioGenerator::~cmLocalVisualStudioGenerator() = default;
32 cmGlobalVisualStudioGenerator::VSVersion
33 cmLocalVisualStudioGenerator::GetVersion() const
35 cmGlobalVisualStudioGenerator* gg =
36 static_cast<cmGlobalVisualStudioGenerator*>(this->GlobalGenerator);
37 return gg->GetVersion();
40 void cmLocalVisualStudioGenerator::ComputeObjectFilenames(
41 std::map<cmSourceFile const*, std::string>& mapping,
42 cmGeneratorTarget const* gt)
44 char const* custom_ext = gt->GetCustomObjectExtension();
45 std::string dir_max = this->ComputeLongestObjectDirectory(gt);
47 // Count the number of object files with each name. Note that
48 // windows file names are not case sensitive.
49 std::map<std::string, int> counts;
51 for (auto const& si : mapping) {
52 cmSourceFile const* sf = si.first;
53 std::string objectNameLower = cmSystemTools::LowerCase(
54 cmSystemTools::GetFilenameWithoutLastExtension(sf->GetFullPath()));
55 if (custom_ext) {
56 objectNameLower += custom_ext;
57 } else {
58 objectNameLower +=
59 this->GlobalGenerator->GetLanguageOutputExtension(*sf);
61 counts[objectNameLower] += 1;
64 // For all source files producing duplicate names we need unique
65 // object name computation.
67 for (auto& si : mapping) {
68 cmSourceFile const* sf = si.first;
69 std::string objectName =
70 cmSystemTools::GetFilenameWithoutLastExtension(sf->GetFullPath());
71 if (custom_ext) {
72 objectName += custom_ext;
73 } else {
74 objectName += this->GlobalGenerator->GetLanguageOutputExtension(*sf);
76 if (counts[cmSystemTools::LowerCase(objectName)] > 1) {
77 const_cast<cmGeneratorTarget*>(gt)->AddExplicitObjectName(sf);
78 bool keptSourceExtension;
79 objectName = this->GetObjectFileNameWithoutTarget(
80 *sf, dir_max, &keptSourceExtension, custom_ext);
82 si.second = objectName;
86 std::unique_ptr<cmCustomCommand>
87 cmLocalVisualStudioGenerator::MaybeCreateImplibDir(cmGeneratorTarget* target,
88 const std::string& config,
89 bool isFortran)
91 std::unique_ptr<cmCustomCommand> pcc;
93 // If an executable exports symbols then VS wants to create an
94 // import library but forgets to create the output directory.
95 // The Intel Fortran plugin always forgets to the directory.
96 if (target->GetType() != cmStateEnums::EXECUTABLE &&
97 !(isFortran && target->GetType() == cmStateEnums::SHARED_LIBRARY)) {
98 return pcc;
100 std::string outDir =
101 target->GetDirectory(config, cmStateEnums::RuntimeBinaryArtifact);
102 std::string impDir =
103 target->GetDirectory(config, cmStateEnums::ImportLibraryArtifact);
104 if (impDir == outDir) {
105 return pcc;
108 // Add a pre-build event to create the directory.
109 cmCustomCommandLines commands = cmMakeSingleCommandLine(
110 { cmSystemTools::GetCMakeCommand(), "-E", "make_directory", impDir });
111 pcc = cm::make_unique<cmCustomCommand>();
112 pcc->SetCommandLines(commands);
113 pcc->SetStdPipesUTF8(true);
114 pcc->SetEscapeOldStyle(false);
115 pcc->SetEscapeAllowMakeVars(true);
116 return pcc;
119 const char* cmLocalVisualStudioGenerator::ReportErrorLabel() const
121 return ":VCReportError";
124 const char* cmLocalVisualStudioGenerator::GetReportErrorLabel() const
126 return this->ReportErrorLabel();
129 std::string cmLocalVisualStudioGenerator::ConstructScript(
130 cmCustomCommandGenerator const& ccg, const std::string& newline_text)
132 bool useLocal = this->CustomCommandUseLocal();
133 std::string workingDirectory = ccg.GetWorkingDirectory();
135 // Avoid leading or trailing newlines.
136 std::string newline;
138 // Line to check for error between commands.
139 std::string check_error = newline_text;
140 if (useLocal) {
141 check_error += "if %errorlevel% neq 0 goto :cmEnd";
142 } else {
143 check_error += "if errorlevel 1 goto ";
144 check_error += this->GetReportErrorLabel();
147 // Store the script in a string.
148 std::string script;
150 // Open a local context.
151 if (useLocal) {
152 script += newline;
153 newline = newline_text;
154 script += "setlocal";
157 if (!workingDirectory.empty()) {
158 // Change the working directory.
159 script += newline;
160 newline = newline_text;
161 script += "cd ";
162 script += this->ConvertToOutputFormat(workingDirectory, SHELL);
163 script += check_error;
165 // Change the working drive.
166 if (workingDirectory.size() > 1 && workingDirectory[1] == ':') {
167 script += newline;
168 newline = newline_text;
169 script += workingDirectory[0];
170 script += workingDirectory[1];
171 script += check_error;
175 // for visual studio IDE add extra stuff to the PATH
176 // if CMAKE_MSVCIDE_RUN_PATH is set.
177 if (this->GetGlobalGenerator()->IsVisualStudio()) {
178 cmValue extraPath =
179 this->Makefile->GetDefinition("CMAKE_MSVCIDE_RUN_PATH");
180 if (extraPath) {
181 script += newline;
182 newline = newline_text;
183 script += "set PATH=";
184 script += *extraPath;
185 script += ";%PATH%";
189 // Write each command on a single line.
190 for (unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) {
191 // Add this command line.
192 std::string cmd = ccg.GetCommand(c);
194 if (cmd.empty()) {
195 continue;
198 // Start a new line.
199 script += newline;
200 newline = newline_text;
202 // Use "call " before any invocations of .bat or .cmd files
203 // invoked as custom commands.
205 std::string suffix;
206 if (cmd.size() > 4) {
207 suffix = cmSystemTools::LowerCase(cmd.substr(cmd.size() - 4));
208 if (suffix == ".bat"_s || suffix == ".cmd"_s) {
209 script += "call ";
213 if (workingDirectory.empty()) {
214 script += this->ConvertToOutputFormat(
215 this->MaybeRelativeToCurBinDir(cmd), cmOutputConverter::SHELL);
216 } else {
217 script += this->ConvertToOutputFormat(cmd.c_str(), SHELL);
219 ccg.AppendArguments(c, script);
221 // After each custom command, check for an error result.
222 // If there was an error, jump to the VCReportError label,
223 // skipping the run of any subsequent commands in this
224 // sequence.
225 script += check_error;
228 // Close the local context.
229 if (useLocal) {
230 script += newline;
231 script += ":cmEnd";
232 script += newline;
233 script += "endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone";
234 script += newline;
235 script += ":cmErrorLevel";
236 script += newline;
237 script += "exit /b %1";
238 script += newline;
239 script += ":cmDone";
240 script += newline;
241 script += "if %errorlevel% neq 0 goto ";
242 script += this->GetReportErrorLabel();
245 return script;
248 std::string cmLocalVisualStudioGenerator::FinishConstructScript(
249 VsProjectType projectType, const std::string& newline)
251 bool useLocal = this->CustomCommandUseLocal();
253 // Store the script in a string.
254 std::string script;
256 if (useLocal && projectType != VsProjectType::vcxproj) {
257 // This label is not provided by MSBuild for C# projects.
258 script += newline;
259 script += this->GetReportErrorLabel();
262 return script;