Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(276)

Side by Side Diff: tools/gn/ninja_build_writer.cc

Issue 455193003: GN: Generate error if multiple rules generate same file (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tools/gn/ninja_build_writer.h ('k') | tools/gn/ninja_writer.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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(Err* err) {
95 WriteNinjaRules(); 96 WriteNinjaRules();
96 WriteLinkPool(); 97 WriteLinkPool();
97 WriteSubninjas(); 98 WriteSubninjas();
98 WritePhonyAndAllRules(); 99 return WritePhonyAndAllRules(err);
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,
108 Err* err) {
107 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, "build.ninja"); 109 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, "build.ninja");
108 110
109 base::FilePath ninja_file(build_settings->GetFullPath( 111 base::FilePath ninja_file(build_settings->GetFullPath(
110 SourceFile(build_settings->build_dir().value() + "build.ninja"))); 112 SourceFile(build_settings->build_dir().value() + "build.ninja")));
111 base::CreateDirectory(ninja_file.DirName()); 113 base::CreateDirectory(ninja_file.DirName());
112 114
113 std::ofstream file; 115 std::ofstream file;
114 file.open(FilePathToUTF8(ninja_file).c_str(), 116 file.open(FilePathToUTF8(ninja_file).c_str(),
115 std::ios_base::out | std::ios_base::binary); 117 std::ios_base::out | std::ios_base::binary);
116 if (file.fail()) 118 if (file.fail()) {
119 *err = Err(Location(), "Couldn't open build.ninja for writing");
117 return false; 120 return false;
121 }
118 122
119 std::ofstream depfile; 123 std::ofstream depfile;
120 depfile.open((FilePathToUTF8(ninja_file) + ".d").c_str(), 124 depfile.open((FilePathToUTF8(ninja_file) + ".d").c_str(),
121 std::ios_base::out | std::ios_base::binary); 125 std::ios_base::out | std::ios_base::binary);
122 if (depfile.fail()) 126 if (depfile.fail()) {
127 *err = Err(Location(), "Couldn't open depfile for writing");
123 return false; 128 return false;
129 }
124 130
125 NinjaBuildWriter gen(build_settings, all_settings, default_toolchain, 131 NinjaBuildWriter gen(build_settings, all_settings, default_toolchain,
126 default_toolchain_targets, file, depfile); 132 default_toolchain_targets, file, depfile);
127 gen.Run(); 133 return gen.Run(err);
128 return true;
129 } 134 }
130 135
131 void NinjaBuildWriter::WriteNinjaRules() { 136 void NinjaBuildWriter::WriteNinjaRules() {
132 out_ << "rule gn\n"; 137 out_ << "rule gn\n";
133 out_ << " command = " << GetSelfInvocationCommand(build_settings_) << "\n"; 138 out_ << " command = " << GetSelfInvocationCommand(build_settings_) << "\n";
134 out_ << " description = Regenerating ninja files\n\n"; 139 out_ << " description = Regenerating ninja files\n\n";
135 140
136 // This rule will regenerate the ninja files when any input file has changed. 141 // This rule will regenerate the ninja files when any input file has changed.
137 out_ << "build build.ninja: gn\n" 142 out_ << "build build.ninja: gn\n"
138 << " generator = 1\n" 143 << " generator = 1\n"
(...skipping 25 matching lines...) Expand all
164 169
165 void NinjaBuildWriter::WriteSubninjas() { 170 void NinjaBuildWriter::WriteSubninjas() {
166 for (size_t i = 0; i < all_settings_.size(); i++) { 171 for (size_t i = 0; i < all_settings_.size(); i++) {
167 out_ << "subninja "; 172 out_ << "subninja ";
168 path_output_.WriteFile(out_, GetNinjaFileForToolchain(all_settings_[i])); 173 path_output_.WriteFile(out_, GetNinjaFileForToolchain(all_settings_[i]));
169 out_ << std::endl; 174 out_ << std::endl;
170 } 175 }
171 out_ << std::endl; 176 out_ << std::endl;
172 } 177 }
173 178
174 void NinjaBuildWriter::WritePhonyAndAllRules() { 179 bool NinjaBuildWriter::WritePhonyAndAllRules(Err* err) {
175 std::string all_rules; 180 std::string all_rules;
176 181
177 // Write phony rules for all uniquely-named targets in the default toolchain. 182 // 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 183 // 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 184 // isn't unique, also skip it. The exception is for the toplevel targets
180 // which we also find. 185 // which we also find.
181 std::map<std::string, int> small_name_count; 186 std::map<std::string, int> small_name_count;
182 std::vector<const Target*> toplevel_targets; 187 std::vector<const Target*> toplevel_targets;
188 base::hash_set<std::string> target_files;
183 for (size_t i = 0; i < default_toolchain_targets_.size(); i++) { 189 for (size_t i = 0; i < default_toolchain_targets_.size(); i++) {
184 const Target* target = default_toolchain_targets_[i]; 190 const Target* target = default_toolchain_targets_[i];
185 const Label& label = target->label(); 191 const Label& label = target->label();
186 small_name_count[label.name()]++; 192 small_name_count[label.name()]++;
187 193
188 // Look for targets with a name of the form 194 // Look for targets with a name of the form
189 // dir = "//foo/", name = "foo" 195 // dir = "//foo/", name = "foo"
190 // i.e. where the target name matches the top level directory. We will 196 // 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 197 // always write phony rules for these even if there is another target with
192 // the same short name. 198 // the same short name.
193 const std::string& dir_string = label.dir().value(); 199 const std::string& dir_string = label.dir().value();
194 if (dir_string.size() == label.name().size() + 3 && // Size matches. 200 if (dir_string.size() == label.name().size() + 3 && // Size matches.
195 dir_string[0] == '/' && dir_string[1] == '/' && // "//" at beginning. 201 dir_string[0] == '/' && dir_string[1] == '/' && // "//" at beginning.
196 dir_string[dir_string.size() - 1] == '/' && // "/" at end. 202 dir_string[dir_string.size() - 1] == '/' && // "/" at end.
197 dir_string.compare(2, label.name().size(), label.name()) == 0) 203 dir_string.compare(2, label.name().size(), label.name()) == 0)
198 toplevel_targets.push_back(target); 204 toplevel_targets.push_back(target);
199 } 205 }
200 206
201 for (size_t i = 0; i < default_toolchain_targets_.size(); i++) { 207 for (size_t i = 0; i < default_toolchain_targets_.size(); i++) {
202 const Target* target = default_toolchain_targets_[i]; 208 const Target* target = default_toolchain_targets_[i];
203 const Label& label = target->label(); 209 const Label& label = target->label();
204 OutputFile target_file(target->dependency_output_file()); 210 OutputFile target_file(target->dependency_output_file());
205 // The output files may have leading "./" so normalize those away. 211 // The output files may have leading "./" so normalize those away.
206 NormalizePath(&target_file.value()); 212 NormalizePath(&target_file.value());
213 if (!target_files.insert(target_file.value()).second) {
214 *err = Err(Location(), "Duplicate rules for " + target_file.value());
215 return false;
216 }
207 217
208 // Write the long name "foo/bar:baz" for the target "//foo/bar:baz". 218 // Write the long name "foo/bar:baz" for the target "//foo/bar:baz".
209 std::string long_name = label.GetUserVisibleName(false); 219 std::string long_name = label.GetUserVisibleName(false);
210 base::TrimString(long_name, "/", &long_name); 220 base::TrimString(long_name, "/", &long_name);
211 WritePhonyRule(target, target_file, long_name); 221 WritePhonyRule(target, target_file, long_name);
212 222
213 // Write the directory name with no target name if they match 223 // Write the directory name with no target name if they match
214 // (e.g. "//foo/bar:bar" -> "foo/bar"). 224 // (e.g. "//foo/bar:bar" -> "foo/bar").
215 if (FindLastDirComponent(label.dir()) == label.name()) { 225 if (FindLastDirComponent(label.dir()) == label.name()) {
216 std::string medium_name = DirectoryWithNoLastSlash(label.dir()); 226 std::string medium_name = DirectoryWithNoLastSlash(label.dir());
(...skipping 20 matching lines...) Expand all
237 const Target* target = toplevel_targets[i]; 247 const Target* target = toplevel_targets[i];
238 WritePhonyRule(target, target->dependency_output_file(), 248 WritePhonyRule(target, target->dependency_output_file(),
239 target->label().name()); 249 target->label().name());
240 } 250 }
241 } 251 }
242 252
243 if (!all_rules.empty()) { 253 if (!all_rules.empty()) {
244 out_ << "\nbuild all: phony " << all_rules << std::endl; 254 out_ << "\nbuild all: phony " << all_rules << std::endl;
245 out_ << "default all" << std::endl; 255 out_ << "default all" << std::endl;
246 } 256 }
257 return true;
247 } 258 }
248 259
249 void NinjaBuildWriter::WritePhonyRule(const Target* target, 260 void NinjaBuildWriter::WritePhonyRule(const Target* target,
250 const OutputFile& target_file, 261 const OutputFile& target_file,
251 const std::string& phony_name) { 262 const std::string& phony_name) {
252 if (target_file.value() == phony_name) 263 if (target_file.value() == phony_name)
253 return; // No need for a phony rule. 264 return; // No need for a phony rule.
254 265
255 EscapeOptions ninja_escape; 266 EscapeOptions ninja_escape;
256 ninja_escape.mode = ESCAPE_NINJA; 267 ninja_escape.mode = ESCAPE_NINJA;
257 268
258 // Escape for special chars Ninja will handle. 269 // Escape for special chars Ninja will handle.
259 std::string escaped = EscapeString(phony_name, ninja_escape, NULL); 270 std::string escaped = EscapeString(phony_name, ninja_escape, NULL);
260 271
261 out_ << "build " << escaped << ": phony "; 272 out_ << "build " << escaped << ": phony ";
262 path_output_.WriteFile(out_, target_file); 273 path_output_.WriteFile(out_, target_file);
263 out_ << std::endl; 274 out_ << std::endl;
264 } 275 }
OLDNEW
« no previous file with comments | « tools/gn/ninja_build_writer.h ('k') | tools/gn/ninja_writer.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698