/src/CMake/Source/cmGlobalGhsMultiGenerator.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 "cmGlobalGhsMultiGenerator.h" |
4 | | |
5 | | #include <algorithm> |
6 | | #include <functional> |
7 | | #include <map> |
8 | | #include <sstream> |
9 | | #include <utility> |
10 | | |
11 | | #include <cm/memory> |
12 | | #include <cm/string> |
13 | | #include <cm/string_view> |
14 | | #include <cmext/algorithm> |
15 | | #include <cmext/memory> |
16 | | |
17 | | #include "cmCustomCommand.h" |
18 | | #include "cmCustomCommandLines.h" |
19 | | #include "cmDiagnostics.h" |
20 | | #include "cmGeneratedFileStream.h" |
21 | | #include "cmGeneratorTarget.h" |
22 | | #include "cmGhsMultiGpj.h" |
23 | | #include "cmList.h" |
24 | | #include "cmLocalGenerator.h" |
25 | | #include "cmLocalGhsMultiGenerator.h" |
26 | | #include "cmMakefile.h" |
27 | | #include "cmMessageType.h" |
28 | | #include "cmSourceFile.h" |
29 | | #include "cmState.h" |
30 | | #include "cmStateTypes.h" |
31 | | #include "cmStringAlgorithms.h" |
32 | | #include "cmSystemTools.h" |
33 | | #include "cmTarget.h" |
34 | | #include "cmTargetTypes.h" |
35 | | #include "cmValue.h" |
36 | | #include "cmVersion.h" |
37 | | #include "cmake.h" |
38 | | |
39 | | char const* cmGlobalGhsMultiGenerator::FILE_EXTENSION = ".gpj"; |
40 | | #ifdef __linux__ |
41 | | char const* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild"; |
42 | | #elif defined(_WIN32) |
43 | | char const* cmGlobalGhsMultiGenerator::DEFAULT_BUILD_PROGRAM = "gbuild.exe"; |
44 | | #endif |
45 | | char const* cmGlobalGhsMultiGenerator::CHECK_BUILD_SYSTEM_TARGET = |
46 | | "RERUN_CMAKE"; |
47 | | |
48 | | cmGlobalGhsMultiGenerator::cmGlobalGhsMultiGenerator(cmake* cm) |
49 | 0 | : cmGlobalGenerator(cm) |
50 | 0 | { |
51 | 0 | cm->GetState()->SetGhsMultiIDE(true); |
52 | 0 | } |
53 | | |
54 | 0 | cmGlobalGhsMultiGenerator::~cmGlobalGhsMultiGenerator() = default; |
55 | | |
56 | | std::unique_ptr<cmLocalGenerator> |
57 | | cmGlobalGhsMultiGenerator::CreateLocalGenerator(cmMakefile* mf) |
58 | 0 | { |
59 | 0 | return std::unique_ptr<cmLocalGenerator>( |
60 | 0 | cm::make_unique<cmLocalGhsMultiGenerator>(this, mf)); |
61 | 0 | } |
62 | | |
63 | | cmDocumentationEntry cmGlobalGhsMultiGenerator::GetDocumentation() |
64 | 0 | { |
65 | 0 | return { |
66 | 0 | GetActualName(), |
67 | 0 | "Generates Green Hills MULTI files (experimental, work-in-progress)." |
68 | 0 | }; |
69 | 0 | } |
70 | | |
71 | | void cmGlobalGhsMultiGenerator::ComputeTargetObjectDirectory( |
72 | | cmGeneratorTarget* gt) const |
73 | 0 | { |
74 | | // Compute full path to object file directory for this target. |
75 | 0 | std::string dir = cmStrCat(gt->GetSupportDirectory(), '/'); |
76 | 0 | gt->ObjectDirectory = dir; |
77 | 0 | } |
78 | | |
79 | | bool cmGlobalGhsMultiGenerator::SetGeneratorToolset(std::string const& ts, |
80 | | bool build, cmMakefile* mf) |
81 | 0 | { |
82 | | /* In build mode nothing to be done. |
83 | | * Toolset already determined and build tool absolute path is cached. |
84 | | */ |
85 | 0 | if (build) { |
86 | 0 | return true; |
87 | 0 | } |
88 | | |
89 | | /* Determine the absolute directory for the toolset */ |
90 | 0 | std::string tsp; |
91 | 0 | this->GetToolset(mf, tsp, ts); |
92 | | |
93 | | /* no toolset was found */ |
94 | 0 | if (tsp.empty()) { |
95 | 0 | return false; |
96 | 0 | } |
97 | | |
98 | | /* set the build tool to use */ |
99 | 0 | std::string gbuild = |
100 | 0 | cmStrCat(tsp, ((tsp.back() == '/') ? "" : "/"), DEFAULT_BUILD_PROGRAM); |
101 | 0 | cmValue prevTool = mf->GetDefinition("CMAKE_MAKE_PROGRAM"); |
102 | | |
103 | | /* check if the toolset changed from last generate */ |
104 | 0 | if (cmNonempty(prevTool) && !cmSystemTools::ComparePath(gbuild, *prevTool)) { |
105 | 0 | std::string const& e = |
106 | 0 | cmStrCat("toolset build tool: ", gbuild, |
107 | 0 | "\n" |
108 | 0 | "Does not match the previously used build tool: ", |
109 | 0 | *prevTool, |
110 | 0 | "\n" |
111 | 0 | "Either remove the CMakeCache.txt file and CMakeFiles " |
112 | 0 | "directory or choose a different binary directory."); |
113 | 0 | mf->IssueMessage(MessageType::FATAL_ERROR, e); |
114 | 0 | return false; |
115 | 0 | } |
116 | | |
117 | | /* store the toolset that is being used for this build */ |
118 | 0 | mf->AddCacheDefinition("CMAKE_MAKE_PROGRAM", gbuild, "build program to use", |
119 | 0 | cmStateEnums::INTERNAL, true); |
120 | |
|
121 | 0 | mf->AddDefinition("CMAKE_SYSTEM_VERSION", tsp); |
122 | |
|
123 | 0 | return true; |
124 | 0 | } |
125 | | |
126 | | bool cmGlobalGhsMultiGenerator::SetGeneratorPlatform(std::string const& p, |
127 | | cmMakefile* mf) |
128 | 0 | { |
129 | | /* set primary target */ |
130 | 0 | cmValue t = mf->GetDefinition("GHS_PRIMARY_TARGET"); |
131 | 0 | if (t.IsOff()) { |
132 | | /* Use the value from `-A` or use `arm` */ |
133 | 0 | std::string arch = "arm"; |
134 | 0 | if (!cmIsOff(p)) { |
135 | 0 | arch = p; |
136 | 0 | } |
137 | 0 | cmValue platform = mf->GetDefinition("GHS_TARGET_PLATFORM"); |
138 | 0 | std::string tgt = cmStrCat(arch, '_', platform, ".tgt"); |
139 | | |
140 | | /* update the primary target name*/ |
141 | 0 | mf->AddDefinition("GHS_PRIMARY_TARGET", tgt); |
142 | 0 | } |
143 | 0 | return true; |
144 | 0 | } |
145 | | |
146 | | void cmGlobalGhsMultiGenerator::EnableLanguage( |
147 | | std::vector<std::string> const& l, cmMakefile* mf, bool optional) |
148 | 0 | { |
149 | 0 | mf->AddDefinition("CMAKE_SYSTEM_NAME", "GHS-MULTI"); |
150 | |
|
151 | 0 | mf->AddDefinition("GHSMULTI", "1"); // identifier for user CMake files |
152 | |
|
153 | 0 | this->cmGlobalGenerator::EnableLanguage(l, mf, optional); |
154 | 0 | } |
155 | | |
156 | | bool cmGlobalGhsMultiGenerator::FindMakeProgram(cmMakefile* /*mf*/) |
157 | 0 | { |
158 | | // The GHS generator only knows how to lookup its build tool |
159 | | // during generation of the project files, but this |
160 | | // can only be done after the toolset is specified. |
161 | |
|
162 | 0 | return true; |
163 | 0 | } |
164 | | |
165 | | void cmGlobalGhsMultiGenerator::GetToolset(cmMakefile* mf, std::string& tsp, |
166 | | std::string const& ts) |
167 | 0 | { |
168 | | /* Determine tsp - full path of the toolset from ts (toolset hint via -T) */ |
169 | |
|
170 | 0 | std::string root = mf->GetSafeDefinition("GHS_TOOLSET_ROOT"); |
171 | | |
172 | | // Check if `-T` was set by user |
173 | 0 | if (ts.empty()) { |
174 | | // Enter toolset search mode |
175 | 0 | std::vector<std::string> output; |
176 | | |
177 | | // Make sure root exists... |
178 | 0 | if (!cmSystemTools::PathExists(root)) { |
179 | 0 | std::string msg = |
180 | 0 | cmStrCat("GHS_TOOLSET_ROOT directory \"", root, "\" does not exist."); |
181 | 0 | mf->IssueMessage(MessageType::FATAL_ERROR, msg); |
182 | 0 | tsp = ""; |
183 | 0 | return; |
184 | 0 | } |
185 | | |
186 | | // Add a directory separator |
187 | 0 | if (root.back() != '/') { |
188 | 0 | root += '/'; |
189 | 0 | } |
190 | | |
191 | | // Get all compiler directories in toolset root |
192 | 0 | cmSystemTools::Glob(root, "comp_[^;]+", output); |
193 | |
|
194 | 0 | if (output.empty()) { |
195 | | // No compiler directories found |
196 | 0 | std::string msg = |
197 | 0 | cmStrCat("No GHS toolsets found in GHS_TOOLSET_ROOT \"", root, "\"."); |
198 | 0 | mf->IssueMessage(MessageType::FATAL_ERROR, msg); |
199 | 0 | tsp = ""; |
200 | 0 | } else { |
201 | | // Use latest? version |
202 | 0 | tsp = root + output.back(); |
203 | 0 | } |
204 | |
|
205 | 0 | } else { |
206 | | // Toolset was provided by user |
207 | 0 | std::string tryPath; |
208 | | |
209 | | // NOTE: CollapseFullPath() will determine if user toolset was full path or |
210 | | // or relative path. |
211 | 0 | tryPath = cmSystemTools::CollapseFullPath(ts, root); |
212 | 0 | if (!cmSystemTools::FileExists(tryPath)) { |
213 | 0 | std::string msg = |
214 | 0 | cmStrCat("GHS toolset \"", tryPath, "\" does not exist."); |
215 | 0 | mf->IssueMessage(MessageType::FATAL_ERROR, msg); |
216 | 0 | tsp = ""; |
217 | 0 | } else { |
218 | 0 | tsp = tryPath; |
219 | 0 | } |
220 | 0 | } |
221 | 0 | } |
222 | | |
223 | | void cmGlobalGhsMultiGenerator::WriteFileHeader(std::ostream& fout) |
224 | 0 | { |
225 | | /* clang-format off */ |
226 | 0 | fout << "#!gbuild\n" |
227 | 0 | "#\n" |
228 | 0 | "# CMAKE generated file: DO NOT EDIT!\n" |
229 | 0 | "# Generated by \"" << GetActualName() << "\"" |
230 | 0 | " Generator, CMake Version " << cmVersion::GetMajorVersion() << '.' |
231 | 0 | << cmVersion::GetMinorVersion() << "\n" |
232 | 0 | "#\n\n"; |
233 | | /* clang-format on */ |
234 | 0 | } |
235 | | |
236 | | void cmGlobalGhsMultiGenerator::WriteCustomRuleBOD(std::ostream& fout) |
237 | 0 | { |
238 | 0 | fout << "Commands {\n" |
239 | 0 | " Custom_Rule_Command {\n" |
240 | 0 | " name = \"Custom Rule Command\"\n" |
241 | 0 | " exec = \"" |
242 | | #ifdef _WIN32 |
243 | | "cmd.exe" |
244 | | #else |
245 | 0 | "/bin/sh" |
246 | 0 | #endif |
247 | 0 | "\"\n" |
248 | 0 | " options = {\"SpecialOptions\"}\n" |
249 | 0 | " }\n" |
250 | 0 | "}\n" |
251 | |
|
252 | 0 | "\n\n" |
253 | 0 | "FileTypes {\n" |
254 | 0 | " CmakeRule {\n" |
255 | 0 | " name = \"Custom Rule\"\n" |
256 | 0 | " action = \"&Run\"\n" |
257 | 0 | " extensions = {\"" |
258 | | #ifdef _WIN32 |
259 | | "bat" |
260 | | #else |
261 | 0 | "sh" |
262 | 0 | #endif |
263 | 0 | "\"}\n" |
264 | 0 | " grepable = false\n" |
265 | 0 | " command = \"Custom Rule Command\"\n" |
266 | 0 | " commandLine = \"$COMMAND " |
267 | | #ifdef _WIN32 |
268 | | "/c" |
269 | | #endif |
270 | 0 | " $INPUTFILE\"\n" |
271 | 0 | " progress = \"Processing Custom Rule\"\n" |
272 | 0 | " promoteToFirstPass = true\n" |
273 | 0 | " outputType = \"None\"\n" |
274 | 0 | " color = \"#800080\"\n" |
275 | 0 | " }\n" |
276 | 0 | "}\n"; |
277 | 0 | } |
278 | | |
279 | | void cmGlobalGhsMultiGenerator::WriteCustomTargetBOD(std::ostream& fout) |
280 | 0 | { |
281 | 0 | fout << "FileTypes {\n" |
282 | 0 | " CmakeTarget {\n" |
283 | 0 | " name = \"Custom Target\"\n" |
284 | 0 | " action = \"&Execute\"\n" |
285 | 0 | " grepable = false\n" |
286 | 0 | " outputType = \"None\"\n" |
287 | 0 | " color = \"#800080\"\n" |
288 | 0 | " }\n" |
289 | 0 | "}\n"; |
290 | 0 | } |
291 | | |
292 | | void cmGlobalGhsMultiGenerator::WriteTopLevelProject(std::ostream& fout, |
293 | | cmLocalGenerator* root) |
294 | 0 | { |
295 | 0 | this->WriteFileHeader(fout); |
296 | 0 | this->WriteMacros(fout, root); |
297 | 0 | this->WriteHighLevelDirectives(fout, root); |
298 | 0 | GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fout); |
299 | |
|
300 | 0 | fout << "# Top Level Project File\n"; |
301 | | |
302 | | // Specify BSP option if supplied by user |
303 | | // -- not all platforms require this entry in the project file |
304 | 0 | cmValue bspName = root->GetMakefile()->GetDefinition("GHS_BSP_NAME"); |
305 | 0 | if (!bspName.IsOff()) { |
306 | 0 | fout << " -bsp " << *bspName << '\n'; |
307 | 0 | } |
308 | | |
309 | | // Specify OS DIR if supplied by user |
310 | | // -- not all platforms require this entry in the project file |
311 | 0 | cmValue osDir = root->GetMakefile()->GetDefinition("GHS_OS_DIR"); |
312 | 0 | if (!osDir.IsOff()) { |
313 | 0 | cmValue osDirOption = |
314 | 0 | root->GetMakefile()->GetDefinition("GHS_OS_DIR_OPTION"); |
315 | 0 | fout << " "; |
316 | 0 | if (osDirOption.IsOff()) { |
317 | 0 | fout << ""; |
318 | 0 | } else { |
319 | 0 | fout << *osDirOption; |
320 | 0 | } |
321 | 0 | fout << "\"" << osDir << "\"\n"; |
322 | 0 | } |
323 | 0 | } |
324 | | |
325 | | void cmGlobalGhsMultiGenerator::WriteSubProjects(std::ostream& fout, |
326 | | bool filterPredefined) |
327 | 0 | { |
328 | 0 | std::set<std::string> predefinedTargets; |
329 | 0 | predefinedTargets.insert(this->GetInstallTargetName()); |
330 | 0 | predefinedTargets.insert(this->GetAllTargetName()); |
331 | 0 | predefinedTargets.insert(std::string(CHECK_BUILD_SYSTEM_TARGET)); |
332 | | |
333 | | // All known targets |
334 | 0 | for (cmGeneratorTarget const* target : this->ProjectTargets) { |
335 | 0 | if (target->GetType() == cm::TargetType::INTERFACE_LIBRARY || |
336 | 0 | target->GetType() == cm::TargetType::MODULE_LIBRARY || |
337 | 0 | target->GetType() == cm::TargetType::SHARED_LIBRARY || |
338 | 0 | (target->GetType() == cm::TargetType::GLOBAL_TARGET && |
339 | 0 | target->GetName() != this->GetInstallTargetName())) { |
340 | 0 | continue; |
341 | 0 | } |
342 | | /* Check if the current target is a predefined CMake target */ |
343 | 0 | bool predefinedTarget = |
344 | 0 | predefinedTargets.find(target->GetName()) != predefinedTargets.end(); |
345 | 0 | if ((filterPredefined && predefinedTarget) || |
346 | 0 | (!filterPredefined && !predefinedTarget)) { |
347 | 0 | fout << cmStrCat(target->GetName(), ".tgt", FILE_EXTENSION) |
348 | 0 | << " [Project]\n"; |
349 | 0 | } |
350 | 0 | } |
351 | 0 | } |
352 | | |
353 | | void cmGlobalGhsMultiGenerator::WriteProjectLine( |
354 | | std::ostream& fout, cmGeneratorTarget const* target, |
355 | | std::string& rootBinaryDir) |
356 | 0 | { |
357 | 0 | cmValue projFile = target->GetProperty("GENERATOR_FILE_NAME"); |
358 | 0 | cmValue projType = target->GetProperty("GENERATOR_FILE_NAME_EXT"); |
359 | | /* If either value is not valid then this particular target is an |
360 | | * unsupported target type and should be skipped. |
361 | | */ |
362 | 0 | if (projFile && projType) { |
363 | 0 | std::string path = cmSystemTools::RelativePath(rootBinaryDir, *projFile); |
364 | |
|
365 | 0 | fout << path; |
366 | 0 | fout << ' ' << *projType << '\n'; |
367 | 0 | } |
368 | 0 | } |
369 | | |
370 | | void cmGlobalGhsMultiGenerator::WriteTargets(cmLocalGenerator* root) |
371 | 0 | { |
372 | 0 | std::string rootBinaryDir = root->GetCurrentBinaryDirectory(); |
373 | | |
374 | | // All known targets |
375 | 0 | for (cmGeneratorTarget const* target : this->ProjectTargets) { |
376 | 0 | if (target->GetType() == cm::TargetType::INTERFACE_LIBRARY || |
377 | 0 | target->GetType() == cm::TargetType::MODULE_LIBRARY || |
378 | 0 | target->GetType() == cm::TargetType::SHARED_LIBRARY || |
379 | 0 | (target->GetType() == cm::TargetType::GLOBAL_TARGET && |
380 | 0 | target->GetName() != this->GetInstallTargetName())) { |
381 | 0 | continue; |
382 | 0 | } |
383 | | |
384 | | // create target build file |
385 | 0 | std::string name = cmStrCat(target->GetName(), ".tgt", FILE_EXTENSION); |
386 | 0 | std::string fname = cmStrCat(rootBinaryDir, '/', name); |
387 | 0 | cmGeneratedFileStream fbld(fname); |
388 | 0 | fbld.SetCopyIfDifferent(true); |
389 | 0 | this->WriteFileHeader(fbld); |
390 | 0 | GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fbld); |
391 | 0 | std::vector<cmGeneratorTarget const*> build; |
392 | 0 | if (this->ComputeTargetBuildOrder(target, build)) { |
393 | 0 | cmSystemTools::Error( |
394 | 0 | cmStrCat("The inter-target dependency graph for target [", |
395 | 0 | target->GetName(), "] had a cycle.\n")); |
396 | 0 | } else { |
397 | 0 | for (auto& tgt : build) { |
398 | 0 | this->WriteProjectLine(fbld, tgt, rootBinaryDir); |
399 | 0 | } |
400 | 0 | } |
401 | 0 | fbld.Close(); |
402 | 0 | } |
403 | 0 | } |
404 | | |
405 | | void cmGlobalGhsMultiGenerator::Generate() |
406 | 0 | { |
407 | 0 | std::string fname; |
408 | | |
409 | | // first do the superclass method |
410 | 0 | this->cmGlobalGenerator::Generate(); |
411 | | |
412 | | // output top-level projects |
413 | 0 | for (auto& it : this->ProjectMap) { |
414 | 0 | this->OutputTopLevelProject(it.second[0], it.second); |
415 | 0 | } |
416 | | |
417 | | // create custom rule BOD file |
418 | 0 | fname = this->GetCMakeInstance()->GetHomeOutputDirectory() + |
419 | 0 | "/CMakeFiles/custom_rule.bod"; |
420 | 0 | cmGeneratedFileStream frule(fname); |
421 | 0 | frule.SetCopyIfDifferent(true); |
422 | 0 | this->WriteFileHeader(frule); |
423 | 0 | this->WriteCustomRuleBOD(frule); |
424 | 0 | frule.Close(); |
425 | | |
426 | | // create custom target BOD file |
427 | 0 | fname = this->GetCMakeInstance()->GetHomeOutputDirectory() + |
428 | 0 | "/CMakeFiles/custom_target.bod"; |
429 | 0 | cmGeneratedFileStream ftarget(fname); |
430 | 0 | ftarget.SetCopyIfDifferent(true); |
431 | 0 | this->WriteFileHeader(ftarget); |
432 | 0 | this->WriteCustomTargetBOD(ftarget); |
433 | 0 | ftarget.Close(); |
434 | 0 | } |
435 | | |
436 | | void cmGlobalGhsMultiGenerator::OutputTopLevelProject( |
437 | | cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators) |
438 | 0 | { |
439 | 0 | std::string fname; |
440 | |
|
441 | 0 | if (generators.empty()) { |
442 | 0 | return; |
443 | 0 | } |
444 | | |
445 | | // Collect all targets under this root generator and the transitive |
446 | | // closure of their dependencies. |
447 | 0 | TargetDependSet const projectTargets = |
448 | 0 | this->GetTargetsForProject(root, generators); |
449 | 0 | OrderedTargetDependSet sortedProjectTargets(projectTargets, ""); |
450 | 0 | this->ProjectTargets.clear(); |
451 | 0 | for (cmGeneratorTarget const* t : sortedProjectTargets) { |
452 | | /* save list of all targets in sorted order */ |
453 | 0 | this->ProjectTargets.push_back(t); |
454 | 0 | } |
455 | | |
456 | | /* Name top-level projects as filename.top.gpj to avoid name clashes |
457 | | * with target projects. This avoid the issue where the project has |
458 | | * the same name as the executable target. |
459 | | */ |
460 | 0 | fname = cmStrCat(root->GetCurrentBinaryDirectory(), '/', |
461 | 0 | root->GetProjectName(), ".top", FILE_EXTENSION); |
462 | |
|
463 | 0 | cmGeneratedFileStream top(fname); |
464 | 0 | top.SetCopyIfDifferent(true); |
465 | 0 | this->WriteTopLevelProject(top, root); |
466 | 0 | this->WriteTargets(root); |
467 | 0 | this->WriteSubProjects(top, true); |
468 | 0 | this->WriteSubProjects(top, false); |
469 | 0 | top.Close(); |
470 | 0 | } |
471 | | |
472 | | std::vector<cmGlobalGenerator::GeneratedMakeCommand> |
473 | | cmGlobalGhsMultiGenerator::GenerateBuildCommand( |
474 | | std::string const& makeProgram, std::string const& projectName, |
475 | | std::string const& projectDir, std::vector<std::string> const& targetNames, |
476 | | std::string const& /*config*/, int jobs, bool verbose, |
477 | | cmBuildOptions /*buildOptions*/, std::vector<std::string> const& makeOptions, |
478 | | BuildTryCompile /*isInTryCompile*/) |
479 | 0 | { |
480 | 0 | GeneratedMakeCommand makeCommand; |
481 | |
|
482 | 0 | makeCommand.Add(this->SelectMakeProgram(makeProgram)); |
483 | |
|
484 | 0 | if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) { |
485 | 0 | if (jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL) { |
486 | 0 | makeCommand.Add("-parallel"); |
487 | 0 | } else { |
488 | 0 | makeCommand.Add("-parallel=" + std::to_string(jobs)); |
489 | 0 | } |
490 | 0 | } |
491 | | |
492 | | /* determine the top-project file in the project directory */ |
493 | 0 | std::string proj = cmStrCat(projectName, ".top", FILE_EXTENSION); |
494 | 0 | std::vector<std::string> files; |
495 | 0 | cmSystemTools::Glob(projectDir, ".*\\.top\\.gpj", files); |
496 | 0 | if (!files.empty()) { |
497 | | /* use the real top-level project in the directory */ |
498 | 0 | proj = files.at(0); |
499 | 0 | } |
500 | 0 | makeCommand.Add("-top", proj); |
501 | | |
502 | | /* determine targets to build */ |
503 | 0 | bool build_all = false; |
504 | 0 | if (!targetNames.empty()) { |
505 | 0 | for (auto const& tname : targetNames) { |
506 | 0 | if (!tname.empty()) { |
507 | 0 | if (tname == "clean") { |
508 | 0 | makeCommand.Add("-clean"); |
509 | 0 | } else { |
510 | 0 | makeCommand.Add(tname + ".tgt.gpj"); |
511 | 0 | } |
512 | 0 | } else { |
513 | 0 | build_all = true; |
514 | 0 | } |
515 | 0 | } |
516 | 0 | } else { |
517 | 0 | build_all = true; |
518 | 0 | } |
519 | |
|
520 | 0 | if (build_all) { |
521 | 0 | /* transform name to default build */; |
522 | 0 | std::string all = cmStrCat(this->GetAllTargetName(), ".tgt.gpj"); |
523 | 0 | makeCommand.Add(all); |
524 | 0 | } |
525 | |
|
526 | 0 | if (verbose) { |
527 | 0 | makeCommand.Add("-commands"); |
528 | 0 | } |
529 | 0 | makeCommand.Add(makeOptions.begin(), makeOptions.end()); |
530 | |
|
531 | 0 | return { std::move(makeCommand) }; |
532 | 0 | } |
533 | | |
534 | | void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout, |
535 | | cmLocalGenerator* root) |
536 | 0 | { |
537 | 0 | fout << "macro PROJ_NAME=" << root->GetProjectName() << '\n'; |
538 | 0 | cmValue ghsGpjMacros = root->GetMakefile()->GetDefinition("GHS_GPJ_MACROS"); |
539 | 0 | if (ghsGpjMacros) { |
540 | 0 | cmList expandedList{ *ghsGpjMacros }; |
541 | 0 | for (std::string const& arg : expandedList) { |
542 | 0 | fout << "macro " << arg << '\n'; |
543 | 0 | } |
544 | 0 | } |
545 | 0 | } |
546 | | |
547 | | void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives( |
548 | | std::ostream& fout, cmLocalGenerator* root) |
549 | 0 | { |
550 | | /* put primary target and customization files into project file */ |
551 | 0 | cmValue const tgt = root->GetMakefile()->GetDefinition("GHS_PRIMARY_TARGET"); |
552 | | |
553 | | /* clang-format off */ |
554 | 0 | fout << "primaryTarget=" << tgt << "\n" |
555 | 0 | "customization=" << root->GetBinaryDirectory() |
556 | 0 | << "/CMakeFiles/custom_rule.bod\n" |
557 | 0 | "customization=" << root->GetBinaryDirectory() |
558 | 0 | << "/CMakeFiles/custom_target.bod" << '\n'; |
559 | | /* clang-format on */ |
560 | |
|
561 | 0 | cmValue const customization = |
562 | 0 | root->GetMakefile()->GetDefinition("GHS_CUSTOMIZATION"); |
563 | 0 | if (cmNonempty(customization)) { |
564 | 0 | fout << "customization=" |
565 | 0 | << cmGlobalGhsMultiGenerator::TrimQuotes(*customization) << '\n'; |
566 | 0 | this->GetCMakeInstance()->MarkCliAsUsed("GHS_CUSTOMIZATION"); |
567 | 0 | } |
568 | 0 | } |
569 | | |
570 | | std::string cmGlobalGhsMultiGenerator::TrimQuotes(std::string str) |
571 | 0 | { |
572 | 0 | cm::erase(str, '"'); |
573 | 0 | return str; |
574 | 0 | } |
575 | | |
576 | | bool cmGlobalGhsMultiGenerator::TargetCompare::operator()( |
577 | | cmGeneratorTarget const* l, cmGeneratorTarget const* r) const |
578 | 0 | { |
579 | | // Make sure a given named target is ordered first, |
580 | | // e.g. to set ALL_BUILD as the default active project. |
581 | | // When the empty string is named this is a no-op. |
582 | 0 | if (r->GetName() == this->First) { |
583 | 0 | return false; |
584 | 0 | } |
585 | 0 | if (l->GetName() == this->First) { |
586 | 0 | return true; |
587 | 0 | } |
588 | 0 | return l->GetName() < r->GetName(); |
589 | 0 | } |
590 | | |
591 | | cmGlobalGhsMultiGenerator::OrderedTargetDependSet::OrderedTargetDependSet( |
592 | | TargetDependSet const& targets, std::string const& first) |
593 | 0 | : derived(TargetCompare(first)) |
594 | 0 | { |
595 | 0 | this->insert(targets.begin(), targets.end()); |
596 | 0 | } |
597 | | |
598 | | bool cmGlobalGhsMultiGenerator::ComputeTargetBuildOrder( |
599 | | cmGeneratorTarget const* tgt, std::vector<cmGeneratorTarget const*>& build) |
600 | 0 | { |
601 | 0 | std::vector<cmGeneratorTarget const*> t{ tgt }; |
602 | 0 | return this->ComputeTargetBuildOrder(t, build); |
603 | 0 | } |
604 | | |
605 | | bool cmGlobalGhsMultiGenerator::ComputeTargetBuildOrder( |
606 | | std::vector<cmGeneratorTarget const*>& tgt, |
607 | | std::vector<cmGeneratorTarget const*>& build) |
608 | 0 | { |
609 | 0 | std::set<cmGeneratorTarget const*> temp; |
610 | 0 | std::set<cmGeneratorTarget const*> perm; |
611 | |
|
612 | 0 | for (auto const* const ti : tgt) { |
613 | 0 | bool r = this->VisitTarget(temp, perm, build, ti); |
614 | 0 | if (r) { |
615 | 0 | return r; |
616 | 0 | } |
617 | 0 | } |
618 | 0 | return false; |
619 | 0 | } |
620 | | |
621 | | bool cmGlobalGhsMultiGenerator::VisitTarget( |
622 | | std::set<cmGeneratorTarget const*>& temp, |
623 | | std::set<cmGeneratorTarget const*>& perm, |
624 | | std::vector<cmGeneratorTarget const*>& order, cmGeneratorTarget const* ti) |
625 | 0 | { |
626 | | /* check if permanent mark is set*/ |
627 | 0 | if (perm.find(ti) == perm.end()) { |
628 | | /* set temporary mark; check if revisit*/ |
629 | 0 | if (temp.insert(ti).second) { |
630 | | /* sort targets lexicographically to ensure that nodes are always visited |
631 | | * in the same order */ |
632 | 0 | OrderedTargetDependSet sortedTargets(this->GetTargetDirectDepends(ti), |
633 | 0 | ""); |
634 | 0 | for (auto const& di : sortedTargets) { |
635 | 0 | if (this->VisitTarget(temp, perm, order, di)) { |
636 | 0 | return true; |
637 | 0 | } |
638 | 0 | } |
639 | | /* mark as complete; insert into beginning of list*/ |
640 | 0 | perm.insert(ti); |
641 | 0 | order.push_back(ti); |
642 | 0 | return false; |
643 | 0 | } |
644 | | /* revisiting item - not a DAG */ |
645 | 0 | return true; |
646 | 0 | } |
647 | | /* already complete */ |
648 | 0 | return false; |
649 | 0 | } |
650 | | |
651 | | bool cmGlobalGhsMultiGenerator::AddCheckTarget() |
652 | 0 | { |
653 | | // Skip the target if no regeneration is to be done. |
654 | 0 | if (this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) { |
655 | 0 | return false; |
656 | 0 | } |
657 | | |
658 | | // Get the generators. |
659 | 0 | std::vector<std::unique_ptr<cmLocalGenerator>> const& generators = |
660 | 0 | this->LocalGenerators; |
661 | 0 | auto& lg = |
662 | 0 | cm::static_reference_cast<cmLocalGhsMultiGenerator>(generators[0]); |
663 | | |
664 | | // The name of the output file for the custom command. |
665 | 0 | this->StampFile = cmStrCat(lg.GetBinaryDirectory(), "/CMakeFiles/", |
666 | 0 | CHECK_BUILD_SYSTEM_TARGET); |
667 | | |
668 | | // Add a custom rule to re-run CMake if any input files changed. |
669 | 0 | { |
670 | | // Collect the input files used to generate all targets in this |
671 | | // project. |
672 | 0 | std::vector<std::string> listFiles; |
673 | 0 | for (auto const& gen : generators) { |
674 | 0 | cm::append(listFiles, gen->GetMakefile()->GetListFiles()); |
675 | 0 | } |
676 | | |
677 | | // Add the cache file. |
678 | 0 | listFiles.emplace_back(cmStrCat( |
679 | 0 | this->GetCMakeInstance()->GetHomeOutputDirectory(), "/CMakeCache.txt")); |
680 | | |
681 | | // Print not implemented warning. |
682 | 0 | if (this->GetCMakeInstance()->DoWriteGlobVerifyTarget()) { |
683 | 0 | std::ostringstream msg; |
684 | 0 | msg << "Any pre-check scripts, such as those generated for file(GLOB " |
685 | 0 | "CONFIGURE_DEPENDS), will not be run by gbuild."; |
686 | 0 | this->GetCMakeInstance()->IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, |
687 | 0 | msg.str()); |
688 | 0 | } |
689 | | |
690 | | // Sort the list of input files and remove duplicates. |
691 | 0 | std::sort(listFiles.begin(), listFiles.end(), std::less<std::string>()); |
692 | 0 | auto newEnd = std::unique(listFiles.begin(), listFiles.end()); |
693 | 0 | listFiles.erase(newEnd, listFiles.end()); |
694 | | |
695 | | // Create a rule to re-run CMake and create output file. |
696 | 0 | cmCustomCommandLines commandLines; |
697 | 0 | commandLines.emplace_back( |
698 | 0 | cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", "rm", "-f", |
699 | 0 | this->StampFile })); |
700 | 0 | std::string argS = cmStrCat("-S", lg.GetSourceDirectory()); |
701 | 0 | std::string argB = cmStrCat("-B", lg.GetBinaryDirectory()); |
702 | 0 | commandLines.emplace_back( |
703 | 0 | cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), argS, argB })); |
704 | 0 | commandLines.emplace_back(cmMakeCommandLine( |
705 | 0 | { cmSystemTools::GetCMakeCommand(), "-E", "touch", this->StampFile })); |
706 | | |
707 | | /* Create the target(Exclude from ALL_BUILD). |
708 | | * |
709 | | * The build tool, currently, does not support rereading the project files |
710 | | * if they get updated. So do not run this target as part of ALL_BUILD. |
711 | | */ |
712 | 0 | auto cc = cm::make_unique<cmCustomCommand>(); |
713 | 0 | cmTarget* tgt = |
714 | 0 | lg.AddUtilityCommand(CHECK_BUILD_SYSTEM_TARGET, true, std::move(cc)); |
715 | 0 | auto ptr = cm::make_unique<cmGeneratorTarget>(tgt, &lg); |
716 | 0 | auto* gt = ptr.get(); |
717 | 0 | lg.AddGeneratorTarget(std::move(ptr)); |
718 | | |
719 | | // Add the rule. |
720 | 0 | cc = cm::make_unique<cmCustomCommand>(); |
721 | 0 | cc->SetOutputs(this->StampFile); |
722 | 0 | cc->SetDepends(listFiles); |
723 | 0 | cc->SetCommandLines(commandLines); |
724 | 0 | cc->SetComment("Checking Build System"); |
725 | 0 | cc->SetEscapeOldStyle(false); |
726 | 0 | cc->SetStdPipesUTF8(true); |
727 | |
|
728 | 0 | if (cmSourceFile* file = |
729 | 0 | lg.AddCustomCommandToOutput(std::move(cc), true)) { |
730 | 0 | gt->AddSource(file->ResolveFullPath()); |
731 | 0 | } else { |
732 | 0 | cmSystemTools::Error("Error adding rule for " + this->StampFile); |
733 | 0 | } |
734 | | // Organize in the "predefined targets" folder: |
735 | 0 | if (this->UseFolderProperty()) { |
736 | 0 | tgt->SetProperty("FOLDER", this->GetPredefinedTargetsFolder()); |
737 | 0 | } |
738 | 0 | } |
739 | |
|
740 | 0 | return true; |
741 | 0 | } |
742 | | |
743 | | void cmGlobalGhsMultiGenerator::AddAllTarget() |
744 | 0 | { |
745 | | // Add a special target that depends on ALL projects for easy build |
746 | | // of one configuration only. |
747 | 0 | for (auto const& it : this->ProjectMap) { |
748 | 0 | std::vector<cmLocalGenerator*> const& gen = it.second; |
749 | | // add the ALL_BUILD to the first local generator of each project |
750 | 0 | if (!gen.empty()) { |
751 | | // Use no actual command lines so that the target itself is not |
752 | | // considered always out of date. |
753 | 0 | auto cc = cm::make_unique<cmCustomCommand>(); |
754 | 0 | cc->SetEscapeOldStyle(false); |
755 | 0 | cc->SetComment("Build all projects"); |
756 | 0 | cmTarget* allBuild = gen[0]->AddUtilityCommand(this->GetAllTargetName(), |
757 | 0 | true, std::move(cc)); |
758 | |
|
759 | 0 | gen[0]->AddGeneratorTarget( |
760 | 0 | cm::make_unique<cmGeneratorTarget>(allBuild, gen[0])); |
761 | | |
762 | | // Organize in the "predefined targets" folder: |
763 | 0 | if (this->UseFolderProperty()) { |
764 | 0 | allBuild->SetProperty("FOLDER", this->GetPredefinedTargetsFolder()); |
765 | 0 | } |
766 | | |
767 | | // Now make all targets depend on the ALL_BUILD target |
768 | 0 | for (cmLocalGenerator const* i : gen) { |
769 | 0 | for (auto const& tgt : i->GetGeneratorTargets()) { |
770 | | // Skip global or imported targets |
771 | 0 | if (tgt->GetType() == cm::TargetType::GLOBAL_TARGET || |
772 | 0 | tgt->IsImported()) { |
773 | 0 | continue; |
774 | 0 | } |
775 | | // Skip Exclude From All Targets |
776 | 0 | if (!this->IsExcluded(gen[0], tgt.get())) { |
777 | 0 | allBuild->AddUtility(tgt->GetName(), false); |
778 | 0 | } |
779 | 0 | } |
780 | 0 | } |
781 | 0 | } |
782 | 0 | } |
783 | 0 | } |
784 | | |
785 | | void cmGlobalGhsMultiGenerator::AddExtraIDETargets() |
786 | 0 | { |
787 | | // Add a special target that depends on ALL projects. |
788 | 0 | this->AddAllTarget(); |
789 | | |
790 | | /* Add Custom Target to check if CMake needs to be rerun. |
791 | | * |
792 | | * The build tool, currently, does not support rereading the project files |
793 | | * if they get updated. So do not make the other targets dependent on this |
794 | | * check. |
795 | | */ |
796 | 0 | this->AddCheckTarget(); |
797 | 0 | } |