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