blob: 6e6d009826e721fa9f22240885e6c18decdd532c [file] [log] [blame]
/* 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;
}