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 "base/atomicops.h" | 5 #include "base/atomicops.h" |
6 #include "base/bind.h" | 6 #include "base/bind.h" |
7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
8 #include "base/strings/string_number_conversions.h" | 8 #include "base/strings/string_number_conversions.h" |
| 9 #include "base/strings/stringprintf.h" |
9 #include "base/timer/elapsed_timer.h" | 10 #include "base/timer/elapsed_timer.h" |
10 #include "tools/gn/build_settings.h" | 11 #include "tools/gn/build_settings.h" |
11 #include "tools/gn/commands.h" | 12 #include "tools/gn/commands.h" |
12 #include "tools/gn/ninja_target_writer.h" | 13 #include "tools/gn/ninja_target_writer.h" |
13 #include "tools/gn/ninja_writer.h" | 14 #include "tools/gn/ninja_writer.h" |
14 #include "tools/gn/runtime_deps.h" | 15 #include "tools/gn/runtime_deps.h" |
15 #include "tools/gn/scheduler.h" | 16 #include "tools/gn/scheduler.h" |
16 #include "tools/gn/setup.h" | 17 #include "tools/gn/setup.h" |
17 #include "tools/gn/standard_out.h" | 18 #include "tools/gn/standard_out.h" |
18 #include "tools/gn/switches.h" | 19 #include "tools/gn/switches.h" |
(...skipping 18 matching lines...) Expand all Loading... |
37 base::subtle::NoBarrier_AtomicIncrement(write_counter, 1); | 38 base::subtle::NoBarrier_AtomicIncrement(write_counter, 1); |
38 | 39 |
39 const Item* item = record->item(); | 40 const Item* item = record->item(); |
40 const Target* target = item->AsTarget(); | 41 const Target* target = item->AsTarget(); |
41 if (target) { | 42 if (target) { |
42 g_scheduler->IncrementWorkCount(); | 43 g_scheduler->IncrementWorkCount(); |
43 g_scheduler->ScheduleWork(base::Bind(&BackgroundDoWrite, target)); | 44 g_scheduler->ScheduleWork(base::Bind(&BackgroundDoWrite, target)); |
44 } | 45 } |
45 } | 46 } |
46 | 47 |
| 48 // Returns a pointer to the target with the given file as an output, or null |
| 49 // if no targets generate the file. This is brute force since this is an |
| 50 // error condition and performance shouldn't matter. |
| 51 const Target* FindTargetThatGeneratesFile(const Builder* builder, |
| 52 const SourceFile& file) { |
| 53 std::vector<const Target*> targets = builder->GetAllResolvedTargets(); |
| 54 if (targets.empty()) |
| 55 return nullptr; |
| 56 |
| 57 OutputFile output_file(targets[0]->settings()->build_settings(), file); |
| 58 for (const Target* target : targets) { |
| 59 for (const auto& cur_output : target->computed_outputs()) { |
| 60 if (cur_output == output_file) |
| 61 return target; |
| 62 } |
| 63 } |
| 64 return nullptr; |
| 65 } |
| 66 |
| 67 // Prints an error that the given file was present as a source or input in |
| 68 // the given target(s) but was not generated by any of its dependencies. |
| 69 void PrintInvalidGeneratedInput(const Builder* builder, |
| 70 const SourceFile& file, |
| 71 const std::vector<const Target*>& targets) { |
| 72 std::string err; |
| 73 |
| 74 const std::string target_str = targets.size() > 1 ? "targets" : "target"; |
| 75 err += "The file:\n"; |
| 76 err += " " + file.value() + "\n"; |
| 77 err += "is listed as an input or source for the " + target_str + ":\n"; |
| 78 for (const Target* target : targets) |
| 79 err += " " + target->label().GetUserVisibleName(false) + "\n"; |
| 80 |
| 81 const Target* generator = FindTargetThatGeneratesFile(builder, file); |
| 82 if (generator) { |
| 83 err += "but this file was not generated by any dependencies of the " + |
| 84 target_str + ". The target\nthat generates the file is:\n "; |
| 85 err += generator->label().GetUserVisibleName(false); |
| 86 } else { |
| 87 err += "but no targets in the build generate that file."; |
| 88 } |
| 89 |
| 90 Err(Location(), "Input to " + target_str + " not generated by a dependency.", |
| 91 err).PrintToStdout(); |
| 92 } |
| 93 |
| 94 bool CheckForInvalidGeneratedInputs(Setup* setup) { |
| 95 std::multimap<SourceFile, const Target*> unknown_inputs = |
| 96 g_scheduler->GetUnknownGeneratedInputs(); |
| 97 if (unknown_inputs.empty()) |
| 98 return true; // No bad files. |
| 99 |
| 100 int errors_found = 0; |
| 101 auto cur = unknown_inputs.begin(); |
| 102 while (cur != unknown_inputs.end()) { |
| 103 errors_found++; |
| 104 auto end_of_range = unknown_inputs.upper_bound(cur->first); |
| 105 |
| 106 // Package the values more conveniently for printing. |
| 107 SourceFile bad_input = cur->first; |
| 108 std::vector<const Target*> targets; |
| 109 while (cur != end_of_range) |
| 110 targets.push_back((cur++)->second); |
| 111 |
| 112 PrintInvalidGeneratedInput(setup->builder(), bad_input, targets); |
| 113 OutputString("\n"); |
| 114 } |
| 115 |
| 116 OutputString( |
| 117 "If you have generated inputs, there needs to be a dependency path " |
| 118 "between the\ntwo targets in addition to just listing the files. For " |
| 119 "indirect dependencies,\nthe intermediate ones must be public_deps. " |
| 120 "data_deps don't count since they're\nonly runtime dependencies. If " |
| 121 "you think a dependency chain exists, it might be\nbecause the chain " |
| 122 "is private. Try \"gn path\" to analyze.\n"); |
| 123 |
| 124 if (errors_found > 1) { |
| 125 OutputString(base::StringPrintf("\n%d generated input errors found.\n", |
| 126 errors_found), DECORATION_YELLOW); |
| 127 } |
| 128 return false; |
| 129 } |
| 130 |
47 } // namespace | 131 } // namespace |
48 | 132 |
49 const char kGen[] = "gen"; | 133 const char kGen[] = "gen"; |
50 const char kGen_HelpShort[] = | 134 const char kGen_HelpShort[] = |
51 "gen: Generate ninja files."; | 135 "gen: Generate ninja files."; |
52 const char kGen_Help[] = | 136 const char kGen_Help[] = |
53 "gn gen: Generate ninja files.\n" | 137 "gn gen: Generate ninja files.\n" |
54 "\n" | 138 "\n" |
55 " gn gen <out_dir>\n" | 139 " gn gen <out_dir>\n" |
56 "\n" | 140 "\n" |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
100 &err)) { | 184 &err)) { |
101 err.PrintToStdout(); | 185 err.PrintToStdout(); |
102 return 1; | 186 return 1; |
103 } | 187 } |
104 | 188 |
105 if (!WriteRuntimeDepsFilesIfNecessary(*setup->builder(), &err)) { | 189 if (!WriteRuntimeDepsFilesIfNecessary(*setup->builder(), &err)) { |
106 err.PrintToStdout(); | 190 err.PrintToStdout(); |
107 return 1; | 191 return 1; |
108 } | 192 } |
109 | 193 |
| 194 if (!CheckForInvalidGeneratedInputs(setup)) |
| 195 return 1; |
| 196 |
110 base::TimeDelta elapsed_time = timer.Elapsed(); | 197 base::TimeDelta elapsed_time = timer.Elapsed(); |
111 | 198 |
112 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kQuiet)) { | 199 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kQuiet)) { |
113 OutputString("Done. ", DECORATION_GREEN); | 200 OutputString("Done. ", DECORATION_GREEN); |
114 | 201 |
115 std::string stats = "Wrote " + | 202 std::string stats = "Wrote " + |
116 base::IntToString(static_cast<int>(write_counter)) + | 203 base::IntToString(static_cast<int>(write_counter)) + |
117 " targets from " + | 204 " targets from " + |
118 base::IntToString( | 205 base::IntToString( |
119 setup->scheduler().input_file_manager()->GetInputFileCount()) + | 206 setup->scheduler().input_file_manager()->GetInputFileCount()) + |
120 " files in " + | 207 " files in " + |
121 base::Int64ToString(elapsed_time.InMilliseconds()) + "ms\n"; | 208 base::Int64ToString(elapsed_time.InMilliseconds()) + "ms\n"; |
122 OutputString(stats); | 209 OutputString(stats); |
123 } | 210 } |
124 | 211 |
125 return 0; | 212 return 0; |
126 } | 213 } |
127 | 214 |
128 } // namespace commands | 215 } // namespace commands |
OLD | NEW |