1 /*============================================================================
2 CMake - Cross Platform Makefile Generator
3 Copyright 2004-2009 Kitware, Inc.
4 Copyright 2004 Alexander Neundorf (neundorf@kde.org)
6 Distributed under the OSI-approved BSD License (the "License");
7 see accompanying file Copyright.txt for details.
9 This software is distributed WITHOUT ANY WARRANTY; without even the
10 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 See the License for more information.
12 ============================================================================*/
13 #include "cmExtraCodeBlocksGenerator.h"
14 #include "cmGlobalUnixMakefileGenerator3.h"
15 #include "cmLocalUnixMakefileGenerator3.h"
16 #include "cmMakefile.h"
18 #include "cmSourceFile.h"
19 #include "cmGeneratedFileStream.h"
21 #include "cmSystemTools.h"
22 #include "cmXMLSafe.h"
24 #include <cmsys/SystemTools.hxx>
28 http://www.codeblocks.org
31 http://wiki.codeblocks.org/index.php?title=File_formats_description
32 http://wiki.codeblocks.org/index.php?title=Workspace_file
33 http://wiki.codeblocks.org/index.php?title=Project_file
36 http://forums.codeblocks.org/index.php/topic,6789.0.html
39 //----------------------------------------------------------------------------
40 void cmExtraCodeBlocksGenerator
41 ::GetDocumentation(cmDocumentationEntry& entry, const char*) const
43 entry.Name = this->GetName();
44 entry.Brief = "Generates CodeBlocks project files.";
46 "Project files for CodeBlocks will be created in the top directory "
47 "and in every subdirectory which features a CMakeLists.txt file "
48 "containing a PROJECT() call. "
49 "Additionally a hierarchy of makefiles is generated into the "
50 "build tree. The appropriate make program can build the project through "
51 "the default make target. A \"make install\" target is also provided.";
54 cmExtraCodeBlocksGenerator::cmExtraCodeBlocksGenerator()
55 :cmExternalMakefileProjectGenerator()
58 this->SupportedGlobalGenerators.push_back("MinGW Makefiles");
59 this->SupportedGlobalGenerators.push_back("NMake Makefiles");
60 // disable until somebody actually tests it:
61 // this->SupportedGlobalGenerators.push_back("MSYS Makefiles");
63 this->SupportedGlobalGenerators.push_back("Ninja");
64 this->SupportedGlobalGenerators.push_back("Unix Makefiles");
68 void cmExtraCodeBlocksGenerator::Generate()
70 // for each sub project in the project create a codeblocks project
71 for (std::map<cmStdString, std::vector<cmLocalGenerator*> >::const_iterator
72 it = this->GlobalGenerator->GetProjectMap().begin();
73 it!= this->GlobalGenerator->GetProjectMap().end();
76 // create a project file
77 this->CreateProjectFile(it->second);
82 /* create the project file */
83 void cmExtraCodeBlocksGenerator::CreateProjectFile(
84 const std::vector<cmLocalGenerator*>& lgs)
86 const cmMakefile* mf=lgs[0]->GetMakefile();
87 std::string outputDir=mf->GetStartOutputDirectory();
88 std::string projectName=mf->GetProjectName();
90 std::string filename=outputDir+"/";
91 filename+=projectName+".cbp";
92 std::string sessionFilename=outputDir+"/";
93 sessionFilename+=projectName+".layout";
95 this->CreateNewProjectFile(lgs, filename);
99 /* Tree is used to create a "Virtual Folder" in CodeBlocks, in which all
100 CMake files this project depends on will be put. This means additionally
101 to the "Sources" and "Headers" virtual folders of CodeBlocks, there will
102 now also be a "CMake Files" virtual folder.
103 Patch by Daniel Teske <daniel.teske AT nokia.com> (which use C::B project
104 files in QtCreator).*/
107 std::string path; //only one component of the path
108 std::vector<Tree> folders;
109 std::vector<std::string> files;
110 void InsertPath(const std::vector<std::string>& splitted,
111 std::vector<std::string>::size_type start,
112 const std::string& fileName);
113 void BuildVirtualFolder(std::string& virtualFolders) const;
114 void BuildVirtualFolderImpl(std::string& virtualFolders,
115 const std::string& prefix) const;
116 void BuildUnit(std::string& unitString, const std::string& fsPath) const;
117 void BuildUnitImpl(std::string& unitString,
118 const std::string& virtualFolderPath,
119 const std::string& fsPath) const;
123 void Tree::InsertPath(const std::vector<std::string>& splitted,
124 std::vector<std::string>::size_type start,
125 const std::string& fileName)
127 if (start == splitted.size())
129 files.push_back(fileName);
132 for (std::vector<Tree>::iterator
133 it = folders.begin();
137 if ((*it).path == splitted[start])
139 if (start + 1 < splitted.size())
141 it->InsertPath(splitted, start + 1, fileName);
146 // last part of splitted
147 it->files.push_back(fileName);
152 // Not found in folders, thus insert
154 newFolder.path = splitted[start];
155 if (start + 1 < splitted.size())
157 newFolder.InsertPath(splitted, start + 1, fileName);
158 folders.push_back(newFolder);
163 // last part of splitted
164 newFolder.files.push_back(fileName);
165 folders.push_back(newFolder);
171 void Tree::BuildVirtualFolder(std::string& virtualFolders) const
173 virtualFolders += "<Option virtualFolders=\"CMake Files\\;";
174 for (std::vector<Tree>::const_iterator it = folders.begin();
178 it->BuildVirtualFolderImpl(virtualFolders, "");
180 virtualFolders += "\" />";
184 void Tree::BuildVirtualFolderImpl(std::string& virtualFolders,
185 const std::string& prefix) const
187 virtualFolders += "CMake Files\\" + prefix + path + "\\;";
188 for (std::vector<Tree>::const_iterator it = folders.begin();
192 it->BuildVirtualFolderImpl(virtualFolders, prefix + path + "\\");
197 void Tree::BuildUnit(std::string& unitString, const std::string& fsPath) const
199 for (std::vector<std::string>::const_iterator it = files.begin();
203 unitString += " <Unit filename=\"" + fsPath + *it + "\">\n";
204 unitString += " <Option virtualFolder=\"CMake Files\\\" />\n";
205 unitString += " </Unit>\n";
207 for (std::vector<Tree>::const_iterator it = folders.begin();
211 it->BuildUnitImpl(unitString, "", fsPath);
216 void Tree::BuildUnitImpl(std::string& unitString,
217 const std::string& virtualFolderPath,
218 const std::string& fsPath) const
220 for (std::vector<std::string>::const_iterator it = files.begin();
224 unitString += " <Unit filename=\"" +fsPath+path+ "/" + *it + "\">\n";
225 unitString += " <Option virtualFolder=\"CMake Files\\"
226 + virtualFolderPath + path + "\\\" />\n";
227 unitString += " </Unit>\n";
229 for (std::vector<Tree>::const_iterator it = folders.begin();
233 it->BuildUnitImpl(unitString,
234 virtualFolderPath + path + "\\", fsPath + path + "/");
239 void cmExtraCodeBlocksGenerator
240 ::CreateNewProjectFile(const std::vector<cmLocalGenerator*>& lgs,
241 const std::string& filename)
243 const cmMakefile* mf=lgs[0]->GetMakefile();
244 cmGeneratedFileStream fout(filename.c_str());
252 // build tree of virtual folders
253 for (std::map<cmStdString, std::vector<cmLocalGenerator*> >::const_iterator
254 it = this->GlobalGenerator->GetProjectMap().begin();
255 it != this->GlobalGenerator->GetProjectMap().end();
259 std::vector<std::string> listFiles;
260 for (std::vector<cmLocalGenerator *>::const_iterator
261 jt = it->second.begin();
262 jt != it->second.end();
265 const std::vector<std::string> & files =
266 (*jt)->GetMakefile()->GetListFiles();
267 listFiles.insert(listFiles.end(), files.begin(), files.end());
271 const char* cmakeRoot = mf->GetDefinition("CMAKE_ROOT");
272 for (std::vector<std::string>::const_iterator jt = listFiles.begin();
273 jt != listFiles.end();
276 // don't put cmake's own files into the project (#12110):
277 if (jt->find(cmakeRoot) == 0)
282 const std::string &relative = cmSystemTools::RelativePath(
283 it->second[0]->GetMakefile()->GetHomeDirectory(),
285 std::vector<std::string> splitted;
286 cmSystemTools::SplitPath(relative.c_str(), splitted, false);
287 // Split filename from path
288 std::string fileName = *(splitted.end()-1);
289 splitted.erase(splitted.end() - 1, splitted.end());
291 // We don't want paths with CMakeFiles in them
293 // In speedcrunch those where purely internal
294 if (splitted.size() >= 1
295 && relative.find("CMakeFiles") == std::string::npos)
297 tree.InsertPath(splitted, 1, fileName);
302 // Now build a virtual tree string
303 std::string virtualFolders;
304 tree.BuildVirtualFolder(virtualFolders);
305 // And one for <Unit>
306 std::string unitFiles;
307 tree.BuildUnit(unitFiles, std::string(mf->GetHomeDirectory()) + "/");
309 // figure out the compiler
310 std::string compiler = this->GetCBCompilerId(mf);
311 std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
313 fout<<"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>\n"
314 "<CodeBlocks_project_file>\n"
315 " <FileVersion major=\"1\" minor=\"6\" />\n"
317 " <Option title=\"" << mf->GetProjectName()<<"\" />\n"
318 " <Option makefile_is_custom=\"1\" />\n"
319 " <Option compiler=\"" << compiler << "\" />\n"
320 " "<<virtualFolders<<"\n"
323 this->AppendTarget(fout, "all", 0, make.c_str(), mf, compiler.c_str());
325 // add all executable and library targets and some of the GLOBAL
326 // and UTILITY targets
327 for (std::vector<cmLocalGenerator*>::const_iterator lg=lgs.begin();
330 cmMakefile* makefile=(*lg)->GetMakefile();
331 cmTargets& targets=makefile->GetTargets();
332 for (cmTargets::iterator ti = targets.begin();
333 ti != targets.end(); ti++)
335 switch(ti->second.GetType())
337 case cmTarget::GLOBAL_TARGET:
339 bool insertTarget = false;
340 // Only add the global targets from CMAKE_BINARY_DIR,
341 // not from the subdirs
342 if (strcmp(makefile->GetStartOutputDirectory(),
343 makefile->GetHomeOutputDirectory())==0)
346 // only add the "edit_cache" target if it's not ccmake, because
347 // this will not work within the IDE
348 if (ti->first == "edit_cache")
350 const char* editCommand = makefile->GetDefinition
351 ("CMAKE_EDIT_COMMAND");
352 if (editCommand == 0)
354 insertTarget = false;
356 else if (strstr(editCommand, "ccmake")!=NULL)
358 insertTarget = false;
364 this->AppendTarget(fout, ti->first.c_str(), 0,
365 make.c_str(), makefile, compiler.c_str());
369 case cmTarget::UTILITY:
370 // Add all utility targets, except the Nightly/Continuous/
371 // Experimental-"sub"targets as e.g. NightlyStart
372 if (((ti->first.find("Nightly")==0) &&(ti->first!="Nightly"))
373 || ((ti->first.find("Continuous")==0)&&(ti->first!="Continuous"))
374 || ((ti->first.find("Experimental")==0)
375 && (ti->first!="Experimental")))
380 this->AppendTarget(fout, ti->first.c_str(), 0,
381 make.c_str(), makefile, compiler.c_str());
383 case cmTarget::EXECUTABLE:
384 case cmTarget::STATIC_LIBRARY:
385 case cmTarget::SHARED_LIBRARY:
386 case cmTarget::MODULE_LIBRARY:
387 case cmTarget::OBJECT_LIBRARY:
389 this->AppendTarget(fout, ti->first.c_str(), &ti->second,
390 make.c_str(), makefile, compiler.c_str());
391 std::string fastTarget = ti->first;
392 fastTarget += "/fast";
393 this->AppendTarget(fout, fastTarget.c_str(), &ti->second,
394 make.c_str(), makefile, compiler.c_str());
406 // Collect all used source files in the project
407 // Sort them into two containers, one for C/C++ implementation files
408 // which may have an acompanying header, one for all other files
409 std::map<std::string, cmSourceFile*> cFiles;
410 std::set<std::string> otherFiles;
411 for (std::vector<cmLocalGenerator*>::const_iterator lg=lgs.begin();
414 cmMakefile* makefile=(*lg)->GetMakefile();
415 cmTargets& targets=makefile->GetTargets();
416 for (cmTargets::iterator ti = targets.begin();
417 ti != targets.end(); ti++)
419 switch(ti->second.GetType())
421 case cmTarget::EXECUTABLE:
422 case cmTarget::STATIC_LIBRARY:
423 case cmTarget::SHARED_LIBRARY:
424 case cmTarget::MODULE_LIBRARY:
425 case cmTarget::OBJECT_LIBRARY:
426 case cmTarget::UTILITY: // can have sources since 2.6.3
428 const std::vector<cmSourceFile*>&sources=ti->second.GetSourceFiles();
429 for (std::vector<cmSourceFile*>::const_iterator si=sources.begin();
430 si!=sources.end(); si++)
432 // don't add source files which have the GENERATED property set:
433 if ((*si)->GetPropertyAsBool("GENERATED"))
438 // check whether it is a C/C++ implementation file
439 bool isCFile = false;
440 if ((*si)->GetLanguage() && (*(*si)->GetLanguage() == 'C'))
442 for(std::vector<std::string>::const_iterator
443 ext = mf->GetSourceExtensions().begin();
444 ext != mf->GetSourceExtensions().end();
447 if ((*si)->GetExtension() == *ext)
455 // then put it accordingly into one of the two containers
458 cFiles[(*si)->GetFullPath()] = *si ;
462 otherFiles.insert((*si)->GetFullPath());
466 default: // intended fallthrough
472 // The following loop tries to add header files matching to implementation
473 // files to the project. It does that by iterating over all source files,
474 // replacing the file name extension with ".h" and checks whether such a
475 // file exists. If it does, it is inserted into the map of files.
476 // A very similar version of that code exists also in the kdevelop
477 // project generator.
478 for (std::map<std::string, cmSourceFile*>::const_iterator
483 std::string headerBasename=cmSystemTools::GetFilenamePath(sit->first);
485 headerBasename+=cmSystemTools::GetFilenameWithoutExtension(sit->first);
487 // check if there's a matching header around
488 for(std::vector<std::string>::const_iterator
489 ext = mf->GetHeaderExtensions().begin();
490 ext != mf->GetHeaderExtensions().end();
493 std::string hname=headerBasename;
496 // if it's already in the set, don't check if it exists on disk
497 std::set<std::string>::const_iterator headerIt=otherFiles.find(hname);
498 if (headerIt != otherFiles.end())
503 if(cmSystemTools::FileExists(hname.c_str()))
505 otherFiles.insert(hname);
511 // insert all source files in the CodeBlocks project
512 // first the C/C++ implementation files, then all others
513 for (std::map<std::string, cmSourceFile*>::const_iterator
518 fout<<" <Unit filename=\""<< sit->first <<"\">\n"
521 for (std::set<std::string>::const_iterator
522 sit=otherFiles.begin();
523 sit!=otherFiles.end();
526 fout<<" <Unit filename=\""<< sit->c_str() <<"\">\n"
530 // Add CMakeLists.txt
533 fout<<" </Project>\n"
534 "</CodeBlocks_project_file>\n";
538 // Write a dummy file for OBJECT libraries, so C::B can reference some file
539 std::string cmExtraCodeBlocksGenerator::CreateDummyTargetFile(
540 cmMakefile* mf, cmTarget* target) const
542 // this file doesn't seem to be used by C::B in custom makefile mode,
543 // but we generate a unique file for each OBJECT library so in case
544 // C::B uses it in some way, the targets don't interfere with each other.
545 std::string filename = mf->GetCurrentOutputDirectory();
547 filename += mf->GetLocalGenerator()->GetTargetDirectory(*target);
549 filename += target->GetName();
550 filename += ".objlib";
551 cmGeneratedFileStream fout(filename.c_str());
554 fout << "# This is a dummy file for the OBJECT library "
556 << " for the CMake CodeBlocks project generator.\n"
557 << "# Don't edit, this file will be overwritten.\n";
563 // Generate the xml code for one target.
564 void cmExtraCodeBlocksGenerator::AppendTarget(cmGeneratedFileStream& fout,
565 const char* targetName,
568 const cmMakefile* makefile,
569 const char* compiler)
571 std::string makefileName = makefile->GetStartOutputDirectory();
572 makefileName += "/Makefile";
574 fout<<" <Target title=\"" << targetName << "\">\n";
577 int cbTargetType = this->GetCBTargetType(target);
578 std::string workingDir = makefile->GetStartOutputDirectory();
579 if ( target->GetType()==cmTarget::EXECUTABLE)
581 // Determine the directory where the executable target is created, and
582 // set the working directory to this dir.
583 const char* runtimeOutputDir = makefile->GetDefinition(
584 "CMAKE_RUNTIME_OUTPUT_DIRECTORY");
585 if (runtimeOutputDir != 0)
587 workingDir = runtimeOutputDir;
591 const char* executableOutputDir = makefile->GetDefinition(
592 "EXECUTABLE_OUTPUT_PATH");
593 if (executableOutputDir != 0)
595 workingDir = executableOutputDir;
600 const char* buildType = makefile->GetDefinition("CMAKE_BUILD_TYPE");
601 std::string location;
602 if ( target->GetType()==cmTarget::OBJECT_LIBRARY)
604 location = this->CreateDummyTargetFile(const_cast<cmMakefile*>(makefile),
609 location = target->GetLocation(buildType);
612 fout<<" <Option output=\"" << location
613 << "\" prefix_auto=\"0\" extension_auto=\"0\" />\n"
614 " <Option working_dir=\"" << workingDir << "\" />\n"
615 " <Option object_output=\"./\" />\n"
616 " <Option type=\"" << cbTargetType << "\" />\n"
617 " <Option compiler=\"" << compiler << "\" />\n"
620 cmGeneratorTarget *gtgt = this->GlobalGenerator
621 ->GetGeneratorTarget(target);
623 // the compilerdefines for this target
624 std::vector<std::string> cdefs;
625 target->GetCompileDefinitions(cdefs, buildType);
628 for(std::vector<std::string>::const_iterator di = cdefs.begin();
629 di != cdefs.end(); ++di)
631 cmXMLSafe safedef(di->c_str());
632 fout <<" <Add option=\"-D" << safedef.str() << "\" />\n";
635 // the include directories for this target
636 std::set<std::string> uniqIncludeDirs;
638 std::vector<std::string> includes;
639 target->GetMakefile()->GetLocalGenerator()->
640 GetIncludeDirectories(includes, gtgt, "C", buildType);
641 for(std::vector<std::string>::const_iterator dirIt=includes.begin();
642 dirIt != includes.end();
645 uniqIncludeDirs.insert(*dirIt);
648 std::string systemIncludeDirs = makefile->GetSafeDefinition(
649 "CMAKE_EXTRA_GENERATOR_C_SYSTEM_INCLUDE_DIRS");
650 if (!systemIncludeDirs.empty())
652 std::vector<std::string> dirs;
653 cmSystemTools::ExpandListArgument(systemIncludeDirs.c_str(), dirs);
654 for(std::vector<std::string>::const_iterator dirIt=dirs.begin();
658 uniqIncludeDirs.insert(*dirIt);
662 systemIncludeDirs = makefile->GetSafeDefinition(
663 "CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_INCLUDE_DIRS");
664 if (!systemIncludeDirs.empty())
666 std::vector<std::string> dirs;
667 cmSystemTools::ExpandListArgument(systemIncludeDirs.c_str(), dirs);
668 for(std::vector<std::string>::const_iterator dirIt=dirs.begin();
672 uniqIncludeDirs.insert(*dirIt);
676 for(std::set<std::string>::const_iterator dirIt=uniqIncludeDirs.begin();
677 dirIt != uniqIncludeDirs.end();
680 fout <<" <Add directory=\"" << dirIt->c_str() << "\" />\n";
683 fout<<" </Compiler>\n";
685 else // e.g. all and the GLOBAL and UTILITY targets
687 fout<<" <Option working_dir=\""
688 << makefile->GetStartOutputDirectory() << "\" />\n"
689 <<" <Option type=\"" << 4 << "\" />\n";
692 fout<<" <MakeCommands>\n"
694 << this->BuildMakeCommand(make, makefileName.c_str(), targetName)
696 " <CompileFile command=\""
697 << this->BuildMakeCommand(make, makefileName.c_str(),""$file"")
700 << this->BuildMakeCommand(make, makefileName.c_str(), "clean")
702 " <DistClean command=\""
703 << this->BuildMakeCommand(make, makefileName.c_str(), "clean")
711 // Translate the cmake compiler id into the CodeBlocks compiler id
712 std::string cmExtraCodeBlocksGenerator::GetCBCompilerId(const cmMakefile* mf)
714 // figure out which language to use
715 // for now care only for C and C++
716 std::string compilerIdVar = "CMAKE_CXX_COMPILER_ID";
717 if (this->GlobalGenerator->GetLanguageEnabled("CXX") == false)
719 compilerIdVar = "CMAKE_C_COMPILER_ID";
722 std::string hostSystemName = mf->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME");
723 std::string systemName = mf->GetSafeDefinition("CMAKE_SYSTEM_NAME");
724 std::string compilerId = mf->GetSafeDefinition(compilerIdVar.c_str());
725 std::string compiler = "gcc"; // default to gcc
726 if (compilerId == "MSVC")
730 else if (compilerId == "Borland")
734 else if (compilerId == "SDCC")
738 else if (compilerId == "Intel")
742 else if (compilerId == "Watcom")
746 else if (compilerId == "GNU")
754 // Translate the cmake target type into the CodeBlocks target type id
755 int cmExtraCodeBlocksGenerator::GetCBTargetType(cmTarget* target)
757 if ( target->GetType()==cmTarget::EXECUTABLE)
759 if ((target->GetPropertyAsBool("WIN32_EXECUTABLE"))
760 || (target->GetPropertyAsBool("MACOSX_BUNDLE")))
769 else if (( target->GetType()==cmTarget::STATIC_LIBRARY)
770 || (target->GetType()==cmTarget::OBJECT_LIBRARY))
774 else if ((target->GetType()==cmTarget::SHARED_LIBRARY)
775 || (target->GetType()==cmTarget::MODULE_LIBRARY))
782 // Create the command line for building the given target using the selected
784 std::string cmExtraCodeBlocksGenerator::BuildMakeCommand(
785 const std::string& make, const char* makefile, const char* target)
787 std::string command = make;
788 if (strcmp(this->GlobalGenerator->GetName(), "NMake Makefiles")==0)
790 std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile);
791 command += " /NOLOGO /f "";
792 command += makefileName;
793 command += "" ";
794 command += " VERBOSE=1 ";
797 else if (strcmp(this->GlobalGenerator->GetName(), "MinGW Makefiles")==0)
799 // no escaping of spaces in this case, see
800 // http://public.kitware.com/Bug/view.php?id=10014
801 std::string makefileName = makefile;
802 command += " -f "";
803 command += makefileName;
804 command += "" ";
805 command += " VERBOSE=1 ";
808 else if (strcmp(this->GlobalGenerator->GetName(), "Ninja")==0)
815 std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile);
816 command += " -f "";
817 command += makefileName;
818 command += "" ";
819 command += " VERBOSE=1 ";