Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / tools / gn / ninja_build_writer.cc
1 // Copyright (c) 2013 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 "tools/gn/ninja_build_writer.h"
6
7 #include <fstream>
8 #include <map>
9
10 #include "base/command_line.h"
11 #include "base/files/file_util.h"
12 #include "base/path_service.h"
13 #include "base/process/process_handle.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "build/build_config.h"
17 #include "tools/gn/build_settings.h"
18 #include "tools/gn/escape.h"
19 #include "tools/gn/filesystem_utils.h"
20 #include "tools/gn/input_file_manager.h"
21 #include "tools/gn/ninja_utils.h"
22 #include "tools/gn/scheduler.h"
23 #include "tools/gn/target.h"
24 #include "tools/gn/trace.h"
25
26 #if defined(OS_WIN)
27 #include <windows.h>
28 #endif
29
30 namespace {
31
32 std::string GetSelfInvocationCommand(const BuildSettings* build_settings) {
33   base::FilePath executable;
34   PathService::Get(base::FILE_EXE, &executable);
35
36   CommandLine cmdline(executable.NormalizePathSeparatorsTo('/'));
37   cmdline.AppendArg("gen");
38   cmdline.AppendArg(build_settings->build_dir().value());
39   cmdline.AppendSwitchPath("--root", build_settings->root_path());
40   cmdline.AppendSwitch("-q");  // Don't write output.
41
42   EscapeOptions escape_shell;
43   escape_shell.mode = ESCAPE_NINJA_COMMAND;
44 #if defined(OS_WIN)
45   // The command line code quoting varies by platform. We have one string,
46   // possibly with spaces, that we want to quote. The Windows command line
47   // quotes again, so we don't want quoting. The Posix one doesn't.
48   escape_shell.inhibit_quoting = true;
49 #endif
50
51   const CommandLine& our_cmdline = *CommandLine::ForCurrentProcess();
52   const CommandLine::SwitchMap& switches = our_cmdline.GetSwitches();
53   for (CommandLine::SwitchMap::const_iterator i = switches.begin();
54        i != switches.end(); ++i) {
55     // Only write arguments we haven't already written. Always skip "args"
56     // since those will have been written to the file and will be used
57     // implicitly in the future. Keeping --args would mean changes to the file
58     // would be ignored.
59     if (i->first != "q" && i->first != "root" && i->first != "args") {
60       std::string escaped_value =
61           EscapeString(FilePathToUTF8(i->second), escape_shell, NULL);
62       cmdline.AppendSwitchASCII(i->first, escaped_value);
63     }
64   }
65
66 #if defined(OS_WIN)
67   return base::WideToUTF8(cmdline.GetCommandLineString());
68 #else
69   return cmdline.GetCommandLineString();
70 #endif
71 }
72
73 }  // namespace
74
75 NinjaBuildWriter::NinjaBuildWriter(
76     const BuildSettings* build_settings,
77     const std::vector<const Settings*>& all_settings,
78     const Toolchain* default_toolchain,
79     const std::vector<const Target*>& default_toolchain_targets,
80     std::ostream& out,
81     std::ostream& dep_out)
82     : build_settings_(build_settings),
83       all_settings_(all_settings),
84       default_toolchain_(default_toolchain),
85       default_toolchain_targets_(default_toolchain_targets),
86       out_(out),
87       dep_out_(dep_out),
88       path_output_(build_settings->build_dir(), ESCAPE_NINJA) {
89 }
90
91 NinjaBuildWriter::~NinjaBuildWriter() {
92 }
93
94 void NinjaBuildWriter::Run() {
95   WriteNinjaRules();
96   WriteLinkPool();
97   WriteSubninjas();
98   WritePhonyAndAllRules();
99 }
100
101 // static
102 bool NinjaBuildWriter::RunAndWriteFile(
103     const BuildSettings* build_settings,
104     const std::vector<const Settings*>& all_settings,
105     const Toolchain* default_toolchain,
106     const std::vector<const Target*>& default_toolchain_targets) {
107   ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, "build.ninja");
108
109   base::FilePath ninja_file(build_settings->GetFullPath(
110       SourceFile(build_settings->build_dir().value() + "build.ninja")));
111   base::CreateDirectory(ninja_file.DirName());
112
113   std::ofstream file;
114   file.open(FilePathToUTF8(ninja_file).c_str(),
115             std::ios_base::out | std::ios_base::binary);
116   if (file.fail())
117     return false;
118
119   std::ofstream depfile;
120   depfile.open((FilePathToUTF8(ninja_file) + ".d").c_str(),
121                std::ios_base::out | std::ios_base::binary);
122   if (depfile.fail())
123     return false;
124
125   NinjaBuildWriter gen(build_settings, all_settings, default_toolchain,
126                        default_toolchain_targets, file, depfile);
127   gen.Run();
128   return true;
129 }
130
131 void NinjaBuildWriter::WriteNinjaRules() {
132   out_ << "rule gn\n";
133   out_ << "  command = " << GetSelfInvocationCommand(build_settings_) << "\n";
134   out_ << "  description = Regenerating ninja files\n\n";
135
136   // This rule will regenerate the ninja files when any input file has changed.
137   out_ << "build build.ninja: gn\n"
138        << "  generator = 1\n"
139        << "  depfile = build.ninja.d\n";
140
141   // Input build files. These go in the ".d" file. If we write them as
142   // dependencies in the .ninja file itself, ninja will expect the files to
143   // exist and will error if they don't. When files are listed in a depfile,
144   // missing files are ignored.
145   dep_out_ << "build.ninja:";
146   std::vector<base::FilePath> input_files;
147   g_scheduler->input_file_manager()->GetAllPhysicalInputFileNames(&input_files);
148   for (size_t i = 0; i < input_files.size(); i++)
149     dep_out_ << " " << FilePathToUTF8(input_files[i]);
150
151   // Other files read by the build.
152   std::vector<base::FilePath> other_files = g_scheduler->GetGenDependencies();
153   for (size_t i = 0; i < other_files.size(); i++)
154     dep_out_ << " " << FilePathToUTF8(other_files[i]);
155
156   out_ << std::endl;
157 }
158
159 void NinjaBuildWriter::WriteLinkPool() {
160   out_ << "pool link_pool\n"
161        << "  depth = " << default_toolchain_->concurrent_links() << std::endl
162        << std::endl;
163 }
164
165 void NinjaBuildWriter::WriteSubninjas() {
166   for (size_t i = 0; i < all_settings_.size(); i++) {
167     out_ << "subninja ";
168     path_output_.WriteFile(out_, GetNinjaFileForToolchain(all_settings_[i]));
169     out_ << std::endl;
170   }
171   out_ << std::endl;
172 }
173
174 void NinjaBuildWriter::WritePhonyAndAllRules() {
175   std::string all_rules;
176
177   // Write phony rules for all uniquely-named targets in the default toolchain.
178   // Don't do other toolchains or we'll get naming conflicts, and if the name
179   // isn't unique, also skip it. The exception is for the toplevel targets
180   // which we also find.
181   std::map<std::string, int> small_name_count;
182   std::vector<const Target*> toplevel_targets;
183   for (size_t i = 0; i < default_toolchain_targets_.size(); i++) {
184     const Target* target = default_toolchain_targets_[i];
185     const Label& label = target->label();
186     small_name_count[label.name()]++;
187
188     // Look for targets with a name of the form
189     //   dir = "//foo/", name = "foo"
190     // i.e. where the target name matches the top level directory. We will
191     // always write phony rules for these even if there is another target with
192     // the same short name.
193     const std::string& dir_string = label.dir().value();
194     if (dir_string.size() == label.name().size() + 3 &&  // Size matches.
195         dir_string[0] == '/' && dir_string[1] == '/' &&  // "//" at beginning.
196         dir_string[dir_string.size() - 1] == '/' &&  // "/" at end.
197         dir_string.compare(2, label.name().size(), label.name()) == 0)
198       toplevel_targets.push_back(target);
199   }
200
201   for (size_t i = 0; i < default_toolchain_targets_.size(); i++) {
202     const Target* target = default_toolchain_targets_[i];
203     const Label& label = target->label();
204     OutputFile target_file(target->dependency_output_file());
205     // The output files may have leading "./" so normalize those away.
206     NormalizePath(&target_file.value());
207
208     // Write the long name "foo/bar:baz" for the target "//foo/bar:baz".
209     std::string long_name = label.GetUserVisibleName(false);
210     base::TrimString(long_name, "/", &long_name);
211     WritePhonyRule(target, target_file, long_name);
212
213     // Write the directory name with no target name if they match
214     // (e.g. "//foo/bar:bar" -> "foo/bar").
215     if (FindLastDirComponent(label.dir()) == label.name()) {
216       std::string medium_name =  DirectoryWithNoLastSlash(label.dir());
217       base::TrimString(medium_name, "/", &medium_name);
218       // That may have generated a name the same as the short name of the
219       // target which we already wrote.
220       if (medium_name != label.name())
221         WritePhonyRule(target, target_file, medium_name);
222     }
223
224     // Write short names for ones which are unique.
225     if (small_name_count[label.name()] == 1)
226       WritePhonyRule(target, target_file, label.name());
227
228     if (!all_rules.empty())
229       all_rules.append(" $\n    ");
230     all_rules.append(target_file.value());
231   }
232
233   // Pick up phony rules for the toplevel targets with non-unique names (which
234   // would have been skipped in the above loop).
235   for (size_t i = 0; i < toplevel_targets.size(); i++) {
236     if (small_name_count[toplevel_targets[i]->label().name()] > 1) {
237       const Target* target = toplevel_targets[i];
238       WritePhonyRule(target, target->dependency_output_file(),
239                      target->label().name());
240     }
241   }
242
243   if (!all_rules.empty()) {
244     out_ << "\nbuild all: phony " << all_rules << std::endl;
245     out_ << "default all" << std::endl;
246   }
247 }
248
249 void NinjaBuildWriter::WritePhonyRule(const Target* target,
250                                       const OutputFile& target_file,
251                                       const std::string& phony_name) {
252   if (target_file.value() == phony_name)
253     return;  // No need for a phony rule.
254
255   EscapeOptions ninja_escape;
256   ninja_escape.mode = ESCAPE_NINJA;
257
258   // Escape for special chars Ninja will handle.
259   std::string escaped = EscapeString(phony_name, ninja_escape, NULL);
260
261   out_ << "build " << escaped << ": phony ";
262   path_output_.WriteFile(out_, target_file);
263   out_ << std::endl;
264 }