| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmExtraKateGenerator.h" | |
| #include <cstring> | |
| #include <memory> | |
| #include <ostream> | |
| #include <set> | |
| #include <vector> | |
| #include "cmCMakePath.h" | |
| #include "cmGeneratedFileStream.h" | |
| #include "cmGeneratorTarget.h" | |
| #include "cmGlobalGenerator.h" | |
| #include "cmLocalGenerator.h" | |
| #include "cmMakefile.h" | |
| #include "cmSourceFile.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmTargetTypes.h" | |
| #include "cmValue.h" | |
| cmExtraKateGenerator::cmExtraKateGenerator() = default; | |
| cmExternalMakefileProjectGeneratorFactory* cmExtraKateGenerator::GetFactory() | |
| { | |
| static cmExternalMakefileProjectGeneratorSimpleFactory<cmExtraKateGenerator> | |
| factory("Kate", "Generates Kate project files (deprecated)."); | |
| 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("Ninja Multi-Config"); | |
| factory.AddSupportedGlobalGenerator("Unix Makefiles"); | |
| } | |
| return &factory; | |
| } | |
| void cmExtraKateGenerator::Generate() | |
| { | |
| auto const& lg = this->GlobalGenerator->GetLocalGenerators()[0]; | |
| cmMakefile const* mf = lg->GetMakefile(); | |
| this->ProjectName = this->GenerateProjectName( | |
| lg->GetProjectName(), mf->GetSafeDefinition("CMAKE_BUILD_TYPE"), | |
| this->GetPathBasename(lg->GetBinaryDirectory())); | |
| this->UseNinja = | |
| ((this->GlobalGenerator->GetName() == "Ninja") || | |
| (this->GlobalGenerator->GetName() == "Ninja Multi-Config")); | |
| this->CreateKateProjectFile(*lg); | |
| this->CreateDummyKateProjectFile(*lg); | |
| } | |
| void cmExtraKateGenerator::CreateKateProjectFile( | |
| cmLocalGenerator const& lg) const | |
| { | |
| std::string filename = cmStrCat(lg.GetBinaryDirectory(), "/.kateproject"); | |
| cmGeneratedFileStream fout(filename); | |
| if (!fout) { | |
| return; | |
| } | |
| /* clang-format off */ | |
| fout << | |
| "{\n" | |
| "\t\"name\": \"" << this->ProjectName << "\",\n" | |
| "\t\"directory\": \"" << lg.GetSourceDirectory() << "\",\n" | |
| "\t\"files\": [ { " << this->GenerateFilesString(lg) << "} ],\n"; | |
| /* clang-format on */ | |
| this->WriteTargets(lg, fout); | |
| fout << "}\n"; | |
| } | |
| void cmExtraKateGenerator::WriteTargets(cmLocalGenerator const& lg, | |
| cmGeneratedFileStream& fout) const | |
| { | |
| cmMakefile const* mf = lg.GetMakefile(); | |
| std::string const& make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"); | |
| std::string const& makeArgs = | |
| mf->GetSafeDefinition("CMAKE_KATE_MAKE_ARGUMENTS"); | |
| std::string const& homeOutputDir = lg.GetBinaryDirectory(); | |
| auto const configs = mf->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); | |
| /* clang-format off */ | |
| fout << | |
| "\t\"build\": {\n" | |
| "\t\t\"directory\": \"" << homeOutputDir << "\",\n" | |
| "\t\t\"default_target\": \"all\",\n" | |
| "\t\t\"clean_target\": \"clean\",\n"; | |
| /* clang-format on */ | |
| // build, clean and quick are for the build plugin kate <= 4.12: | |
| fout << "\t\t\"build\": \"" << make << " -C \\\"" << homeOutputDir << "\\\" " | |
| << makeArgs << " " | |
| << "all\",\n"; | |
| fout << "\t\t\"clean\": \"" << make << " -C \\\"" << homeOutputDir << "\\\" " | |
| << makeArgs << " " | |
| << "clean\",\n"; | |
| fout << "\t\t\"quick\": \"" << make << " -C \\\"" << homeOutputDir << "\\\" " | |
| << makeArgs << " " | |
| << "install\",\n"; | |
| // this is for kate >= 4.13: | |
| fout << "\t\t\"targets\":[\n"; | |
| this->AppendTarget(fout, "all", configs, make, makeArgs, homeOutputDir, | |
| homeOutputDir); | |
| this->AppendTarget(fout, "clean", configs, make, makeArgs, homeOutputDir, | |
| homeOutputDir); | |
| // add all executable and library targets and some of the GLOBAL | |
| // and UTILITY targets | |
| for (auto const& localGen : this->GlobalGenerator->GetLocalGenerators()) { | |
| auto const& targets = localGen->GetGeneratorTargets(); | |
| std::string const currentDir = localGen->GetCurrentBinaryDirectory(); | |
| bool topLevel = (currentDir == localGen->GetBinaryDirectory()); | |
| for (auto const& target : targets) { | |
| std::string const& targetName = target->GetName(); | |
| switch (target->GetType()) { | |
| case cm::TargetType::GLOBAL_TARGET: { | |
| bool insertTarget = false; | |
| // Only add the global targets from CMAKE_BINARY_DIR, | |
| // not from the subdirs | |
| if (topLevel) { | |
| insertTarget = true; | |
| // only add the "edit_cache" target if it's not ccmake, because | |
| // this will not work within the IDE | |
| if (targetName == "edit_cache") { | |
| cmValue editCommand = | |
| localGen->GetMakefile()->GetDefinition("CMAKE_EDIT_COMMAND"); | |
| if (!editCommand || | |
| strstr(editCommand->c_str(), "ccmake") != nullptr) { | |
| insertTarget = false; | |
| } | |
| } | |
| } | |
| if (insertTarget) { | |
| this->AppendTarget(fout, targetName, configs, make, makeArgs, | |
| currentDir, homeOutputDir); | |
| } | |
| } break; | |
| case cm::TargetType::UTILITY: | |
| // Add all utility targets, except the Nightly/Continuous/ | |
| // Experimental-"sub"targets as e.g. NightlyStart | |
| if ((cmHasLiteralPrefix(targetName, "Nightly") && | |
| (targetName != "Nightly")) || | |
| (cmHasLiteralPrefix(targetName, "Continuous") && | |
| (targetName != "Continuous")) || | |
| (cmHasLiteralPrefix(targetName, "Experimental") && | |
| (targetName != "Experimental"))) { | |
| break; | |
| } | |
| this->AppendTarget(fout, targetName, configs, make, makeArgs, | |
| currentDir, homeOutputDir); | |
| break; | |
| case cm::TargetType::EXECUTABLE: | |
| case cm::TargetType::STATIC_LIBRARY: | |
| case cm::TargetType::SHARED_LIBRARY: | |
| case cm::TargetType::MODULE_LIBRARY: | |
| case cm::TargetType::OBJECT_LIBRARY: { | |
| this->AppendTarget(fout, targetName, configs, make, makeArgs, | |
| currentDir, homeOutputDir); | |
| if (!this->UseNinja) { | |
| std::string fastTarget = cmStrCat(targetName, "/fast"); | |
| this->AppendTarget(fout, fastTarget, configs, make, makeArgs, | |
| currentDir, homeOutputDir); | |
| } | |
| } break; | |
| default: | |
| break; | |
| } | |
| } | |
| // insert rules for compiling, preprocessing and assembling individual | |
| // files | |
| std::vector<std::string> objectFileTargets; | |
| localGen->GetIndividualFileTargets(objectFileTargets); | |
| for (std::string const& f : objectFileTargets) { | |
| this->AppendTarget(fout, f, configs, make, makeArgs, currentDir, | |
| homeOutputDir); | |
| } | |
| } | |
| fout << "\t] }\n"; | |
| } | |
| void cmExtraKateGenerator::AppendTarget( | |
| cmGeneratedFileStream& fout, std::string const& target, | |
| std::vector<std::string> const& configs, std::string const& make, | |
| std::string const& makeArgs, std::string const& path, | |
| std::string const& homeOutputDir) const | |
| { | |
| static char JsonSep = ' '; | |
| for (std::string const& conf : configs) { | |
| fout << "\t\t\t" << JsonSep << R"({"name":")" << target | |
| << ((configs.size() > 1) ? (std::string(":") + conf) : std::string()) | |
| << "\", " | |
| "\"build_cmd\":\"" | |
| << make << " -C \\\"" << (this->UseNinja ? homeOutputDir : path) | |
| << "\\\" " | |
| << ((this->UseNinja && configs.size() > 1) | |
| ? cmStrCat(" -f build-", conf, ".ninja") | |
| : std::string()) | |
| << makeArgs << " " << target << "\"}\n"; | |
| JsonSep = ','; | |
| } | |
| } | |
| void cmExtraKateGenerator::CreateDummyKateProjectFile( | |
| cmLocalGenerator const& lg) const | |
| { | |
| std::string filename = | |
| cmStrCat(lg.GetBinaryDirectory(), '/', this->ProjectName, ".kateproject"); | |
| cmGeneratedFileStream fout(filename); | |
| if (!fout) { | |
| return; | |
| } | |
| fout << "#Generated by " << cmSystemTools::GetCMakeCommand() | |
| << ", do not edit.\n"; | |
| } | |
| std::string cmExtraKateGenerator::GenerateFilesString( | |
| cmLocalGenerator const& lg) const | |
| { | |
| cmMakefile const* mf = lg.GetMakefile(); | |
| std::string mode = | |
| cmSystemTools::UpperCase(mf->GetSafeDefinition("CMAKE_KATE_FILES_MODE")); | |
| static std::string const gitString = "\"git\": 1 "; | |
| static std::string const svnString = "\"svn\": 1 "; | |
| static std::string const hgString = "\"hg\": 1 "; | |
| static std::string const fossilString = "\"fossil\": 1 "; | |
| if (mode == "SVN") { | |
| return svnString; | |
| } | |
| if (mode == "GIT") { | |
| return gitString; | |
| } | |
| if (mode == "HG") { | |
| return hgString; | |
| } | |
| if (mode == "FOSSIL") { | |
| return fossilString; | |
| } | |
| // check for the VCS files except when "forced" to "FILES" mode: | |
| if (mode != "LIST") { | |
| cmCMakePath startDir(lg.GetSourceDirectory(), cmCMakePath::auto_format); | |
| // move the directories up to the root directory to see whether we are in | |
| // a subdir of a svn, git, hg or fossil checkout | |
| for (;;) { | |
| std::string s = startDir.String() + "/.git"; | |
| if (cmSystemTools::FileExists(s)) { | |
| return gitString; | |
| } | |
| s = startDir.String() + "/.svn"; | |
| if (cmSystemTools::FileExists(s)) { | |
| return svnString; | |
| } | |
| s = startDir.String() + "/.hg"; | |
| if (cmSystemTools::FileExists(s)) { | |
| return hgString; | |
| } | |
| s = startDir.String() + "/.fslckout"; | |
| if (cmSystemTools::FileExists(s)) { | |
| return fossilString; | |
| } | |
| if (!startDir.HasRelativePath()) { // have we reached the root dir ? | |
| break; | |
| } | |
| startDir = startDir.GetParentPath(); | |
| } | |
| } | |
| std::set<std::string> files; | |
| std::string tmp; | |
| auto const& lgs = this->GlobalGenerator->GetLocalGenerators(); | |
| for (auto const& lgen : lgs) { | |
| cmMakefile* makefile = lgen->GetMakefile(); | |
| std::vector<std::string> const& listFiles = makefile->GetListFiles(); | |
| for (std::string const& listFile : listFiles) { | |
| if (listFile.find("/CMakeFiles/") == std::string::npos) { | |
| files.insert(listFile); | |
| } | |
| } | |
| for (auto const& sf : makefile->GetSourceFiles()) { | |
| if (sf->GetIsGenerated()) { | |
| continue; | |
| } | |
| tmp = sf->ResolveFullPath(); | |
| files.insert(tmp); | |
| } | |
| } | |
| char const* sep = ""; | |
| tmp = "\"list\": ["; | |
| for (std::string const& f : files) { | |
| tmp += sep; | |
| tmp += " \""; | |
| tmp += f; | |
| tmp += "\""; | |
| sep = ","; | |
| } | |
| tmp += "] "; | |
| return tmp; | |
| } | |
| std::string cmExtraKateGenerator::GenerateProjectName( | |
| std::string const& name, std::string const& type, | |
| std::string const& path) const | |
| { | |
| return cmStrCat(name, (type.empty() ? "" : "-"), type, '@', path); | |
| } | |
| std::string cmExtraKateGenerator::GetPathBasename( | |
| std::string const& path) const | |
| { | |
| std::string outputBasename = path; | |
| while (!outputBasename.empty() && | |
| (outputBasename.back() == '/' || outputBasename.back() == '\\')) { | |
| outputBasename.resize(outputBasename.size() - 1); | |
| } | |
| std::string::size_type loc = outputBasename.find_last_of("/\\"); | |
| if (loc != std::string::npos) { | |
| outputBasename = outputBasename.substr(loc + 1); | |
| } | |
| return outputBasename; | |
| } |