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