| /*============================================================================ |
| CMake - Cross Platform Makefile Generator |
| Copyright 2004-2009 Kitware, Inc. |
| Copyright 2004 Alexander Neundorf (neundorf@kde.org) |
| |
| Distributed under the OSI-approved BSD License (the "License"); |
| see accompanying file Copyright.txt for details. |
| |
| This software is distributed WITHOUT ANY WARRANTY; without even the |
| implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the License for more information. |
| ============================================================================*/ |
| #include "cmExtraSublimeTextGenerator.h" |
| #include "cmake.h" |
| #include "cmGeneratedFileStream.h" |
| #include "cmGeneratorTarget.h" |
| #include "cmGlobalUnixMakefileGenerator3.h" |
| #include "cmLocalGenerator.h" |
| #include "cmLocalUnixMakefileGenerator3.h" |
| #include "cmMakefile.h" |
| #include "cmSourceFile.h" |
| #include "cmSystemTools.h" |
| #include "cmTarget.h" |
| #include "cmXMLSafe.h" |
| |
| #include <cmsys/SystemTools.hxx> |
| |
| /* |
| Sublime Text 2 Generator |
| Author: Morné Chamberlain |
| This generator was initially based off of the CodeBlocks generator. |
| |
| Some useful URLs: |
| Homepage: |
| http://www.sublimetext.com/ |
| |
| File format docs: |
| http://www.sublimetext.com/docs/2/projects.html |
| http://sublimetext.info/docs/en/reference/build_systems.html |
| */ |
| |
| //---------------------------------------------------------------------------- |
| void cmExtraSublimeTextGenerator |
| ::GetDocumentation(cmDocumentationEntry& entry, const char*) const |
| { |
| entry.Name = this->GetName(); |
| entry.Brief = "Generates Sublime Text 2 project files."; |
| } |
| |
| cmExtraSublimeTextGenerator::cmExtraSublimeTextGenerator() |
| :cmExternalMakefileProjectGenerator() |
| { |
| #if defined(_WIN32) |
| this->SupportedGlobalGenerators.push_back("MinGW Makefiles"); |
| this->SupportedGlobalGenerators.push_back("NMake Makefiles"); |
| // disable until somebody actually tests it: |
| // this->SupportedGlobalGenerators.push_back("MSYS Makefiles"); |
| #endif |
| this->SupportedGlobalGenerators.push_back("Ninja"); |
| this->SupportedGlobalGenerators.push_back("Unix Makefiles"); |
| } |
| |
| |
| void cmExtraSublimeTextGenerator::Generate() |
| { |
| // for each sub project in the project create a sublime text 2 project |
| for (std::map<cmStdString, std::vector<cmLocalGenerator*> >::const_iterator |
| it = this->GlobalGenerator->GetProjectMap().begin(); |
| it!= this->GlobalGenerator->GetProjectMap().end(); |
| ++it) |
| { |
| // create a project file |
| this->CreateProjectFile(it->second); |
| } |
| } |
| |
| |
| void cmExtraSublimeTextGenerator::CreateProjectFile( |
| const std::vector<cmLocalGenerator*>& lgs) |
| { |
| const cmMakefile* mf=lgs[0]->GetMakefile(); |
| std::string outputDir=mf->GetStartOutputDirectory(); |
| std::string projectName=mf->GetProjectName(); |
| |
| const std::string filename = |
| outputDir + "/" + projectName + ".sublime-project"; |
| |
| this->CreateNewProjectFile(lgs, filename); |
| } |
| |
| void cmExtraSublimeTextGenerator |
| ::CreateNewProjectFile(const std::vector<cmLocalGenerator*>& lgs, |
| const std::string& filename) |
| { |
| const cmMakefile* mf=lgs[0]->GetMakefile(); |
| cmGeneratedFileStream fout(filename.c_str()); |
| if(!fout) |
| { |
| return; |
| } |
| |
| const std::string &sourceRootRelativeToOutput = cmSystemTools::RelativePath( |
| mf->GetHomeOutputDirectory(), |
| mf->GetHomeDirectory()); |
| // Write the folder entries to the project file |
| fout << "{\n"; |
| fout << "\t\"folders\":\n\t[\n\t"; |
| if (!sourceRootRelativeToOutput.empty()) |
| { |
| fout << "\t{\n\t\t\t\"path\": \"" << sourceRootRelativeToOutput << "\""; |
| const std::string &outputRelativeToSourceRoot = |
| cmSystemTools::RelativePath(mf->GetHomeDirectory(), |
| mf->GetHomeOutputDirectory()); |
| if ((!outputRelativeToSourceRoot.empty()) && |
| ((outputRelativeToSourceRoot.length() < 3) || |
| (outputRelativeToSourceRoot.substr(0, 3) != "../"))) |
| { |
| fout << ",\n\t\t\t\"folder_exclude_patterns\": [\"" << |
| outputRelativeToSourceRoot << "\"]"; |
| } |
| } |
| else |
| { |
| fout << "\t{\n\t\t\t\"path\": \"./\""; |
| } |
| fout << "\n\t\t}"; |
| // End of the folders section |
| fout << "\n\t]"; |
| |
| // Write the beginning of the build systems section to the project file |
| fout << ",\n\t\"build_systems\":\n\t[\n\t"; |
| |
| // Set of include directories over all targets (sublime text/sublimeclang |
| // doesn't currently support these settings per build system, only project |
| // wide |
| MapSourceFileFlags sourceFileFlags; |
| AppendAllTargets(lgs, mf, fout, sourceFileFlags); |
| |
| // End of build_systems |
| fout << "\n\t]"; |
| fout << "\n\t}"; |
| } |
| |
| |
| void cmExtraSublimeTextGenerator:: |
| AppendAllTargets(const std::vector<cmLocalGenerator*>& lgs, |
| const cmMakefile* mf, |
| cmGeneratedFileStream& fout, |
| MapSourceFileFlags& sourceFileFlags) |
| { |
| std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); |
| std::string compiler = ""; |
| if (!lgs.empty()) |
| { |
| this->AppendTarget(fout, "all", lgs[0], 0, make.c_str(), mf, |
| compiler.c_str(), sourceFileFlags, true); |
| this->AppendTarget(fout, "clean", lgs[0], 0, make.c_str(), mf, |
| compiler.c_str(), sourceFileFlags, false); |
| } |
| |
| // add all executable and library targets and some of the GLOBAL |
| // and UTILITY targets |
| for (std::vector<cmLocalGenerator*>::const_iterator lg=lgs.begin(); |
| lg!=lgs.end(); lg++) |
| { |
| cmMakefile* makefile=(*lg)->GetMakefile(); |
| cmTargets& targets=makefile->GetTargets(); |
| for (cmTargets::iterator ti = targets.begin(); |
| ti != targets.end(); ti++) |
| { |
| switch(ti->second.GetType()) |
| { |
| case cmTarget::GLOBAL_TARGET: |
| { |
| // Only add the global targets from CMAKE_BINARY_DIR, |
| // not from the subdirs |
| if (strcmp(makefile->GetStartOutputDirectory(), |
| makefile->GetHomeOutputDirectory())==0) |
| { |
| this->AppendTarget(fout, ti->first.c_str(), *lg, 0, |
| make.c_str(), makefile, compiler.c_str(), |
| sourceFileFlags, false); |
| } |
| } |
| break; |
| case cmTarget::UTILITY: |
| // Add all utility targets, except the Nightly/Continuous/ |
| // Experimental-"sub"targets as e.g. NightlyStart |
| if (((ti->first.find("Nightly")==0) &&(ti->first!="Nightly")) |
| || ((ti->first.find("Continuous")==0)&&(ti->first!="Continuous")) |
| || ((ti->first.find("Experimental")==0) |
| && (ti->first!="Experimental"))) |
| { |
| break; |
| } |
| |
| this->AppendTarget(fout, ti->first.c_str(), *lg, 0, |
| make.c_str(), makefile, compiler.c_str(), |
| sourceFileFlags, false); |
| break; |
| case cmTarget::EXECUTABLE: |
| case cmTarget::STATIC_LIBRARY: |
| case cmTarget::SHARED_LIBRARY: |
| case cmTarget::MODULE_LIBRARY: |
| case cmTarget::OBJECT_LIBRARY: |
| { |
| this->AppendTarget(fout, ti->first.c_str(), *lg, &ti->second, |
| make.c_str(), makefile, compiler.c_str(), |
| sourceFileFlags, false); |
| std::string fastTarget = ti->first; |
| fastTarget += "/fast"; |
| this->AppendTarget(fout, fastTarget.c_str(), *lg, &ti->second, |
| make.c_str(), makefile, compiler.c_str(), |
| sourceFileFlags, false); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| } |
| |
| void cmExtraSublimeTextGenerator:: |
| AppendTarget(cmGeneratedFileStream& fout, |
| const char* targetName, |
| cmLocalGenerator* lg, |
| cmTarget* target, |
| const char* make, |
| const cmMakefile* makefile, |
| const char*, //compiler |
| MapSourceFileFlags& sourceFileFlags, |
| bool firstTarget) |
| { |
| |
| if (target != 0) |
| { |
| cmGeneratorTarget *gtgt = this->GlobalGenerator |
| ->GetGeneratorTarget(target); |
| std::vector<cmSourceFile*> sourceFiles; |
| target->GetSourceFiles(sourceFiles); |
| std::vector<cmSourceFile*>::const_iterator sourceFilesEnd = |
| sourceFiles.end(); |
| for (std::vector<cmSourceFile*>::const_iterator iter = |
| sourceFiles.begin(); iter != sourceFilesEnd; ++iter) |
| { |
| cmSourceFile* sourceFile = *iter; |
| MapSourceFileFlags::iterator sourceFileFlagsIter = |
| sourceFileFlags.find(sourceFile->GetFullPath()); |
| if (sourceFileFlagsIter == sourceFileFlags.end()) |
| { |
| sourceFileFlagsIter = |
| sourceFileFlags.insert(MapSourceFileFlags::value_type( |
| sourceFile->GetFullPath(), std::vector<std::string>())).first; |
| } |
| std::vector<std::string>& flags = sourceFileFlagsIter->second; |
| std::string flagsString = |
| this->ComputeFlagsForObject(*iter, lg, target, gtgt); |
| std::string definesString = |
| this->ComputeDefines(*iter, lg, target, gtgt); |
| flags.clear(); |
| cmsys::RegularExpression flagRegex; |
| // Regular expression to extract compiler flags from a string |
| // https://gist.github.com/3944250 |
| const char* regexString = |
| "(^|[ ])-[DIOUWfgs][^= ]+(=\\\"[^\"]+\\\"|=[^\"][^ ]+)?"; |
| flagRegex.compile(regexString); |
| std::string workString = flagsString + " " + definesString; |
| while (flagRegex.find(workString)) |
| { |
| std::string::size_type start = flagRegex.start(); |
| if (workString[start] == ' ') |
| { |
| start++; |
| } |
| flags.push_back(workString.substr(start, |
| flagRegex.end() - start)); |
| if (flagRegex.end() < workString.size()) |
| { |
| workString = workString.substr(flagRegex.end()); |
| } |
| else |
| { |
| workString = ""; |
| } |
| } |
| } |
| } |
| |
| // Ninja uses ninja.build files (look for a way to get the output file name |
| // from cmMakefile or something) |
| std::string makefileName; |
| if (strcmp(this->GlobalGenerator->GetName(), "Ninja")==0) |
| { |
| makefileName = "build.ninja"; |
| } |
| else |
| { |
| makefileName = "Makefile"; |
| } |
| if (!firstTarget) |
| { |
| fout << ",\n\t"; |
| } |
| fout << "\t{\n\t\t\t\"name\": \"" << makefile->GetProjectName() << " - " << |
| targetName << "\",\n"; |
| fout << "\t\t\t\"cmd\": [" << |
| this->BuildMakeCommand(make, makefileName.c_str(), targetName) << |
| "],\n"; |
| fout << "\t\t\t\"working_dir\": \"${project_path}\",\n"; |
| fout << "\t\t\t\"file_regex\": \"^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$\"\n"; |
| fout << "\t\t}"; |
| } |
| |
| // Create the command line for building the given target using the selected |
| // make |
| std::string cmExtraSublimeTextGenerator::BuildMakeCommand( |
| const std::string& make, const char* makefile, const char* target) |
| { |
| std::string command = "\""; |
| command += make + "\""; |
| if (strcmp(this->GlobalGenerator->GetName(), "NMake Makefiles")==0) |
| { |
| std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile); |
| command += ", \"/NOLOGO\", \"/f\", \""; |
| command += makefileName + "\""; |
| command += ", \"VERBOSE=1\", \""; |
| command += target; |
| command += "\""; |
| } |
| else if (strcmp(this->GlobalGenerator->GetName(), "Ninja")==0) |
| { |
| std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile); |
| command += ", \"-f\", \""; |
| command += makefileName + "\""; |
| command += ", \"-v\", \""; |
| command += target; |
| command += "\""; |
| } |
| else |
| { |
| std::string makefileName; |
| if (strcmp(this->GlobalGenerator->GetName(), "MinGW Makefiles")==0) |
| { |
| // no escaping of spaces in this case, see |
| // http://public.kitware.com/Bug/view.php?id=10014 |
| makefileName = makefile; |
| } |
| else |
| { |
| makefileName = cmSystemTools::ConvertToOutputPath(makefile); |
| } |
| command += ", \"-f\", \""; |
| command += makefileName + "\""; |
| command += ", \"VERBOSE=1\", \""; |
| command += target; |
| command += "\""; |
| } |
| return command; |
| } |
| |
| // TODO: Most of the code is picked up from the Ninja generator, refactor it. |
| std::string |
| cmExtraSublimeTextGenerator::ComputeFlagsForObject(cmSourceFile* source, |
| cmLocalGenerator* lg, |
| cmTarget *target, |
| cmGeneratorTarget* gtgt) |
| { |
| std::string flags; |
| |
| cmMakefile *makefile = lg->GetMakefile(); |
| const char* language = source->GetLanguage(); |
| if (language == NULL) |
| { |
| language = "C"; |
| } |
| const char* config = makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"); |
| // Add language-specific flags. |
| lg->AddLanguageFlags(flags, language, config); |
| |
| lg->AddArchitectureFlags(flags, gtgt, language, config); |
| |
| // TODO: Fortran support. |
| // // Fortran-specific flags computed for this target. |
| // if(*l == "Fortran") |
| // { |
| // this->AddFortranFlags(flags); |
| // } |
| |
| // Add shared-library flags if needed. |
| lg->AddCMP0018Flags(flags, target, language, config); |
| |
| // Add include directory flags. |
| { |
| std::vector<std::string> includes; |
| lg->GetIncludeDirectories(includes, gtgt, language, config); |
| std::string includeFlags = |
| lg->GetIncludeFlags(includes, gtgt, language, true); // full include paths |
| lg->AppendFlags(flags, includeFlags.c_str()); |
| } |
| |
| // Append old-style preprocessor definition flags. |
| lg->AppendFlags(flags, makefile->GetDefineFlags()); |
| |
| // Add target-specific flags. |
| lg->AddCompileOptions(flags, target, language, config); |
| |
| // Add source file specific flags. |
| lg->AppendFlags(flags, source->GetProperty("COMPILE_FLAGS")); |
| |
| // TODO: Handle Apple frameworks. |
| |
| return flags; |
| } |
| |
| // TODO: Refactor with |
| // void cmMakefileTargetGenerator::WriteTargetLanguageFlags(). |
| std::string |
| cmExtraSublimeTextGenerator:: |
| ComputeDefines(cmSourceFile *source, cmLocalGenerator* lg, cmTarget *target, |
| cmGeneratorTarget*) |
| |
| { |
| std::set<std::string> defines; |
| cmMakefile *makefile = lg->GetMakefile(); |
| const char* language = source->GetLanguage(); |
| if (language == NULL) |
| { |
| language = ""; |
| } |
| const char* config = makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"); |
| |
| // Add the export symbol definition for shared library objects. |
| if(const char* exportMacro = target->GetExportMacro()) |
| { |
| lg->AppendDefines(defines, exportMacro); |
| } |
| |
| // Add preprocessor definitions for this target and configuration. |
| lg->AddCompileDefinitions(defines, target, config); |
| lg->AppendDefines(defines, source->GetProperty("COMPILE_DEFINITIONS")); |
| { |
| std::string defPropName = "COMPILE_DEFINITIONS_"; |
| defPropName += cmSystemTools::UpperCase(config); |
| lg->AppendDefines(defines, source->GetProperty(defPropName.c_str())); |
| } |
| |
| std::string definesString; |
| lg->JoinDefines(defines, definesString, language); |
| |
| return definesString; |
| } |