• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "gn/qt_creator_writer.h"
6 
7 #include <optional>
8 #include <set>
9 #include <sstream>
10 #include <string>
11 
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 
16 #include "gn/builder.h"
17 #include "gn/config_values_extractors.h"
18 #include "gn/deps_iterator.h"
19 #include "gn/filesystem_utils.h"
20 #include "gn/label.h"
21 #include "gn/loader.h"
22 
23 namespace {
24 base::FilePath::CharType kProjectDirName[] =
25     FILE_PATH_LITERAL("qtcreator_project");
26 base::FilePath::CharType kProjectName[] = FILE_PATH_LITERAL("all");
27 base::FilePath::CharType kMainProjectFileSuffix[] =
28     FILE_PATH_LITERAL(".creator");
29 base::FilePath::CharType kSourcesFileSuffix[] = FILE_PATH_LITERAL(".files");
30 base::FilePath::CharType kIncludesFileSuffix[] = FILE_PATH_LITERAL(".includes");
31 base::FilePath::CharType kDefinesFileSuffix[] = FILE_PATH_LITERAL(".config");
32 }  // namespace
33 
34 // static
RunAndWriteFile(const BuildSettings * build_settings,const Builder & builder,Err * err,const std::string & root_target)35 bool QtCreatorWriter::RunAndWriteFile(const BuildSettings* build_settings,
36                                       const Builder& builder,
37                                       Err* err,
38                                       const std::string& root_target) {
39   base::FilePath project_dir =
40       build_settings->GetFullPath(build_settings->build_dir())
41           .Append(kProjectDirName);
42   if (!base::DirectoryExists(project_dir)) {
43     base::File::Error error;
44     if (!base::CreateDirectoryAndGetError(project_dir, &error)) {
45       *err =
46           Err(Location(), "Could not create the QtCreator project directory '" +
47                               FilePathToUTF8(project_dir) +
48                               "': " + base::File::ErrorToString(error));
49       return false;
50     }
51   }
52 
53   base::FilePath project_prefix = project_dir.Append(kProjectName);
54   QtCreatorWriter gen(build_settings, builder, project_prefix, root_target);
55   gen.Run();
56   if (gen.err_.has_error()) {
57     *err = gen.err_;
58     return false;
59   }
60   return true;
61 }
62 
QtCreatorWriter(const BuildSettings * build_settings,const Builder & builder,const base::FilePath & project_prefix,const std::string & root_target_name)63 QtCreatorWriter::QtCreatorWriter(const BuildSettings* build_settings,
64                                  const Builder& builder,
65                                  const base::FilePath& project_prefix,
66                                  const std::string& root_target_name)
67     : build_settings_(build_settings),
68       builder_(builder),
69       project_prefix_(project_prefix),
70       root_target_name_(root_target_name) {}
71 
72 QtCreatorWriter::~QtCreatorWriter() = default;
73 
CollectDeps(const Target * target)74 void QtCreatorWriter::CollectDeps(const Target* target) {
75   for (const auto& dep : target->GetDeps(Target::DEPS_ALL)) {
76     const Target* dep_target = dep.ptr;
77     if (targets_.count(dep_target))
78       continue;
79     targets_.insert(dep_target);
80     CollectDeps(dep_target);
81   }
82 }
83 
DiscoverTargets()84 bool QtCreatorWriter::DiscoverTargets() {
85   auto all_targets = builder_.GetAllResolvedTargets();
86 
87   if (root_target_name_.empty()) {
88     targets_ = std::set<const Target*>(all_targets.begin(), all_targets.end());
89     return true;
90   }
91 
92   const Target* root_target = nullptr;
93   for (const Target* target : all_targets) {
94     if (target->label().name() == root_target_name_) {
95       root_target = target;
96       break;
97     }
98   }
99 
100   if (!root_target) {
101     err_ = Err(Location(), "Target '" + root_target_name_ + "' not found.");
102     return false;
103   }
104 
105   targets_.insert(root_target);
106   CollectDeps(root_target);
107   return true;
108 }
109 
AddToSources(const Target::FileList & files)110 void QtCreatorWriter::AddToSources(const Target::FileList& files) {
111   for (const SourceFile& file : files) {
112     const std::string& file_path =
113         FilePathToUTF8(build_settings_->GetFullPath(file));
114     sources_.insert(file_path);
115   }
116 }
117 
118 namespace QtCreatorWriterUtils {
119 
120 enum class CVersion {
121   C99,
122   C11,
123 };
124 
125 enum class CxxVersion {
126   CXX98,
127   CXX03,
128   CXX11,
129   CXX14,
130   CXX17,
131 };
132 
ToMacro(CVersion version)133 std::string ToMacro(CVersion version) {
134   const std::string s = "__STDC_VERSION__";
135 
136   switch (version) {
137     case CVersion::C99:
138       return s + " 199901L";
139     case CVersion::C11:
140       return s + " 201112L";
141   }
142 
143   return std::string();
144 }
145 
ToMacro(CxxVersion version)146 std::string ToMacro(CxxVersion version) {
147   const std::string name = "__cplusplus";
148 
149   switch (version) {
150     case CxxVersion::CXX98:
151     case CxxVersion::CXX03:
152       return name + " 199711L";
153     case CxxVersion::CXX11:
154       return name + " 201103L";
155     case CxxVersion::CXX14:
156       return name + " 201402L";
157     case CxxVersion::CXX17:
158       return name + " 201703L";
159   }
160 
161   return std::string();
162 }
163 
164 const std::map<std::string, CVersion> kFlagToCVersion{
165     {"-std=gnu99", CVersion::C99},
166     {"-std=c99", CVersion::C99},
167     {"-std=gnu11", CVersion::C11},
168     {"-std=c11", CVersion::C11}};
169 
170 const std::map<std::string, CxxVersion> kFlagToCxxVersion{
171     {"-std=gnu++11", CxxVersion::CXX11}, {"-std=c++11", CxxVersion::CXX11},
172     {"-std=gnu++98", CxxVersion::CXX98}, {"-std=c++98", CxxVersion::CXX98},
173     {"-std=gnu++03", CxxVersion::CXX03}, {"-std=c++03", CxxVersion::CXX03},
174     {"-std=gnu++14", CxxVersion::CXX14}, {"-std=c++14", CxxVersion::CXX14},
175     {"-std=c++1y", CxxVersion::CXX14},   {"-std=gnu++17", CxxVersion::CXX17},
176     {"-std=c++17", CxxVersion::CXX17},   {"-std=c++1z", CxxVersion::CXX17},
177 };
178 
179 template <typename Enum>
180 struct CompVersion {
operator ()QtCreatorWriterUtils::CompVersion181   bool operator()(Enum a, Enum b) {
182     return static_cast<int>(a) < static_cast<int>(b);
183   }
184 };
185 
186 struct CompilerOptions {
187   std::optional<CVersion> c_version_;
188   std::optional<CxxVersion> cxx_version_;
189 
SetCVersionQtCreatorWriterUtils::CompilerOptions190   void SetCVersion(CVersion ver) { SetVersionImpl(c_version_, ver); }
191 
SetCxxVersionQtCreatorWriterUtils::CompilerOptions192   void SetCxxVersion(CxxVersion ver) { SetVersionImpl(cxx_version_, ver); }
193 
194  private:
195   template <typename Version>
SetVersionImplQtCreatorWriterUtils::CompilerOptions196   void SetVersionImpl(std::optional<Version>& cur_ver, Version ver) {
197     if (cur_ver)
198       cur_ver = std::max(*cur_ver, ver, CompVersion<Version>{});
199     else
200       cur_ver = ver;
201   }
202 };
203 
ParseCompilerOption(const std::string & flag,CompilerOptions * options)204 void ParseCompilerOption(const std::string& flag, CompilerOptions* options) {
205   auto c_ver = kFlagToCVersion.find(flag);
206   if (c_ver != kFlagToCVersion.end())
207     options->SetCVersion(c_ver->second);
208 
209   auto cxx_ver = kFlagToCxxVersion.find(flag);
210   if (cxx_ver != kFlagToCxxVersion.end())
211     options->SetCxxVersion(cxx_ver->second);
212 }
213 
ParseCompilerOptions(const std::vector<std::string> & cflags,CompilerOptions * options)214 void ParseCompilerOptions(const std::vector<std::string>& cflags,
215                           CompilerOptions* options) {
216   for (const std::string& flag : cflags)
217     ParseCompilerOption(flag, options);
218 }
219 
220 }  // namespace QtCreatorWriterUtils
221 
HandleTarget(const Target * target)222 void QtCreatorWriter::HandleTarget(const Target* target) {
223   using namespace QtCreatorWriterUtils;
224 
225   SourceFile build_file = Loader::BuildFileForLabel(target->label());
226   sources_.insert(FilePathToUTF8(build_settings_->GetFullPath(build_file)));
227   AddToSources(target->settings()->import_manager().GetImportedFiles());
228 
229   AddToSources(target->sources());
230   AddToSources(target->public_headers());
231 
232   for (ConfigValuesIterator it(target); !it.done(); it.Next()) {
233     for (const auto& input : it.cur().inputs())
234       sources_.insert(FilePathToUTF8(build_settings_->GetFullPath(input)));
235 
236     SourceFile precompiled_source = it.cur().precompiled_source();
237     if (!precompiled_source.is_null()) {
238       sources_.insert(
239           FilePathToUTF8(build_settings_->GetFullPath(precompiled_source)));
240     }
241 
242     for (const SourceDir& include_dir : it.cur().include_dirs()) {
243       includes_.insert(
244           FilePathToUTF8(build_settings_->GetFullPath(include_dir)));
245     }
246 
247     static constexpr const char* define_str = "#define ";
248     for (std::string define : it.cur().defines()) {
249       size_t equal_pos = define.find('=');
250       if (equal_pos != std::string::npos)
251         define[equal_pos] = ' ';
252       define.insert(0, define_str);
253       defines_.insert(define);
254     }
255 
256     CompilerOptions options;
257     ParseCompilerOptions(it.cur().cflags(), &options);
258     ParseCompilerOptions(it.cur().cflags_c(), &options);
259     ParseCompilerOptions(it.cur().cflags_cc(), &options);
260 
261     auto add_define_version = [this](auto& ver) {
262       if (ver)
263         defines_.insert(define_str + ToMacro(*ver));
264     };
265     add_define_version(options.c_version_);
266     add_define_version(options.cxx_version_);
267   }
268 }
269 
GenerateFile(const base::FilePath::CharType * suffix,const std::set<std::string> & items)270 void QtCreatorWriter::GenerateFile(const base::FilePath::CharType* suffix,
271                                    const std::set<std::string>& items) {
272   const base::FilePath file_path = project_prefix_.AddExtension(suffix);
273   std::ostringstream output;
274   for (const std::string& item : items)
275     output << item << std::endl;
276   WriteFileIfChanged(file_path, output.str(), &err_);
277 }
278 
Run()279 void QtCreatorWriter::Run() {
280   if (!DiscoverTargets())
281     return;
282 
283   for (const Target* target : targets_) {
284     if (target->toolchain()->label() !=
285         builder_.loader()->GetDefaultToolchain())
286       continue;
287     HandleTarget(target);
288   }
289 
290   std::set<std::string> empty_list;
291 
292   GenerateFile(kMainProjectFileSuffix, empty_list);
293   GenerateFile(kSourcesFileSuffix, sources_);
294   GenerateFile(kIncludesFileSuffix, includes_);
295   GenerateFile(kDefinesFileSuffix, defines_);
296 }
297