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.
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"
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
;
50 DependentSetup
* release
;
51 DependentSetup
* debug64
;
52 DependentSetup
* release64
;
53 DependentSetup
* xcode_debug
;
54 DependentSetup
* xcode_release
;
57 struct TargetVectors
{
60 RecordVector host_debug
;
61 RecordVector host_release
;
63 RecordVector release64
;
64 RecordVector xcode_debug
;
65 RecordVector xcode_release
;
66 RecordVector xcode_host_debug
;
67 RecordVector xcode_host_release
;
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
+ "/");
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());
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
)
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.
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() &&
108 result
.push_back(all
[i
]);
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
) {
123 Label search_toolchain
= toolchain
;
124 if (search_toolchain
.is_null()) {
125 // Find the default toolchain.
127 records
[0]->item()->settings()->default_toolchain_label();
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
;
137 // Groups targets sharing the same label between debug and release.
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.
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
) {
150 CorrelateRecordVector(targets
.debug
, Label(), correlated
,
151 &TargetGroup::debug
);
152 CorrelateRecordVector(targets
.release
, Label(), correlated
,
153 &TargetGroup::release
);
156 CorrelateRecordVector(targets
.debug64
, Label(), correlated
,
157 &TargetGroup::debug64
);
158 CorrelateRecordVector(targets
.release64
, Label(), correlated
,
159 &TargetGroup::release64
);
162 CorrelateRecordVector(targets
.xcode_debug
, Label(), correlated
,
163 &TargetGroup::xcode_debug
);
164 CorrelateRecordVector(targets
.xcode_release
, Label(), correlated
,
165 &TargetGroup::xcode_release
);
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
);
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
);
182 // Verifies that both debug and release variants match. They can differ only
184 bool EnsureTargetsMatch(const TargetGroup
& group
, Err
* err
) {
185 if (!group
.debug
&& !group
.release
)
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));
198 const Target
* debug_target
= group
.debug
->item()->AsTarget();
199 const Target
* release_target
= group
.release
->item()->AsTarget();
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.");
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 "
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());
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 "
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));
249 // Returns the (number of targets, number of GYP files).
250 std::pair
<int, int> WriteGypFiles(Setups
& setups
, Err
* err
) {
251 TargetVectors targets
;
253 targets
.debug
= GetAllResolvedTargetRecords(setups
.debug
->builder());
254 targets
.release
= GetAllResolvedTargetRecords(setups
.release
->builder());
256 // 64-bit build is optional.
257 if (setups
.debug64
&& setups
.release64
) {
259 GetAllResolvedTargetRecords(setups
.debug64
->builder());
261 GetAllResolvedTargetRecords(setups
.release64
->builder());
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());
272 // Match up the debug and release version of each target by label.
273 CorrelatedTargetsMap correlated
;
274 CorrelateTargets(targets
, GetHostToolchain(setups
), &correlated
);
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.
289 if (!EnsureTargetsMatch(group
, err
))
290 return std::make_pair(0, 0);
294 helper
.GetGypFileForTarget(group
.debug
->item()->AsTarget(), err
)]
296 if (err
->has_error())
297 return std::make_pair(0, 0);
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());
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);
316 return std::make_pair(target_count
,
317 static_cast<int>(grouped_targets
.size()));
320 // Verifies that all build argument overrides are used by at least one of the
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(
327 setups
.release
->build_settings().build_args().MergeDeclaredArguments(
329 if (setups
.debug64
&& setups
.release64
) {
330 setups
.debug64
->build_settings().build_args().MergeDeclaredArguments(
332 setups
.release64
->build_settings().build_args().MergeDeclaredArguments(
335 if (setups
.xcode_debug
&& setups
.xcode_release
) {
336 setups
.xcode_debug
->build_settings().build_args().MergeDeclaredArguments(
338 setups
.xcode_release
->build_settings().build_args().MergeDeclaredArguments(
342 Scope::KeyValueMap used
=
343 setups
.debug
->build_settings().build_args().GetAllOverrides();
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.
355 // Suppress output on success.
356 const char kSwitchQuiet
[] = "q";
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"
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"
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"
376 " out/Debug/gn gyp\n"
377 " python build/gyp_chromiunm\n"
378 " ninja -C out/Debug foo_target\n"
380 " Two variables are used to control how a target relates to GYP:\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"
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"
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"
401 " The GYP variables converted to a GN-style string lookup.\n"
403 " --gyp_vars=\"component=\\\"shared_library\\\" use_aura=\\\"1\\\"\"\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"
414 " direct_dependen_configs = [ \":gyp_target_config\" ]\n"
417 " executable(\"my_app\") {\n"
418 " deps = [ \":gyp_target\" ]\n"
419 " gyp_file = \"//foo/myapp.gyp\"\n"
423 int RunGyp(const std::vector
<std::string
>& args
) {
424 base::ElapsedTimer timer
;
427 const CommandLine
* cmdline
= CommandLine::ForCurrentProcess();
429 // Compute output directory.
430 std::string build_dir
;
431 if (args
.size() == 1) {
434 // Backwards-compat code for the old invocation that uses
435 // "gn gyp --output=foo"
436 // TODO(brettw) This should be removed when gyp_chromium has been updated.
438 // Switch to set the build output directory.
439 const char kSwitchBuildOutput
[] = "output";
440 build_dir
= cmdline
->GetSwitchValueASCII(kSwitchBuildOutput
);
441 if (build_dir
.empty())
442 build_dir
= "//out/Default/";
445 // Deliberately leaked to avoid expensive process teardown. We also turn off
446 // unused override checking since we want to merge all declared arguments and
447 // check those, rather than check each build individually. Otherwise, you
448 // couldn't have an arg that was used in only one build type. This comes up
449 // because some args are build-type specific.
450 setups
.debug
= new Setup
;
451 setups
.debug
->set_check_for_unused_overrides(false);
452 if (!setups
.debug
->DoSetup(build_dir
))
454 const char kIsDebug
[] = "is_debug";
456 SourceDir base_build_dir
= setups
.debug
->build_settings().build_dir();
457 setups
.debug
->build_settings().SetBuildDir(
458 AppendDirSuffix(base_build_dir
, ".Debug"));
460 // Make a release build based on the debug one. We use a new directory for
461 // the build output so that they don't stomp on each other.
462 setups
.release
= new DependentSetup(setups
.debug
);
463 setups
.release
->build_settings().build_args().AddArgOverride(
464 kIsDebug
, Value(NULL
, false));
465 setups
.release
->build_settings().SetBuildDir(
466 AppendDirSuffix(base_build_dir
, ".Release"));
468 // 64-bit build (Windows only).
470 static const char kForceWin64
[] = "force_win64";
471 setups
.debug64
= new DependentSetup(setups
.debug
);
472 setups
.debug64
->build_settings().build_args().AddArgOverride(
473 kForceWin64
, Value(NULL
, true));
474 setups
.debug64
->build_settings().SetBuildDir(
475 AppendDirSuffix(base_build_dir
, ".Debug64"));
477 setups
.release64
= new DependentSetup(setups
.release
);
478 setups
.release64
->build_settings().build_args().AddArgOverride(
479 kForceWin64
, Value(NULL
, true));
480 setups
.release64
->build_settings().SetBuildDir(
481 AppendDirSuffix(base_build_dir
, ".Release64"));
484 // XCode build (Mac only).
485 #if defined(OS_MACOSX)
486 static const char kGypXCode
[] = "is_gyp_xcode_generator";
487 setups
.xcode_debug
= new DependentSetup(setups
.debug
);
488 setups
.xcode_debug
->build_settings().build_args().AddArgOverride(
489 kGypXCode
, Value(NULL
, true));
490 setups
.xcode_debug
->build_settings().SetBuildDir(
491 AppendDirSuffix(base_build_dir
, ".XCodeDebug"));
493 setups
.xcode_release
= new DependentSetup(setups
.release
);
494 setups
.xcode_release
->build_settings().build_args().AddArgOverride(
495 kGypXCode
, Value(NULL
, true));
496 setups
.xcode_release
->build_settings().SetBuildDir(
497 AppendDirSuffix(base_build_dir
, ".XCodeRelease"));
500 // Run all the builds in parellel.
501 setups
.release
->RunPreMessageLoop();
502 if (setups
.debug64
&& setups
.release64
) {
503 setups
.debug64
->RunPreMessageLoop();
504 setups
.release64
->RunPreMessageLoop();
506 if (setups
.xcode_debug
&& setups
.xcode_release
) {
507 setups
.xcode_debug
->RunPreMessageLoop();
508 setups
.xcode_release
->RunPreMessageLoop();
511 if (!setups
.debug
->Run())
514 if (!setups
.release
->RunPostMessageLoop())
516 if (setups
.debug64
&& !setups
.debug64
->RunPostMessageLoop())
518 if (setups
.release64
&& !setups
.release64
->RunPostMessageLoop())
520 if (setups
.xcode_debug
&& !setups
.xcode_debug
->RunPostMessageLoop())
522 if (setups
.xcode_release
&& !setups
.xcode_release
->RunPostMessageLoop())
525 VerifyAllOverridesUsed(setups
);
528 std::pair
<int, int> counts
= WriteGypFiles(setups
, &err
);
529 if (err
.has_error()) {
534 base::TimeDelta elapsed_time
= timer
.Elapsed();
536 if (!cmdline
->HasSwitch(kSwitchQuiet
)) {
537 OutputString("Done. ", DECORATION_GREEN
);
539 std::string stats
= "Wrote " +
540 base::IntToString(counts
.first
) + " targets to " +
541 base::IntToString(counts
.second
) + " GYP files read from " +
543 setups
.debug
->scheduler().input_file_manager()->GetInputFileCount())
545 base::IntToString(elapsed_time
.InMilliseconds()) + "ms\n";
553 } // namespace commands