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