/*============================================================================ 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 /* 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."; entry.Full = "Project files for Sublime Text 2 will be created in the top directory " "and in every subdirectory which features a CMakeLists.txt file " "containing a PROJECT() call. " "Additionally Makefiles (or build.ninja files) are generated into the " "build tree. The appropriate make program can build the project through " "the default make target. A \"make install\" target is also provided."; } 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 >::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& 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& 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& 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::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: { bool insertTarget = false; // Only add the global targets from CMAKE_BINARY_DIR, // not from the subdirs if (strcmp(makefile->GetStartOutputDirectory(), makefile->GetHomeOutputDirectory())==0) { insertTarget = true; // only add the "edit_cache" target if it's not ccmake, because // this will not work within the IDE if (ti->first == "edit_cache") { const char* editCommand = makefile->GetDefinition ("CMAKE_EDIT_COMMAND"); if (editCommand == 0) { insertTarget = false; } else if (strstr(editCommand, "ccmake")!=NULL) { insertTarget = false; } } } if (insertTarget) { 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 const& sourceFiles = target->GetSourceFiles(); std::vector::const_iterator sourceFilesEnd = sourceFiles.end(); for (std::vector::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())).first; } std::vector& 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 includes; lg->GetIncludeDirectories(includes, gtgt, language, config); std::string includeFlags = lg->GetIncludeFlags(includes, 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, config, language); // 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 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->AppendDefines(defines, target->GetCompileDefinitions(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; }