1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt 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 "cmGeneratedFileStream.h"
12 #include "cmGeneratorTarget.h"
13 #include "cmGlobalGenerator.h"
14 #include "cmLocalGenerator.h"
15 #include "cmMakefile.h"
16 #include "cmSourceFile.h"
17 #include "cmStateTypes.h"
18 #include "cmStringAlgorithms.h"
19 #include "cmSystemTools.h"
20 #include "cmValue.h"
21
22 cmExtraKateGenerator::cmExtraKateGenerator() = default;
23
GetFactory()24 cmExternalMakefileProjectGeneratorFactory* cmExtraKateGenerator::GetFactory()
25 {
26 static cmExternalMakefileProjectGeneratorSimpleFactory<cmExtraKateGenerator>
27 factory("Kate", "Generates Kate project files.");
28
29 if (factory.GetSupportedGlobalGenerators().empty()) {
30 #if defined(_WIN32)
31 factory.AddSupportedGlobalGenerator("MinGW Makefiles");
32 factory.AddSupportedGlobalGenerator("NMake Makefiles");
33 // disable until somebody actually tests it:
34 // factory.AddSupportedGlobalGenerator("MSYS Makefiles");
35 #endif
36 factory.AddSupportedGlobalGenerator("Ninja");
37 factory.AddSupportedGlobalGenerator("Unix Makefiles");
38 }
39
40 return &factory;
41 }
42
Generate()43 void cmExtraKateGenerator::Generate()
44 {
45 const auto& lg = this->GlobalGenerator->GetLocalGenerators()[0];
46 const cmMakefile* mf = lg->GetMakefile();
47 this->ProjectName = this->GenerateProjectName(
48 lg->GetProjectName(), mf->GetSafeDefinition("CMAKE_BUILD_TYPE"),
49 this->GetPathBasename(lg->GetBinaryDirectory()));
50 this->UseNinja = (this->GlobalGenerator->GetName() == "Ninja");
51
52 this->CreateKateProjectFile(*lg);
53 this->CreateDummyKateProjectFile(*lg);
54 }
55
CreateKateProjectFile(const cmLocalGenerator & lg) const56 void cmExtraKateGenerator::CreateKateProjectFile(
57 const cmLocalGenerator& lg) const
58 {
59 std::string filename = cmStrCat(lg.GetBinaryDirectory(), "/.kateproject");
60 cmGeneratedFileStream fout(filename);
61 if (!fout) {
62 return;
63 }
64
65 /* clang-format off */
66 fout <<
67 "{\n"
68 "\t\"name\": \"" << this->ProjectName << "\",\n"
69 "\t\"directory\": \"" << lg.GetSourceDirectory() << "\",\n"
70 "\t\"files\": [ { " << this->GenerateFilesString(lg) << "} ],\n";
71 /* clang-format on */
72 this->WriteTargets(lg, fout);
73 fout << "}\n";
74 }
75
WriteTargets(const cmLocalGenerator & lg,cmGeneratedFileStream & fout) const76 void cmExtraKateGenerator::WriteTargets(const cmLocalGenerator& lg,
77 cmGeneratedFileStream& fout) const
78 {
79 cmMakefile const* mf = lg.GetMakefile();
80 const std::string& make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
81 const std::string& makeArgs =
82 mf->GetSafeDefinition("CMAKE_KATE_MAKE_ARGUMENTS");
83 std::string const& homeOutputDir = lg.GetBinaryDirectory();
84
85 /* clang-format off */
86 fout <<
87 "\t\"build\": {\n"
88 "\t\t\"directory\": \"" << homeOutputDir << "\",\n"
89 "\t\t\"default_target\": \"all\",\n"
90 "\t\t\"clean_target\": \"clean\",\n";
91 /* clang-format on */
92
93 // build, clean and quick are for the build plugin kate <= 4.12:
94 fout << "\t\t\"build\": \"" << make << " -C \\\"" << homeOutputDir << "\\\" "
95 << makeArgs << " "
96 << "all\",\n";
97 fout << "\t\t\"clean\": \"" << make << " -C \\\"" << homeOutputDir << "\\\" "
98 << makeArgs << " "
99 << "clean\",\n";
100 fout << "\t\t\"quick\": \"" << make << " -C \\\"" << homeOutputDir << "\\\" "
101 << makeArgs << " "
102 << "install\",\n";
103
104 // this is for kate >= 4.13:
105 fout << "\t\t\"targets\":[\n";
106
107 this->AppendTarget(fout, "all", make, makeArgs, homeOutputDir,
108 homeOutputDir);
109 this->AppendTarget(fout, "clean", make, makeArgs, homeOutputDir,
110 homeOutputDir);
111
112 // add all executable and library targets and some of the GLOBAL
113 // and UTILITY targets
114 for (const auto& localGen : this->GlobalGenerator->GetLocalGenerators()) {
115 const auto& targets = localGen->GetGeneratorTargets();
116 std::string currentDir = localGen->GetCurrentBinaryDirectory();
117 bool topLevel = (currentDir == localGen->GetBinaryDirectory());
118
119 for (const auto& target : targets) {
120 std::string const& targetName = target->GetName();
121 switch (target->GetType()) {
122 case cmStateEnums::GLOBAL_TARGET: {
123 bool insertTarget = false;
124 // Only add the global targets from CMAKE_BINARY_DIR,
125 // not from the subdirs
126 if (topLevel) {
127 insertTarget = true;
128 // only add the "edit_cache" target if it's not ccmake, because
129 // this will not work within the IDE
130 if (targetName == "edit_cache") {
131 cmValue editCommand =
132 localGen->GetMakefile()->GetDefinition("CMAKE_EDIT_COMMAND");
133 if (!editCommand ||
134 strstr(editCommand->c_str(), "ccmake") != nullptr) {
135 insertTarget = false;
136 }
137 }
138 }
139 if (insertTarget) {
140 this->AppendTarget(fout, targetName, make, makeArgs, currentDir,
141 homeOutputDir);
142 }
143 } break;
144 case cmStateEnums::UTILITY:
145 // Add all utility targets, except the Nightly/Continuous/
146 // Experimental-"sub"targets as e.g. NightlyStart
147 if ((cmHasLiteralPrefix(targetName, "Nightly") &&
148 (targetName != "Nightly")) ||
149 (cmHasLiteralPrefix(targetName, "Continuous") &&
150 (targetName != "Continuous")) ||
151 (cmHasLiteralPrefix(targetName, "Experimental") &&
152 (targetName != "Experimental"))) {
153 break;
154 }
155
156 this->AppendTarget(fout, targetName, make, makeArgs, currentDir,
157 homeOutputDir);
158 break;
159 case cmStateEnums::EXECUTABLE:
160 case cmStateEnums::STATIC_LIBRARY:
161 case cmStateEnums::SHARED_LIBRARY:
162 case cmStateEnums::MODULE_LIBRARY:
163 case cmStateEnums::OBJECT_LIBRARY: {
164 this->AppendTarget(fout, targetName, make, makeArgs, currentDir,
165 homeOutputDir);
166 std::string fastTarget = cmStrCat(targetName, "/fast");
167 this->AppendTarget(fout, fastTarget, make, makeArgs, currentDir,
168 homeOutputDir);
169
170 } break;
171 default:
172 break;
173 }
174 }
175
176 // insert rules for compiling, preprocessing and assembling individual
177 // files
178 std::vector<std::string> objectFileTargets;
179 localGen->GetIndividualFileTargets(objectFileTargets);
180 for (std::string const& f : objectFileTargets) {
181 this->AppendTarget(fout, f, make, makeArgs, currentDir, homeOutputDir);
182 }
183 }
184
185 fout << "\t] }\n";
186 }
187
AppendTarget(cmGeneratedFileStream & fout,const std::string & target,const std::string & make,const std::string & makeArgs,const std::string & path,const std::string & homeOutputDir) const188 void cmExtraKateGenerator::AppendTarget(cmGeneratedFileStream& fout,
189 const std::string& target,
190 const std::string& make,
191 const std::string& makeArgs,
192 const std::string& path,
193 const std::string& homeOutputDir) const
194 {
195 static char JsonSep = ' ';
196
197 fout << "\t\t\t" << JsonSep << R"({"name":")" << target
198 << "\", "
199 "\"build_cmd\":\""
200 << make << " -C \\\"" << (this->UseNinja ? homeOutputDir : path)
201 << "\\\" " << makeArgs << " " << target << "\"}\n";
202
203 JsonSep = ',';
204 }
205
CreateDummyKateProjectFile(const cmLocalGenerator & lg) const206 void cmExtraKateGenerator::CreateDummyKateProjectFile(
207 const cmLocalGenerator& lg) const
208 {
209 std::string filename =
210 cmStrCat(lg.GetBinaryDirectory(), '/', this->ProjectName, ".kateproject");
211 cmGeneratedFileStream fout(filename);
212 if (!fout) {
213 return;
214 }
215
216 fout << "#Generated by " << cmSystemTools::GetCMakeCommand()
217 << ", do not edit.\n";
218 }
219
GenerateFilesString(const cmLocalGenerator & lg) const220 std::string cmExtraKateGenerator::GenerateFilesString(
221 const cmLocalGenerator& lg) const
222 {
223 std::string s = cmStrCat(lg.GetSourceDirectory(), "/.git");
224 if (cmSystemTools::FileExists(s)) {
225 return "\"git\": 1 ";
226 }
227
228 s = cmStrCat(lg.GetSourceDirectory(), "/.svn");
229 if (cmSystemTools::FileExists(s)) {
230 return "\"svn\": 1 ";
231 }
232
233 s = cmStrCat(lg.GetSourceDirectory(), '/');
234
235 std::set<std::string> files;
236 std::string tmp;
237 const auto& lgs = this->GlobalGenerator->GetLocalGenerators();
238
239 for (const auto& lgen : lgs) {
240 cmMakefile* makefile = lgen->GetMakefile();
241 const std::vector<std::string>& listFiles = makefile->GetListFiles();
242 for (std::string const& listFile : listFiles) {
243 tmp = listFile;
244 {
245 files.insert(tmp);
246 }
247 }
248
249 for (const auto& sf : makefile->GetSourceFiles()) {
250 if (sf->GetIsGenerated()) {
251 continue;
252 }
253
254 tmp = sf->ResolveFullPath();
255 files.insert(tmp);
256 }
257 }
258
259 const char* sep = "";
260 tmp = "\"list\": [";
261 for (std::string const& f : files) {
262 tmp += sep;
263 tmp += " \"";
264 tmp += f;
265 tmp += "\"";
266 sep = ",";
267 }
268 tmp += "] ";
269
270 return tmp;
271 }
272
GenerateProjectName(const std::string & name,const std::string & type,const std::string & path) const273 std::string cmExtraKateGenerator::GenerateProjectName(
274 const std::string& name, const std::string& type,
275 const std::string& path) const
276 {
277 return name + (type.empty() ? "" : "-") + type + '@' + path;
278 }
279
GetPathBasename(const std::string & path) const280 std::string cmExtraKateGenerator::GetPathBasename(
281 const std::string& path) const
282 {
283 std::string outputBasename = path;
284 while (!outputBasename.empty() &&
285 (outputBasename.back() == '/' || outputBasename.back() == '\\')) {
286 outputBasename.resize(outputBasename.size() - 1);
287 }
288 std::string::size_type loc = outputBasename.find_last_of("/\\");
289 if (loc != std::string::npos) {
290 outputBasename = outputBasename.substr(loc + 1);
291 }
292
293 return outputBasename;
294 }
295