• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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