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.
4
5 #include <iostream>
6 #include <map>
7 #include <utility>
8 #include <vector>
9
10 #include "base/command_line.h"
11 #include "base/environment.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/time/time.h"
14 #include "tools/gn/build_settings.h"
15 #include "tools/gn/commands.h"
16 #include "tools/gn/err.h"
17 #include "tools/gn/gyp_helper.h"
18 #include "tools/gn/gyp_target_writer.h"
19 #include "tools/gn/location.h"
20 #include "tools/gn/parser.h"
21 #include "tools/gn/setup.h"
22 #include "tools/gn/source_file.h"
23 #include "tools/gn/standard_out.h"
24 #include "tools/gn/target.h"
25 #include "tools/gn/tokenizer.h"
26
27 namespace commands {
28
29 namespace {
30
31 typedef GypTargetWriter::TargetGroup TargetGroup;
32 typedef std::map<Label, TargetGroup> CorrelatedTargetsMap;
33 typedef std::map<SourceFile, std::vector<TargetGroup> > GroupedTargetsMap;
34 typedef std::map<std::string, std::string> StringStringMap;
35 typedef std::vector<const BuilderRecord*> RecordVector;
36
GetAllResolvedTargetRecords(const Builder * builder)37 std::vector<const BuilderRecord*> GetAllResolvedTargetRecords(
38 const Builder* builder) {
39 std::vector<const BuilderRecord*> all = builder->GetAllRecords();
40 std::vector<const BuilderRecord*> result;
41 result.reserve(all.size());
42 for (size_t i = 0; i < all.size(); i++) {
43 if (all[i]->type() == BuilderRecord::ITEM_TARGET &&
44 all[i]->should_generate() &&
45 all[i]->item())
46 result.push_back(all[i]);
47 }
48 return result;
49 }
50
51 // Groups targets sharing the same label between debug and release.
52 //
53 // We strip the toolchain label because the 64-bit and 32-bit builds, for
54 // example, will have different toolchains but we want to correlate them.
55 //
56 // TODO(brettw) this assumes that everything in the build has the same
57 // toolchain. To support cross-compiling and nacl, we'll need to differentiate
58 // the 32-vs-64-bit case and the default-toolchain-vs-not case. When we find
59 // a target not using hte default toolchain, we should probably just shell
60 // out to ninja.
CorrelateTargets(const RecordVector & debug_targets,const RecordVector & release_targets,const RecordVector & host_debug_targets,const RecordVector & host_release_targets,const RecordVector & debug64_targets,const RecordVector & release64_targets,CorrelatedTargetsMap * correlated)61 void CorrelateTargets(const RecordVector& debug_targets,
62 const RecordVector& release_targets,
63 const RecordVector& host_debug_targets,
64 const RecordVector& host_release_targets,
65 const RecordVector& debug64_targets,
66 const RecordVector& release64_targets,
67 CorrelatedTargetsMap* correlated) {
68 // Normal.
69 for (size_t i = 0; i < debug_targets.size(); i++) {
70 const BuilderRecord* record = debug_targets[i];
71 (*correlated)[record->label().GetWithNoToolchain()].debug = record;
72 }
73 for (size_t i = 0; i < release_targets.size(); i++) {
74 const BuilderRecord* record = release_targets[i];
75 (*correlated)[record->label().GetWithNoToolchain()].release = record;
76 }
77
78 // Host build.
79 for (size_t i = 0; i < host_debug_targets.size(); i++) {
80 const BuilderRecord* record = host_debug_targets[i];
81 (*correlated)[record->label().GetWithNoToolchain()].host_debug = record;
82 }
83 for (size_t i = 0; i < host_release_targets.size(); i++) {
84 const BuilderRecord* record = host_release_targets[i];
85 (*correlated)[record->label().GetWithNoToolchain()].host_release = record;
86 }
87
88 // Host build.
89 for (size_t i = 0; i < debug64_targets.size(); i++) {
90 const BuilderRecord* record = debug64_targets[i];
91 (*correlated)[record->label().GetWithNoToolchain()].debug64 = record;
92 }
93 for (size_t i = 0; i < release64_targets.size(); i++) {
94 const BuilderRecord* record = release64_targets[i];
95 (*correlated)[record->label().GetWithNoToolchain()].release64 = record;
96 }
97 }
98
99 // Verifies that both debug and release variants match. They can differ only
100 // by flags.
EnsureTargetsMatch(const TargetGroup & group,Err * err)101 bool EnsureTargetsMatch(const TargetGroup& group, Err* err) {
102 // Check that both debug and release made this target.
103 if (!group.debug || !group.release) {
104 const BuilderRecord* non_null_one =
105 group.debug ? group.debug : group.release;
106 *err = Err(Location(), "The debug and release builds did not both generate "
107 "a target with the name\n" +
108 non_null_one->label().GetUserVisibleName(true));
109 return false;
110 }
111
112 const Target* debug_target = group.debug->item()->AsTarget();
113 const Target* release_target = group.release->item()->AsTarget();
114
115 // Check the flags that determine if and where we write the GYP file.
116 if (group.debug->should_generate() != group.release->should_generate() ||
117 debug_target->external() != release_target->external() ||
118 debug_target->gyp_file() != release_target->gyp_file()) {
119 *err = Err(Location(), "The metadata for the target\n" +
120 group.debug->label().GetUserVisibleName(true) +
121 "\ndoesn't match between the debug and release builds.");
122 return false;
123 }
124
125 // Check that the sources match.
126 if (debug_target->sources().size() != release_target->sources().size()) {
127 *err = Err(Location(), "The source file count for the target\n" +
128 group.debug->label().GetUserVisibleName(true) +
129 "\ndoesn't have the same number of files between the debug and "
130 "release builds.");
131 return false;
132 }
133 for (size_t i = 0; i < debug_target->sources().size(); i++) {
134 if (debug_target->sources()[i] != release_target->sources()[i]) {
135 *err = Err(Location(), "The debug and release version of the target \n" +
136 group.debug->label().GetUserVisibleName(true) +
137 "\ndon't agree on the file\n" +
138 debug_target->sources()[i].value());
139 return false;
140 }
141 }
142
143 // Check that the deps match.
144 if (debug_target->deps().size() != release_target->deps().size()) {
145 *err = Err(Location(), "The source file count for the target\n" +
146 group.debug->label().GetUserVisibleName(true) +
147 "\ndoesn't have the same number of deps between the debug and "
148 "release builds.");
149 return false;
150 }
151 for (size_t i = 0; i < debug_target->deps().size(); i++) {
152 if (debug_target->deps()[i].label != release_target->deps()[i].label) {
153 *err = Err(Location(), "The debug and release version of the target \n" +
154 group.debug->label().GetUserVisibleName(true) +
155 "\ndon't agree on the dep\n" +
156 debug_target->deps()[i].label.GetUserVisibleName(true));
157 return false;
158 }
159 }
160 return true;
161 }
162
163 // Returns the (number of targets, number of GYP files).
WriteGypFiles(CommonSetup * debug_setup,CommonSetup * release_setup,CommonSetup * host_debug_setup,CommonSetup * host_release_setup,CommonSetup * debug64_setup,CommonSetup * release64_setup,Err * err)164 std::pair<int, int> WriteGypFiles(CommonSetup* debug_setup,
165 CommonSetup* release_setup,
166 CommonSetup* host_debug_setup,
167 CommonSetup* host_release_setup,
168 CommonSetup* debug64_setup,
169 CommonSetup* release64_setup,
170 Err* err) {
171 // Group all targets by output GYP file name.
172 std::vector<const BuilderRecord*> debug_targets =
173 GetAllResolvedTargetRecords(debug_setup->builder());
174 std::vector<const BuilderRecord*> release_targets =
175 GetAllResolvedTargetRecords(release_setup->builder());
176
177 // Host build is optional.
178 std::vector<const BuilderRecord*> host_debug_targets;
179 std::vector<const BuilderRecord*> host_release_targets;
180 if (host_debug_setup && host_release_setup) {
181 host_debug_targets = GetAllResolvedTargetRecords(
182 host_debug_setup->builder());
183 host_release_targets = GetAllResolvedTargetRecords(
184 host_release_setup->builder());
185 }
186
187 // 64-bit build is optional.
188 std::vector<const BuilderRecord*> debug64_targets;
189 std::vector<const BuilderRecord*> release64_targets;
190 if (debug64_setup && release64_setup) {
191 debug64_targets = GetAllResolvedTargetRecords(
192 debug64_setup->builder());
193 release64_targets = GetAllResolvedTargetRecords(
194 release64_setup->builder());
195 }
196
197 // Match up the debug and release version of each target by label.
198 CorrelatedTargetsMap correlated;
199 CorrelateTargets(debug_targets, release_targets,
200 host_debug_targets, host_release_targets,
201 debug64_targets, release64_targets,
202 &correlated);
203
204 GypHelper helper;
205 GroupedTargetsMap grouped_targets;
206 int target_count = 0;
207 for (CorrelatedTargetsMap::iterator i = correlated.begin();
208 i != correlated.end(); ++i) {
209 const TargetGroup& group = i->second;
210 if (!group.debug->should_generate())
211 continue; // Skip non-generated ones.
212 if (group.debug->item()->AsTarget()->external())
213 continue; // Skip external ones.
214 if (group.debug->item()->AsTarget()->gyp_file().is_null())
215 continue; // Skip ones without GYP files.
216
217 if (!EnsureTargetsMatch(group, err))
218 return std::make_pair(0, 0);
219
220 target_count++;
221 grouped_targets[
222 helper.GetGypFileForTarget(group.debug->item()->AsTarget(), err)]
223 .push_back(group);
224 if (err->has_error())
225 return std::make_pair(0, 0);
226 }
227
228 // Extract the toolchain for the debug targets.
229 const Toolchain* debug_toolchain = NULL;
230 if (!grouped_targets.empty()) {
231 debug_toolchain = debug_setup->builder()->GetToolchain(
232 grouped_targets.begin()->second[0].debug->item()->settings()->
233 default_toolchain_label());
234 }
235
236 // Write each GYP file.
237 for (GroupedTargetsMap::iterator i = grouped_targets.begin();
238 i != grouped_targets.end(); ++i) {
239 GypTargetWriter::WriteFile(i->first, i->second, debug_toolchain, err);
240 if (err->has_error())
241 return std::make_pair(0, 0);
242 }
243
244 return std::make_pair(target_count,
245 static_cast<int>(grouped_targets.size()));
246 }
247
248 } // namespace
249
250 // Suppress output on success.
251 const char kSwitchQuiet[] = "q";
252
253 const char kGyp[] = "gyp";
254 const char kGyp_HelpShort[] =
255 "gyp: Make GYP files from GN.";
256 const char kGyp_Help[] =
257 "gyp: Make GYP files from GN.\n"
258 "\n"
259 " This command will generate GYP files from GN sources. You can then run\n"
260 " GYP over the result to produce a build. Native GYP targets can depend\n"
261 " on any GN target except source sets. GN targets can depend on native\n"
262 " GYP targets, but all/direct dependent settings will NOT be pushed\n"
263 " across the boundary.\n"
264 "\n"
265 " To make this work you first need to manually run GN, then GYP, then\n"
266 " do the build. Because GN doesn't generate the final .ninja files,\n"
267 " there will be no rules to regenerate the .ninja files if the inputs\n"
268 " change, so you will have to manually repeat these steps each time\n"
269 " something changes:\n"
270 "\n"
271 " out/Debug/gn gyp\n"
272 " python build/gyp_chromiunm\n"
273 " ninja -C out/Debug foo_target\n"
274 "\n"
275 " Two variables are used to control how a target relates to GYP:\n"
276 "\n"
277 " - \"external != true\" and \"gyp_file\" is set: This target will be\n"
278 " written to the named GYP file in the source tree (not restricted to\n"
279 " an output or generated files directory).\n"
280 "\n"
281 " - \"external == true\" and \"gyp_file\" is set: The target will not\n"
282 " be written to a GYP file. But other targets being written to GYP\n"
283 " files can depend on it, and they will reference the given GYP file\n"
284 " name for GYP to use. This allows you to specify how GN->GYP\n"
285 " dependencies and named, and provides a place to manually set the\n"
286 " dependent configs from GYP to GN.\n"
287 "\n"
288 " - \"gyp_file\" is unset: Like the previous case, but if a GN target is\n"
289 " being written to a GYP file that depends on this one, the default\n"
290 " GYP file name will be assumed. The default name will match the name\n"
291 " of the current directory, so \"//foo/bar:baz\" would be\n"
292 " \"<(DEPTH)/foo/bar/bar.gyp:baz\".\n"
293 "\n"
294 "Switches\n"
295 " --gyp_vars\n"
296 " The GYP variables converted to a GN-style string lookup.\n"
297 " For example:\n"
298 " --gyp_vars=\"component=\\\"shared_library\\\" use_aura=\\\"1\\\"\"\n"
299 "\n"
300 "Example:\n"
301 " # This target is assumed to be in the GYP build in the file\n"
302 " # \"foo/foo.gyp\". This declaration tells GN where to find the GYP\n"
303 " # equivalent, and gives it some direct dependent settings that targets\n"
304 " # depending on it should receive (since these don't flow from GYP to\n"
305 " # GN-generated targets).\n"
306 " shared_library(\"gyp_target\") {\n"
307 " gyp_file = \"//foo/foo.gyp\"\n"
308 " external = true\n"
309 " direct_dependen_configs = [ \":gyp_target_config\" ]\n"
310 " }\n"
311 "\n"
312 " executable(\"my_app\") {\n"
313 " deps = [ \":gyp_target\" ]\n"
314 " gyp_file = \"//foo/myapp.gyp\"\n"
315 " sources = ...\n"
316 " }\n";
317
RunGyp(const std::vector<std::string> & args)318 int RunGyp(const std::vector<std::string>& args) {
319 const CommandLine* cmdline = CommandLine::ForCurrentProcess();
320
321 base::TimeTicks begin_time = base::TimeTicks::Now();
322
323 // Deliberately leaked to avoid expensive process teardown.
324 Setup* setup_debug = new Setup;
325 if (!setup_debug->DoSetup())
326 return 1;
327 const char kIsDebug[] = "is_debug";
328
329 // Make a release build based on the debug one. We use a new directory for
330 // the build output so that they don't stomp on each other.
331 DependentSetup* setup_release = new DependentSetup(setup_debug);
332 setup_release->build_settings().build_args().AddArgOverride(
333 kIsDebug, Value(NULL, false));
334 setup_release->build_settings().SetBuildDir(
335 SourceDir(setup_release->build_settings().build_dir().value() +
336 "gn_release.tmp/"));
337
338 // Host build.
339 DependentSetup* setup_host_debug = NULL;
340 DependentSetup* setup_host_release = NULL;
341 // TODO(brettw) hook up host build.
342
343 // 64-bit build (Windows only).
344 DependentSetup* setup_debug64 = NULL;
345 DependentSetup* setup_release64 = NULL;
346 #if defined(OS_WIN)
347 static const char kForceWin64[] = "force_win64";
348 setup_debug64 = new DependentSetup(setup_debug);
349 setup_debug64->build_settings().build_args().AddArgOverride(
350 kForceWin64, Value(NULL, true));
351 setup_debug64->build_settings().SetBuildDir(
352 SourceDir(setup_release->build_settings().build_dir().value() +
353 "gn_debug64.tmp/"));
354
355 setup_release64 = new DependentSetup(setup_release);
356 setup_release64->build_settings().build_args().AddArgOverride(
357 kForceWin64, Value(NULL, true));
358 setup_release64->build_settings().SetBuildDir(
359 SourceDir(setup_release->build_settings().build_dir().value() +
360 "gn_release64.tmp/"));
361 #endif
362
363 // Run all the builds in parellel.
364 setup_release->RunPreMessageLoop();
365 if (setup_host_debug && setup_host_release) {
366 setup_host_debug->RunPreMessageLoop();
367 setup_host_release->RunPreMessageLoop();
368 }
369 if (setup_debug64 && setup_release64) {
370 setup_debug64->RunPreMessageLoop();
371 setup_release64->RunPreMessageLoop();
372 }
373
374 if (!setup_debug->Run())
375 return 1;
376
377 if (!setup_release->RunPostMessageLoop())
378 return 1;
379 if (setup_host_debug && !setup_host_debug->RunPostMessageLoop())
380 return 1;
381 if (setup_host_release && !setup_host_release->RunPostMessageLoop())
382 return 1;
383 if (setup_debug64 && !setup_debug64->RunPostMessageLoop())
384 return 1;
385 if (setup_release64 && !setup_release64->RunPostMessageLoop())
386 return 1;
387
388 Err err;
389 std::pair<int, int> counts =
390 WriteGypFiles(setup_debug, setup_release,
391 setup_host_debug, setup_host_release,
392 setup_debug64, setup_release64,
393 &err);
394 if (err.has_error()) {
395 err.PrintToStdout();
396 return 1;
397 }
398
399 // Timing info.
400 base::TimeTicks end_time = base::TimeTicks::Now();
401 if (!cmdline->HasSwitch(kSwitchQuiet)) {
402 OutputString("Done. ", DECORATION_GREEN);
403
404 std::string stats = "Wrote " +
405 base::IntToString(counts.first) + " targets to " +
406 base::IntToString(counts.second) + " GYP files read from " +
407 base::IntToString(
408 setup_debug->scheduler().input_file_manager()->GetInputFileCount())
409 + " GN files in " +
410 base::IntToString((end_time - begin_time).InMilliseconds()) + "ms\n";
411
412 OutputString(stats);
413 }
414
415 return 0;
416 }
417
418 } // namespace commands
419