1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-06-09 07:20:19 +08:00
CMake/Source/cmExtraKateGenerator.cxx
Alexander Neundorf f6f8f89e6c Kate: support for the targets of multi config ninja
With this patch, cmake now generates the targets once for each
config into the kate project file, so users can build the targets
for all configs with this generator.
2023-02-18 23:23:11 +01:00

349 lines
11 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt 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 "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.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()
{
const auto& lg = this->GlobalGenerator->GetLocalGenerators()[0];
const cmMakefile* 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(
const cmLocalGenerator& 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(const cmLocalGenerator& lg,
cmGeneratedFileStream& fout) const
{
cmMakefile const* mf = lg.GetMakefile();
const std::string& make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
const std::string& makeArgs =
mf->GetSafeDefinition("CMAKE_KATE_MAKE_ARGUMENTS");
std::string const& homeOutputDir = lg.GetBinaryDirectory();
const auto 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 (const auto& localGen : this->GlobalGenerator->GetLocalGenerators()) {
const auto& targets = localGen->GetGeneratorTargets();
const std::string currentDir = localGen->GetCurrentBinaryDirectory();
bool topLevel = (currentDir == localGen->GetBinaryDirectory());
for (const auto& target : targets) {
std::string const& targetName = target->GetName();
switch (target->GetType()) {
case cmStateEnums::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 cmStateEnums::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 cmStateEnums::EXECUTABLE:
case cmStateEnums::STATIC_LIBRARY:
case cmStateEnums::SHARED_LIBRARY:
case cmStateEnums::MODULE_LIBRARY:
case cmStateEnums::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, const std::string& target,
const std::vector<std::string>& configs, const std::string& make,
const std::string& makeArgs, const std::string& path,
const std::string& homeOutputDir) const
{
static char JsonSep = ' ';
for (const std::string& 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)
? std::string(" -f build-") + conf + ".ninja"
: std::string())
<< makeArgs << " " << target << "\"}\n";
JsonSep = ',';
}
}
void cmExtraKateGenerator::CreateDummyKateProjectFile(
const cmLocalGenerator& 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(
const cmLocalGenerator& lg) const
{
const cmMakefile* mf = lg.GetMakefile();
std::string mode =
cmSystemTools::UpperCase(mf->GetSafeDefinition("CMAKE_KATE_FILES_MODE"));
static const std::string gitString = "\"git\": 1 ";
static const std::string svnString = "\"svn\": 1 ";
static const std::string hgString = "\"hg\": 1 ";
static const std::string 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;
const auto& lgs = this->GlobalGenerator->GetLocalGenerators();
for (const auto& lgen : lgs) {
cmMakefile* makefile = lgen->GetMakefile();
const std::vector<std::string>& listFiles = makefile->GetListFiles();
for (std::string const& listFile : listFiles) {
if (listFile.find("/CMakeFiles/") == std::string::npos) {
files.insert(listFile);
}
}
for (const auto& sf : makefile->GetSourceFiles()) {
if (sf->GetIsGenerated()) {
continue;
}
tmp = sf->ResolveFullPath();
files.insert(tmp);
}
}
const char* sep = "";
tmp = "\"list\": [";
for (std::string const& f : files) {
tmp += sep;
tmp += " \"";
tmp += f;
tmp += "\"";
sep = ",";
}
tmp += "] ";
return tmp;
}
std::string cmExtraKateGenerator::GenerateProjectName(
const std::string& name, const std::string& type,
const std::string& path) const
{
return name + (type.empty() ? "" : "-") + type + '@' + path;
}
std::string cmExtraKateGenerator::GetPathBasename(
const std::string& 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;
}