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"
8 #include <cmext/string_view>
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"
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()));
56 objectNameLower
+= custom_ext
;
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());
72 objectName
+= custom_ext
;
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
,
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
)) {
101 target
->GetDirectory(config
, cmStateEnums::RuntimeBinaryArtifact
);
103 target
->GetDirectory(config
, cmStateEnums::ImportLibraryArtifact
);
104 if (impDir
== outDir
) {
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);
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.
138 // Line to check for error between commands.
139 std::string check_error
= newline_text
;
141 check_error
+= "if %errorlevel% neq 0 goto :cmEnd";
143 check_error
+= "if errorlevel 1 goto ";
144 check_error
+= this->GetReportErrorLabel();
147 // Store the script in a string.
150 // Open a local context.
153 newline
= newline_text
;
154 script
+= "setlocal";
157 if (!workingDirectory
.empty()) {
158 // Change the working directory.
160 newline
= newline_text
;
162 script
+= this->ConvertToOutputFormat(workingDirectory
, SHELL
);
163 script
+= check_error
;
165 // Change the working drive.
166 if (workingDirectory
.size() > 1 && workingDirectory
[1] == ':') {
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()) {
179 this->Makefile
->GetDefinition("CMAKE_MSVCIDE_RUN_PATH");
182 newline
= newline_text
;
183 script
+= "set PATH=";
184 script
+= *extraPath
;
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
);
200 newline
= newline_text
;
202 // Use "call " before any invocations of .bat or .cmd files
203 // invoked as custom commands.
206 if (cmd
.size() > 4) {
207 suffix
= cmSystemTools::LowerCase(cmd
.substr(cmd
.size() - 4));
208 if (suffix
== ".bat"_s
|| suffix
== ".cmd"_s
) {
213 if (workingDirectory
.empty()) {
214 script
+= this->ConvertToOutputFormat(
215 this->MaybeRelativeToCurBinDir(cmd
), cmOutputConverter::SHELL
);
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
225 script
+= check_error
;
228 // Close the local context.
233 script
+= "endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone";
235 script
+= ":cmErrorLevel";
237 script
+= "exit /b %1";
241 script
+= "if %errorlevel% neq 0 goto ";
242 script
+= this->GetReportErrorLabel();
248 std::string
cmLocalVisualStudioGenerator::FinishConstructScript(
249 VsProjectType projectType
, const std::string
& newline
)
251 bool useLocal
= this->CustomCommandUseLocal();
253 // Store the script in a string.
256 if (useLocal
&& projectType
!= VsProjectType::vcxproj
) {
257 // This label is not provided by MSBuild for C# projects.
259 script
+= this->GetReportErrorLabel();