summaryrefslogtreecommitdiffstats
path: root/Source/cmExtraSublimeTextGenerator.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmExtraSublimeTextGenerator.cxx')
-rw-r--r--Source/cmExtraSublimeTextGenerator.cxx410
1 files changed, 410 insertions, 0 deletions
diff --git a/Source/cmExtraSublimeTextGenerator.cxx b/Source/cmExtraSublimeTextGenerator.cxx
new file mode 100644
index 0000000..36ba520
--- /dev/null
+++ b/Source/cmExtraSublimeTextGenerator.cxx
@@ -0,0 +1,410 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmExtraSublimeTextGenerator.h"
+
+#include <cmsys/RegularExpression.hxx>
+#include <set>
+#include <sstream>
+#include <string.h>
+#include <utility>
+
+#include "cmGeneratedFileStream.h"
+#include "cmGeneratorExpression.h"
+#include "cmGeneratorTarget.h"
+#include "cmGlobalGenerator.h"
+#include "cmLocalGenerator.h"
+#include "cmMakefile.h"
+#include "cmSourceFile.h"
+#include "cmStateTypes.h"
+#include "cmSystemTools.h"
+#include "cmake.h"
+
+/*
+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
+*/
+
+cmExternalMakefileProjectGeneratorFactory*
+cmExtraSublimeTextGenerator::GetFactory()
+{
+ static cmExternalMakefileProjectGeneratorSimpleFactory<
+ cmExtraSublimeTextGenerator>
+ factory("Sublime Text 2", "Generates Sublime Text 2 project files.");
+
+ if (factory.GetSupportedGlobalGenerators().empty()) {
+#if defined(_WIN32)
+ factory.AddSupportedGlobalGenerator("MinGW Makefiles");
+ factory.AddSupportedGlobalGenerator("NMake Makefiles");
+// disable until somebody actually tests it:
+// factory.AddSupportedGlobalGenerator("MSYS Makefiles");
+#endif
+ factory.AddSupportedGlobalGenerator("Ninja");
+ factory.AddSupportedGlobalGenerator("Unix Makefiles");
+ }
+
+ return &factory;
+}
+
+cmExtraSublimeTextGenerator::cmExtraSublimeTextGenerator()
+ : cmExternalMakefileProjectGenerator()
+{
+ this->ExcludeBuildFolder = false;
+}
+
+void cmExtraSublimeTextGenerator::Generate()
+{
+ this->ExcludeBuildFolder = this->GlobalGenerator->GlobalSettingIsOn(
+ "CMAKE_SUBLIME_TEXT_2_EXCLUDE_BUILD_TREE");
+ this->EnvSettings = this->GlobalGenerator->GetSafeGlobalSetting(
+ "CMAKE_SUBLIME_TEXT_2_ENV_SETTINGS");
+
+ // for each sub project in the project create a sublime text 2 project
+ for (std::map<std::string, 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)
+{
+ std::string outputDir = lgs[0]->GetCurrentBinaryDirectory();
+ std::string projectName = lgs[0]->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(
+ lgs[0]->GetBinaryDirectory(), lgs[0]->GetSourceDirectory());
+ // 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(lgs[0]->GetSourceDirectory(),
+ lgs[0]->GetBinaryDirectory());
+ if ((!outputRelativeToSourceRoot.empty()) &&
+ ((outputRelativeToSourceRoot.length() < 3) ||
+ (outputRelativeToSourceRoot.substr(0, 3) != "../"))) {
+ if (this->ExcludeBuildFolder) {
+ 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]";
+ std::string systemName = mf->GetSafeDefinition("CMAKE_SYSTEM_NAME");
+ std::vector<std::string> tokens;
+ cmSystemTools::ExpandListArgument(this->EnvSettings, tokens);
+
+ if (!this->EnvSettings.empty()) {
+ fout << ",";
+ fout << "\n\t\"env\":";
+ fout << "\n\t{";
+ fout << "\n\t\t" << systemName << ":";
+ fout << "\n\t\t{";
+ for (std::vector<std::string>::iterator i = tokens.begin();
+ i != tokens.end(); ++i) {
+ size_t const pos = i->find_first_of('=');
+
+ if (pos != std::string::npos) {
+ std::string varName = i->substr(0, pos);
+ std::string varValue = i->substr(pos + 1);
+
+ fout << "\n\t\t\t\"" << varName << "\":\"" << varValue << "\"";
+ } else {
+ std::ostringstream e;
+ e << "Could not parse Env Vars specified in "
+ "\"CMAKE_SUBLIME_TEXT_2_ENV_SETTINGS\""
+ << ", corrupted string " << *i;
+ mf->IssueMessage(cmake::FATAL_ERROR, e.str());
+ }
+ }
+ fout << "\n\t\t}";
+ fout << "\n\t}";
+ }
+ fout << "\n}";
+}
+
+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], CM_NULLPTR, make.c_str(), mf,
+ compiler.c_str(), sourceFileFlags, true);
+ this->AppendTarget(fout, "clean", lgs[0], CM_NULLPTR, 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();
+ std::vector<cmGeneratorTarget*> targets = (*lg)->GetGeneratorTargets();
+ for (std::vector<cmGeneratorTarget*>::iterator ti = targets.begin();
+ ti != targets.end(); ti++) {
+ std::string targetName = (*ti)->GetName();
+ switch ((*ti)->GetType()) {
+ case cmStateEnums::GLOBAL_TARGET: {
+ // Only add the global targets from CMAKE_BINARY_DIR,
+ // not from the subdirs
+ if (strcmp((*lg)->GetCurrentBinaryDirectory(),
+ (*lg)->GetBinaryDirectory()) == 0) {
+ this->AppendTarget(fout, targetName, *lg, CM_NULLPTR, make.c_str(),
+ makefile, compiler.c_str(), sourceFileFlags,
+ false);
+ }
+ } break;
+ case cmStateEnums::UTILITY:
+ // Add all utility targets, except the Nightly/Continuous/
+ // Experimental-"sub"targets as e.g. NightlyStart
+ if (((targetName.find("Nightly") == 0) &&
+ (targetName != "Nightly")) ||
+ ((targetName.find("Continuous") == 0) &&
+ (targetName != "Continuous")) ||
+ ((targetName.find("Experimental") == 0) &&
+ (targetName != "Experimental"))) {
+ break;
+ }
+
+ this->AppendTarget(fout, targetName, *lg, CM_NULLPTR, make.c_str(),
+ makefile, compiler.c_str(), sourceFileFlags,
+ false);
+ break;
+ case cmStateEnums::EXECUTABLE:
+ case cmStateEnums::STATIC_LIBRARY:
+ case cmStateEnums::SHARED_LIBRARY:
+ case cmStateEnums::MODULE_LIBRARY:
+ case cmStateEnums::OBJECT_LIBRARY: {
+ this->AppendTarget(fout, targetName, *lg, *ti, make.c_str(),
+ makefile, compiler.c_str(), sourceFileFlags,
+ false);
+ std::string fastTarget = targetName;
+ fastTarget += "/fast";
+ this->AppendTarget(fout, fastTarget, *lg, *ti, make.c_str(),
+ makefile, compiler.c_str(), sourceFileFlags,
+ false);
+ } break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+void cmExtraSublimeTextGenerator::AppendTarget(
+ cmGeneratedFileStream& fout, const std::string& targetName,
+ cmLocalGenerator* lg, cmGeneratorTarget* target, const char* make,
+ const cmMakefile* makefile, const char* /*compiler*/,
+ MapSourceFileFlags& sourceFileFlags, bool firstTarget)
+{
+
+ if (target != CM_NULLPTR) {
+ std::vector<cmSourceFile*> sourceFiles;
+ target->GetSourceFiles(sourceFiles,
+ makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
+ 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);
+ std::string definesString = this->ComputeDefines(*iter, lg, target);
+ 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 (this->GlobalGenerator->GetName() == "Ninja") {
+ makefileName = "build.ninja";
+ } else {
+ makefileName = "Makefile";
+ }
+ if (!firstTarget) {
+ fout << ",\n\t";
+ }
+ fout << "\t{\n\t\t\t\"name\": \"" << lg->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]+):)?\\\\s*(.*)"
+ "\"\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 std::string& target)
+{
+ std::string command = "\"";
+ command += make + "\"";
+ std::string generator = this->GlobalGenerator->GetName();
+ if (generator == "NMake Makefiles") {
+ std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile);
+ command += ", \"/NOLOGO\", \"/f\", \"";
+ command += makefileName + "\"";
+ command += ", \"" + target + "\"";
+ } else if (generator == "Ninja") {
+ std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile);
+ command += ", \"-f\", \"";
+ command += makefileName + "\"";
+ command += ", \"" + target + "\"";
+ } else {
+ std::string makefileName;
+ if (generator == "MinGW Makefiles") {
+ // no escaping of spaces in this case, see
+ // https://gitlab.kitware.com/cmake/cmake/issues/10014
+ makefileName = makefile;
+ } else {
+ makefileName = cmSystemTools::ConvertToOutputPath(makefile);
+ }
+ command += ", \"-f\", \"";
+ command += makefileName + "\"";
+ command += ", \"" + target + "\"";
+ }
+ return command;
+}
+
+// TODO: Most of the code is picked up from the Ninja generator, refactor it.
+std::string cmExtraSublimeTextGenerator::ComputeFlagsForObject(
+ cmSourceFile* source, cmLocalGenerator* lg, cmGeneratorTarget* gtgt)
+{
+ std::string flags;
+ std::string language = source->GetLanguage();
+ if (language.empty()) {
+ language = "C";
+ }
+ std::string const& config =
+ lg->GetMakefile()->GetSafeDefinition("CMAKE_BUILD_TYPE");
+
+ lg->GetTargetCompileFlags(gtgt, config, language, flags);
+
+ // 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);
+ }
+
+ // Add source file specific flags.
+ if (const char* cflags = source->GetProperty("COMPILE_FLAGS")) {
+ cmGeneratorExpression ge;
+ const char* processed = ge.Parse(cflags)->Evaluate(lg, config);
+ lg->AppendFlags(flags, processed);
+ }
+
+ return flags;
+}
+
+// TODO: Refactor with
+// void cmMakefileTargetGenerator::WriteTargetLanguageFlags().
+std::string cmExtraSublimeTextGenerator::ComputeDefines(
+ cmSourceFile* source, cmLocalGenerator* lg, cmGeneratorTarget* target)
+
+{
+ std::set<std::string> defines;
+ cmMakefile* makefile = lg->GetMakefile();
+ const std::string& language = source->GetLanguage();
+ const std::string& 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, language);
+ lg->AppendDefines(defines, source->GetProperty("COMPILE_DEFINITIONS"));
+ {
+ std::string defPropName = "COMPILE_DEFINITIONS_";
+ defPropName += cmSystemTools::UpperCase(config);
+ lg->AppendDefines(defines, source->GetProperty(defPropName));
+ }
+
+ std::string definesString;
+ lg->JoinDefines(defines, definesString, language);
+
+ return definesString;
+}