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