Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmExtraKateGenerator.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
#include "cmExtraKateGenerator.h"
4
5
#include <cstring>
6
#include <memory>
7
#include <ostream>
8
#include <set>
9
#include <vector>
10
11
#include "cmCMakePath.h"
12
#include "cmGeneratedFileStream.h"
13
#include "cmGeneratorTarget.h"
14
#include "cmGlobalGenerator.h"
15
#include "cmLocalGenerator.h"
16
#include "cmMakefile.h"
17
#include "cmSourceFile.h"
18
#include "cmStateTypes.h"
19
#include "cmStringAlgorithms.h"
20
#include "cmSystemTools.h"
21
#include "cmValue.h"
22
23
0
cmExtraKateGenerator::cmExtraKateGenerator() = default;
24
25
cmExternalMakefileProjectGeneratorFactory* cmExtraKateGenerator::GetFactory()
26
35
{
27
35
  static cmExternalMakefileProjectGeneratorSimpleFactory<cmExtraKateGenerator>
28
35
    factory("Kate", "Generates Kate project files (deprecated).");
29
30
35
  if (factory.GetSupportedGlobalGenerators().empty()) {
31
#if defined(_WIN32)
32
    factory.AddSupportedGlobalGenerator("MinGW Makefiles");
33
    factory.AddSupportedGlobalGenerator("NMake Makefiles");
34
// disable until somebody actually tests it:
35
// factory.AddSupportedGlobalGenerator("MSYS Makefiles");
36
#endif
37
1
    factory.AddSupportedGlobalGenerator("Ninja");
38
1
    factory.AddSupportedGlobalGenerator("Ninja Multi-Config");
39
1
    factory.AddSupportedGlobalGenerator("Unix Makefiles");
40
1
  }
41
42
35
  return &factory;
43
35
}
44
45
void cmExtraKateGenerator::Generate()
46
0
{
47
0
  auto const& lg = this->GlobalGenerator->GetLocalGenerators()[0];
48
0
  cmMakefile const* mf = lg->GetMakefile();
49
0
  this->ProjectName = this->GenerateProjectName(
50
0
    lg->GetProjectName(), mf->GetSafeDefinition("CMAKE_BUILD_TYPE"),
51
0
    this->GetPathBasename(lg->GetBinaryDirectory()));
52
0
  this->UseNinja =
53
0
    ((this->GlobalGenerator->GetName() == "Ninja") ||
54
0
     (this->GlobalGenerator->GetName() == "Ninja Multi-Config"));
55
56
0
  this->CreateKateProjectFile(*lg);
57
0
  this->CreateDummyKateProjectFile(*lg);
58
0
}
59
60
void cmExtraKateGenerator::CreateKateProjectFile(
61
  cmLocalGenerator const& lg) const
62
0
{
63
0
  std::string filename = cmStrCat(lg.GetBinaryDirectory(), "/.kateproject");
64
0
  cmGeneratedFileStream fout(filename);
65
0
  if (!fout) {
66
0
    return;
67
0
  }
68
69
  /* clang-format off */
70
0
  fout <<
71
0
    "{\n"
72
0
    "\t\"name\": \"" << this->ProjectName << "\",\n"
73
0
    "\t\"directory\": \"" << lg.GetSourceDirectory() << "\",\n"
74
0
    "\t\"files\": [ { " << this->GenerateFilesString(lg) << "} ],\n";
75
  /* clang-format on */
76
0
  this->WriteTargets(lg, fout);
77
0
  fout << "}\n";
78
0
}
79
80
void cmExtraKateGenerator::WriteTargets(cmLocalGenerator const& lg,
81
                                        cmGeneratedFileStream& fout) const
82
0
{
83
0
  cmMakefile const* mf = lg.GetMakefile();
84
0
  std::string const& make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
85
0
  std::string const& makeArgs =
86
0
    mf->GetSafeDefinition("CMAKE_KATE_MAKE_ARGUMENTS");
87
0
  std::string const& homeOutputDir = lg.GetBinaryDirectory();
88
0
  auto const configs = mf->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
89
90
  /* clang-format off */
91
0
  fout <<
92
0
  "\t\"build\": {\n"
93
0
  "\t\t\"directory\": \"" << homeOutputDir << "\",\n"
94
0
  "\t\t\"default_target\": \"all\",\n"
95
0
  "\t\t\"clean_target\": \"clean\",\n";
96
  /* clang-format on */
97
98
  // build, clean and quick are for the build plugin kate <= 4.12:
99
0
  fout << "\t\t\"build\": \"" << make << " -C \\\"" << homeOutputDir << "\\\" "
100
0
       << makeArgs << " "
101
0
       << "all\",\n";
102
0
  fout << "\t\t\"clean\": \"" << make << " -C \\\"" << homeOutputDir << "\\\" "
103
0
       << makeArgs << " "
104
0
       << "clean\",\n";
105
0
  fout << "\t\t\"quick\": \"" << make << " -C \\\"" << homeOutputDir << "\\\" "
106
0
       << makeArgs << " "
107
0
       << "install\",\n";
108
109
  // this is for kate >= 4.13:
110
0
  fout << "\t\t\"targets\":[\n";
111
112
0
  this->AppendTarget(fout, "all", configs, make, makeArgs, homeOutputDir,
113
0
                     homeOutputDir);
114
0
  this->AppendTarget(fout, "clean", configs, make, makeArgs, homeOutputDir,
115
0
                     homeOutputDir);
116
117
  // add all executable and library targets and some of the GLOBAL
118
  // and UTILITY targets
119
0
  for (auto const& localGen : this->GlobalGenerator->GetLocalGenerators()) {
120
0
    auto const& targets = localGen->GetGeneratorTargets();
121
0
    std::string const currentDir = localGen->GetCurrentBinaryDirectory();
122
0
    bool topLevel = (currentDir == localGen->GetBinaryDirectory());
123
124
0
    for (auto const& target : targets) {
125
0
      std::string const& targetName = target->GetName();
126
0
      switch (target->GetType()) {
127
0
        case cmStateEnums::GLOBAL_TARGET: {
128
0
          bool insertTarget = false;
129
          // Only add the global targets from CMAKE_BINARY_DIR,
130
          // not from the subdirs
131
0
          if (topLevel) {
132
0
            insertTarget = true;
133
            // only add the "edit_cache" target if it's not ccmake, because
134
            // this will not work within the IDE
135
0
            if (targetName == "edit_cache") {
136
0
              cmValue editCommand =
137
0
                localGen->GetMakefile()->GetDefinition("CMAKE_EDIT_COMMAND");
138
0
              if (!editCommand ||
139
0
                  strstr(editCommand->c_str(), "ccmake") != nullptr) {
140
0
                insertTarget = false;
141
0
              }
142
0
            }
143
0
          }
144
0
          if (insertTarget) {
145
0
            this->AppendTarget(fout, targetName, configs, make, makeArgs,
146
0
                               currentDir, homeOutputDir);
147
0
          }
148
0
        } break;
149
0
        case cmStateEnums::UTILITY:
150
          // Add all utility targets, except the Nightly/Continuous/
151
          // Experimental-"sub"targets as e.g. NightlyStart
152
0
          if ((cmHasLiteralPrefix(targetName, "Nightly") &&
153
0
               (targetName != "Nightly")) ||
154
0
              (cmHasLiteralPrefix(targetName, "Continuous") &&
155
0
               (targetName != "Continuous")) ||
156
0
              (cmHasLiteralPrefix(targetName, "Experimental") &&
157
0
               (targetName != "Experimental"))) {
158
0
            break;
159
0
          }
160
161
0
          this->AppendTarget(fout, targetName, configs, make, makeArgs,
162
0
                             currentDir, homeOutputDir);
163
0
          break;
164
0
        case cmStateEnums::EXECUTABLE:
165
0
        case cmStateEnums::STATIC_LIBRARY:
166
0
        case cmStateEnums::SHARED_LIBRARY:
167
0
        case cmStateEnums::MODULE_LIBRARY:
168
0
        case cmStateEnums::OBJECT_LIBRARY: {
169
0
          this->AppendTarget(fout, targetName, configs, make, makeArgs,
170
0
                             currentDir, homeOutputDir);
171
0
          if (!this->UseNinja) {
172
0
            std::string fastTarget = cmStrCat(targetName, "/fast");
173
0
            this->AppendTarget(fout, fastTarget, configs, make, makeArgs,
174
0
                               currentDir, homeOutputDir);
175
0
          }
176
177
0
        } break;
178
0
        default:
179
0
          break;
180
0
      }
181
0
    }
182
183
    // insert rules for compiling, preprocessing and assembling individual
184
    // files
185
0
    std::vector<std::string> objectFileTargets;
186
0
    localGen->GetIndividualFileTargets(objectFileTargets);
187
0
    for (std::string const& f : objectFileTargets) {
188
0
      this->AppendTarget(fout, f, configs, make, makeArgs, currentDir,
189
0
                         homeOutputDir);
190
0
    }
191
0
  }
192
193
0
  fout << "\t] }\n";
194
0
}
195
196
void cmExtraKateGenerator::AppendTarget(
197
  cmGeneratedFileStream& fout, std::string const& target,
198
  std::vector<std::string> const& configs, std::string const& make,
199
  std::string const& makeArgs, std::string const& path,
200
  std::string const& homeOutputDir) const
201
0
{
202
0
  static char JsonSep = ' ';
203
204
0
  for (std::string const& conf : configs) {
205
0
    fout << "\t\t\t" << JsonSep << R"({"name":")" << target
206
0
         << ((configs.size() > 1) ? (std::string(":") + conf) : std::string())
207
0
         << "\", "
208
0
            "\"build_cmd\":\""
209
0
         << make << " -C \\\"" << (this->UseNinja ? homeOutputDir : path)
210
0
         << "\\\" "
211
0
         << ((this->UseNinja && configs.size() > 1)
212
0
               ? cmStrCat(" -f build-", conf, ".ninja")
213
0
               : std::string())
214
0
         << makeArgs << " " << target << "\"}\n";
215
216
0
    JsonSep = ',';
217
0
  }
218
0
}
219
220
void cmExtraKateGenerator::CreateDummyKateProjectFile(
221
  cmLocalGenerator const& lg) const
222
0
{
223
0
  std::string filename =
224
0
    cmStrCat(lg.GetBinaryDirectory(), '/', this->ProjectName, ".kateproject");
225
0
  cmGeneratedFileStream fout(filename);
226
0
  if (!fout) {
227
0
    return;
228
0
  }
229
230
0
  fout << "#Generated by " << cmSystemTools::GetCMakeCommand()
231
0
       << ", do not edit.\n";
232
0
}
233
234
std::string cmExtraKateGenerator::GenerateFilesString(
235
  cmLocalGenerator const& lg) const
236
0
{
237
0
  cmMakefile const* mf = lg.GetMakefile();
238
0
  std::string mode =
239
0
    cmSystemTools::UpperCase(mf->GetSafeDefinition("CMAKE_KATE_FILES_MODE"));
240
0
  static std::string const gitString = "\"git\": 1 ";
241
0
  static std::string const svnString = "\"svn\": 1 ";
242
0
  static std::string const hgString = "\"hg\": 1 ";
243
0
  static std::string const fossilString = "\"fossil\": 1 ";
244
245
0
  if (mode == "SVN") {
246
0
    return svnString;
247
0
  }
248
0
  if (mode == "GIT") {
249
0
    return gitString;
250
0
  }
251
0
  if (mode == "HG") {
252
0
    return hgString;
253
0
  }
254
0
  if (mode == "FOSSIL") {
255
0
    return fossilString;
256
0
  }
257
258
  // check for the VCS files except when "forced" to "FILES" mode:
259
0
  if (mode != "LIST") {
260
0
    cmCMakePath startDir(lg.GetSourceDirectory(), cmCMakePath::auto_format);
261
    // move the directories up to the root directory to see whether we are in
262
    // a subdir of a svn, git, hg or fossil checkout
263
0
    for (;;) {
264
0
      std::string s = startDir.String() + "/.git";
265
0
      if (cmSystemTools::FileExists(s)) {
266
0
        return gitString;
267
0
      }
268
269
0
      s = startDir.String() + "/.svn";
270
0
      if (cmSystemTools::FileExists(s)) {
271
0
        return svnString;
272
0
      }
273
274
0
      s = startDir.String() + "/.hg";
275
0
      if (cmSystemTools::FileExists(s)) {
276
0
        return hgString;
277
0
      }
278
0
      s = startDir.String() + "/.fslckout";
279
0
      if (cmSystemTools::FileExists(s)) {
280
0
        return fossilString;
281
0
      }
282
283
0
      if (!startDir.HasRelativePath()) { // have we reached the root dir ?
284
0
        break;
285
0
      }
286
0
      startDir = startDir.GetParentPath();
287
0
    }
288
0
  }
289
290
0
  std::set<std::string> files;
291
0
  std::string tmp;
292
0
  auto const& lgs = this->GlobalGenerator->GetLocalGenerators();
293
294
0
  for (auto const& lgen : lgs) {
295
0
    cmMakefile* makefile = lgen->GetMakefile();
296
0
    std::vector<std::string> const& listFiles = makefile->GetListFiles();
297
0
    for (std::string const& listFile : listFiles) {
298
0
      if (listFile.find("/CMakeFiles/") == std::string::npos) {
299
0
        files.insert(listFile);
300
0
      }
301
0
    }
302
303
0
    for (auto const& sf : makefile->GetSourceFiles()) {
304
0
      if (sf->GetIsGenerated()) {
305
0
        continue;
306
0
      }
307
308
0
      tmp = sf->ResolveFullPath();
309
0
      files.insert(tmp);
310
0
    }
311
0
  }
312
313
0
  char const* sep = "";
314
0
  tmp = "\"list\": [";
315
0
  for (std::string const& f : files) {
316
0
    tmp += sep;
317
0
    tmp += " \"";
318
0
    tmp += f;
319
0
    tmp += "\"";
320
0
    sep = ",";
321
0
  }
322
0
  tmp += "] ";
323
324
0
  return tmp;
325
0
}
326
327
std::string cmExtraKateGenerator::GenerateProjectName(
328
  std::string const& name, std::string const& type,
329
  std::string const& path) const
330
0
{
331
0
  return cmStrCat(name, (type.empty() ? "" : "-"), type, '@', path);
332
0
}
333
334
std::string cmExtraKateGenerator::GetPathBasename(
335
  std::string const& path) const
336
0
{
337
0
  std::string outputBasename = path;
338
0
  while (!outputBasename.empty() &&
339
0
         (outputBasename.back() == '/' || outputBasename.back() == '\\')) {
340
0
    outputBasename.resize(outputBasename.size() - 1);
341
0
  }
342
0
  std::string::size_type loc = outputBasename.find_last_of("/\\");
343
0
  if (loc != std::string::npos) {
344
0
    outputBasename = outputBasename.substr(loc + 1);
345
0
  }
346
347
0
  return outputBasename;
348
0
}