/src/CMake/Source/cmGhsMultiTargetGenerator.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmGhsMultiTargetGenerator.h" |
4 | | |
5 | | #include <algorithm> |
6 | | #include <memory> |
7 | | #include <ostream> |
8 | | #include <set> |
9 | | #include <utility> |
10 | | #include <vector> |
11 | | |
12 | | #include <cm/optional> |
13 | | #include <cmext/string_view> |
14 | | |
15 | | #include "cmCustomCommand.h" |
16 | | #include "cmCustomCommandGenerator.h" |
17 | | #include "cmGeneratedFileStream.h" |
18 | | #include "cmGeneratorFileSet.h" |
19 | | #include "cmGeneratorFileSets.h" |
20 | | #include "cmGeneratorOptions.h" |
21 | | #include "cmGeneratorTarget.h" |
22 | | #include "cmGlobalGhsMultiGenerator.h" |
23 | | #include "cmLinkLineComputer.h" // IWYU pragma: keep |
24 | | #include "cmList.h" |
25 | | #include "cmListFileCache.h" |
26 | | #include "cmLocalGenerator.h" |
27 | | #include "cmLocalGhsMultiGenerator.h" |
28 | | #include "cmMakefile.h" |
29 | | #include "cmOutputConverter.h" |
30 | | #include "cmSourceFile.h" |
31 | | #include "cmSourceFileLocation.h" |
32 | | #include "cmSourceGroup.h" |
33 | | #include "cmStateDirectory.h" |
34 | | #include "cmStateSnapshot.h" |
35 | | #include "cmStateTypes.h" |
36 | | #include "cmStringAlgorithms.h" |
37 | | #include "cmSystemTools.h" |
38 | | #include "cmTarget.h" |
39 | | #include "cmValue.h" |
40 | | |
41 | | cmGhsMultiTargetGenerator::cmGhsMultiTargetGenerator(cmGeneratorTarget* target) |
42 | 0 | : GeneratorTarget(target) |
43 | | , LocalGenerator( |
44 | 0 | static_cast<cmLocalGhsMultiGenerator*>(target->GetLocalGenerator())) |
45 | 0 | , Makefile(target->Target->GetMakefile()) |
46 | 0 | , Name(target->GetName()) |
47 | 0 | { |
48 | | // Store the configuration name that is being used |
49 | 0 | if (cmValue config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE")) { |
50 | | // Use the build type given by the user. |
51 | 0 | this->ConfigName = *config; |
52 | 0 | } else { |
53 | | // No configuration type given. |
54 | 0 | this->ConfigName.clear(); |
55 | 0 | } |
56 | 0 | } |
57 | | |
58 | 0 | cmGhsMultiTargetGenerator::~cmGhsMultiTargetGenerator() = default; |
59 | | |
60 | | void cmGhsMultiTargetGenerator::Generate() |
61 | 0 | { |
62 | | // Determine type of target for this project |
63 | 0 | switch (this->GeneratorTarget->GetType()) { |
64 | 0 | case cmStateEnums::EXECUTABLE: { |
65 | | // Get the name of the executable to generate. |
66 | 0 | this->TargetNameReal = |
67 | 0 | this->GeneratorTarget->GetExecutableNames(this->ConfigName).Real; |
68 | 0 | if (this->cmGhsMultiTargetGenerator::DetermineIfIntegrityApp()) { |
69 | 0 | this->TagType = GhsMultiGpj::INTEGRITY_APPLICATION; |
70 | 0 | } else { |
71 | 0 | this->TagType = GhsMultiGpj::PROGRAM; |
72 | 0 | } |
73 | 0 | break; |
74 | 0 | } |
75 | 0 | case cmStateEnums::STATIC_LIBRARY: { |
76 | 0 | this->TargetNameReal = |
77 | 0 | this->GeneratorTarget->GetLibraryNames(this->ConfigName).Real; |
78 | 0 | this->TagType = GhsMultiGpj::LIBRARY; |
79 | 0 | break; |
80 | 0 | } |
81 | 0 | case cmStateEnums::SHARED_LIBRARY: { |
82 | 0 | std::string msg = |
83 | 0 | cmStrCat("add_library(<name> SHARED ...) not supported: ", this->Name); |
84 | 0 | cmSystemTools::Message(msg); |
85 | 0 | return; |
86 | 0 | } |
87 | 0 | case cmStateEnums::OBJECT_LIBRARY: { |
88 | 0 | this->TargetNameReal = |
89 | 0 | this->GeneratorTarget->GetLibraryNames(this->ConfigName).Real; |
90 | 0 | this->TagType = GhsMultiGpj::SUBPROJECT; |
91 | 0 | break; |
92 | 0 | } |
93 | 0 | case cmStateEnums::MODULE_LIBRARY: { |
94 | 0 | std::string msg = |
95 | 0 | cmStrCat("add_library(<name> MODULE ...) not supported: ", this->Name); |
96 | 0 | cmSystemTools::Message(msg); |
97 | 0 | return; |
98 | 0 | } |
99 | 0 | case cmStateEnums::UTILITY: { |
100 | 0 | this->TargetNameReal = this->GeneratorTarget->GetName(); |
101 | 0 | this->TagType = GhsMultiGpj::CUSTOM_TARGET; |
102 | 0 | break; |
103 | 0 | } |
104 | 0 | case cmStateEnums::GLOBAL_TARGET: { |
105 | 0 | this->TargetNameReal = this->GeneratorTarget->GetName(); |
106 | 0 | if (this->TargetNameReal == |
107 | 0 | this->GetGlobalGenerator()->GetInstallTargetName()) { |
108 | 0 | this->TagType = GhsMultiGpj::CUSTOM_TARGET; |
109 | 0 | } else { |
110 | 0 | return; |
111 | 0 | } |
112 | 0 | break; |
113 | 0 | } |
114 | 0 | default: |
115 | 0 | return; |
116 | 0 | } |
117 | | |
118 | 0 | this->GenerateTarget(); |
119 | 0 | } |
120 | | |
121 | | void cmGhsMultiTargetGenerator::GenerateTarget() |
122 | 0 | { |
123 | 0 | if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE && |
124 | 0 | !this->GeneratorTarget |
125 | 0 | ->GetLinkerTypeProperty( |
126 | 0 | this->GeneratorTarget->GetLinkerLanguage(this->ConfigName), |
127 | 0 | this->ConfigName) |
128 | 0 | .empty()) { |
129 | | // Green Hill MULTI does not support this feature. |
130 | 0 | cmSystemTools::Message( |
131 | 0 | cmStrCat("'LINKER_TYPE' property, specified on target '", |
132 | 0 | this->GeneratorTarget->GetName(), |
133 | 0 | "', is not supported by this generator.")); |
134 | 0 | } |
135 | | |
136 | | // Open the target file in copy-if-different mode. |
137 | 0 | std::string fproj = |
138 | 0 | cmStrCat(this->GeneratorTarget->GetSupportDirectory(), '/', this->Name, |
139 | 0 | cmGlobalGhsMultiGenerator::FILE_EXTENSION); |
140 | | |
141 | | // Tell the global generator the name of the project file |
142 | 0 | this->GeneratorTarget->Target->SetProperty("GENERATOR_FILE_NAME", fproj); |
143 | 0 | this->GeneratorTarget->Target->SetProperty( |
144 | 0 | "GENERATOR_FILE_NAME_EXT", GhsMultiGpj::GetGpjTag(this->TagType)); |
145 | |
|
146 | 0 | cmGeneratedFileStream fout(fproj); |
147 | 0 | fout.SetCopyIfDifferent(true); |
148 | |
|
149 | 0 | this->GetGlobalGenerator()->WriteFileHeader(fout); |
150 | 0 | GhsMultiGpj::WriteGpjTag(this->TagType, fout); |
151 | |
|
152 | 0 | if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) { |
153 | 0 | std::string const language( |
154 | 0 | this->GeneratorTarget->GetLinkerLanguage(this->ConfigName)); |
155 | 0 | this->WriteTargetSpecifics(fout, this->ConfigName); |
156 | 0 | this->SetCompilerFlags(this->ConfigName, language); |
157 | 0 | this->WriteCompilerFlags(fout, this->ConfigName, language); |
158 | 0 | this->WriteCompilerDefinitions(fout, this->ConfigName, language); |
159 | 0 | this->WriteIncludes(fout, this->ConfigName, language); |
160 | 0 | this->WriteTargetLinkLine(fout, this->ConfigName); |
161 | 0 | this->WriteBuildEvents(fout); |
162 | 0 | } |
163 | 0 | this->WriteSources(fout); |
164 | 0 | fout.Close(); |
165 | 0 | } |
166 | | |
167 | | cmGlobalGhsMultiGenerator* cmGhsMultiTargetGenerator::GetGlobalGenerator() |
168 | | const |
169 | 0 | { |
170 | 0 | return static_cast<cmGlobalGhsMultiGenerator*>( |
171 | 0 | this->LocalGenerator->GetGlobalGenerator()); |
172 | 0 | } |
173 | | |
174 | | void cmGhsMultiTargetGenerator::WriteTargetSpecifics(std::ostream& fout, |
175 | | std::string const& config) |
176 | 0 | { |
177 | 0 | std::string outpath; |
178 | | |
179 | | /* Determine paths from the target project file to where the output artifacts |
180 | | * need to be located. |
181 | | */ |
182 | 0 | if (this->TagType != GhsMultiGpj::SUBPROJECT) { |
183 | | // set target binary file destination |
184 | 0 | std::string binpath = this->GeneratorTarget->GetSupportDirectory(); |
185 | 0 | outpath = cmSystemTools::RelativePath( |
186 | 0 | binpath, this->GeneratorTarget->GetDirectory(config)); |
187 | | /* clang-format off */ |
188 | 0 | fout << " :binDirRelative=\"" << outpath << "\"\n" |
189 | 0 | " -o \"" << this->TargetNameReal << "\"\n"; |
190 | | /* clang-format on */ |
191 | 0 | } |
192 | | |
193 | | // set target object file destination |
194 | 0 | outpath = "."; |
195 | 0 | fout << " :outputDirRelative=\"" << outpath << "\"\n"; |
196 | 0 | } |
197 | | |
198 | | void cmGhsMultiTargetGenerator::SetCompilerFlags(std::string const& config, |
199 | | std::string const& language) |
200 | 0 | { |
201 | 0 | auto i = this->FlagsByLanguage.find(language); |
202 | 0 | if (i == this->FlagsByLanguage.end()) { |
203 | 0 | std::string flags; |
204 | 0 | this->LocalGenerator->AddLanguageFlags( |
205 | 0 | flags, this->GeneratorTarget, cmBuildStep::Compile, language, config); |
206 | 0 | this->LocalGenerator->AddFeatureFlags(flags, this->GeneratorTarget, |
207 | 0 | language, config); |
208 | 0 | this->LocalGenerator->AddVisibilityPresetFlags( |
209 | 0 | flags, this->GeneratorTarget, language); |
210 | 0 | this->LocalGenerator->AddColorDiagnosticsFlags(flags, language); |
211 | | |
212 | | // Append old-style preprocessor definition flags. |
213 | 0 | if (this->Makefile->GetDefineFlags() != " ") { |
214 | 0 | this->LocalGenerator->AppendFlags(flags, |
215 | 0 | this->Makefile->GetDefineFlags()); |
216 | 0 | } |
217 | | |
218 | | // Add target-specific flags. |
219 | 0 | this->LocalGenerator->AddCompileOptions(flags, this->GeneratorTarget, |
220 | 0 | language, config); |
221 | |
|
222 | 0 | std::map<std::string, std::string>::value_type entry(language, flags); |
223 | 0 | i = this->FlagsByLanguage.insert(entry).first; |
224 | 0 | } |
225 | 0 | } |
226 | | |
227 | | std::string cmGhsMultiTargetGenerator::GetDefines(std::string const& language, |
228 | | std::string const& config) |
229 | 0 | { |
230 | 0 | auto i = this->DefinesByLanguage.find(language); |
231 | 0 | if (i == this->DefinesByLanguage.end()) { |
232 | 0 | std::set<std::string> defines; |
233 | | // Add preprocessor definitions for this target and configuration. |
234 | 0 | this->LocalGenerator->GetTargetDefines(this->GeneratorTarget, config, |
235 | 0 | language, defines); |
236 | |
|
237 | 0 | std::string definesString; |
238 | 0 | this->LocalGenerator->JoinDefines(defines, definesString, language); |
239 | |
|
240 | 0 | std::map<std::string, std::string>::value_type entry(language, |
241 | 0 | definesString); |
242 | 0 | i = this->DefinesByLanguage.insert(entry).first; |
243 | 0 | } |
244 | 0 | return i->second; |
245 | 0 | } |
246 | | |
247 | | void cmGhsMultiTargetGenerator::WriteCompilerFlags(std::ostream& fout, |
248 | | std::string const&, |
249 | | std::string const& language) |
250 | 0 | { |
251 | 0 | auto flagsByLangI = this->FlagsByLanguage.find(language); |
252 | 0 | if (flagsByLangI != this->FlagsByLanguage.end()) { |
253 | 0 | if (!flagsByLangI->second.empty()) { |
254 | 0 | std::vector<std::string> ghsCompFlags = |
255 | 0 | cmSystemTools::ParseArguments(flagsByLangI->second); |
256 | 0 | for (std::string const& f : ghsCompFlags) { |
257 | 0 | fout << " " << f << '\n'; |
258 | 0 | } |
259 | 0 | } |
260 | 0 | } |
261 | 0 | } |
262 | | |
263 | | void cmGhsMultiTargetGenerator::WriteCompilerDefinitions( |
264 | | std::ostream& fout, std::string const& config, std::string const& language) |
265 | 0 | { |
266 | 0 | std::vector<std::string> compileDefinitions; |
267 | 0 | this->GeneratorTarget->GetCompileDefinitions(compileDefinitions, config, |
268 | 0 | language); |
269 | 0 | for (std::string const& compileDefinition : compileDefinitions) { |
270 | 0 | fout << " -D" << compileDefinition << '\n'; |
271 | 0 | } |
272 | 0 | } |
273 | | |
274 | | void cmGhsMultiTargetGenerator::WriteIncludes(std::ostream& fout, |
275 | | std::string const& config, |
276 | | std::string const& language) |
277 | 0 | { |
278 | 0 | std::vector<std::string> includes; |
279 | 0 | this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget, |
280 | 0 | language, config); |
281 | |
|
282 | 0 | for (std::string const& include : includes) { |
283 | 0 | fout << " -I\"" << include << "\"\n"; |
284 | 0 | } |
285 | 0 | } |
286 | | |
287 | | void cmGhsMultiTargetGenerator::WriteTargetLinkLine(std::ostream& fout, |
288 | | std::string const& config) |
289 | 0 | { |
290 | 0 | if (this->TagType == GhsMultiGpj::INTEGRITY_APPLICATION) { |
291 | 0 | return; |
292 | 0 | } |
293 | | |
294 | 0 | std::string linkLibraries; |
295 | 0 | std::string flags; |
296 | 0 | std::string linkFlags; |
297 | 0 | std::string frameworkPath; |
298 | 0 | std::string linkPath; |
299 | |
|
300 | 0 | std::unique_ptr<cmLinkLineComputer> linkLineComputer = |
301 | 0 | this->GetGlobalGenerator()->CreateLinkLineComputer( |
302 | 0 | this->LocalGenerator, |
303 | 0 | this->LocalGenerator->GetStateSnapshot().GetDirectory()); |
304 | |
|
305 | 0 | this->LocalGenerator->GetTargetFlags( |
306 | 0 | linkLineComputer.get(), config, linkLibraries, flags, linkFlags, |
307 | 0 | frameworkPath, linkPath, this->GeneratorTarget); |
308 | | |
309 | | // write out link options |
310 | 0 | std::vector<std::string> lopts = cmSystemTools::ParseArguments(linkFlags); |
311 | 0 | for (std::string const& l : lopts) { |
312 | 0 | fout << " " << l << '\n'; |
313 | 0 | } |
314 | | |
315 | | // write out link search paths |
316 | | // must be quoted for paths that contain spaces |
317 | 0 | std::vector<std::string> lpath = cmSystemTools::ParseArguments(linkPath); |
318 | 0 | for (std::string const& l : lpath) { |
319 | 0 | fout << " -L\"" << l << "\"\n"; |
320 | 0 | } |
321 | | |
322 | | // write out link libs |
323 | | // must be quoted for filepaths that contains spaces |
324 | 0 | std::string cbd = this->LocalGenerator->GetCurrentBinaryDirectory(); |
325 | |
|
326 | 0 | std::vector<std::string> llibs = |
327 | 0 | cmSystemTools::ParseArguments(linkLibraries); |
328 | 0 | for (std::string const& l : llibs) { |
329 | 0 | if (l.compare(0, 2, "-l") == 0) { |
330 | 0 | fout << " \"" << l << "\"\n"; |
331 | 0 | } else { |
332 | 0 | std::string rl = cmSystemTools::CollapseFullPath(l, cbd); |
333 | 0 | fout << " -l\"" << rl << "\"\n"; |
334 | 0 | } |
335 | 0 | } |
336 | 0 | } |
337 | | |
338 | | void cmGhsMultiTargetGenerator::WriteBuildEvents(std::ostream& fout) |
339 | 0 | { |
340 | 0 | this->WriteBuildEventsHelper(fout, |
341 | 0 | this->GeneratorTarget->GetPreBuildCommands(), |
342 | 0 | std::string("prebuild"), |
343 | | #ifdef _WIN32 |
344 | | std::string("preexecShell") |
345 | | #else |
346 | 0 | std::string("preexec") |
347 | 0 | #endif |
348 | 0 | ); |
349 | |
|
350 | 0 | if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) { |
351 | 0 | this->WriteBuildEventsHelper(fout, |
352 | 0 | this->GeneratorTarget->GetPreLinkCommands(), |
353 | 0 | std::string("prelink"), |
354 | | #ifdef _WIN32 |
355 | | std::string("preexecShell") |
356 | | #else |
357 | 0 | std::string("preexec") |
358 | 0 | #endif |
359 | 0 | ); |
360 | 0 | } |
361 | |
|
362 | 0 | this->WriteBuildEventsHelper(fout, |
363 | 0 | this->GeneratorTarget->GetPostBuildCommands(), |
364 | 0 | std::string("postbuild"), |
365 | | #ifdef _WIN32 |
366 | | std::string("postexecShell") |
367 | | #else |
368 | 0 | std::string("postexec") |
369 | 0 | #endif |
370 | 0 | ); |
371 | 0 | } |
372 | | |
373 | | void cmGhsMultiTargetGenerator::WriteBuildEventsHelper( |
374 | | std::ostream& fout, std::vector<cmCustomCommand> const& ccv, |
375 | | std::string const& name, std::string const& cmd) |
376 | 0 | { |
377 | 0 | int cmdcount = 0; |
378 | | #ifdef _WIN32 |
379 | | std::string fext = ".bat"; |
380 | | std::string shell; |
381 | | #else |
382 | 0 | std::string fext = ".sh"; |
383 | 0 | std::string shell = "/bin/sh "; |
384 | 0 | #endif |
385 | |
|
386 | 0 | for (cmCustomCommand const& cc : ccv) { |
387 | 0 | cmCustomCommandGenerator ccg(cc, this->ConfigName, this->LocalGenerator); |
388 | | // Open the filestream for this custom command |
389 | 0 | std::string fname = cmStrCat(this->GeneratorTarget->GetSupportDirectory(), |
390 | 0 | '/', this->Name, '_', name, cmdcount++, fext); |
391 | |
|
392 | 0 | cmGeneratedFileStream f(fname); |
393 | 0 | f.SetCopyIfDifferent(true); |
394 | 0 | this->WriteCustomCommandsHelper(f, ccg); |
395 | 0 | f.Close(); |
396 | 0 | if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) { |
397 | 0 | fout << " :" << cmd << "=\"" << shell << fname << "\"\n"; |
398 | 0 | } else { |
399 | 0 | fout << fname << "\n :outputName=\"" << fname << ".rule\"\n"; |
400 | 0 | } |
401 | 0 | for (auto const& byp : ccg.GetByproducts()) { |
402 | 0 | fout << " :extraOutputFile=\"" << byp << "\"\n"; |
403 | 0 | } |
404 | 0 | } |
405 | 0 | } |
406 | | |
407 | | void cmGhsMultiTargetGenerator::WriteCustomCommandsHelper( |
408 | | std::ostream& fout, cmCustomCommandGenerator const& ccg) |
409 | 0 | { |
410 | 0 | std::vector<std::string> cmdLines; |
411 | | |
412 | | // if the command specified a working directory use it. |
413 | 0 | std::string dir = this->LocalGenerator->GetCurrentBinaryDirectory(); |
414 | 0 | std::string workingDir = ccg.GetWorkingDirectory(); |
415 | 0 | if (!workingDir.empty()) { |
416 | 0 | dir = workingDir; |
417 | 0 | } |
418 | | |
419 | | // Line to check for error between commands. |
420 | | #ifdef _WIN32 |
421 | | std::string check_error = "if %errorlevel% neq 0 exit /b %errorlevel%"; |
422 | | #else |
423 | 0 | std::string check_error = "if [ $? -ne 0 ]; then exit 1; fi"; |
424 | 0 | #endif |
425 | |
|
426 | | #ifdef _WIN32 |
427 | | cmdLines.push_back("@echo off"); |
428 | | #endif |
429 | | // Echo the custom command's comment text. |
430 | 0 | if (cm::optional<std::string> comment = ccg.GetComment()) { |
431 | 0 | std::string escapedComment = this->LocalGenerator->EscapeForShell( |
432 | 0 | *comment, ccg.GetCC().GetEscapeAllowMakeVars()); |
433 | 0 | std::string echocmd = cmStrCat("echo ", escapedComment); |
434 | 0 | cmdLines.push_back(std::move(echocmd)); |
435 | 0 | } |
436 | | |
437 | | // Switch to working directory |
438 | 0 | std::string cdCmd; |
439 | | #ifdef _WIN32 |
440 | | std::string cdStr = "cd /D "; |
441 | | #else |
442 | 0 | std::string cdStr = "cd "; |
443 | 0 | #endif |
444 | 0 | cdCmd = cdStr + |
445 | 0 | this->LocalGenerator->ConvertToOutputFormat(dir, cmOutputConverter::SHELL); |
446 | 0 | cmdLines.push_back(std::move(cdCmd)); |
447 | |
|
448 | 0 | for (unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) { |
449 | | // Build the command line in a single string. |
450 | 0 | std::string cmd = ccg.GetCommand(c); |
451 | 0 | if (!cmd.empty()) { |
452 | | // Use "call " before any invocations of .bat or .cmd files |
453 | | // invoked as custom commands in the WindowsShell. |
454 | | // |
455 | 0 | bool useCall = false; |
456 | |
|
457 | | #ifdef _WIN32 |
458 | | std::string suffix; |
459 | | if (cmd.size() > 4) { |
460 | | suffix = cmSystemTools::LowerCase(cmd.substr(cmd.size() - 4)); |
461 | | if (suffix == ".bat" || suffix == ".cmd") { |
462 | | useCall = true; |
463 | | } |
464 | | } |
465 | | #endif |
466 | |
|
467 | 0 | cmSystemTools::ReplaceString(cmd, "/./", "/"); |
468 | | // Convert the command to a relative path only if the current |
469 | | // working directory will be the start-output directory. |
470 | 0 | bool had_slash = cmd.find('/') != std::string::npos; |
471 | 0 | if (workingDir.empty()) { |
472 | 0 | cmd = this->LocalGenerator->MaybeRelativeToCurBinDir(cmd); |
473 | 0 | } |
474 | 0 | bool has_slash = cmd.find('/') != std::string::npos; |
475 | 0 | if (had_slash && !has_slash) { |
476 | | // This command was specified as a path to a file in the |
477 | | // current directory. Add a leading "./" so it can run |
478 | | // without the current directory being in the search path. |
479 | 0 | cmd = cmStrCat("./", cmd); |
480 | 0 | } |
481 | 0 | cmd = this->LocalGenerator->ConvertToOutputFormat( |
482 | 0 | cmd, cmOutputConverter::SHELL); |
483 | 0 | if (useCall) { |
484 | 0 | cmd = cmStrCat("call ", cmd); |
485 | 0 | } |
486 | 0 | ccg.AppendArguments(c, cmd); |
487 | 0 | cmdLines.push_back(std::move(cmd)); |
488 | 0 | } |
489 | 0 | } |
490 | | |
491 | | // push back the custom commands |
492 | 0 | for (auto const& c : cmdLines) { |
493 | 0 | fout << c << '\n' << check_error << '\n'; |
494 | 0 | } |
495 | 0 | } |
496 | | |
497 | | void cmGhsMultiTargetGenerator::WriteSourceProperty( |
498 | | std::ostream& fout, cmSourceFile const* sf, std::string const& propName, |
499 | | std::string const& propFlag) |
500 | 0 | { |
501 | 0 | cmValue prop = sf->GetProperty(propName); |
502 | 0 | if (prop) { |
503 | 0 | cmList list{ *prop }; |
504 | 0 | for (std::string const& p : list) { |
505 | 0 | fout << " " << propFlag << p << '\n'; |
506 | 0 | } |
507 | 0 | } |
508 | 0 | } |
509 | | |
510 | | void cmGhsMultiTargetGenerator::WriteFileSetProperty( |
511 | | std::ostream& fout, cmGeneratorFileSet const* fileSet, |
512 | | std::string const& lang, cm::string_view propName, cm::string_view propFlag) |
513 | 0 | { |
514 | 0 | if (!fileSet) { |
515 | 0 | return; |
516 | 0 | } |
517 | | |
518 | 0 | std::vector<BT<std::string>> entries; |
519 | 0 | if (propName == "COMPILE_OPTIONS"_s) { |
520 | 0 | entries = fileSet->BelongsTo(this->GeneratorTarget) |
521 | 0 | ? fileSet->GetCompileOptions(this->ConfigName, lang) |
522 | 0 | : fileSet->GetInterfaceCompileOptions(this->ConfigName, lang); |
523 | 0 | } else if (propName == "COMPILE_DEFINITIONS"_s) { |
524 | 0 | entries = fileSet->BelongsTo(this->GeneratorTarget) |
525 | 0 | ? fileSet->GetCompileDefinitions(this->ConfigName, lang) |
526 | 0 | : fileSet->GetInterfaceCompileDefinitions(this->ConfigName, lang); |
527 | 0 | } else if (propName == "INCLUDE_DIRECTORIES"_s) { |
528 | 0 | entries = fileSet->BelongsTo(this->GeneratorTarget) |
529 | 0 | ? fileSet->GetIncludeDirectories(this->ConfigName, lang) |
530 | 0 | : fileSet->GetInterfaceIncludeDirectories(this->ConfigName, lang); |
531 | 0 | } |
532 | |
|
533 | 0 | for (auto const& entry : entries) { |
534 | 0 | fout << " " << propFlag << entry.Value << '\n'; |
535 | 0 | } |
536 | 0 | } |
537 | | |
538 | | void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj) |
539 | 0 | { |
540 | | /* vector of all sources for this target */ |
541 | 0 | std::vector<cmSourceFile*> sources; |
542 | 0 | this->GeneratorTarget->GetSourceFiles(sources, this->ConfigName); |
543 | | |
544 | | /* for each source file assign it to its group */ |
545 | 0 | std::map<std::string, std::vector<cmSourceFile*>> groupFiles; |
546 | 0 | std::set<std::string> groupNames; |
547 | 0 | for (cmSourceFile* sf : sources) { |
548 | 0 | cmSourceGroup* sourceGroup = |
549 | 0 | this->LocalGenerator->FindSourceGroup(sf->ResolveFullPath()); |
550 | 0 | std::string gn = sourceGroup->GetFullName(); |
551 | 0 | groupFiles[gn].push_back(sf); |
552 | 0 | groupNames.insert(std::move(gn)); |
553 | 0 | } |
554 | | |
555 | | /* list of known groups and the order they are displayed in a project file */ |
556 | 0 | std::vector<std::string> const standardGroups = { |
557 | 0 | "CMake Rules", "Header Files", "Source Files", |
558 | 0 | "Object Files", "Object Libraries", "Resources" |
559 | 0 | }; |
560 | | |
561 | | /* list of groups in the order they are displayed in a project file*/ |
562 | 0 | std::vector<std::string> groupFilesList(groupFiles.size()); |
563 | | |
564 | | /* put the groups in the order they should be listed |
565 | | * - standard groups first, and then everything else |
566 | | * in the order used by std::map. |
567 | | */ |
568 | 0 | int i = 0; |
569 | 0 | for (std::string const& gn : standardGroups) { |
570 | 0 | auto n = groupNames.find(gn); |
571 | 0 | if (n != groupNames.end()) { |
572 | 0 | groupFilesList[i] = *n; |
573 | 0 | i += 1; |
574 | 0 | groupNames.erase(gn); |
575 | 0 | } else if (this->TagType == GhsMultiGpj::CUSTOM_TARGET && |
576 | 0 | gn == "CMake Rules") { |
577 | | /* make sure that rules folder always exists in case of custom targets |
578 | | * that have no custom commands except for pre or post build events. |
579 | | */ |
580 | 0 | groupFilesList.resize(groupFilesList.size() + 1); |
581 | 0 | groupFilesList[i] = gn; |
582 | 0 | i += 1; |
583 | 0 | } |
584 | 0 | } |
585 | |
|
586 | 0 | { /* catch-all group - is last item */ |
587 | 0 | std::string gn; |
588 | 0 | auto n = groupNames.find(gn); |
589 | 0 | if (n != groupNames.end()) { |
590 | 0 | groupFilesList.back() = *n; |
591 | 0 | groupNames.erase(gn); |
592 | 0 | } |
593 | 0 | } |
594 | |
|
595 | 0 | for (auto const& n : groupNames) { |
596 | 0 | groupFilesList[i] = n; |
597 | 0 | i += 1; |
598 | 0 | } |
599 | | |
600 | | /* sort the files within each group */ |
601 | 0 | for (auto& n : groupFilesList) { |
602 | 0 | std::sort(groupFiles[n].begin(), groupFiles[n].end(), |
603 | 0 | [](cmSourceFile* l, cmSourceFile* r) { |
604 | 0 | return l->ResolveFullPath() < r->ResolveFullPath(); |
605 | 0 | }); |
606 | 0 | } |
607 | | |
608 | | /* list of open project files */ |
609 | 0 | std::vector<cmGeneratedFileStream*> gfiles; |
610 | | |
611 | | /* write files into the proper project file |
612 | | * -- groups go into main project file |
613 | | * unless NO_SOURCE_GROUP_FILE property or variable is set. |
614 | | */ |
615 | 0 | for (auto& sg : groupFilesList) { |
616 | 0 | std::ostream* fout; |
617 | 0 | bool useProjectFile = |
618 | 0 | this->GeneratorTarget->GetProperty("GHS_NO_SOURCE_GROUP_FILE").IsOn() || |
619 | 0 | this->Makefile->IsOn("CMAKE_GHS_NO_SOURCE_GROUP_FILE"); |
620 | 0 | if (useProjectFile || sg.empty()) { |
621 | 0 | fout = &fout_proj; |
622 | 0 | } else { |
623 | | // Open the filestream in copy-if-different mode. |
624 | 0 | std::string gname = sg; |
625 | 0 | cmsys::SystemTools::ReplaceString(gname, "\\", "_"); |
626 | 0 | std::string lpath = |
627 | 0 | cmStrCat(gname, cmGlobalGhsMultiGenerator::FILE_EXTENSION); |
628 | 0 | std::string fpath = |
629 | 0 | cmStrCat(this->GeneratorTarget->GetSupportDirectory(), '/', lpath); |
630 | 0 | cmGeneratedFileStream* f = new cmGeneratedFileStream(fpath); |
631 | 0 | f->SetCopyIfDifferent(true); |
632 | 0 | gfiles.push_back(f); |
633 | 0 | fout = f; |
634 | 0 | this->GetGlobalGenerator()->WriteFileHeader(*f); |
635 | 0 | GhsMultiGpj::WriteGpjTag(GhsMultiGpj::SUBPROJECT, *f); |
636 | 0 | fout_proj << lpath << " "; |
637 | 0 | GhsMultiGpj::WriteGpjTag(GhsMultiGpj::SUBPROJECT, fout_proj); |
638 | 0 | } |
639 | |
|
640 | 0 | if (useProjectFile) { |
641 | 0 | if (sg.empty()) { |
642 | 0 | *fout << "{comment} Others" << '\n'; |
643 | 0 | } else { |
644 | 0 | *fout << "{comment} " << sg << '\n'; |
645 | 0 | } |
646 | 0 | } else if (sg.empty()) { |
647 | 0 | *fout << "{comment} Others\n"; |
648 | 0 | } |
649 | |
|
650 | 0 | if (sg != "CMake Rules") { |
651 | | /* output rule for each source file */ |
652 | 0 | for (cmSourceFile const* si : groupFiles[sg]) { |
653 | 0 | bool compile = true; |
654 | | // Convert filename to native system |
655 | | // WORKAROUND: GHS MULTI 6.1.4 and 6.1.6 are known to need backslash on |
656 | | // windows when opening some files from the search window. |
657 | 0 | std::string fname(si->GetFullPath()); |
658 | 0 | cmSystemTools::ConvertToOutputSlashes(fname); |
659 | | |
660 | | /* For custom targets list any associated sources, |
661 | | * comment out source code to prevent it from being |
662 | | * compiled when processing this target. |
663 | | * Otherwise, comment out any custom command (main) dependencies that |
664 | | * are listed as source files to prevent them from being considered |
665 | | * part of the build. |
666 | | */ |
667 | 0 | std::string comment; |
668 | 0 | if ((this->TagType == GhsMultiGpj::CUSTOM_TARGET && |
669 | 0 | !si->GetLanguage().empty()) || |
670 | 0 | si->GetCustomCommand()) { |
671 | 0 | comment = "{comment} "; |
672 | 0 | compile = false; |
673 | 0 | } |
674 | |
|
675 | 0 | *fout << comment << fname << WriteObjectLangOverride(si) << '\n'; |
676 | 0 | if (compile) { |
677 | | // lookup for the associated file set, if any. |
678 | 0 | auto const* fileSet = |
679 | 0 | this->GeneratorTarget->GetGeneratorFileSets()->GetFileSetForSource( |
680 | 0 | this->ConfigName, si); |
681 | |
|
682 | 0 | this->WriteFileSetProperty(*fout, fileSet, si->GetLanguage(), |
683 | 0 | "INCLUDE_DIRECTORIES"_s, ""); |
684 | 0 | this->WriteSourceProperty(*fout, si, "INCLUDE_DIRECTORIES", "-I"); |
685 | 0 | this->WriteSourceProperty(*fout, si, "COMPILE_DEFINITIONS", "-D"); |
686 | 0 | this->WriteFileSetProperty(*fout, fileSet, si->GetLanguage(), |
687 | 0 | "COMPILE_DEFINITIONS"_s, ""); |
688 | 0 | this->WriteSourceProperty(*fout, si, "COMPILE_OPTIONS", ""); |
689 | 0 | this->WriteFileSetProperty(*fout, fileSet, si->GetLanguage(), |
690 | 0 | "COMPILE_OPTIONS"_s, ""); |
691 | | |
692 | | /* to avoid clutter in the GUI only print out the objectName if it |
693 | | * has been renamed */ |
694 | 0 | std::string objectName = this->GeneratorTarget->GetObjectName(si); |
695 | 0 | if (!objectName.empty() && |
696 | 0 | this->GeneratorTarget->HasExplicitObjectName(si)) { |
697 | 0 | *fout << " -o " << objectName << '\n'; |
698 | 0 | } |
699 | 0 | } |
700 | 0 | } |
701 | 0 | } else { |
702 | 0 | std::vector<cmSourceFile const*> customCommands; |
703 | 0 | if (this->ComputeCustomCommandOrder(customCommands)) { |
704 | 0 | std::string message = |
705 | 0 | cmStrCat("The custom commands for target [", |
706 | 0 | this->GeneratorTarget->GetName(), "] had a cycle.\n"); |
707 | 0 | cmSystemTools::Error(message); |
708 | 0 | } else { |
709 | | /* Custom targets do not have a dependency on SOURCES files. |
710 | | * Therefore the dependency list may include SOURCES files after the |
711 | | * custom target. Because nothing can depend on the custom target just |
712 | | * move it to the last item. |
713 | | */ |
714 | 0 | for (auto sf = customCommands.begin(); sf != customCommands.end(); |
715 | 0 | ++sf) { |
716 | 0 | if (((*sf)->GetLocation()).GetName() == this->Name + ".rule") { |
717 | 0 | std::rotate(sf, sf + 1, customCommands.end()); |
718 | 0 | break; |
719 | 0 | } |
720 | 0 | } |
721 | 0 | int cmdcount = 0; |
722 | | #ifdef _WIN32 |
723 | | std::string fext = ".bat"; |
724 | | #else |
725 | 0 | std::string fext = ".sh"; |
726 | 0 | #endif |
727 | 0 | for (auto& sf : customCommands) { |
728 | 0 | cmCustomCommand const* cc = sf->GetCustomCommand(); |
729 | 0 | cmCustomCommandGenerator ccg(*cc, this->ConfigName, |
730 | 0 | this->LocalGenerator); |
731 | | |
732 | | // Open the filestream for this custom command |
733 | 0 | std::string fname = cmStrCat( |
734 | 0 | this->GeneratorTarget->GetSupportDirectory(), '/', this->Name, |
735 | 0 | "_cc", cmdcount++, '_', (sf->GetLocation()).GetName(), fext); |
736 | |
|
737 | 0 | cmGeneratedFileStream f(fname); |
738 | 0 | f.SetCopyIfDifferent(true); |
739 | 0 | this->WriteCustomCommandsHelper(f, ccg); |
740 | 0 | f.Close(); |
741 | 0 | this->WriteCustomCommandLine(*fout, fname, ccg); |
742 | 0 | } |
743 | 0 | } |
744 | 0 | if (this->TagType == GhsMultiGpj::CUSTOM_TARGET) { |
745 | 0 | this->WriteBuildEvents(*fout); |
746 | 0 | } |
747 | 0 | } |
748 | 0 | } |
749 | |
|
750 | 0 | for (cmGeneratedFileStream* f : gfiles) { |
751 | 0 | f->Close(); |
752 | 0 | } |
753 | 0 | } |
754 | | |
755 | | void cmGhsMultiTargetGenerator::WriteCustomCommandLine( |
756 | | std::ostream& fout, std::string& fname, cmCustomCommandGenerator const& ccg) |
757 | 0 | { |
758 | | /* NOTE: Customization Files are not well documented. Testing showed |
759 | | * that ":outputName=file" can only be used once per script. The |
760 | | * script will only run if ":outputName=file" is missing or just run |
761 | | * once if ":outputName=file" is not specified. If there are |
762 | | * multiple outputs then the script needs to be listed multiple times |
763 | | * for each output. Otherwise it won't rerun the script if one of |
764 | | * the outputs is manually deleted. |
765 | | */ |
766 | 0 | bool specifyExtra = true; |
767 | 0 | for (auto const& out : ccg.GetOutputs()) { |
768 | 0 | fout << fname << '\n'; |
769 | 0 | fout << " :outputName=\"" << out << "\"\n"; |
770 | 0 | if (specifyExtra) { |
771 | 0 | for (auto const& byp : ccg.GetByproducts()) { |
772 | 0 | fout << " :extraOutputFile=\"" << byp << "\"\n"; |
773 | 0 | } |
774 | 0 | for (auto const& dep : ccg.GetDepends()) { |
775 | 0 | fout << " :depends=\"" << dep << "\"\n"; |
776 | 0 | } |
777 | 0 | specifyExtra = false; |
778 | 0 | } |
779 | 0 | } |
780 | 0 | } |
781 | | |
782 | | std::string cmGhsMultiTargetGenerator::WriteObjectLangOverride( |
783 | | cmSourceFile const* sourceFile) |
784 | 0 | { |
785 | 0 | std::string ret; |
786 | 0 | cmValue rawLangProp = sourceFile->GetProperty("LANGUAGE"); |
787 | 0 | if (rawLangProp) { |
788 | 0 | ret = cmStrCat(" [", *rawLangProp, ']'); |
789 | 0 | } |
790 | |
|
791 | 0 | return ret; |
792 | 0 | } |
793 | | |
794 | | bool cmGhsMultiTargetGenerator::DetermineIfIntegrityApp() |
795 | 0 | { |
796 | 0 | if (cmValue p = this->GeneratorTarget->GetProperty("ghs_integrity_app")) { |
797 | 0 | return p.IsOn(); |
798 | 0 | } |
799 | 0 | std::vector<cmSourceFile*> sources; |
800 | 0 | this->GeneratorTarget->GetSourceFiles(sources, this->ConfigName); |
801 | 0 | return std::any_of(sources.begin(), sources.end(), |
802 | 0 | [](cmSourceFile const* sf) -> bool { |
803 | 0 | return "int" == sf->GetExtension(); |
804 | 0 | }); |
805 | 0 | } |
806 | | |
807 | | bool cmGhsMultiTargetGenerator::ComputeCustomCommandOrder( |
808 | | std::vector<cmSourceFile const*>& order) |
809 | 0 | { |
810 | 0 | std::set<cmSourceFile const*> temp; |
811 | 0 | std::set<cmSourceFile const*> perm; |
812 | | |
813 | | // Collect all custom commands for this target |
814 | 0 | std::vector<cmSourceFile const*> customCommands; |
815 | 0 | this->GeneratorTarget->GetCustomCommands(customCommands, this->ConfigName); |
816 | |
|
817 | 0 | for (cmSourceFile const* si : customCommands) { |
818 | 0 | bool r = this->VisitCustomCommand(temp, perm, order, si); |
819 | 0 | if (r) { |
820 | 0 | return r; |
821 | 0 | } |
822 | 0 | } |
823 | 0 | return false; |
824 | 0 | } |
825 | | |
826 | | bool cmGhsMultiTargetGenerator::VisitCustomCommand( |
827 | | std::set<cmSourceFile const*>& temp, std::set<cmSourceFile const*>& perm, |
828 | | std::vector<cmSourceFile const*>& order, cmSourceFile const* si) |
829 | 0 | { |
830 | | /* check if permanent mark is set*/ |
831 | 0 | if (perm.find(si) == perm.end()) { |
832 | | /* set temporary mark; check if revisit*/ |
833 | 0 | if (temp.insert(si).second) { |
834 | 0 | for (auto const& di : si->GetCustomCommand()->GetDepends()) { |
835 | 0 | cmSourceFile const* sf = |
836 | 0 | this->GeneratorTarget->GetLocalGenerator()->GetSourceFileWithOutput( |
837 | 0 | di); |
838 | | /* if sf exists then visit */ |
839 | 0 | if (sf && this->VisitCustomCommand(temp, perm, order, sf)) { |
840 | 0 | return true; |
841 | 0 | } |
842 | 0 | } |
843 | | /* mark as complete; insert into beginning of list*/ |
844 | 0 | perm.insert(si); |
845 | 0 | order.push_back(si); |
846 | 0 | return false; |
847 | 0 | } |
848 | | /* revisiting item - not a DAG */ |
849 | 0 | return true; |
850 | 0 | } |
851 | | /* already complete */ |
852 | 0 | return false; |
853 | 0 | } |