| 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 <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <fstream> | 9 #include <fstream> |
| 10 #include <map> | 10 #include <map> |
| 11 #include <sstream> |
| 11 | 12 |
| 12 #include "base/command_line.h" | 13 #include "base/command_line.h" |
| 13 #include "base/files/file_util.h" | 14 #include "base/files/file_util.h" |
| 14 #include "base/path_service.h" | 15 #include "base/path_service.h" |
| 15 #include "base/process/process_handle.h" | 16 #include "base/process/process_handle.h" |
| 16 #include "base/strings/string_util.h" | 17 #include "base/strings/string_util.h" |
| 17 #include "base/strings/utf_string_conversions.h" | 18 #include "base/strings/utf_string_conversions.h" |
| 18 #include "build/build_config.h" | 19 #include "build/build_config.h" |
| 19 #include "tools/gn/build_settings.h" | 20 #include "tools/gn/build_settings.h" |
| 20 #include "tools/gn/err.h" | 21 #include "tools/gn/err.h" |
| 21 #include "tools/gn/escape.h" | 22 #include "tools/gn/escape.h" |
| 22 #include "tools/gn/filesystem_utils.h" | 23 #include "tools/gn/filesystem_utils.h" |
| 23 #include "tools/gn/input_file_manager.h" | 24 #include "tools/gn/input_file_manager.h" |
| 24 #include "tools/gn/ninja_utils.h" | 25 #include "tools/gn/ninja_utils.h" |
| 25 #include "tools/gn/scheduler.h" | 26 #include "tools/gn/scheduler.h" |
| 26 #include "tools/gn/switches.h" | 27 #include "tools/gn/switches.h" |
| 27 #include "tools/gn/target.h" | 28 #include "tools/gn/target.h" |
| 28 #include "tools/gn/trace.h" | 29 #include "tools/gn/trace.h" |
| 29 | 30 |
| 30 #if defined(OS_WIN) | 31 #if defined(OS_WIN) |
| 31 #include <windows.h> | 32 #include <windows.h> |
| 32 #endif | 33 #endif |
| 33 | 34 |
| 34 namespace { | 35 namespace { |
| 35 | 36 |
| 37 struct Counts { |
| 38 Counts() : count(0), last_seen(nullptr) {} |
| 39 |
| 40 // Number of targets of this type. |
| 41 int count; |
| 42 |
| 43 // The last one we encountered. |
| 44 const Target* last_seen; |
| 45 }; |
| 46 |
| 36 std::string GetSelfInvocationCommand(const BuildSettings* build_settings) { | 47 std::string GetSelfInvocationCommand(const BuildSettings* build_settings) { |
| 37 base::FilePath executable; | 48 base::FilePath executable; |
| 38 PathService::Get(base::FILE_EXE, &executable); | 49 PathService::Get(base::FILE_EXE, &executable); |
| 39 | 50 |
| 40 base::CommandLine cmdline(executable.NormalizePathSeparatorsTo('/')); | 51 base::CommandLine cmdline(executable.NormalizePathSeparatorsTo('/')); |
| 41 cmdline.AppendArg("gen"); | 52 cmdline.AppendArg("gen"); |
| 42 cmdline.AppendArg(build_settings->build_dir().value()); | 53 cmdline.AppendArg(build_settings->build_dir().value()); |
| 43 cmdline.AppendSwitchPath(std::string("--") + switches::kRoot, | 54 cmdline.AppendSwitchPath(std::string("--") + switches::kRoot, |
| 44 build_settings->root_path()); | 55 build_settings->root_path()); |
| 45 // Successful automatic invocations shouldn't print output. | 56 // Successful automatic invocations shouldn't print output. |
| (...skipping 26 matching lines...) Expand all Loading... |
| 72 } | 83 } |
| 73 } | 84 } |
| 74 | 85 |
| 75 #if defined(OS_WIN) | 86 #if defined(OS_WIN) |
| 76 return base::WideToUTF8(cmdline.GetCommandLineString()); | 87 return base::WideToUTF8(cmdline.GetCommandLineString()); |
| 77 #else | 88 #else |
| 78 return cmdline.GetCommandLineString(); | 89 return cmdline.GetCommandLineString(); |
| 79 #endif | 90 #endif |
| 80 } | 91 } |
| 81 | 92 |
| 82 OutputFile GetTargetOutputFile(const Target* target) { | |
| 83 OutputFile result(target->dependency_output_file()); | |
| 84 | |
| 85 // The output files may have leading "./" so normalize those away. | |
| 86 NormalizePath(&result.value()); | |
| 87 return result; | |
| 88 } | |
| 89 | |
| 90 bool HasOutputIdenticalToLabel(const Target* target, | |
| 91 const std::string& short_name) { | |
| 92 if (target->output_type() != Target::ACTION && | |
| 93 target->output_type() != Target::ACTION_FOREACH) | |
| 94 return false; | |
| 95 | |
| 96 // Rather than convert all outputs to be relative to the build directory | |
| 97 // and then compare to the short name, convert the short name to look like a | |
| 98 // file in the output directory since this is only one conversion. | |
| 99 SourceFile short_name_as_source_file( | |
| 100 target->settings()->build_settings()->build_dir().value() + short_name); | |
| 101 | |
| 102 std::vector<SourceFile> outputs_as_source; | |
| 103 target->action_values().GetOutputsAsSourceFiles(target, &outputs_as_source); | |
| 104 for (const SourceFile& output_as_source : outputs_as_source) { | |
| 105 if (output_as_source == short_name_as_source_file) | |
| 106 return true; | |
| 107 } | |
| 108 return false; | |
| 109 } | |
| 110 | |
| 111 // Given an output that appears more than once, generates an error message | 93 // Given an output that appears more than once, generates an error message |
| 112 // that describes the problem and which targets generate it. | 94 // that describes the problem and which targets generate it. |
| 113 Err GetDuplicateOutputError(const std::vector<const Target*>& all_targets, | 95 Err GetDuplicateOutputError(const std::vector<const Target*>& all_targets, |
| 114 const OutputFile& bad_output) { | 96 const OutputFile& bad_output) { |
| 115 std::vector<const Target*> matches; | 97 std::vector<const Target*> matches; |
| 116 for (const Target* target : all_targets) { | 98 for (const Target* target : all_targets) { |
| 117 for (const auto& output : target->computed_outputs()) { | 99 for (const auto& output : target->computed_outputs()) { |
| 118 if (output == bad_output) { | 100 if (output == bad_output) { |
| 119 matches.push_back(target); | 101 matches.push_back(target); |
| 120 break; | 102 break; |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 171 | 153 |
| 172 // static | 154 // static |
| 173 bool NinjaBuildWriter::RunAndWriteFile( | 155 bool NinjaBuildWriter::RunAndWriteFile( |
| 174 const BuildSettings* build_settings, | 156 const BuildSettings* build_settings, |
| 175 const std::vector<const Settings*>& all_settings, | 157 const std::vector<const Settings*>& all_settings, |
| 176 const Toolchain* default_toolchain, | 158 const Toolchain* default_toolchain, |
| 177 const std::vector<const Target*>& default_toolchain_targets, | 159 const std::vector<const Target*>& default_toolchain_targets, |
| 178 Err* err) { | 160 Err* err) { |
| 179 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, "build.ninja"); | 161 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, "build.ninja"); |
| 180 | 162 |
| 181 base::FilePath ninja_file(build_settings->GetFullPath( | 163 std::stringstream file; |
| 182 SourceFile(build_settings->build_dir().value() + "build.ninja"))); | 164 std::stringstream depfile; |
| 183 base::CreateDirectory(ninja_file.DirName()); | |
| 184 | |
| 185 std::ofstream file; | |
| 186 file.open(FilePathToUTF8(ninja_file).c_str(), | |
| 187 std::ios_base::out | std::ios_base::binary); | |
| 188 if (file.fail()) { | |
| 189 *err = Err(Location(), "Couldn't open build.ninja for writing"); | |
| 190 return false; | |
| 191 } | |
| 192 | |
| 193 std::ofstream depfile; | |
| 194 depfile.open((FilePathToUTF8(ninja_file) + ".d").c_str(), | |
| 195 std::ios_base::out | std::ios_base::binary); | |
| 196 if (depfile.fail()) { | |
| 197 *err = Err(Location(), "Couldn't open depfile for writing"); | |
| 198 return false; | |
| 199 } | |
| 200 | |
| 201 NinjaBuildWriter gen(build_settings, all_settings, default_toolchain, | 165 NinjaBuildWriter gen(build_settings, all_settings, default_toolchain, |
| 202 default_toolchain_targets, file, depfile); | 166 default_toolchain_targets, file, depfile); |
| 203 return gen.Run(err); | 167 if (!gen.Run(err)) |
| 168 return false; |
| 169 |
| 170 base::FilePath ninja_file_name(build_settings->GetFullPath( |
| 171 SourceFile(build_settings->build_dir().value() + "build.ninja"))); |
| 172 base::FilePath dep_file_name(build_settings->GetFullPath( |
| 173 SourceFile(build_settings->build_dir().value() + "build.ninja.d"))); |
| 174 base::CreateDirectory(ninja_file_name.DirName()); |
| 175 |
| 176 if (!WriteFileIfChanged(ninja_file_name, file.str(), err) || |
| 177 !WriteFileIfChanged(dep_file_name, depfile.str(), err)) |
| 178 return false; |
| 179 |
| 180 return true; |
| 204 } | 181 } |
| 205 | 182 |
| 206 void NinjaBuildWriter::WriteNinjaRules() { | 183 void NinjaBuildWriter::WriteNinjaRules() { |
| 207 out_ << "rule gn\n"; | 184 out_ << "rule gn\n"; |
| 208 out_ << " command = " << GetSelfInvocationCommand(build_settings_) << "\n"; | 185 out_ << " command = " << GetSelfInvocationCommand(build_settings_) << "\n"; |
| 209 out_ << " description = Regenerating ninja files\n\n"; | 186 out_ << " description = Regenerating ninja files\n\n"; |
| 210 | 187 |
| 211 // This rule will regenerate the ninja files when any input file has changed. | 188 // This rule will regenerate the ninja files when any input file has changed. |
| 212 out_ << "build build.ninja: gn\n" | 189 out_ << "build build.ninja: gn\n" |
| 213 << " generator = 1\n" | 190 << " generator = 1\n" |
| (...skipping 30 matching lines...) Expand all Loading... |
| 244 void NinjaBuildWriter::WriteSubninjas() { | 221 void NinjaBuildWriter::WriteSubninjas() { |
| 245 for (const auto& elem : all_settings_) { | 222 for (const auto& elem : all_settings_) { |
| 246 out_ << "subninja "; | 223 out_ << "subninja "; |
| 247 path_output_.WriteFile(out_, GetNinjaFileForToolchain(elem)); | 224 path_output_.WriteFile(out_, GetNinjaFileForToolchain(elem)); |
| 248 out_ << std::endl; | 225 out_ << std::endl; |
| 249 } | 226 } |
| 250 out_ << std::endl; | 227 out_ << std::endl; |
| 251 } | 228 } |
| 252 | 229 |
| 253 bool NinjaBuildWriter::WritePhonyAndAllRules(Err* err) { | 230 bool NinjaBuildWriter::WritePhonyAndAllRules(Err* err) { |
| 254 std::string all_rules; | |
| 255 | |
| 256 // Track rules as we generate them so we don't accidentally write a phony | 231 // Track rules as we generate them so we don't accidentally write a phony |
| 257 // rule that collides with something else. | 232 // rule that collides with something else. |
| 258 // GN internally generates an "all" target, so don't duplicate it. | 233 // GN internally generates an "all" target, so don't duplicate it. |
| 259 std::set<std::string> written_rules; | 234 base::hash_set<std::string> written_rules; |
| 260 written_rules.insert("all"); | 235 written_rules.insert("all"); |
| 261 | 236 |
| 262 // Write phony rules for all uniquely-named targets in the default toolchain. | 237 // Set if we encounter a target named "//:default". |
| 263 // Don't do other toolchains or we'll get naming conflicts, and if the name | 238 bool default_target_exists = false; |
| 264 // isn't unique, also skip it. The exception is for the toplevel targets | 239 |
| 265 // which we also find. | 240 // Targets in the root build file. |
| 266 std::map<std::string, int> small_name_count; | |
| 267 std::map<std::string, int> exe_count; | |
| 268 std::vector<const Target*> toplevel_targets; | 241 std::vector<const Target*> toplevel_targets; |
| 269 base::hash_set<std::string> target_files; | 242 |
| 270 for (const auto& target : default_toolchain_targets_) { | 243 // Targets with names matching their toplevel directories. For example |
| 244 // "//foo:foo". Expect this is the naming scheme for "big components." |
| 245 std::vector<const Target*> toplevel_dir_targets; |
| 246 |
| 247 // Tracks the number of each target with the given short name, as well |
| 248 // as the short names of executables (which will be a subset of short_names). |
| 249 std::map<std::string, Counts> short_names; |
| 250 std::map<std::string, Counts> exes; |
| 251 |
| 252 for (const Target* target : default_toolchain_targets_) { |
| 271 const Label& label = target->label(); | 253 const Label& label = target->label(); |
| 272 small_name_count[label.name()]++; | 254 const std::string& short_name = label.name(); |
| 273 | 255 |
| 274 // Look for targets with a name of the form | 256 if (label.dir().value() == "//" && label.name() == "default") |
| 275 // dir = "//foo/", name = "foo" | 257 default_target_exists = true; |
| 276 // i.e. where the target name matches the top level directory. We will | 258 |
| 277 // always write phony rules for these even if there is another target with | 259 // Count the number of targets with the given short name. |
| 278 // the same short name. | 260 Counts& short_names_counts = short_names[short_name]; |
| 261 short_names_counts.count++; |
| 262 short_names_counts.last_seen = target; |
| 263 |
| 264 // Count executables with the given short name. |
| 265 if (target->output_type() == Target::EXECUTABLE) { |
| 266 Counts& exes_counts = exes[short_name]; |
| 267 exes_counts.count++; |
| 268 exes_counts.last_seen = target; |
| 269 } |
| 270 |
| 271 // Find targets in "important" directories. |
| 279 const std::string& dir_string = label.dir().value(); | 272 const std::string& dir_string = label.dir().value(); |
| 280 if (dir_string.size() == label.name().size() + 3 && // Size matches. | 273 if (dir_string.size() == 2 && |
| 274 dir_string[0] == '/' && dir_string[1] == '/') { |
| 275 toplevel_targets.push_back(target); |
| 276 } else if ( |
| 277 dir_string.size() == label.name().size() + 3 && // Size matches. |
| 281 dir_string[0] == '/' && dir_string[1] == '/' && // "//" at beginning. | 278 dir_string[0] == '/' && dir_string[1] == '/' && // "//" at beginning. |
| 282 dir_string[dir_string.size() - 1] == '/' && // "/" at end. | 279 dir_string[dir_string.size() - 1] == '/' && // "/" at end. |
| 283 dir_string.compare(2, label.name().size(), label.name()) == 0) | 280 dir_string.compare(2, label.name().size(), label.name()) == 0) { |
| 284 toplevel_targets.push_back(target); | 281 toplevel_dir_targets.push_back(target); |
| 282 } |
| 285 | 283 |
| 286 // Look for executables; later we will generate phony rules for them | 284 // Add the output files from each target to the written rules so that |
| 287 // even if there are non-executable targets with the same name. | 285 // we don't write phony rules that collide with anything generated by the |
| 288 if (target->output_type() == Target::EXECUTABLE) | 286 // build. |
| 289 exe_count[label.name()]++; | 287 // |
| 290 | 288 // If at this point there is a collision (no phony rules have been |
| 291 // Add the files to the list of generated targets so we don't write phony | 289 // generated yet), two targets make the same output so throw an error. |
| 292 // rules that collide. | |
| 293 std::string target_file(target->dependency_output_file().value()); | |
| 294 NormalizePath(&target_file); | |
| 295 written_rules.insert(target_file); | |
| 296 } | |
| 297 | |
| 298 for (const auto& target : default_toolchain_targets_) { | |
| 299 const Label& label = target->label(); | |
| 300 for (const auto& output : target->computed_outputs()) { | 290 for (const auto& output : target->computed_outputs()) { |
| 301 if (!target_files.insert(output.value()).second) { | 291 // Need to normalize because many toolchain outputs will be preceeded |
| 292 // with "./". |
| 293 std::string output_string(output.value()); |
| 294 NormalizePath(&output_string); |
| 295 if (!written_rules.insert(output_string).second) { |
| 302 *err = GetDuplicateOutputError(default_toolchain_targets_, output); | 296 *err = GetDuplicateOutputError(default_toolchain_targets_, output); |
| 303 return false; | 297 return false; |
| 304 } | 298 } |
| 305 } | 299 } |
| 300 } |
| 306 | 301 |
| 307 OutputFile target_file = GetTargetOutputFile(target); | 302 // First prefer the short names of toplevel targets. |
| 303 for (const Target* target : toplevel_targets) { |
| 304 if (written_rules.insert(target->label().name()).second) |
| 305 WritePhonyRule(target, target->label().name()); |
| 306 } |
| 307 |
| 308 // Next prefer short names of toplevel dir targets. |
| 309 for (const Target* target : toplevel_dir_targets) { |
| 310 if (written_rules.insert(target->label().name()).second) |
| 311 WritePhonyRule(target, target->label().name()); |
| 312 } |
| 313 |
| 314 // Write out the names labels of executables. Many toolchains will produce |
| 315 // executables in the root build directory with no extensions, so the names |
| 316 // will already exist and this will be a no-op. But on Windows such programs |
| 317 // will have extensions, and executables may override the output directory to |
| 318 // go into some other place. |
| 319 // |
| 320 // Putting this after the "toplevel" rules above also means that you can |
| 321 // steal the short name from an executable by outputting the executable to |
| 322 // a different directory or using a different output name, and writing a |
| 323 // toplevel build rule. |
| 324 for (const auto& pair : exes) { |
| 325 const Counts& counts = pair.second; |
| 326 const std::string& short_name = counts.last_seen->label().name(); |
| 327 if (counts.count == 1 && written_rules.insert(short_name).second) |
| 328 WritePhonyRule(counts.last_seen, short_name); |
| 329 } |
| 330 |
| 331 // Write short names when those names are unique and not already taken. |
| 332 for (const auto& pair : short_names) { |
| 333 const Counts& counts = pair.second; |
| 334 const std::string& short_name = counts.last_seen->label().name(); |
| 335 if (counts.count == 1 && written_rules.insert(short_name).second) |
| 336 WritePhonyRule(counts.last_seen, short_name); |
| 337 } |
| 338 |
| 339 // Write the label variants of the target name. |
| 340 for (const Target* target : default_toolchain_targets_) { |
| 341 const Label& label = target->label(); |
| 342 |
| 308 // Write the long name "foo/bar:baz" for the target "//foo/bar:baz". | 343 // Write the long name "foo/bar:baz" for the target "//foo/bar:baz". |
| 309 std::string long_name = label.GetUserVisibleName(false); | 344 std::string long_name = label.GetUserVisibleName(false); |
| 310 base::TrimString(long_name, "/", &long_name); | 345 base::TrimString(long_name, "/", &long_name); |
| 311 WritePhonyRule(target, target_file, long_name, &written_rules); | 346 if (written_rules.insert(long_name).second) |
| 347 WritePhonyRule(target, long_name); |
| 312 | 348 |
| 313 // Write the directory name with no target name if they match | 349 // Write the directory name with no target name if they match |
| 314 // (e.g. "//foo/bar:bar" -> "foo/bar"). | 350 // (e.g. "//foo/bar:bar" -> "foo/bar"). |
| 315 if (FindLastDirComponent(label.dir()) == label.name()) { | 351 if (FindLastDirComponent(label.dir()) == label.name()) { |
| 316 std::string medium_name = DirectoryWithNoLastSlash(label.dir()); | 352 std::string medium_name = DirectoryWithNoLastSlash(label.dir()); |
| 317 base::TrimString(medium_name, "/", &medium_name); | 353 base::TrimString(medium_name, "/", &medium_name); |
| 318 // That may have generated a name the same as the short name of the | 354 // That may have generated a name the same as the short name of the |
| 319 // target which we already wrote. | 355 // target which we already wrote. |
| 320 if (medium_name != label.name()) | 356 if (medium_name != label.name() && |
| 321 WritePhonyRule(target, target_file, medium_name, &written_rules); | 357 written_rules.insert(medium_name).second) |
| 358 WritePhonyRule(target, medium_name); |
| 322 } | 359 } |
| 323 | 360 |
| 324 // Write short names for ones which are either completely unique or there | 361 // Write the short name if no other target shares that short name and |
| 325 // at least only one of them in the default toolchain that is an exe. | 362 // non of the higher-priority rules above claimed it. |
| 326 if (small_name_count[label.name()] == 1 || | 363 if (short_names[label.name()].count == 1 && |
| 327 (target->output_type() == Target::EXECUTABLE && | 364 written_rules.insert(label.name()).second) |
| 328 exe_count[label.name()] == 1)) { | 365 WritePhonyRule(target, label.name()); |
| 329 // It's reasonable to generate output files in the root build directory | |
| 330 // with the same name as the target. Don't generate phony rules for | |
| 331 // these cases. | |
| 332 // | |
| 333 // All of this does not do the general checking of all target's outputs | |
| 334 // which may theoretically collide. But it's not very reasonable for | |
| 335 // a script target named "foo" to generate a file named "bar" with no | |
| 336 // extension in the root build directory while another target is named | |
| 337 // "bar". If this does occur, the user is likely to be confused when | |
| 338 // building "bar" that is builds foo anyway, so you probably just | |
| 339 // shouldn't do that. | |
| 340 // | |
| 341 // We should fix this however, and build up all generated script outputs | |
| 342 // and check everything against that. There are other edge cases that the | |
| 343 // current phony rule generator doesn't check. We may need to make a big | |
| 344 // set of every possible generated file in the build for this purpose. | |
| 345 if (!HasOutputIdenticalToLabel(target, label.name())) | |
| 346 WritePhonyRule(target, target_file, label.name(), &written_rules); | |
| 347 } | |
| 348 | |
| 349 if (!all_rules.empty()) | |
| 350 all_rules.append(" $\n "); | |
| 351 all_rules.append(target_file.value()); | |
| 352 } | 366 } |
| 353 | 367 |
| 354 // Pick up phony rules for the toplevel targets with non-unique names (which | 368 // Write the autogenerated "all" rule. |
| 355 // would have been skipped in the above loop). | 369 if (!default_toolchain_targets_.empty()) { |
| 356 for (const auto& toplevel_target : toplevel_targets) { | 370 out_ << "\nbuild all: phony"; |
| 357 if (small_name_count[toplevel_target->label().name()] > 1) { | 371 |
| 358 WritePhonyRule(toplevel_target, toplevel_target->dependency_output_file(), | 372 EscapeOptions ninja_escape; |
| 359 toplevel_target->label().name(), &written_rules); | 373 ninja_escape.mode = ESCAPE_NINJA; |
| 374 for (const Target* target : default_toolchain_targets_) { |
| 375 out_ << " $\n "; |
| 376 path_output_.WriteFile(out_, target->dependency_output_file()); |
| 360 } | 377 } |
| 361 } | 378 } |
| 379 out_ << std::endl; |
| 362 | 380 |
| 363 // Figure out if the BUILD file wants to declare a custom "default" | 381 if (default_target_exists) |
| 364 // target (rather than building 'all' by default). By convention | 382 out_ << "\ndefault default" << std::endl; |
| 365 // we use group("default") but it doesn't have to be a group. | 383 else if (!default_toolchain_targets_.empty()) |
| 366 bool default_target_exists = false; | 384 out_ << "\ndefault all" << std::endl; |
| 367 for (const auto& target : default_toolchain_targets_) { | |
| 368 const Label& label = target->label(); | |
| 369 if (label.dir().value() == "//" && label.name() == "default") | |
| 370 default_target_exists = true; | |
| 371 } | |
| 372 | |
| 373 if (!all_rules.empty()) { | |
| 374 out_ << "\nbuild all: phony " << all_rules << std::endl; | |
| 375 } | |
| 376 | |
| 377 if (default_target_exists) { | |
| 378 out_ << "default default" << std::endl; | |
| 379 } else if (!all_rules.empty()) { | |
| 380 out_ << "default all" << std::endl; | |
| 381 } | |
| 382 | 385 |
| 383 return true; | 386 return true; |
| 384 } | 387 } |
| 385 | 388 |
| 386 void NinjaBuildWriter::WritePhonyRule(const Target* target, | 389 void NinjaBuildWriter::WritePhonyRule(const Target* target, |
| 387 const OutputFile& target_file, | 390 const std::string& phony_name) { |
| 388 const std::string& phony_name, | |
| 389 std::set<std::string>* written_rules) { | |
| 390 if (target_file.value() == phony_name) | |
| 391 return; // No need for a phony rule. | |
| 392 | |
| 393 if (written_rules->find(phony_name) != written_rules->end()) | |
| 394 return; // Already exists. | |
| 395 written_rules->insert(phony_name); | |
| 396 | |
| 397 EscapeOptions ninja_escape; | 391 EscapeOptions ninja_escape; |
| 398 ninja_escape.mode = ESCAPE_NINJA; | 392 ninja_escape.mode = ESCAPE_NINJA; |
| 399 | 393 |
| 400 // Escape for special chars Ninja will handle. | 394 // Escape for special chars Ninja will handle. |
| 401 std::string escaped = EscapeString(phony_name, ninja_escape, nullptr); | 395 std::string escaped = EscapeString(phony_name, ninja_escape, nullptr); |
| 402 | 396 |
| 403 out_ << "build " << escaped << ": phony "; | 397 out_ << "build " << escaped << ": phony "; |
| 404 path_output_.WriteFile(out_, target_file); | 398 path_output_.WriteFile(out_, target->dependency_output_file()); |
| 405 out_ << std::endl; | 399 out_ << std::endl; |
| 406 } | 400 } |
| OLD | NEW |