Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / tools / gn / command_gyp.cc
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/timer/elapsed_timer.h"
14 #include "build/build_config.h"
15 #include "tools/gn/build_settings.h"
16 #include "tools/gn/commands.h"
17 #include "tools/gn/err.h"
18 #include "tools/gn/filesystem_utils.h"
19 #include "tools/gn/gyp_helper.h"
20 #include "tools/gn/gyp_target_writer.h"
21 #include "tools/gn/location.h"
22 #include "tools/gn/parser.h"
23 #include "tools/gn/setup.h"
24 #include "tools/gn/source_file.h"
25 #include "tools/gn/standard_out.h"
26 #include "tools/gn/target.h"
27 #include "tools/gn/tokenizer.h"
28
29 namespace commands {
30
31 namespace {
32
33 typedef GypTargetWriter::TargetGroup TargetGroup;
34 typedef std::map<Label, TargetGroup> CorrelatedTargetsMap;
35 typedef std::map<SourceFile, std::vector<TargetGroup> > GroupedTargetsMap;
36 typedef std::map<std::string, std::string> StringStringMap;
37 typedef std::vector<const BuilderRecord*> RecordVector;
38
39 struct Setups {
40   Setups()
41       : debug(NULL),
42         release(NULL),
43         debug64(NULL),
44         release64(NULL),
45         xcode_debug(NULL),
46         xcode_release(NULL) {
47   }
48
49   Setup* debug;
50   DependentSetup* release;
51   DependentSetup* debug64;
52   DependentSetup* release64;
53   DependentSetup* xcode_debug;
54   DependentSetup* xcode_release;
55 };
56
57 struct TargetVectors {
58   RecordVector debug;
59   RecordVector release;
60   RecordVector host_debug;
61   RecordVector host_release;
62   RecordVector debug64;
63   RecordVector release64;
64   RecordVector xcode_debug;
65   RecordVector xcode_release;
66   RecordVector xcode_host_debug;
67   RecordVector xcode_host_release;
68 };
69
70 // This function appends a suffix to the given source directory name. We append
71 // a suffix to the last directory component rather than adding a new level so
72 // that the relative location of the files don't change (i.e. a file
73 // relative to the build dir might be "../../foo/bar.cc") and we want these to
74 // be the same in all builds, and in particular the GYP build directories.
75 SourceDir AppendDirSuffix(const SourceDir& base, const std::string& suffix) {
76   return SourceDir(DirectoryWithNoLastSlash(base) + suffix + "/");
77 }
78
79 // Returns the empty label if there is no separate host build.
80 Label GetHostToolchain(const Setups& setups) {
81   const Loader* loader = setups.debug->loader();
82   const Settings* default_settings =
83       loader->GetToolchainSettings(loader->GetDefaultToolchain());
84
85   // Chrome's master build config file puts the host toolchain label into the
86   // variable "host_toolchain".
87   const Value* host_value =
88       default_settings->base_config()->GetValue("host_toolchain");
89   if (!host_value || host_value->type() != Value::STRING)
90     return Label();
91
92   Err err;
93   Label host_label = Label::Resolve(SourceDir(), Label(), *host_value, &err);
94   if (host_label == loader->GetDefaultToolchain())
95     return Label();  // Host and target matches, there is no host build.
96   return host_label;
97 }
98
99 std::vector<const BuilderRecord*> GetAllResolvedTargetRecords(
100     const Builder* builder) {
101   std::vector<const BuilderRecord*> all = builder->GetAllRecords();
102   std::vector<const BuilderRecord*> result;
103   result.reserve(all.size());
104   for (size_t i = 0; i < all.size(); i++) {
105     if (all[i]->type() == BuilderRecord::ITEM_TARGET &&
106         all[i]->should_generate() &&
107         all[i]->item())
108       result.push_back(all[i]);
109   }
110   return result;
111 }
112
113 // Adds all targets to the map that match the given toolchain, writing them to
114 // the given destiation vector of the record group. If toolchain is empty, it
115 // indicates the default toolchain should be matched.
116 void CorrelateRecordVector(const RecordVector& records,
117                            const Label& toolchain,
118                            CorrelatedTargetsMap* correlated,
119                            const BuilderRecord* TargetGroup::* record_ptr) {
120   if (records.empty())
121     return;
122
123   Label search_toolchain = toolchain;
124   if (search_toolchain.is_null()) {
125     // Find the default toolchain.
126     search_toolchain =
127         records[0]->item()->settings()->default_toolchain_label();
128   }
129
130   for (size_t i = 0; i < records.size(); i++) {
131     const BuilderRecord* record = records[i];
132     if (record->label().GetToolchainLabel() == search_toolchain)
133       (*correlated)[record->label().GetWithNoToolchain()].*record_ptr = record;
134   }
135 }
136
137 // Groups targets sharing the same label between debug and release.
138 //
139 // If the host toolchain is nonempty, we'll search for targets with this
140 // alternate toolchain and assign them to the corresponding "host" groups.
141 //
142 // TODO(brettw) this doesn't handle any toolchains other than the target or
143 // host ones. To support nacl, we'll need to differentiate the 32-vs-64-bit
144 // case and the default-toolchain-vs-not case. When we find a target not using
145 // hte default toolchain, we should probably just shell out to ninja.
146 void CorrelateTargets(const TargetVectors& targets,
147                       const Label& host_toolchain,
148                       CorrelatedTargetsMap* correlated) {
149   // Normal.
150   CorrelateRecordVector(targets.debug, Label(), correlated,
151                         &TargetGroup::debug);
152   CorrelateRecordVector(targets.release, Label(), correlated,
153                         &TargetGroup::release);
154
155   // 64-bit build.
156   CorrelateRecordVector(targets.debug64, Label(), correlated,
157                         &TargetGroup::debug64);
158   CorrelateRecordVector(targets.release64, Label(), correlated,
159                         &TargetGroup::release64);
160
161   // XCode build.
162   CorrelateRecordVector(targets.xcode_debug, Label(), correlated,
163                         &TargetGroup::xcode_debug);
164   CorrelateRecordVector(targets.xcode_release, Label(), correlated,
165                         &TargetGroup::xcode_release);
166
167   if (!host_toolchain.is_null()) {
168     // Normal host build.
169     CorrelateRecordVector(targets.debug, host_toolchain, correlated,
170                           &TargetGroup::host_debug);
171     CorrelateRecordVector(targets.release, host_toolchain, correlated,
172                           &TargetGroup::host_release);
173
174     // XCode build.
175     CorrelateRecordVector(targets.xcode_debug, host_toolchain, correlated,
176                           &TargetGroup::xcode_host_debug);
177     CorrelateRecordVector(targets.xcode_release, host_toolchain, correlated,
178                           &TargetGroup::xcode_host_release);
179   }
180 }
181
182 // Verifies that both debug and release variants match. They can differ only
183 // by flags.
184 bool EnsureTargetsMatch(const TargetGroup& group, Err* err) {
185   if (!group.debug && !group.release)
186     return true;
187
188   // Check that both debug and release made this target.
189   if (!group.debug || !group.release) {
190     const BuilderRecord* non_null_one =
191         group.debug ? group.debug : group.release;
192     *err = Err(Location(), "The debug and release builds did not both generate "
193         "a target with the name\n" +
194         non_null_one->label().GetUserVisibleName(true));
195     return false;
196   }
197
198   const Target* debug_target = group.debug->item()->AsTarget();
199   const Target* release_target = group.release->item()->AsTarget();
200
201   // Check the flags that determine if and where we write the GYP file.
202   if (group.debug->should_generate() != group.release->should_generate() ||
203       debug_target->external() != release_target->external() ||
204       debug_target->gyp_file() != release_target->gyp_file()) {
205     *err = Err(Location(), "The metadata for the target\n" +
206         group.debug->label().GetUserVisibleName(true) +
207         "\ndoesn't match between the debug and release builds.");
208     return false;
209   }
210
211   // Check that the sources match.
212   if (debug_target->sources().size() != release_target->sources().size()) {
213     *err = Err(Location(), "The source file count for the target\n" +
214         group.debug->label().GetUserVisibleName(true) +
215         "\ndoesn't have the same number of files between the debug and "
216         "release builds.");
217     return false;
218   }
219   for (size_t i = 0; i < debug_target->sources().size(); i++) {
220     if (debug_target->sources()[i] != release_target->sources()[i]) {
221       *err = Err(Location(), "The debug and release version of the target \n" +
222           group.debug->label().GetUserVisibleName(true) +
223           "\ndon't agree on the file\n" +
224           debug_target->sources()[i].value());
225       return false;
226     }
227   }
228
229   // Check that the deps match.
230   if (debug_target->deps().size() != release_target->deps().size()) {
231     *err = Err(Location(), "The source file count for the target\n" +
232         group.debug->label().GetUserVisibleName(true) +
233         "\ndoesn't have the same number of deps between the debug and "
234         "release builds.");
235     return false;
236   }
237   for (size_t i = 0; i < debug_target->deps().size(); i++) {
238     if (debug_target->deps()[i].label != release_target->deps()[i].label) {
239       *err = Err(Location(), "The debug and release version of the target \n" +
240           group.debug->label().GetUserVisibleName(true) +
241           "\ndon't agree on the dep\n" +
242           debug_target->deps()[i].label.GetUserVisibleName(true));
243       return false;
244     }
245   }
246   return true;
247 }
248
249 // Returns the (number of targets, number of GYP files).
250 std::pair<int, int> WriteGypFiles(Setups& setups, Err* err) {
251   TargetVectors targets;
252
253   targets.debug = GetAllResolvedTargetRecords(setups.debug->builder());
254   targets.release = GetAllResolvedTargetRecords(setups.release->builder());
255
256   // 64-bit build is optional.
257   if (setups.debug64 && setups.release64) {
258     targets.debug64 =
259         GetAllResolvedTargetRecords(setups.debug64->builder());
260     targets.release64 =
261         GetAllResolvedTargetRecords(setups.release64->builder());
262   }
263
264   // Xcode build is optional.
265   if (setups.xcode_debug && setups.xcode_release) {
266     targets.xcode_debug =
267         GetAllResolvedTargetRecords(setups.xcode_debug->builder());
268     targets.xcode_release =
269         GetAllResolvedTargetRecords(setups.xcode_release->builder());
270   }
271
272   // Match up the debug and release version of each target by label.
273   CorrelatedTargetsMap correlated;
274   CorrelateTargets(targets, GetHostToolchain(setups), &correlated);
275
276   GypHelper helper;
277   GroupedTargetsMap grouped_targets;
278   int target_count = 0;
279   for (CorrelatedTargetsMap::iterator i = correlated.begin();
280        i != correlated.end(); ++i) {
281     const TargetGroup& group = i->second;
282     if (!group.get()->should_generate())
283       continue;  // Skip non-generated ones.
284     if (group.get()->item()->AsTarget()->external())
285       continue;  // Skip external ones.
286     if (group.get()->item()->AsTarget()->gyp_file().is_null())
287       continue;  // Skip ones without GYP files.
288
289     if (!EnsureTargetsMatch(group, err))
290       return std::make_pair(0, 0);
291
292     target_count++;
293     grouped_targets[
294             helper.GetGypFileForTarget(group.debug->item()->AsTarget(), err)]
295         .push_back(group);
296     if (err->has_error())
297       return std::make_pair(0, 0);
298   }
299
300   // Extract the toolchain for the debug targets.
301   const Toolchain* debug_toolchain = NULL;
302   if (!grouped_targets.empty()) {
303     debug_toolchain = setups.debug->builder()->GetToolchain(
304         grouped_targets.begin()->second[0].debug->item()->settings()->
305         default_toolchain_label());
306   }
307
308   // Write each GYP file.
309   for (GroupedTargetsMap::iterator i = grouped_targets.begin();
310        i != grouped_targets.end(); ++i) {
311     GypTargetWriter::WriteFile(i->first, i->second, debug_toolchain, err);
312     if (err->has_error())
313       return std::make_pair(0, 0);
314   }
315
316   return std::make_pair(target_count,
317                         static_cast<int>(grouped_targets.size()));
318 }
319
320 // Verifies that all build argument overrides are used by at least one of the
321 // build types.
322 void VerifyAllOverridesUsed(const Setups& setups) {
323   // Collect all declared args from all builds.
324   Scope::KeyValueMap declared;
325   setups.debug->build_settings().build_args().MergeDeclaredArguments(
326       &declared);
327   setups.release->build_settings().build_args().MergeDeclaredArguments(
328       &declared);
329   if (setups.debug64 && setups.release64) {
330     setups.debug64->build_settings().build_args().MergeDeclaredArguments(
331         &declared);
332     setups.release64->build_settings().build_args().MergeDeclaredArguments(
333         &declared);
334   }
335   if (setups.xcode_debug && setups.xcode_release) {
336     setups.xcode_debug->build_settings().build_args().MergeDeclaredArguments(
337         &declared);
338     setups.xcode_release->build_settings().build_args().MergeDeclaredArguments(
339         &declared);
340   }
341
342   Scope::KeyValueMap used =
343       setups.debug->build_settings().build_args().GetAllOverrides();
344
345   Err err;
346   if (!Args::VerifyAllOverridesUsed(used, declared, &err)) {
347     // TODO(brettw) implement a system of warnings. Until we have a better
348     // system, print the error but don't cause a failure.
349     err.PrintToStdout();
350   }
351 }
352
353 }  // namespace
354
355 // Suppress output on success.
356 const char kSwitchQuiet[] = "q";
357
358 const char kGyp[] = "gyp";
359 const char kGyp_HelpShort[] =
360     "gyp: Make GYP files from GN.";
361 const char kGyp_Help[] =
362     "gyp: Make GYP files from GN.\n"
363     "\n"
364     "  This command will generate GYP files from GN sources. You can then run\n"
365     "  GYP over the result to produce a build. Native GYP targets can depend\n"
366     "  on any GN target except source sets. GN targets can depend on native\n"
367     "  GYP targets, but all/direct dependent settings will NOT be pushed\n"
368     "  across the boundary.\n"
369     "\n"
370     "  To make this work you first need to manually run GN, then GYP, then\n"
371     "  do the build. Because GN doesn't generate the final .ninja files,\n"
372     "  there will be no rules to regenerate the .ninja files if the inputs\n"
373     "  change, so you will have to manually repeat these steps each time\n"
374     "  something changes:\n"
375     "\n"
376     "    out/Debug/gn gyp\n"
377     "    python build/gyp_chromiunm\n"
378     "    ninja -C out/Debug foo_target\n"
379     "\n"
380     "  Two variables are used to control how a target relates to GYP:\n"
381     "\n"
382     "  - \"external != true\" and \"gyp_file\" is set: This target will be\n"
383     "    written to the named GYP file in the source tree (not restricted to\n"
384     "    an output or generated files directory).\n"
385     "\n"
386     "  - \"external == true\" and \"gyp_file\" is set: The target will not\n"
387     "    be written to a GYP file. But other targets being written to GYP\n"
388     "    files can depend on it, and they will reference the given GYP file\n"
389     "    name for GYP to use. This allows you to specify how GN->GYP\n"
390     "    dependencies and named, and provides a place to manually set the\n"
391     "    dependent configs from GYP to GN.\n"
392     "\n"
393     "  - \"gyp_file\" is unset: Like the previous case, but if a GN target is\n"
394     "    being written to a GYP file that depends on this one, the default\n"
395     "    GYP file name will be assumed. The default name will match the name\n"
396     "    of the current directory, so \"//foo/bar:baz\" would be\n"
397     "    \"<(DEPTH)/foo/bar/bar.gyp:baz\".\n"
398     "\n"
399     "Switches\n"
400     "  --gyp_vars\n"
401     "      The GYP variables converted to a GN-style string lookup.\n"
402     "      For example:\n"
403     "      --gyp_vars=\"component=\\\"shared_library\\\" use_aura=\\\"1\\\"\"\n"
404     "\n"
405     "Example:\n"
406     "  # This target is assumed to be in the GYP build in the file\n"
407     "  # \"foo/foo.gyp\". This declaration tells GN where to find the GYP\n"
408     "  # equivalent, and gives it some direct dependent settings that targets\n"
409     "  # depending on it should receive (since these don't flow from GYP to\n"
410     "  # GN-generated targets).\n"
411     "  shared_library(\"gyp_target\") {\n"
412     "    gyp_file = \"//foo/foo.gyp\"\n"
413     "    external = true\n"
414     "    direct_dependen_configs = [ \":gyp_target_config\" ]\n"
415     "  }\n"
416     "\n"
417     "  executable(\"my_app\") {\n"
418     "    deps = [ \":gyp_target\" ]\n"
419     "    gyp_file = \"//foo/myapp.gyp\"\n"
420     "    sources = ...\n"
421     "  }\n";
422
423 int RunGyp(const std::vector<std::string>& args) {
424   base::ElapsedTimer timer;
425   Setups setups;
426
427   // Deliberately leaked to avoid expensive process teardown. We also turn off
428   // unused override checking since we want to merge all declared arguments and
429   // check those, rather than check each build individually. Otherwise, you
430   // couldn't have an arg that was used in only one build type. This comes up
431   // because some args are build-type specific.
432   setups.debug = new Setup;
433   setups.debug->set_check_for_unused_overrides(false);
434   if (!setups.debug->DoSetup())
435     return 1;
436   const char kIsDebug[] = "is_debug";
437
438   SourceDir base_build_dir = setups.debug->build_settings().build_dir();
439   setups.debug->build_settings().SetBuildDir(
440       AppendDirSuffix(base_build_dir, ".Debug"));
441
442   // Make a release build based on the debug one. We use a new directory for
443   // the build output so that they don't stomp on each other.
444   setups.release = new DependentSetup(setups.debug);
445   setups.release->build_settings().build_args().AddArgOverride(
446       kIsDebug, Value(NULL, false));
447   setups.release->build_settings().SetBuildDir(
448       AppendDirSuffix(base_build_dir, ".Release"));
449
450   // 64-bit build (Windows only).
451 #if defined(OS_WIN)
452   static const char kForceWin64[] = "force_win64";
453   setups.debug64 = new DependentSetup(setups.debug);
454   setups.debug64->build_settings().build_args().AddArgOverride(
455       kForceWin64, Value(NULL, true));
456   setups.debug64->build_settings().SetBuildDir(
457       AppendDirSuffix(base_build_dir, ".Debug64"));
458
459   setups.release64 = new DependentSetup(setups.release);
460   setups.release64->build_settings().build_args().AddArgOverride(
461       kForceWin64, Value(NULL, true));
462   setups.release64->build_settings().SetBuildDir(
463       AppendDirSuffix(base_build_dir, ".Release64"));
464 #endif
465
466   // XCode build (Mac only).
467 #if defined(OS_MACOSX)
468   static const char kGypXCode[] = "is_gyp_xcode_generator";
469   setups.xcode_debug = new DependentSetup(setups.debug);
470   setups.xcode_debug->build_settings().build_args().AddArgOverride(
471       kGypXCode, Value(NULL, true));
472   setups.xcode_debug->build_settings().SetBuildDir(
473       AppendDirSuffix(base_build_dir, ".XCodeDebug"));
474
475   setups.xcode_release = new DependentSetup(setups.release);
476   setups.xcode_release->build_settings().build_args().AddArgOverride(
477       kGypXCode, Value(NULL, true));
478   setups.xcode_release->build_settings().SetBuildDir(
479       AppendDirSuffix(base_build_dir, ".XCodeRelease"));
480 #endif
481
482   // Run all the builds in parellel.
483   setups.release->RunPreMessageLoop();
484   if (setups.debug64 && setups.release64) {
485     setups.debug64->RunPreMessageLoop();
486     setups.release64->RunPreMessageLoop();
487   }
488   if (setups.xcode_debug && setups.xcode_release) {
489     setups.xcode_debug->RunPreMessageLoop();
490     setups.xcode_release->RunPreMessageLoop();
491   }
492
493   if (!setups.debug->Run())
494     return 1;
495
496   if (!setups.release->RunPostMessageLoop())
497     return 1;
498   if (setups.debug64 && !setups.debug64->RunPostMessageLoop())
499     return 1;
500   if (setups.release64 && !setups.release64->RunPostMessageLoop())
501     return 1;
502   if (setups.xcode_debug && !setups.xcode_debug->RunPostMessageLoop())
503     return 1;
504   if (setups.xcode_release && !setups.xcode_release->RunPostMessageLoop())
505     return 1;
506
507   VerifyAllOverridesUsed(setups);
508
509   Err err;
510   std::pair<int, int> counts = WriteGypFiles(setups, &err);
511   if (err.has_error()) {
512     err.PrintToStdout();
513     return 1;
514   }
515
516   base::TimeDelta elapsed_time = timer.Elapsed();
517
518   if (!CommandLine::ForCurrentProcess()->HasSwitch(kSwitchQuiet)) {
519     OutputString("Done. ", DECORATION_GREEN);
520
521     std::string stats = "Wrote " +
522         base::IntToString(counts.first) + " targets to " +
523         base::IntToString(counts.second) + " GYP files read from " +
524         base::IntToString(
525             setups.debug->scheduler().input_file_manager()->GetInputFileCount())
526         + " GN files in " +
527         base::IntToString(elapsed_time.InMilliseconds()) + "ms\n";
528
529     OutputString(stats);
530   }
531
532   return 0;
533 }
534
535 }  // namespace commands