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 "base/atomicops.h"
6 #include "base/bind.h"
7 #include "base/command_line.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/timer/elapsed_timer.h"
10 #include "tools/gn/build_settings.h"
11 #include "tools/gn/commands.h"
12 #include "tools/gn/ninja_target_writer.h"
13 #include "tools/gn/ninja_writer.h"
14 #include "tools/gn/scheduler.h"
15 #include "tools/gn/setup.h"
16 #include "tools/gn/standard_out.h"
17
18 namespace commands {
19
20 namespace {
21
22 // Suppress output on success.
23 const char kSwitchQuiet[] = "q";
24
25 const char kSwitchCheck[] = "check";
26
BackgroundDoWrite(const Target * target,const Toolchain * toolchain,const std::vector<const Item * > & deps_for_visibility)27 void BackgroundDoWrite(const Target* target,
28 const Toolchain* toolchain,
29 const std::vector<const Item*>& deps_for_visibility) {
30 // Validate visibility.
31 Err err;
32 for (size_t i = 0; i < deps_for_visibility.size(); i++) {
33 if (!Visibility::CheckItemVisibility(target, deps_for_visibility[i],
34 &err)) {
35 g_scheduler->FailWithError(err);
36 break; // Don't return early since we need DecrementWorkCount below.
37 }
38 }
39
40 if (!err.has_error())
41 NinjaTargetWriter::RunAndWriteFile(target, toolchain);
42 g_scheduler->DecrementWorkCount();
43 }
44
45 // Called on the main thread.
ItemResolvedCallback(base::subtle::Atomic32 * write_counter,scoped_refptr<Builder> builder,const BuilderRecord * record)46 void ItemResolvedCallback(base::subtle::Atomic32* write_counter,
47 scoped_refptr<Builder> builder,
48 const BuilderRecord* record) {
49 base::subtle::NoBarrier_AtomicIncrement(write_counter, 1);
50
51 const Item* item = record->item();
52 const Target* target = item->AsTarget();
53 if (target) {
54 const Toolchain* toolchain =
55 builder->GetToolchain(target->settings()->toolchain_label());
56 DCHECK(toolchain);
57
58 // Collect all dependencies.
59 std::vector<const Item*> deps;
60 for (BuilderRecord::BuilderRecordSet::const_iterator iter =
61 record->all_deps().begin();
62 iter != record->all_deps().end();
63 ++iter)
64 deps.push_back((*iter)->item());
65
66 g_scheduler->IncrementWorkCount();
67 g_scheduler->ScheduleWork(
68 base::Bind(&BackgroundDoWrite, target, toolchain, deps));
69 }
70 }
71
72 } // namespace
73
74 const char kGen[] = "gen";
75 const char kGen_HelpShort[] =
76 "gen: Generate ninja files.";
77 const char kGen_Help[] =
78 "gn gen: Generate ninja files.\n"
79 "\n"
80 " gn gen <output_directory>\n"
81 "\n"
82 " Generates ninja files from the current tree and puts them in the given\n"
83 " output directory.\n"
84 "\n"
85 " The output directory can be a source-repo-absolute path name such as:\n"
86 " //out/foo\n"
87 " Or it can be a directory relative to the current directory such as:\n"
88 " out/foo\n"
89 "\n"
90 " See \"gn help\" for the common command-line switches.\n";
91
RunGen(const std::vector<std::string> & args)92 int RunGen(const std::vector<std::string>& args) {
93 base::ElapsedTimer timer;
94
95 if (args.size() != 1) {
96 Err(Location(), "Need exactly one build directory to generate.",
97 "I expected something more like \"gn gen out/foo\"\n"
98 "You can also see \"gn help gen\".").PrintToStdout();
99 return 1;
100 }
101
102 // Deliberately leaked to avoid expensive process teardown.
103 Setup* setup = new Setup();
104 if (!setup->DoSetup(args[0]))
105 return 1;
106
107 if (CommandLine::ForCurrentProcess()->HasSwitch(kSwitchCheck))
108 setup->set_check_public_headers(true);
109
110 // Cause the load to also generate the ninja files for each target. We wrap
111 // the writing to maintain a counter.
112 base::subtle::Atomic32 write_counter = 0;
113 setup->builder()->set_resolved_callback(
114 base::Bind(&ItemResolvedCallback, &write_counter,
115 scoped_refptr<Builder>(setup->builder())));
116
117 // Do the actual load. This will also write out the target ninja files.
118 if (!setup->Run())
119 return 1;
120
121 // Write the root ninja files.
122 if (!NinjaWriter::RunAndWriteFiles(&setup->build_settings(),
123 setup->builder()))
124 return 1;
125
126 base::TimeDelta elapsed_time = timer.Elapsed();
127
128 if (!CommandLine::ForCurrentProcess()->HasSwitch(kSwitchQuiet)) {
129 OutputString("Done. ", DECORATION_GREEN);
130
131 std::string stats = "Wrote " +
132 base::IntToString(static_cast<int>(write_counter)) +
133 " targets from " +
134 base::IntToString(
135 setup->scheduler().input_file_manager()->GetInputFileCount()) +
136 " files in " +
137 base::IntToString(elapsed_time.InMilliseconds()) + "ms\n";
138 OutputString(stats);
139 }
140
141 return 0;
142 }
143
144 } // namespace commands
145