Rescan dependencies also if CMakeDirectoryInformation.cmake has changed.
[cmake.git] / Source / cmExtraCodeBlocksGenerator.cxx
blob374225e2eeba5a2e2ce44b150e5533082ee3432a
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmExtraCodeBlocksGenerator.cxx,v $
5 Language: C++
6 Date: $Date: 2009-09-10 17:44:02 $
7 Version: $Revision: 1.28 $
9 Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
10 Copyright (c) 2004 Alexander Neundorf neundorf@kde.org, All rights reserved.
11 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
13 This software is distributed WITHOUT ANY WARRANTY; without even
14 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 PURPOSE. See the above copyright notices for more information.
17 =========================================================================*/
19 #include "cmExtraCodeBlocksGenerator.h"
20 #include "cmGlobalUnixMakefileGenerator3.h"
21 #include "cmLocalUnixMakefileGenerator3.h"
22 #include "cmMakefile.h"
23 #include "cmake.h"
24 #include "cmSourceFile.h"
25 #include "cmGeneratedFileStream.h"
26 #include "cmTarget.h"
27 #include "cmSystemTools.h"
29 #include <cmsys/SystemTools.hxx>
31 /* Some useful URLs:
32 Homepage:
33 http://www.codeblocks.org
35 File format docs:
36 http://wiki.codeblocks.org/index.php?title=File_formats_description
37 http://wiki.codeblocks.org/index.php?title=Workspace_file
38 http://wiki.codeblocks.org/index.php?title=Project_file
40 Discussion:
41 http://forums.codeblocks.org/index.php/topic,6789.0.html
44 //----------------------------------------------------------------------------
45 void cmExtraCodeBlocksGenerator
46 ::GetDocumentation(cmDocumentationEntry& entry, const char*) const
48 entry.Name = this->GetName();
49 entry.Brief = "Generates CodeBlocks project files.";
50 entry.Full =
51 "Project files for CodeBlocks will be created in the top directory "
52 "and in every subdirectory which features a CMakeLists.txt file "
53 "containing a PROJECT() call. "
54 "Additionally a hierarchy of makefiles is generated into the "
55 "build tree. The appropriate make program can build the project through "
56 "the default make target. A \"make install\" target is also provided.";
59 cmExtraCodeBlocksGenerator::cmExtraCodeBlocksGenerator()
60 :cmExternalMakefileProjectGenerator()
62 #if defined(_WIN32)
63 this->SupportedGlobalGenerators.push_back("MinGW Makefiles");
64 this->SupportedGlobalGenerators.push_back("NMake Makefiles");
65 // disable until somebody actually tests it:
66 // this->SupportedGlobalGenerators.push_back("MSYS Makefiles");
67 #endif
68 this->SupportedGlobalGenerators.push_back("Unix Makefiles");
72 void cmExtraCodeBlocksGenerator::SetGlobalGenerator(
73 cmGlobalGenerator* generator)
75 cmExternalMakefileProjectGenerator::SetGlobalGenerator(generator);
76 cmGlobalUnixMakefileGenerator3* mf = (cmGlobalUnixMakefileGenerator3*)
77 generator;
78 mf->SetToolSupportsColor(false);
79 mf->SetForceVerboseMakefiles(true);
82 void cmExtraCodeBlocksGenerator::Generate()
84 // for each sub project in the project create a codeblocks project
85 for (std::map<cmStdString, std::vector<cmLocalGenerator*> >::const_iterator
86 it = this->GlobalGenerator->GetProjectMap().begin();
87 it!= this->GlobalGenerator->GetProjectMap().end();
88 ++it)
90 // create a project file
91 this->CreateProjectFile(it->second);
96 /* create the project file */
97 void cmExtraCodeBlocksGenerator::CreateProjectFile(
98 const std::vector<cmLocalGenerator*>& lgs)
100 const cmMakefile* mf=lgs[0]->GetMakefile();
101 std::string outputDir=mf->GetStartOutputDirectory();
102 std::string projectName=mf->GetProjectName();
104 std::string filename=outputDir+"/";
105 filename+=projectName+".cbp";
106 std::string sessionFilename=outputDir+"/";
107 sessionFilename+=projectName+".layout";
109 this->CreateNewProjectFile(lgs, filename);
113 /* Tree is used to create a "Virtual Folder" in CodeBlocks, in which all
114 CMake files this project depends on will be put. This means additionally
115 to the "Sources" and "Headers" virtual folders of CodeBlocks, there will
116 now also be a "CMake Files" virtual folder.
117 Patch by Daniel Teske <daniel.teske AT nokia.com> (which use C::B project
118 files in QtCreator).*/
119 struct Tree
121 std::string path; //only one component of the path
122 std::vector<Tree> folders;
123 std::vector<std::string> files;
124 void InsertPath(const std::vector<std::string>& splitted,
125 std::vector<std::string>::size_type start,
126 const std::string& fileName);
127 void BuildVirtualFolder(std::string& virtualFolders) const;
128 void BuildVirtualFolderImpl(std::string& virtualFolders,
129 const std::string& prefix) const;
130 void BuildUnit(std::string& unitString, const std::string& fsPath) const;
131 void BuildUnitImpl(std::string& unitString,
132 const std::string& virtualFolderPath,
133 const std::string& fsPath) const;
137 void Tree::InsertPath(const std::vector<std::string>& splitted,
138 std::vector<std::string>::size_type start,
139 const std::string& fileName)
141 if (start == splitted.size())
143 files.push_back(fileName);
144 return;
146 for (std::vector<Tree>::iterator
147 it = folders.begin();
148 it != folders.end();
149 ++it)
151 if ((*it).path == splitted[start])
153 if (start + 1 < splitted.size())
155 it->InsertPath(splitted, start + 1, fileName);
156 return;
158 else
160 // last part of splitted
161 it->files.push_back(fileName);
162 return;
166 // Not found in folders, thus insert
167 Tree newFolder;
168 newFolder.path = splitted[start];
169 if (start + 1 < splitted.size())
171 newFolder.InsertPath(splitted, start + 1, fileName);
172 folders.push_back(newFolder);
173 return;
175 else
177 // last part of splitted
178 newFolder.files.push_back(fileName);
179 folders.push_back(newFolder);
180 return;
185 void Tree::BuildVirtualFolder(std::string& virtualFolders) const
187 virtualFolders += "<Option virtualFolders=\"CMake Files\\;";
188 for (std::vector<Tree>::const_iterator it = folders.begin();
189 it != folders.end();
190 ++it)
192 it->BuildVirtualFolderImpl(virtualFolders, "");
194 virtualFolders += "\" />";
198 void Tree::BuildVirtualFolderImpl(std::string& virtualFolders,
199 const std::string& prefix) const
201 virtualFolders += "CMake Files\\" + prefix + path + "\\;";
202 for (std::vector<Tree>::const_iterator it = folders.begin();
203 it != folders.end();
204 ++it)
206 it->BuildVirtualFolderImpl(virtualFolders, prefix + path + "\\");
211 void Tree::BuildUnit(std::string& unitString, const std::string& fsPath) const
213 for (std::vector<std::string>::const_iterator it = files.begin();
214 it != files.end();
215 ++it)
217 unitString += " <Unit filename=\"" + fsPath + *it + "\">\n";
218 unitString += " <Option virtualFolder=\"CMake Files\\\" />\n";
219 unitString += " </Unit>\n";
221 for (std::vector<Tree>::const_iterator it = folders.begin();
222 it != folders.end();
223 ++it)
225 it->BuildUnitImpl(unitString, "", fsPath);
230 void Tree::BuildUnitImpl(std::string& unitString,
231 const std::string& virtualFolderPath,
232 const std::string& fsPath) const
234 for (std::vector<std::string>::const_iterator it = files.begin();
235 it != files.end();
236 ++it)
238 unitString += " <Unit filename=\"" +fsPath+path+ "/" + *it + "\">\n";
239 unitString += " <Option virtualFolder=\"CMake Files\\"
240 + virtualFolderPath + path + "\\\" />\n";
241 unitString += " </Unit>\n";
243 for (std::vector<Tree>::const_iterator it = folders.begin();
244 it != folders.end();
245 ++it)
247 it->BuildUnitImpl(unitString,
248 virtualFolderPath + path + "\\", fsPath + path + "/");
253 void cmExtraCodeBlocksGenerator
254 ::CreateNewProjectFile(const std::vector<cmLocalGenerator*>& lgs,
255 const std::string& filename)
257 const cmMakefile* mf=lgs[0]->GetMakefile();
258 cmGeneratedFileStream fout(filename.c_str());
259 if(!fout)
261 return;
264 Tree tree;
266 // build tree of virtual folders
267 for (std::map<cmStdString, std::vector<cmLocalGenerator*> >::const_iterator
268 it = this->GlobalGenerator->GetProjectMap().begin();
269 it != this->GlobalGenerator->GetProjectMap().end();
270 ++it)
272 // Convert
273 std::vector<std::string> listFiles =
274 it->second[0]->GetMakefile()->GetListFiles();
276 for (std::vector<std::string>::const_iterator jt = listFiles.begin();
277 jt != listFiles.end();
278 ++jt)
280 const std::string &relative =
281 cmSystemTools::RelativePath(
282 it->second[0]->GetMakefile()->GetHomeDirectory(),
283 jt->c_str());
284 std::vector<std::string> splitted;
285 cmSystemTools::SplitPath(relative.c_str(), splitted, false);
286 // Split filename from path
287 std::string fileName = *(splitted.end()-1);
288 splitted.erase(splitted.end() - 1, splitted.end());
290 // We don't want paths with ".." in them
291 // reasons are that we don't want files outside the project
292 // TODO: the path should be normalized first though
293 // We don't want paths with CMakeFiles in them
294 // or do we?
295 // In speedcrunch those where purely internal
296 if (splitted.size() >= 1
297 && relative.find("..") == std::string::npos
298 && relative.find("CMakeFiles") == std::string::npos)
300 tree.InsertPath(splitted, 1, fileName);
305 // Now build a virtual tree string
306 std::string virtualFolders;
307 tree.BuildVirtualFolder(virtualFolders);
308 // And one for <Unit>
309 std::string unitFiles;
310 tree.BuildUnit(unitFiles, std::string(mf->GetHomeDirectory()) + "/");
312 // figure out the compiler
313 std::string compiler = this->GetCBCompilerId(mf);
314 std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
316 fout<<"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>\n"
317 "<CodeBlocks_project_file>\n"
318 " <FileVersion major=\"1\" minor=\"6\" />\n"
319 " <Project>\n"
320 " <Option title=\"" << mf->GetProjectName()<<"\" />\n"
321 " <Option makefile_is_custom=\"1\" />\n"
322 " <Option compiler=\"" << compiler << "\" />\n"
323 " "<<virtualFolders<<"\n"
324 " <Build>\n";
326 this->AppendTarget(fout, "all", 0, make.c_str(), mf, compiler.c_str());
328 // add all executable and library targets and some of the GLOBAL
329 // and UTILITY targets
330 for (std::vector<cmLocalGenerator*>::const_iterator lg=lgs.begin();
331 lg!=lgs.end(); lg++)
333 cmMakefile* makefile=(*lg)->GetMakefile();
334 cmTargets& targets=makefile->GetTargets();
335 for (cmTargets::iterator ti = targets.begin();
336 ti != targets.end(); ti++)
338 switch(ti->second.GetType())
340 case cmTarget::GLOBAL_TARGET:
342 bool insertTarget = false;
343 // Only add the global targets from CMAKE_BINARY_DIR,
344 // not from the subdirs
345 if (strcmp(makefile->GetStartOutputDirectory(),
346 makefile->GetHomeOutputDirectory())==0)
348 insertTarget = true;
349 // only add the "edit_cache" target if it's not ccmake, because
350 // this will not work within the IDE
351 if (ti->first == "edit_cache")
353 if (strstr(makefile->GetRequiredDefinition
354 ("CMAKE_EDIT_COMMAND"), "ccmake")!=NULL)
356 insertTarget = false;
360 if (insertTarget)
362 this->AppendTarget(fout, ti->first.c_str(), 0,
363 make.c_str(), makefile, compiler.c_str());
366 break;
367 case cmTarget::UTILITY:
368 // Add all utility targets, except the Nightly/Continuous/
369 // Experimental-"sub"targets as e.g. NightlyStart
370 if (((ti->first.find("Nightly")==0) &&(ti->first!="Nightly"))
371 || ((ti->first.find("Continuous")==0)&&(ti->first!="Continuous"))
372 || ((ti->first.find("Experimental")==0)
373 && (ti->first!="Experimental")))
375 break;
378 this->AppendTarget(fout, ti->first.c_str(), 0,
379 make.c_str(), makefile, compiler.c_str());
380 break;
381 case cmTarget::EXECUTABLE:
382 case cmTarget::STATIC_LIBRARY:
383 case cmTarget::SHARED_LIBRARY:
384 case cmTarget::MODULE_LIBRARY:
386 this->AppendTarget(fout, ti->first.c_str(), &ti->second,
387 make.c_str(), makefile, compiler.c_str());
388 std::string fastTarget = ti->first;
389 fastTarget += "/fast";
390 this->AppendTarget(fout, fastTarget.c_str(), &ti->second,
391 make.c_str(), makefile, compiler.c_str());
393 break;
394 // ignore these:
395 case cmTarget::INSTALL_FILES:
396 case cmTarget::INSTALL_PROGRAMS:
397 case cmTarget::INSTALL_DIRECTORY:
398 default:
399 break;
404 fout<<" </Build>\n";
407 // Collect all used source files in the project
408 // Sort them into two containers, one for C/C++ implementation files
409 // which may have an acompanying header, one for all other files
410 std::map<std::string, cmSourceFile*> cFiles;
411 std::set<std::string> otherFiles;
412 for (std::vector<cmLocalGenerator*>::const_iterator lg=lgs.begin();
413 lg!=lgs.end(); lg++)
415 cmMakefile* makefile=(*lg)->GetMakefile();
416 cmTargets& targets=makefile->GetTargets();
417 for (cmTargets::iterator ti = targets.begin();
418 ti != targets.end(); ti++)
420 switch(ti->second.GetType())
422 case cmTarget::EXECUTABLE:
423 case cmTarget::STATIC_LIBRARY:
424 case cmTarget::SHARED_LIBRARY:
425 case cmTarget::MODULE_LIBRARY:
427 const std::vector<cmSourceFile*>&sources=ti->second.GetSourceFiles();
428 for (std::vector<cmSourceFile*>::const_iterator si=sources.begin();
429 si!=sources.end(); si++)
431 // check whether it is a C/C++ implementation file
432 bool isCFile = false;
433 if ((*si)->GetLanguage() && (*(*si)->GetLanguage() == 'C'))
435 for(std::vector<std::string>::const_iterator
436 ext = mf->GetSourceExtensions().begin();
437 ext != mf->GetSourceExtensions().end();
438 ++ext)
440 if ((*si)->GetExtension() == *ext)
442 isCFile = true;
443 break;
448 // then put it accordingly into one of the two containers
449 if (isCFile)
451 cFiles[(*si)->GetFullPath()] = *si ;
453 else
455 otherFiles.insert((*si)->GetFullPath());
459 default: // intended fallthrough
460 break;
465 // The following loop tries to add header files matching to implementation
466 // files to the project. It does that by iterating over all source files,
467 // replacing the file name extension with ".h" and checks whether such a
468 // file exists. If it does, it is inserted into the map of files.
469 // A very similar version of that code exists also in the kdevelop
470 // project generator.
471 for (std::map<std::string, cmSourceFile*>::const_iterator
472 sit=cFiles.begin();
473 sit!=cFiles.end();
474 ++sit)
476 std::string headerBasename=cmSystemTools::GetFilenamePath(sit->first);
477 headerBasename+="/";
478 headerBasename+=cmSystemTools::GetFilenameWithoutExtension(sit->first);
480 // check if there's a matching header around
481 for(std::vector<std::string>::const_iterator
482 ext = mf->GetHeaderExtensions().begin();
483 ext != mf->GetHeaderExtensions().end();
484 ++ext)
486 std::string hname=headerBasename;
487 hname += ".";
488 hname += *ext;
489 // if it's already in the set, don't check if it exists on disk
490 std::set<std::string>::const_iterator headerIt=otherFiles.find(hname);
491 if (headerIt != otherFiles.end())
493 break;
496 if(cmSystemTools::FileExists(hname.c_str()))
498 otherFiles.insert(hname);
499 break;
504 // insert all source files in the CodeBlocks project
505 // first the C/C++ implementation files, then all others
506 for (std::map<std::string, cmSourceFile*>::const_iterator
507 sit=cFiles.begin();
508 sit!=cFiles.end();
509 ++sit)
511 fout<<" <Unit filename=\""<< sit->first <<"\">\n"
512 " </Unit>\n";
514 for (std::set<std::string>::const_iterator
515 sit=otherFiles.begin();
516 sit!=otherFiles.end();
517 ++sit)
519 fout<<" <Unit filename=\""<< sit->c_str() <<"\">\n"
520 " </Unit>\n";
523 // Add CMakeLists.txt
524 fout<<unitFiles;
526 fout<<" </Project>\n"
527 "</CodeBlocks_project_file>\n";
531 // Generate the xml code for one target.
532 void cmExtraCodeBlocksGenerator::AppendTarget(cmGeneratedFileStream& fout,
533 const char* targetName,
534 cmTarget* target,
535 const char* make,
536 const cmMakefile* makefile,
537 const char* compiler)
539 std::string makefileName = makefile->GetStartOutputDirectory();
540 makefileName += "/Makefile";
541 makefileName = cmSystemTools::ConvertToOutputPath(makefileName.c_str());
543 fout<<" <Target title=\"" << targetName << "\">\n";
544 if (target!=0)
546 int cbTargetType = this->GetCBTargetType(target);
547 const char* buildType = makefile->GetDefinition("CMAKE_BUILD_TYPE");
548 fout<<" <Option output=\"" << target->GetLocation(buildType)
549 << "\" prefix_auto=\"0\" extension_auto=\"0\" />\n"
550 " <Option working_dir=\""
551 << makefile->GetStartOutputDirectory() << "\" />\n"
552 " <Option object_output=\"./\" />\n"
553 " <Option type=\"" << cbTargetType << "\" />\n"
554 " <Option compiler=\"" << compiler << "\" />\n"
555 " <Compiler>\n";
556 // the include directories for this target
557 const std::vector<std::string>& incDirs =
558 target->GetMakefile()->GetIncludeDirectories();
559 for(std::vector<std::string>::const_iterator dirIt=incDirs.begin();
560 dirIt != incDirs.end();
561 ++dirIt)
563 fout <<" <Add directory=\"" << dirIt->c_str() << "\" />\n";
565 fout<<" </Compiler>\n";
567 else // e.g. all and the GLOBAL and UTILITY targets
569 fout<<" <Option working_dir=\""
570 << makefile->GetStartOutputDirectory() << "\" />\n"
571 <<" <Option type=\"" << 4 << "\" />\n";
574 fout<<" <MakeCommands>\n"
575 " <Build command=\""
576 << this->BuildMakeCommand(make, makefileName.c_str(), targetName)
577 << "\" />\n"
578 " <CompileFile command=\""
579 << this->BuildMakeCommand(make, makefileName.c_str(),"&quot;$file&quot;")
580 << "\" />\n"
581 " <Clean command=\""
582 << this->BuildMakeCommand(make, makefileName.c_str(), "clean")
583 << "\" />\n"
584 " <DistClean command=\""
585 << this->BuildMakeCommand(make, makefileName.c_str(), "clean")
586 << "\" />\n"
587 " </MakeCommands>\n"
588 " </Target>\n";
593 // Translate the cmake compiler id into the CodeBlocks compiler id
594 std::string cmExtraCodeBlocksGenerator::GetCBCompilerId(const cmMakefile* mf)
596 // figure out which language to use
597 // for now care only for C and C++
598 std::string compilerIdVar = "CMAKE_CXX_COMPILER_ID";
599 if (this->GlobalGenerator->GetLanguageEnabled("CXX") == false)
601 compilerIdVar = "CMAKE_C_COMPILER_ID";
604 std::string hostSystemName = mf->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME");
605 std::string systemName = mf->GetSafeDefinition("CMAKE_SYSTEM_NAME");
606 std::string compilerId = mf->GetRequiredDefinition(compilerIdVar.c_str());
607 std::string compiler = "gcc";
608 if (compilerId == "MSVC")
610 compiler = "msvc8";
612 else if (compilerId == "Borland")
614 compiler = "bcc";
616 else if (compilerId == "SDCC")
618 compiler = "sdcc";
620 else if (compilerId == "Intel")
622 compiler = "icc";
624 else if (compilerId == "Watcom")
626 compiler = "ow";
628 else if (compilerId == "GNU")
630 compiler = "gcc";
632 return compiler;
636 // Translate the cmake target type into the CodeBlocks target type id
637 int cmExtraCodeBlocksGenerator::GetCBTargetType(cmTarget* target)
639 if ( target->GetType()==cmTarget::EXECUTABLE)
641 if ((target->GetPropertyAsBool("WIN32_EXECUTABLE"))
642 || (target->GetPropertyAsBool("MACOSX_BUNDLE")))
644 return 0;
646 else
648 return 1;
651 else if ( target->GetType()==cmTarget::STATIC_LIBRARY)
653 return 2;
655 else if ((target->GetType()==cmTarget::SHARED_LIBRARY)
656 || (target->GetType()==cmTarget::MODULE_LIBRARY))
658 return 3;
660 return 4;
663 // Create the command line for building the given target using the selected
664 // make
665 std::string cmExtraCodeBlocksGenerator::BuildMakeCommand(
666 const std::string& make, const char* makefile, const char* target)
668 std::string command = make;
669 if (strcmp(this->GlobalGenerator->GetName(), "NMake Makefiles")==0)
671 command += " /NOLOGO /f &quot;";
672 command += makefile;
673 command += "&quot; ";
674 command += target;
676 else if (strcmp(this->GlobalGenerator->GetName(), "MinGW Makefiles")==0)
678 command += " -f ";
679 command += makefile;
680 command += " ";
681 command += target;
683 else
685 command += " -f &quot;";
686 command += makefile;
687 command += "&quot; ";
688 command += target;
690 return command;