OLD | NEW |
---|---|
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "tools/gn/ninja_build_writer.h" | 5 #include "tools/gn/ninja_build_writer.h" |
6 | 6 |
7 #include <fstream> | 7 #include <fstream> |
8 #include <map> | 8 #include <map> |
9 | 9 |
10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
11 #include "base/files/file_util.h" | 11 #include "base/files/file_util.h" |
12 #include "base/path_service.h" | 12 #include "base/path_service.h" |
13 #include "base/process/process_handle.h" | 13 #include "base/process/process_handle.h" |
14 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" |
15 #include "base/strings/utf_string_conversions.h" | 15 #include "base/strings/utf_string_conversions.h" |
16 #include "build/build_config.h" | 16 #include "build/build_config.h" |
17 #include "tools/gn/build_settings.h" | 17 #include "tools/gn/build_settings.h" |
18 #include "tools/gn/err.h" | |
18 #include "tools/gn/escape.h" | 19 #include "tools/gn/escape.h" |
19 #include "tools/gn/filesystem_utils.h" | 20 #include "tools/gn/filesystem_utils.h" |
20 #include "tools/gn/input_file_manager.h" | 21 #include "tools/gn/input_file_manager.h" |
21 #include "tools/gn/ninja_utils.h" | 22 #include "tools/gn/ninja_utils.h" |
22 #include "tools/gn/scheduler.h" | 23 #include "tools/gn/scheduler.h" |
23 #include "tools/gn/target.h" | 24 #include "tools/gn/target.h" |
24 #include "tools/gn/trace.h" | 25 #include "tools/gn/trace.h" |
25 | 26 |
26 #if defined(OS_WIN) | 27 #if defined(OS_WIN) |
27 #include <windows.h> | 28 #include <windows.h> |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
84 default_toolchain_(default_toolchain), | 85 default_toolchain_(default_toolchain), |
85 default_toolchain_targets_(default_toolchain_targets), | 86 default_toolchain_targets_(default_toolchain_targets), |
86 out_(out), | 87 out_(out), |
87 dep_out_(dep_out), | 88 dep_out_(dep_out), |
88 path_output_(build_settings->build_dir(), ESCAPE_NINJA) { | 89 path_output_(build_settings->build_dir(), ESCAPE_NINJA) { |
89 } | 90 } |
90 | 91 |
91 NinjaBuildWriter::~NinjaBuildWriter() { | 92 NinjaBuildWriter::~NinjaBuildWriter() { |
92 } | 93 } |
93 | 94 |
94 void NinjaBuildWriter::Run() { | 95 bool NinjaBuildWriter::Run() { |
brettw
2014/09/26 18:01:20
Yeah, this should probably take an Err* out param.
| |
95 WriteNinjaRules(); | 96 WriteNinjaRules(); |
96 WriteLinkPool(); | 97 WriteLinkPool(); |
97 WriteSubninjas(); | 98 WriteSubninjas(); |
98 WritePhonyAndAllRules(); | 99 return WritePhonyAndAllRules(); |
99 } | 100 } |
100 | 101 |
101 // static | 102 // static |
102 bool NinjaBuildWriter::RunAndWriteFile( | 103 bool NinjaBuildWriter::RunAndWriteFile( |
103 const BuildSettings* build_settings, | 104 const BuildSettings* build_settings, |
104 const std::vector<const Settings*>& all_settings, | 105 const std::vector<const Settings*>& all_settings, |
105 const Toolchain* default_toolchain, | 106 const Toolchain* default_toolchain, |
106 const std::vector<const Target*>& default_toolchain_targets) { | 107 const std::vector<const Target*>& default_toolchain_targets) { |
107 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, "build.ninja"); | 108 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, "build.ninja"); |
108 | 109 |
109 base::FilePath ninja_file(build_settings->GetFullPath( | 110 base::FilePath ninja_file(build_settings->GetFullPath( |
110 SourceFile(build_settings->build_dir().value() + "build.ninja"))); | 111 SourceFile(build_settings->build_dir().value() + "build.ninja"))); |
111 base::CreateDirectory(ninja_file.DirName()); | 112 base::CreateDirectory(ninja_file.DirName()); |
112 | 113 |
113 std::ofstream file; | 114 std::ofstream file; |
114 file.open(FilePathToUTF8(ninja_file).c_str(), | 115 file.open(FilePathToUTF8(ninja_file).c_str(), |
115 std::ios_base::out | std::ios_base::binary); | 116 std::ios_base::out | std::ios_base::binary); |
116 if (file.fail()) | 117 if (file.fail()) |
117 return false; | 118 return false; |
118 | 119 |
119 std::ofstream depfile; | 120 std::ofstream depfile; |
120 depfile.open((FilePathToUTF8(ninja_file) + ".d").c_str(), | 121 depfile.open((FilePathToUTF8(ninja_file) + ".d").c_str(), |
121 std::ios_base::out | std::ios_base::binary); | 122 std::ios_base::out | std::ios_base::binary); |
122 if (depfile.fail()) | 123 if (depfile.fail()) |
123 return false; | 124 return false; |
124 | 125 |
125 NinjaBuildWriter gen(build_settings, all_settings, default_toolchain, | 126 NinjaBuildWriter gen(build_settings, all_settings, default_toolchain, |
126 default_toolchain_targets, file, depfile); | 127 default_toolchain_targets, file, depfile); |
127 gen.Run(); | 128 return gen.Run(); |
128 return true; | |
129 } | 129 } |
130 | 130 |
131 void NinjaBuildWriter::WriteNinjaRules() { | 131 void NinjaBuildWriter::WriteNinjaRules() { |
132 out_ << "rule gn\n"; | 132 out_ << "rule gn\n"; |
133 out_ << " command = " << GetSelfInvocationCommand(build_settings_) << "\n"; | 133 out_ << " command = " << GetSelfInvocationCommand(build_settings_) << "\n"; |
134 out_ << " description = Regenerating ninja files\n\n"; | 134 out_ << " description = Regenerating ninja files\n\n"; |
135 | 135 |
136 // This rule will regenerate the ninja files when any input file has changed. | 136 // This rule will regenerate the ninja files when any input file has changed. |
137 out_ << "build build.ninja: gn\n" | 137 out_ << "build build.ninja: gn\n" |
138 << " generator = 1\n" | 138 << " generator = 1\n" |
(...skipping 25 matching lines...) Expand all Loading... | |
164 | 164 |
165 void NinjaBuildWriter::WriteSubninjas() { | 165 void NinjaBuildWriter::WriteSubninjas() { |
166 for (size_t i = 0; i < all_settings_.size(); i++) { | 166 for (size_t i = 0; i < all_settings_.size(); i++) { |
167 out_ << "subninja "; | 167 out_ << "subninja "; |
168 path_output_.WriteFile(out_, GetNinjaFileForToolchain(all_settings_[i])); | 168 path_output_.WriteFile(out_, GetNinjaFileForToolchain(all_settings_[i])); |
169 out_ << std::endl; | 169 out_ << std::endl; |
170 } | 170 } |
171 out_ << std::endl; | 171 out_ << std::endl; |
172 } | 172 } |
173 | 173 |
174 void NinjaBuildWriter::WritePhonyAndAllRules() { | 174 bool NinjaBuildWriter::WritePhonyAndAllRules() { |
175 std::string all_rules; | 175 std::string all_rules; |
176 | 176 |
177 // Write phony rules for all uniquely-named targets in the default toolchain. | 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 | 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 | 179 // isn't unique, also skip it. The exception is for the toplevel targets |
180 // which we also find. | 180 // which we also find. |
181 std::map<std::string, int> small_name_count; | 181 std::map<std::string, int> small_name_count; |
182 std::vector<const Target*> toplevel_targets; | 182 std::vector<const Target*> toplevel_targets; |
183 base::hash_set<std::string> target_files; | |
183 for (size_t i = 0; i < default_toolchain_targets_.size(); i++) { | 184 for (size_t i = 0; i < default_toolchain_targets_.size(); i++) { |
184 const Target* target = default_toolchain_targets_[i]; | 185 const Target* target = default_toolchain_targets_[i]; |
185 const Label& label = target->label(); | 186 const Label& label = target->label(); |
186 small_name_count[label.name()]++; | 187 small_name_count[label.name()]++; |
187 | 188 |
188 // Look for targets with a name of the form | 189 // Look for targets with a name of the form |
189 // dir = "//foo/", name = "foo" | 190 // dir = "//foo/", name = "foo" |
190 // i.e. where the target name matches the top level directory. We will | 191 // 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 // always write phony rules for these even if there is another target with |
192 // the same short name. | 193 // the same short name. |
193 const std::string& dir_string = label.dir().value(); | 194 const std::string& dir_string = label.dir().value(); |
194 if (dir_string.size() == label.name().size() + 3 && // Size matches. | 195 if (dir_string.size() == label.name().size() + 3 && // Size matches. |
195 dir_string[0] == '/' && dir_string[1] == '/' && // "//" at beginning. | 196 dir_string[0] == '/' && dir_string[1] == '/' && // "//" at beginning. |
196 dir_string[dir_string.size() - 1] == '/' && // "/" at end. | 197 dir_string[dir_string.size() - 1] == '/' && // "/" at end. |
197 dir_string.compare(2, label.name().size(), label.name()) == 0) | 198 dir_string.compare(2, label.name().size(), label.name()) == 0) |
198 toplevel_targets.push_back(target); | 199 toplevel_targets.push_back(target); |
199 } | 200 } |
200 | 201 |
201 for (size_t i = 0; i < default_toolchain_targets_.size(); i++) { | 202 for (size_t i = 0; i < default_toolchain_targets_.size(); i++) { |
202 const Target* target = default_toolchain_targets_[i]; | 203 const Target* target = default_toolchain_targets_[i]; |
203 const Label& label = target->label(); | 204 const Label& label = target->label(); |
204 OutputFile target_file(target->dependency_output_file()); | 205 OutputFile target_file(target->dependency_output_file()); |
205 // The output files may have leading "./" so normalize those away. | 206 // The output files may have leading "./" so normalize those away. |
206 NormalizePath(&target_file.value()); | 207 NormalizePath(&target_file.value()); |
208 if (!target_files.insert(target_file.value()).second) { | |
209 Err(Location(), "Duplicate rules for " + target_file.value()) | |
210 .PrintToStdout(); | |
211 return false; | |
212 } | |
207 | 213 |
208 // Write the long name "foo/bar:baz" for the target "//foo/bar:baz". | 214 // Write the long name "foo/bar:baz" for the target "//foo/bar:baz". |
209 std::string long_name = label.GetUserVisibleName(false); | 215 std::string long_name = label.GetUserVisibleName(false); |
210 base::TrimString(long_name, "/", &long_name); | 216 base::TrimString(long_name, "/", &long_name); |
211 WritePhonyRule(target, target_file, long_name); | 217 WritePhonyRule(target, target_file, long_name); |
212 | 218 |
213 // Write the directory name with no target name if they match | 219 // Write the directory name with no target name if they match |
214 // (e.g. "//foo/bar:bar" -> "foo/bar"). | 220 // (e.g. "//foo/bar:bar" -> "foo/bar"). |
215 if (FindLastDirComponent(label.dir()) == label.name()) { | 221 if (FindLastDirComponent(label.dir()) == label.name()) { |
216 std::string medium_name = DirectoryWithNoLastSlash(label.dir()); | 222 std::string medium_name = DirectoryWithNoLastSlash(label.dir()); |
(...skipping 20 matching lines...) Expand all Loading... | |
237 const Target* target = toplevel_targets[i]; | 243 const Target* target = toplevel_targets[i]; |
238 WritePhonyRule(target, target->dependency_output_file(), | 244 WritePhonyRule(target, target->dependency_output_file(), |
239 target->label().name()); | 245 target->label().name()); |
240 } | 246 } |
241 } | 247 } |
242 | 248 |
243 if (!all_rules.empty()) { | 249 if (!all_rules.empty()) { |
244 out_ << "\nbuild all: phony " << all_rules << std::endl; | 250 out_ << "\nbuild all: phony " << all_rules << std::endl; |
245 out_ << "default all" << std::endl; | 251 out_ << "default all" << std::endl; |
246 } | 252 } |
253 return true; | |
247 } | 254 } |
248 | 255 |
249 void NinjaBuildWriter::WritePhonyRule(const Target* target, | 256 void NinjaBuildWriter::WritePhonyRule(const Target* target, |
250 const OutputFile& target_file, | 257 const OutputFile& target_file, |
251 const std::string& phony_name) { | 258 const std::string& phony_name) { |
252 if (target_file.value() == phony_name) | 259 if (target_file.value() == phony_name) |
253 return; // No need for a phony rule. | 260 return; // No need for a phony rule. |
254 | 261 |
255 EscapeOptions ninja_escape; | 262 EscapeOptions ninja_escape; |
256 ninja_escape.mode = ESCAPE_NINJA; | 263 ninja_escape.mode = ESCAPE_NINJA; |
257 | 264 |
258 // Escape for special chars Ninja will handle. | 265 // Escape for special chars Ninja will handle. |
259 std::string escaped = EscapeString(phony_name, ninja_escape, NULL); | 266 std::string escaped = EscapeString(phony_name, ninja_escape, NULL); |
260 | 267 |
261 out_ << "build " << escaped << ": phony "; | 268 out_ << "build " << escaped << ": phony "; |
262 path_output_.WriteFile(out_, target_file); | 269 path_output_.WriteFile(out_, target_file); |
263 out_ << std::endl; | 270 out_ << std::endl; |
264 } | 271 } |
OLD | NEW |