/src/CMake/Source/cmExtraCodeBlocksGenerator.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 "cmExtraCodeBlocksGenerator.h" |
4 | | |
5 | | #include <map> |
6 | | #include <memory> |
7 | | #include <ostream> |
8 | | #include <set> |
9 | | #include <utility> |
10 | | |
11 | | #include <cmext/algorithm> |
12 | | |
13 | | #include "cmAlgorithms.h" |
14 | | #include "cmGeneratedFileStream.h" |
15 | | #include "cmGeneratorTarget.h" |
16 | | #include "cmGlobalGenerator.h" |
17 | | #include "cmList.h" |
18 | | #include "cmLocalGenerator.h" |
19 | | #include "cmMakefile.h" |
20 | | #include "cmRange.h" |
21 | | #include "cmSourceFile.h" |
22 | | #include "cmStateTypes.h" |
23 | | #include "cmStringAlgorithms.h" |
24 | | #include "cmSystemTools.h" |
25 | | #include "cmValue.h" |
26 | | #include "cmXMLWriter.h" |
27 | | #include "cmake.h" |
28 | | |
29 | | /* Some useful URLs: |
30 | | Homepage: |
31 | | http://www.codeblocks.org |
32 | | |
33 | | File format docs: |
34 | | http://wiki.codeblocks.org/index.php?title=File_formats_description |
35 | | http://wiki.codeblocks.org/index.php?title=Workspace_file |
36 | | http://wiki.codeblocks.org/index.php?title=Project_file |
37 | | |
38 | | Discussion: |
39 | | http://forums.codeblocks.org/index.php/topic,6789.0.html |
40 | | */ |
41 | | |
42 | 0 | cmExtraCodeBlocksGenerator::cmExtraCodeBlocksGenerator() = default; |
43 | | |
44 | | cmExternalMakefileProjectGeneratorFactory* |
45 | | cmExtraCodeBlocksGenerator::GetFactory() |
46 | 35 | { |
47 | 35 | static cmExternalMakefileProjectGeneratorSimpleFactory< |
48 | 35 | cmExtraCodeBlocksGenerator> |
49 | 35 | factory("CodeBlocks", "Generates CodeBlocks project files (deprecated)."); |
50 | | |
51 | 35 | if (factory.GetSupportedGlobalGenerators().empty()) { |
52 | | #if defined(_WIN32) |
53 | | factory.AddSupportedGlobalGenerator("MinGW Makefiles"); |
54 | | factory.AddSupportedGlobalGenerator("NMake Makefiles"); |
55 | | factory.AddSupportedGlobalGenerator("NMake Makefiles JOM"); |
56 | | // disable until somebody actually tests it: |
57 | | // this->AddSupportedGlobalGenerator("MSYS Makefiles"); |
58 | | #endif |
59 | 1 | factory.AddSupportedGlobalGenerator("Ninja"); |
60 | 1 | factory.AddSupportedGlobalGenerator("Unix Makefiles"); |
61 | 1 | } |
62 | | |
63 | 35 | return &factory; |
64 | 35 | } |
65 | | |
66 | | void cmExtraCodeBlocksGenerator::Generate() |
67 | 0 | { |
68 | | // for each sub project in the project create a codeblocks project |
69 | 0 | for (auto const& it : this->GlobalGenerator->GetProjectMap()) { |
70 | | // create a project file |
71 | 0 | this->CreateProjectFile(it.second); |
72 | 0 | } |
73 | 0 | } |
74 | | |
75 | | /* create the project file */ |
76 | | void cmExtraCodeBlocksGenerator::CreateProjectFile( |
77 | | std::vector<cmLocalGenerator*> const& lgs) |
78 | 0 | { |
79 | 0 | std::string outputDir = lgs[0]->GetCurrentBinaryDirectory(); |
80 | 0 | std::string projectName = lgs[0]->GetProjectName(); |
81 | |
|
82 | 0 | std::string filename = cmStrCat(outputDir, '/', projectName, ".cbp"); |
83 | 0 | std::string sessionFilename = |
84 | 0 | cmStrCat(outputDir, '/', projectName, ".layout"); |
85 | |
|
86 | 0 | this->CreateNewProjectFile(lgs, filename); |
87 | 0 | } |
88 | | |
89 | | /* Tree is used to create a "Virtual Folder" in CodeBlocks, in which all |
90 | | CMake files this project depends on will be put. This means additionally |
91 | | to the "Sources" and "Headers" virtual folders of CodeBlocks, there will |
92 | | now also be a "CMake Files" virtual folder. |
93 | | Patch by Daniel Teske <daniel.teske AT nokia.com> (which use C::B project |
94 | | files in QtCreator).*/ |
95 | | struct Tree |
96 | | { |
97 | | std::string path; // only one component of the path |
98 | | std::vector<Tree> folders; |
99 | | std::set<std::string> files; |
100 | | void InsertPath(std::vector<std::string> const& split, |
101 | | std::vector<std::string>::size_type start, |
102 | | std::string const& fileName); |
103 | | void BuildVirtualFolder(cmXMLWriter& xml) const; |
104 | | void BuildVirtualFolderImpl(std::string& virtualFolders, |
105 | | std::string const& prefix) const; |
106 | | void BuildUnit(cmXMLWriter& xml, std::string const& fsPath) const; |
107 | | void BuildUnitImpl(cmXMLWriter& xml, std::string const& virtualFolderPath, |
108 | | std::string const& fsPath) const; |
109 | | }; |
110 | | |
111 | | void Tree::InsertPath(std::vector<std::string> const& split, |
112 | | std::vector<std::string>::size_type start, |
113 | | std::string const& fileName) |
114 | 0 | { |
115 | 0 | if (start == split.size()) { |
116 | 0 | this->files.insert(fileName); |
117 | 0 | return; |
118 | 0 | } |
119 | 0 | for (Tree& folder : this->folders) { |
120 | 0 | if (folder.path == split[start]) { |
121 | 0 | if (start + 1 < split.size()) { |
122 | 0 | folder.InsertPath(split, start + 1, fileName); |
123 | 0 | return; |
124 | 0 | } |
125 | | // last part of split |
126 | 0 | folder.files.insert(fileName); |
127 | 0 | return; |
128 | 0 | } |
129 | 0 | } |
130 | | // Not found in folders, thus insert |
131 | 0 | Tree newFolder; |
132 | 0 | newFolder.path = split[start]; |
133 | 0 | if (start + 1 < split.size()) { |
134 | 0 | newFolder.InsertPath(split, start + 1, fileName); |
135 | 0 | this->folders.push_back(newFolder); |
136 | 0 | return; |
137 | 0 | } |
138 | | // last part of split |
139 | 0 | newFolder.files.insert(fileName); |
140 | 0 | this->folders.push_back(newFolder); |
141 | 0 | } |
142 | | |
143 | | void Tree::BuildVirtualFolder(cmXMLWriter& xml) const |
144 | 0 | { |
145 | 0 | xml.StartElement("Option"); |
146 | 0 | std::string virtualFolders = "CMake Files\\;"; |
147 | 0 | for (Tree const& folder : this->folders) { |
148 | 0 | folder.BuildVirtualFolderImpl(virtualFolders, ""); |
149 | 0 | } |
150 | 0 | xml.Attribute("virtualFolders", virtualFolders); |
151 | 0 | xml.EndElement(); |
152 | 0 | } |
153 | | |
154 | | void Tree::BuildVirtualFolderImpl(std::string& virtualFolders, |
155 | | std::string const& prefix) const |
156 | 0 | { |
157 | 0 | virtualFolders += cmStrCat("CMake Files\\", prefix, this->path, "\\;"); |
158 | 0 | for (Tree const& folder : this->folders) { |
159 | 0 | folder.BuildVirtualFolderImpl(virtualFolders, |
160 | 0 | cmStrCat(prefix, this->path, '\\')); |
161 | 0 | } |
162 | 0 | } |
163 | | |
164 | | void Tree::BuildUnit(cmXMLWriter& xml, std::string const& fsPath) const |
165 | 0 | { |
166 | 0 | for (std::string const& f : this->files) { |
167 | 0 | xml.StartElement("Unit"); |
168 | 0 | xml.Attribute("filename", fsPath + f); |
169 | |
|
170 | 0 | xml.StartElement("Option"); |
171 | 0 | xml.Attribute("virtualFolder", "CMake Files\\"); |
172 | 0 | xml.EndElement(); |
173 | |
|
174 | 0 | xml.EndElement(); |
175 | 0 | } |
176 | 0 | for (Tree const& folder : this->folders) { |
177 | 0 | folder.BuildUnitImpl(xml, "", fsPath); |
178 | 0 | } |
179 | 0 | } |
180 | | |
181 | | void Tree::BuildUnitImpl(cmXMLWriter& xml, |
182 | | std::string const& virtualFolderPath, |
183 | | std::string const& fsPath) const |
184 | 0 | { |
185 | 0 | for (std::string const& f : this->files) { |
186 | 0 | xml.StartElement("Unit"); |
187 | 0 | xml.Attribute("filename", cmStrCat(fsPath, this->path, '/', f)); |
188 | |
|
189 | 0 | xml.StartElement("Option"); |
190 | 0 | xml.Attribute( |
191 | 0 | "virtualFolder", |
192 | 0 | cmStrCat("CMake Files\\", virtualFolderPath, this->path, '\\')); |
193 | 0 | xml.EndElement(); |
194 | |
|
195 | 0 | xml.EndElement(); |
196 | 0 | } |
197 | 0 | for (Tree const& folder : this->folders) { |
198 | 0 | folder.BuildUnitImpl(xml, cmStrCat(virtualFolderPath, this->path, '\\'), |
199 | 0 | cmStrCat(fsPath, this->path, '/')); |
200 | 0 | } |
201 | 0 | } |
202 | | |
203 | | void cmExtraCodeBlocksGenerator::CreateNewProjectFile( |
204 | | std::vector<cmLocalGenerator*> const& lgs, std::string const& filename) |
205 | 0 | { |
206 | 0 | cmMakefile const* mf = lgs[0]->GetMakefile(); |
207 | 0 | cmGeneratedFileStream fout(filename); |
208 | 0 | if (!fout) { |
209 | 0 | return; |
210 | 0 | } |
211 | | |
212 | 0 | Tree tree; |
213 | | |
214 | | // build tree of virtual folders |
215 | 0 | for (auto const& it : this->GlobalGenerator->GetProjectMap()) { |
216 | | // Collect all files |
217 | 0 | std::vector<std::string> listFiles; |
218 | 0 | for (cmLocalGenerator* lg : it.second) { |
219 | 0 | cm::append(listFiles, lg->GetMakefile()->GetListFiles()); |
220 | 0 | } |
221 | | |
222 | | // Convert |
223 | 0 | for (std::string const& listFile : listFiles) { |
224 | | // don't put cmake's own files into the project (#12110): |
225 | 0 | if (cmHasPrefix(listFile, cmSystemTools::GetCMakeRoot())) { |
226 | 0 | continue; |
227 | 0 | } |
228 | | |
229 | 0 | std::string const& relative = cmSystemTools::RelativePath( |
230 | 0 | it.second[0]->GetSourceDirectory(), listFile); |
231 | 0 | std::vector<std::string> split; |
232 | 0 | cmSystemTools::SplitPath(relative, split, false); |
233 | | // Split filename from path |
234 | 0 | std::string fileName = *(split.end() - 1); |
235 | 0 | split.erase(split.end() - 1, split.end()); |
236 | | |
237 | | // We don't want paths with CMakeFiles in them |
238 | | // or do we? |
239 | | // In speedcrunch those where purely internal |
240 | | // |
241 | | // Also we can disable external (outside the project) files by setting ON |
242 | | // CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES variable. |
243 | 0 | bool const excludeExternal = it.second[0]->GetMakefile()->IsOn( |
244 | 0 | "CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES"); |
245 | 0 | if (!split.empty() && |
246 | 0 | (!excludeExternal || (relative.find("..") == std::string::npos)) && |
247 | 0 | relative.find("CMakeFiles") == std::string::npos) { |
248 | 0 | tree.InsertPath(split, 1, fileName); |
249 | 0 | } |
250 | 0 | } |
251 | 0 | } |
252 | | |
253 | | // figure out the compiler |
254 | 0 | std::string compiler = this->GetCBCompilerId(mf); |
255 | 0 | std::string const& make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); |
256 | 0 | std::string const& makeArgs = |
257 | 0 | mf->GetSafeDefinition("CMAKE_CODEBLOCKS_MAKE_ARGUMENTS"); |
258 | |
|
259 | 0 | cmXMLWriter xml(fout); |
260 | 0 | xml.StartDocument(); |
261 | 0 | xml.StartElement("CodeBlocks_project_file"); |
262 | |
|
263 | 0 | xml.StartElement("FileVersion"); |
264 | 0 | xml.Attribute("major", 1); |
265 | 0 | xml.Attribute("minor", 6); |
266 | 0 | xml.EndElement(); |
267 | |
|
268 | 0 | xml.StartElement("Project"); |
269 | |
|
270 | 0 | xml.StartElement("Option"); |
271 | 0 | xml.Attribute("title", lgs[0]->GetProjectName()); |
272 | 0 | xml.EndElement(); |
273 | |
|
274 | 0 | xml.StartElement("Option"); |
275 | 0 | xml.Attribute("makefile_is_custom", 1); |
276 | 0 | xml.EndElement(); |
277 | |
|
278 | 0 | xml.StartElement("Option"); |
279 | 0 | xml.Attribute("compiler", compiler); |
280 | 0 | xml.EndElement(); |
281 | | |
282 | | // Now build a virtual tree |
283 | 0 | tree.BuildVirtualFolder(xml); |
284 | |
|
285 | 0 | xml.StartElement("Build"); |
286 | |
|
287 | 0 | this->AppendTarget(xml, "all", nullptr, make, lgs[0], compiler, makeArgs); |
288 | | |
289 | | // add all executable and library targets and some of the GLOBAL |
290 | | // and UTILITY targets |
291 | 0 | for (cmLocalGenerator* lg : lgs) { |
292 | 0 | auto const& targets = lg->GetGeneratorTargets(); |
293 | 0 | for (auto const& target : targets) { |
294 | 0 | std::string targetName = target->GetName(); |
295 | 0 | switch (target->GetType()) { |
296 | 0 | case cmStateEnums::GLOBAL_TARGET: { |
297 | | // Only add the global targets from CMAKE_BINARY_DIR, |
298 | | // not from the subdirs |
299 | 0 | if (lg->GetCurrentBinaryDirectory() == lg->GetBinaryDirectory()) { |
300 | 0 | this->AppendTarget(xml, targetName, nullptr, make, lg, compiler, |
301 | 0 | makeArgs); |
302 | 0 | } |
303 | 0 | } break; |
304 | 0 | case cmStateEnums::UTILITY: |
305 | | // Add all utility targets, except the Nightly/Continuous/ |
306 | | // Experimental-"sub"targets as e.g. NightlyStart |
307 | 0 | if ((cmHasLiteralPrefix(targetName, "Nightly") && |
308 | 0 | (targetName != "Nightly")) || |
309 | 0 | (cmHasLiteralPrefix(targetName, "Continuous") && |
310 | 0 | (targetName != "Continuous")) || |
311 | 0 | (cmHasLiteralPrefix(targetName, "Experimental") && |
312 | 0 | (targetName != "Experimental"))) { |
313 | 0 | break; |
314 | 0 | } |
315 | | |
316 | 0 | this->AppendTarget(xml, targetName, nullptr, make, lg, compiler, |
317 | 0 | makeArgs); |
318 | 0 | break; |
319 | 0 | case cmStateEnums::EXECUTABLE: |
320 | 0 | case cmStateEnums::STATIC_LIBRARY: |
321 | 0 | case cmStateEnums::SHARED_LIBRARY: |
322 | 0 | case cmStateEnums::MODULE_LIBRARY: |
323 | 0 | case cmStateEnums::OBJECT_LIBRARY: { |
324 | 0 | cmGeneratorTarget* gt = target.get(); |
325 | 0 | this->AppendTarget(xml, targetName, gt, make, lg, compiler, |
326 | 0 | makeArgs); |
327 | 0 | std::string fastTarget = cmStrCat(targetName, "/fast"); |
328 | 0 | this->AppendTarget(xml, fastTarget, gt, make, lg, compiler, |
329 | 0 | makeArgs); |
330 | 0 | } break; |
331 | 0 | default: |
332 | 0 | break; |
333 | 0 | } |
334 | 0 | } |
335 | 0 | } |
336 | | |
337 | 0 | xml.EndElement(); // Build |
338 | | |
339 | | // Collect all used source files in the project. |
340 | | // Keep a list of C/C++ source files which might have an accompanying header |
341 | | // that should be looked for. |
342 | 0 | using all_files_map_t = std::map<std::string, CbpUnit>; |
343 | 0 | all_files_map_t allFiles; |
344 | 0 | std::vector<std::string> cFiles; |
345 | |
|
346 | 0 | auto* cm = this->GlobalGenerator->GetCMakeInstance(); |
347 | |
|
348 | 0 | for (cmLocalGenerator* lg : lgs) { |
349 | 0 | cmMakefile* makefile = lg->GetMakefile(); |
350 | 0 | auto const& targets = lg->GetGeneratorTargets(); |
351 | 0 | for (auto const& target : targets) { |
352 | 0 | switch (target->GetType()) { |
353 | 0 | case cmStateEnums::EXECUTABLE: |
354 | 0 | case cmStateEnums::STATIC_LIBRARY: |
355 | 0 | case cmStateEnums::SHARED_LIBRARY: |
356 | 0 | case cmStateEnums::MODULE_LIBRARY: |
357 | 0 | case cmStateEnums::OBJECT_LIBRARY: |
358 | 0 | case cmStateEnums::UTILITY: // can have sources since 2.6.3 |
359 | 0 | { |
360 | 0 | std::vector<cmSourceFile*> sources; |
361 | 0 | target->GetSourceFiles( |
362 | 0 | sources, makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")); |
363 | 0 | for (cmSourceFile* s : sources) { |
364 | | // don't add source files from UTILITY target which have the |
365 | | // GENERATED property set: |
366 | 0 | if (target->GetType() == cmStateEnums::UTILITY && |
367 | 0 | s->GetIsGenerated()) { |
368 | 0 | continue; |
369 | 0 | } |
370 | | |
371 | | // check whether it is a C/C++/CUDA/HIP implementation file |
372 | 0 | bool isCFile = false; |
373 | 0 | std::string lang = s->GetOrDetermineLanguage(); |
374 | 0 | if (lang == "C" || lang == "CXX" || lang == "CUDA" || |
375 | 0 | lang == "HIP") { |
376 | 0 | std::string const& srcext = s->GetExtension(); |
377 | 0 | isCFile = cm->IsACLikeSourceExtension(srcext); |
378 | 0 | } |
379 | |
|
380 | 0 | std::string const& fullPath = s->ResolveFullPath(); |
381 | | |
382 | | // Check file position relative to project root dir. |
383 | 0 | std::string const relative = |
384 | 0 | cmSystemTools::RelativePath(lg->GetSourceDirectory(), fullPath); |
385 | | // Do not add this file if it has ".." in relative path and |
386 | | // if CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES variable is on. |
387 | 0 | bool const excludeExternal = lg->GetMakefile()->IsOn( |
388 | 0 | "CMAKE_CODEBLOCKS_EXCLUDE_EXTERNAL_FILES"); |
389 | 0 | if (excludeExternal && |
390 | 0 | (relative.find("..") != std::string::npos)) { |
391 | 0 | continue; |
392 | 0 | } |
393 | | |
394 | 0 | if (isCFile) { |
395 | 0 | cFiles.push_back(fullPath); |
396 | 0 | } |
397 | |
|
398 | 0 | CbpUnit& cbpUnit = allFiles[fullPath]; |
399 | 0 | cbpUnit.Targets.push_back(target.get()); |
400 | 0 | } |
401 | 0 | } break; |
402 | 0 | default: |
403 | 0 | break; |
404 | 0 | } |
405 | 0 | } |
406 | 0 | } |
407 | | |
408 | 0 | std::vector<std::string> const& headerExts = |
409 | 0 | this->GlobalGenerator->GetCMakeInstance()->GetHeaderExtensions(); |
410 | | |
411 | | // The following loop tries to add header files matching to implementation |
412 | | // files to the project. It does that by iterating over all |
413 | | // C/C++ source files, |
414 | | // replacing the file name extension with ".h" and checks whether such a |
415 | | // file exists. If it does, it is inserted into the map of files. |
416 | | // A very similar version of that code exists also in the CodeLite |
417 | | // project generator. |
418 | 0 | for (std::string const& fileName : cFiles) { |
419 | 0 | std::string headerBasename = |
420 | 0 | cmStrCat(cmSystemTools::GetFilenamePath(fileName), '/', |
421 | 0 | cmSystemTools::GetFilenameWithoutExtension(fileName)); |
422 | | |
423 | | // check if there's a matching header around |
424 | 0 | for (std::string const& ext : headerExts) { |
425 | 0 | std::string hname = cmStrCat(headerBasename, '.', ext); |
426 | | // if it's already in the set, don't check if it exists on disk |
427 | 0 | if (allFiles.find(hname) != allFiles.end()) { |
428 | 0 | break; |
429 | 0 | } |
430 | | |
431 | 0 | if (cmSystemTools::FileExists(hname)) { |
432 | 0 | allFiles[hname].Targets = allFiles[fileName].Targets; |
433 | 0 | break; |
434 | 0 | } |
435 | 0 | } |
436 | 0 | } |
437 | | |
438 | | // insert all source files in the CodeBlocks project |
439 | 0 | for (auto const& s : allFiles) { |
440 | 0 | std::string const& unitFilename = s.first; |
441 | 0 | CbpUnit const& unit = s.second; |
442 | |
|
443 | 0 | xml.StartElement("Unit"); |
444 | 0 | xml.Attribute("filename", unitFilename); |
445 | |
|
446 | 0 | for (cmGeneratorTarget const* tgt : unit.Targets) { |
447 | 0 | xml.StartElement("Option"); |
448 | 0 | xml.Attribute("target", tgt->GetName()); |
449 | 0 | xml.EndElement(); |
450 | 0 | } |
451 | |
|
452 | 0 | xml.EndElement(); |
453 | 0 | } |
454 | | |
455 | | // Add CMakeLists.txt |
456 | 0 | tree.BuildUnit(xml, mf->GetHomeDirectory() + '/'); |
457 | |
|
458 | 0 | xml.EndElement(); // Project |
459 | 0 | xml.EndElement(); // CodeBlocks_project_file |
460 | 0 | xml.EndDocument(); |
461 | 0 | } |
462 | | |
463 | | // Write a dummy file for OBJECT libraries, so C::B can reference some file |
464 | | std::string cmExtraCodeBlocksGenerator::CreateDummyTargetFile( |
465 | | cmGeneratorTarget* target) const |
466 | 0 | { |
467 | | // this file doesn't seem to be used by C::B in custom makefile mode, |
468 | | // but we generate a unique file for each OBJECT library so in case |
469 | | // C::B uses it in some way, the targets don't interfere with each other. |
470 | 0 | std::string filename = |
471 | 0 | cmStrCat(target->GetSupportDirectory(), '/', target->GetName(), ".objlib"); |
472 | 0 | cmGeneratedFileStream fout(filename); |
473 | 0 | if (fout) { |
474 | | /* clang-format off */ |
475 | 0 | fout << "# This is a dummy file for the OBJECT library " |
476 | 0 | << target->GetName() |
477 | 0 | << " for the CMake CodeBlocks project generator.\n" |
478 | 0 | "# Don't edit, this file will be overwritten.\n"; |
479 | | /* clang-format on */ |
480 | 0 | } |
481 | 0 | return filename; |
482 | 0 | } |
483 | | |
484 | | // Generate the xml code for one target. |
485 | | void cmExtraCodeBlocksGenerator::AppendTarget( |
486 | | cmXMLWriter& xml, std::string const& targetName, cmGeneratorTarget* target, |
487 | | std::string const& make, cmLocalGenerator const* lg, |
488 | | std::string const& compiler, std::string const& makeFlags) |
489 | 0 | { |
490 | 0 | cmMakefile const* makefile = lg->GetMakefile(); |
491 | 0 | std::string makefileName = |
492 | 0 | cmStrCat(lg->GetCurrentBinaryDirectory(), "/Makefile"); |
493 | |
|
494 | 0 | xml.StartElement("Target"); |
495 | 0 | xml.Attribute("title", targetName); |
496 | |
|
497 | 0 | if (target) { |
498 | 0 | int cbTargetType = this->GetCBTargetType(target); |
499 | 0 | std::string workingDir = lg->GetCurrentBinaryDirectory(); |
500 | 0 | if (target->GetType() == cmStateEnums::EXECUTABLE) { |
501 | | // Determine the directory where the executable target is created, and |
502 | | // set the working directory to this dir. |
503 | 0 | cmValue runtimeOutputDir = |
504 | 0 | makefile->GetDefinition("CMAKE_RUNTIME_OUTPUT_DIRECTORY"); |
505 | 0 | if (runtimeOutputDir) { |
506 | 0 | workingDir = *runtimeOutputDir; |
507 | 0 | } else { |
508 | 0 | cmValue executableOutputDir = |
509 | 0 | makefile->GetDefinition("EXECUTABLE_OUTPUT_PATH"); |
510 | 0 | if (executableOutputDir) { |
511 | 0 | workingDir = *executableOutputDir; |
512 | 0 | } |
513 | 0 | } |
514 | 0 | } |
515 | |
|
516 | 0 | std::string buildType = makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"); |
517 | 0 | std::string location; |
518 | 0 | if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) { |
519 | 0 | location = this->CreateDummyTargetFile(target); |
520 | 0 | } else { |
521 | 0 | location = target->GetLocation(buildType); |
522 | 0 | } |
523 | |
|
524 | 0 | xml.StartElement("Option"); |
525 | 0 | xml.Attribute("output", location); |
526 | 0 | xml.Attribute("prefix_auto", 0); |
527 | 0 | xml.Attribute("extension_auto", 0); |
528 | 0 | xml.EndElement(); |
529 | |
|
530 | 0 | xml.StartElement("Option"); |
531 | 0 | xml.Attribute("working_dir", workingDir); |
532 | 0 | xml.EndElement(); |
533 | |
|
534 | 0 | xml.StartElement("Option"); |
535 | 0 | xml.Attribute("object_output", "./"); |
536 | 0 | xml.EndElement(); |
537 | |
|
538 | 0 | xml.StartElement("Option"); |
539 | 0 | xml.Attribute("type", cbTargetType); |
540 | 0 | xml.EndElement(); |
541 | |
|
542 | 0 | xml.StartElement("Option"); |
543 | 0 | xml.Attribute("compiler", compiler); |
544 | 0 | xml.EndElement(); |
545 | |
|
546 | 0 | xml.StartElement("Compiler"); |
547 | | |
548 | | // the compilerdefines for this target |
549 | 0 | std::vector<std::string> cdefs; |
550 | 0 | target->GetCompileDefinitions(cdefs, buildType, "C"); |
551 | | |
552 | | // Expand the list. |
553 | 0 | for (std::string const& d : cdefs) { |
554 | 0 | xml.StartElement("Add"); |
555 | 0 | xml.Attribute("option", "-D" + d); |
556 | 0 | xml.EndElement(); |
557 | 0 | } |
558 | | |
559 | | // the include directories for this target |
560 | 0 | std::vector<std::string> allIncludeDirs; |
561 | 0 | { |
562 | 0 | std::vector<std::string> includes; |
563 | 0 | lg->GetIncludeDirectories(includes, target, "C", buildType); |
564 | 0 | cm::append(allIncludeDirs, includes); |
565 | 0 | } |
566 | |
|
567 | 0 | std::string systemIncludeDirs = makefile->GetSafeDefinition( |
568 | 0 | "CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_INCLUDE_DIRS"); |
569 | 0 | if (!systemIncludeDirs.empty()) { |
570 | 0 | cm::append(allIncludeDirs, cmList{ systemIncludeDirs }); |
571 | 0 | } |
572 | |
|
573 | 0 | systemIncludeDirs = makefile->GetSafeDefinition( |
574 | 0 | "CMAKE_EXTRA_GENERATOR_C_SYSTEM_INCLUDE_DIRS"); |
575 | 0 | if (!systemIncludeDirs.empty()) { |
576 | 0 | cm::append(allIncludeDirs, cmList{ systemIncludeDirs }); |
577 | 0 | } |
578 | |
|
579 | 0 | auto end = cmRemoveDuplicates(allIncludeDirs); |
580 | |
|
581 | 0 | for (std::string const& str : cmMakeRange(allIncludeDirs.cbegin(), end)) { |
582 | 0 | xml.StartElement("Add"); |
583 | 0 | xml.Attribute("directory", str); |
584 | 0 | xml.EndElement(); |
585 | 0 | } |
586 | |
|
587 | 0 | xml.EndElement(); // Compiler |
588 | 0 | } else // e.g. all and the GLOBAL and UTILITY targets |
589 | 0 | { |
590 | 0 | xml.StartElement("Option"); |
591 | 0 | xml.Attribute("working_dir", lg->GetCurrentBinaryDirectory()); |
592 | 0 | xml.EndElement(); |
593 | |
|
594 | 0 | xml.StartElement("Option"); |
595 | 0 | xml.Attribute("type", 4); |
596 | 0 | xml.EndElement(); |
597 | 0 | } |
598 | |
|
599 | 0 | xml.StartElement("MakeCommands"); |
600 | |
|
601 | 0 | xml.StartElement("Build"); |
602 | 0 | xml.Attribute( |
603 | 0 | "command", |
604 | 0 | this->BuildMakeCommand(make, makefileName, targetName, makeFlags)); |
605 | 0 | xml.EndElement(); |
606 | |
|
607 | 0 | xml.StartElement("CompileFile"); |
608 | 0 | xml.Attribute( |
609 | 0 | "command", |
610 | 0 | this->BuildMakeCommand(make, makefileName, "\"$file\"", makeFlags)); |
611 | 0 | xml.EndElement(); |
612 | |
|
613 | 0 | xml.StartElement("Clean"); |
614 | 0 | xml.Attribute( |
615 | 0 | "command", this->BuildMakeCommand(make, makefileName, "clean", makeFlags)); |
616 | 0 | xml.EndElement(); |
617 | |
|
618 | 0 | xml.StartElement("DistClean"); |
619 | 0 | xml.Attribute( |
620 | 0 | "command", this->BuildMakeCommand(make, makefileName, "clean", makeFlags)); |
621 | 0 | xml.EndElement(); |
622 | |
|
623 | 0 | xml.EndElement(); // MakeCommands |
624 | 0 | xml.EndElement(); // Target |
625 | 0 | } |
626 | | |
627 | | // Translate the cmake compiler id into the CodeBlocks compiler id |
628 | | std::string cmExtraCodeBlocksGenerator::GetCBCompilerId(cmMakefile const* mf) |
629 | 0 | { |
630 | | // allow the user to overwrite the detected compiler |
631 | 0 | std::string userCompiler = |
632 | 0 | mf->GetSafeDefinition("CMAKE_CODEBLOCKS_COMPILER_ID"); |
633 | 0 | if (!userCompiler.empty()) { |
634 | 0 | return userCompiler; |
635 | 0 | } |
636 | | |
637 | | // figure out which language to use |
638 | | // for now care only for C, C++, and Fortran |
639 | | |
640 | | // projects with C/C++ and Fortran are handled as C/C++ projects |
641 | 0 | bool pureFortran = false; |
642 | 0 | std::string compilerIdVar; |
643 | 0 | if (this->GlobalGenerator->GetLanguageEnabled("CXX")) { |
644 | 0 | compilerIdVar = "CMAKE_CXX_COMPILER_ID"; |
645 | 0 | } else if (this->GlobalGenerator->GetLanguageEnabled("C")) { |
646 | 0 | compilerIdVar = "CMAKE_C_COMPILER_ID"; |
647 | 0 | } else if (this->GlobalGenerator->GetLanguageEnabled("Fortran")) { |
648 | 0 | compilerIdVar = "CMAKE_Fortran_COMPILER_ID"; |
649 | 0 | pureFortran = true; |
650 | 0 | } |
651 | |
|
652 | 0 | std::string const& compilerId = mf->GetSafeDefinition(compilerIdVar); |
653 | 0 | std::string compiler = "gcc"; // default to gcc |
654 | 0 | if (compilerId == "MSVC") { |
655 | 0 | if (mf->IsDefinitionSet("MSVC10")) { |
656 | 0 | compiler = "msvc10"; |
657 | 0 | } else { |
658 | 0 | compiler = "msvc8"; |
659 | 0 | } |
660 | 0 | } else if (compilerId == "Borland") { |
661 | 0 | compiler = "bcc"; |
662 | 0 | } else if (compilerId == "SDCC") { |
663 | 0 | compiler = "sdcc"; |
664 | 0 | } else if (compilerId == "Intel") { |
665 | 0 | if (pureFortran && mf->IsDefinitionSet("WIN32")) { |
666 | 0 | compiler = "ifcwin"; // Intel Fortran for Windows (known by cbFortran) |
667 | 0 | } else { |
668 | 0 | compiler = "icc"; |
669 | 0 | } |
670 | 0 | } else if (compilerId == "Watcom" || compilerId == "OpenWatcom") { |
671 | 0 | compiler = "ow"; |
672 | 0 | } else if (compilerId == "Clang") { |
673 | 0 | compiler = "clang"; |
674 | 0 | } else if (compilerId == "PGI") { |
675 | 0 | if (pureFortran) { |
676 | 0 | compiler = "pgifortran"; |
677 | 0 | } else { |
678 | 0 | compiler = "pgi"; // does not exist as default in CodeBlocks 16.01 |
679 | 0 | } |
680 | 0 | } else if (compilerId == "LCC") { |
681 | 0 | if (pureFortran) { |
682 | 0 | compiler = "lfortran"; |
683 | 0 | } else { |
684 | 0 | compiler = "lcc"; |
685 | 0 | } |
686 | 0 | } else if (compilerId == "GNU") { |
687 | 0 | if (pureFortran) { |
688 | 0 | compiler = "gfortran"; |
689 | 0 | } else { |
690 | 0 | compiler = "gcc"; |
691 | 0 | } |
692 | 0 | } |
693 | 0 | return compiler; |
694 | 0 | } |
695 | | |
696 | | // Translate the cmake target type into the CodeBlocks target type id |
697 | | int cmExtraCodeBlocksGenerator::GetCBTargetType(cmGeneratorTarget* target) |
698 | 0 | { |
699 | 0 | switch (target->GetType()) { |
700 | 0 | case cmStateEnums::EXECUTABLE: |
701 | 0 | if ((target->IsWin32Executable( |
702 | 0 | target->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"))) || |
703 | 0 | (target->GetPropertyAsBool("MACOSX_BUNDLE"))) { |
704 | 0 | return 0; |
705 | 0 | } |
706 | 0 | return 1; |
707 | 0 | case cmStateEnums::STATIC_LIBRARY: |
708 | 0 | case cmStateEnums::OBJECT_LIBRARY: |
709 | 0 | return 2; |
710 | 0 | case cmStateEnums::SHARED_LIBRARY: |
711 | 0 | case cmStateEnums::MODULE_LIBRARY: |
712 | 0 | return 3; |
713 | 0 | default: |
714 | 0 | return 4; |
715 | 0 | } |
716 | 0 | } |
717 | | |
718 | | // Create the command line for building the given target using the selected |
719 | | // make |
720 | | std::string cmExtraCodeBlocksGenerator::BuildMakeCommand( |
721 | | std::string const& make, std::string const& makefile, |
722 | | std::string const& target, std::string const& makeFlags) |
723 | 0 | { |
724 | 0 | std::string command = make; |
725 | 0 | if (!makeFlags.empty()) { |
726 | 0 | command += " "; |
727 | 0 | command += makeFlags; |
728 | 0 | } |
729 | |
|
730 | 0 | std::string generator = this->GlobalGenerator->GetName(); |
731 | 0 | if (generator == "NMake Makefiles" || generator == "NMake Makefiles JOM") { |
732 | | // For Windows ConvertToOutputPath already adds quotes when required. |
733 | | // These need to be escaped, see |
734 | | // https://gitlab.kitware.com/cmake/cmake/-/issues/13952 |
735 | 0 | std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile); |
736 | 0 | command += " /NOLOGO /f "; |
737 | 0 | command += makefileName; |
738 | 0 | command += " VERBOSE=1 "; |
739 | 0 | command += target; |
740 | 0 | } else if (generator == "MinGW Makefiles") { |
741 | | // no escaping of spaces in this case, see |
742 | | // https://gitlab.kitware.com/cmake/cmake/-/issues/10014 |
743 | 0 | std::string const& makefileName = makefile; |
744 | 0 | command += " -f \""; |
745 | 0 | command += makefileName; |
746 | 0 | command += "\" "; |
747 | 0 | command += " VERBOSE=1 "; |
748 | 0 | command += target; |
749 | 0 | } else if (generator == "Ninja") { |
750 | 0 | command += " -v "; |
751 | 0 | command += target; |
752 | 0 | } else { |
753 | 0 | std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile); |
754 | 0 | command += " -f \""; |
755 | 0 | command += makefileName; |
756 | 0 | command += "\" "; |
757 | 0 | command += " VERBOSE=1 "; |
758 | 0 | command += target; |
759 | 0 | } |
760 | 0 | return command; |
761 | 0 | } |