OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <algorithm> | 5 #include <iostream> |
6 #include <fstream> | 6 #include <map> |
7 | 7 #include <utility> |
8 #include "base/atomicops.h" | 8 #include <vector> |
9 #include "base/bind.h" | 9 |
10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
11 #include "base/file_util.h" | 11 #include "base/environment.h" |
12 #include "base/process/launch.h" | |
13 #include "base/strings/string_number_conversions.h" | 12 #include "base/strings/string_number_conversions.h" |
14 #include "base/strings/string_util.h" | |
15 #include "base/time/time.h" | 13 #include "base/time/time.h" |
16 #include "tools/gn/build_settings.h" | 14 #include "tools/gn/build_settings.h" |
17 #include "tools/gn/commands.h" | 15 #include "tools/gn/commands.h" |
18 #include "tools/gn/err.h" | 16 #include "tools/gn/err.h" |
19 #include "tools/gn/filesystem_utils.h" | 17 #include "tools/gn/gyp_helper.h" |
20 #include "tools/gn/ninja_target_writer.h" | 18 #include "tools/gn/gyp_target_writer.h" |
21 #include "tools/gn/ninja_writer.h" | 19 #include "tools/gn/item_node.h" |
22 #include "tools/gn/output_file.h" | 20 #include "tools/gn/location.h" |
23 #include "tools/gn/path_output.h" | |
24 #include "tools/gn/setup.h" | 21 #include "tools/gn/setup.h" |
| 22 #include "tools/gn/source_file.h" |
25 #include "tools/gn/standard_out.h" | 23 #include "tools/gn/standard_out.h" |
| 24 #include "tools/gn/target.h" |
26 | 25 |
27 namespace commands { | 26 namespace commands { |
28 | 27 |
29 namespace { | 28 namespace { |
30 | 29 |
| 30 typedef GypTargetWriter::TargetPair TargetPair; |
| 31 typedef std::map<Label, TargetPair> CorrelatedTargetsMap; |
| 32 typedef std::map<SourceFile, std::vector<TargetPair> > GroupedTargetsMap; |
| 33 typedef std::map<std::string, std::string> StringStringMap; |
| 34 |
| 35 // Groups targets sharing the same label between debug and release. |
| 36 void CorrelateTargets(const std::vector<const Target*>& debug_targets, |
| 37 const std::vector<const Target*>& release_targets, |
| 38 CorrelatedTargetsMap* correlated) { |
| 39 for (size_t i = 0; i < debug_targets.size(); i++) { |
| 40 const Target* target = debug_targets[i]; |
| 41 (*correlated)[target->label()].debug = target; |
| 42 } |
| 43 for (size_t i = 0; i < release_targets.size(); i++) { |
| 44 const Target* target = release_targets[i]; |
| 45 (*correlated)[target->label()].release = target; |
| 46 } |
| 47 } |
| 48 |
| 49 // Verifies that both debug and release variants match. They can differ only |
| 50 // by flags. |
| 51 bool EnsureTargetsMatch(const TargetPair& pair, Err* err) { |
| 52 // Check that both debug and release made this target. |
| 53 if (!pair.debug || !pair.release) { |
| 54 const Target* non_null_one = pair.debug ? pair.debug : pair.release; |
| 55 *err = Err(Location(), "The debug and release builds did not both generate " |
| 56 "a target with the name\n" + |
| 57 non_null_one->label().GetUserVisibleName(true)); |
| 58 return false; |
| 59 } |
| 60 |
| 61 // Check the flags that determine if and where we write the GYP file. |
| 62 if (pair.debug->item_node()->should_generate() != |
| 63 pair.release->item_node()->should_generate() || |
| 64 pair.debug->external() != pair.release->external() || |
| 65 pair.debug->gyp_file() != pair.release->gyp_file()) { |
| 66 *err = Err(Location(), "The metadata for the target\n" + |
| 67 pair.debug->label().GetUserVisibleName(true) + |
| 68 "\ndoesn't match between the debug and release builds."); |
| 69 return false; |
| 70 } |
| 71 |
| 72 // Check that the sources match. |
| 73 if (pair.debug->sources().size() != pair.release->sources().size()) { |
| 74 *err = Err(Location(), "The source file count for the target\n" + |
| 75 pair.debug->label().GetUserVisibleName(true) + |
| 76 "\ndoesn't have the same number of files between the debug and " |
| 77 "release builds."); |
| 78 return false; |
| 79 } |
| 80 for (size_t i = 0; i < pair.debug->sources().size(); i++) { |
| 81 if (pair.debug->sources()[i] != pair.release->sources()[i]) { |
| 82 *err = Err(Location(), "The debug and release version of the target \n" + |
| 83 pair.debug->label().GetUserVisibleName(true) + |
| 84 "\ndon't agree on the file\n" + |
| 85 pair.debug->sources()[i].value()); |
| 86 return false; |
| 87 } |
| 88 } |
| 89 |
| 90 // Check that the deps match. |
| 91 if (pair.debug->deps().size() != pair.release->deps().size()) { |
| 92 *err = Err(Location(), "The source file count for the target\n" + |
| 93 pair.debug->label().GetUserVisibleName(true) + |
| 94 "\ndoesn't have the same number of deps between the debug and " |
| 95 "release builds."); |
| 96 return false; |
| 97 } |
| 98 for (size_t i = 0; i < pair.debug->deps().size(); i++) { |
| 99 if (pair.debug->deps()[i]->label() != pair.release->deps()[i]->label()) { |
| 100 *err = Err(Location(), "The debug and release version of the target \n" + |
| 101 pair.debug->label().GetUserVisibleName(true) + |
| 102 "\ndon't agree on the dep\n" + |
| 103 pair.debug->deps()[i]->label().GetUserVisibleName(true)); |
| 104 return false; |
| 105 } |
| 106 } |
| 107 |
| 108 return true; |
| 109 } |
| 110 |
| 111 // Python uses shlex.split, which we partially emulate here. |
| 112 // |
| 113 // Advances to the next "word" in a GYP_DEFINES entry. This is something |
| 114 // separated by whitespace or '='. We allow backslash escaping and quoting. |
| 115 // The return value will be the index into the array immediately following the |
| 116 // word, and the contents of the word will be placed into |*word|. |
| 117 size_t GetNextGypDefinesWord(const std::string& defines, |
| 118 size_t cur, |
| 119 std::string* word) { |
| 120 size_t i = cur; |
| 121 bool is_quoted = false; |
| 122 if (cur < defines.size() && defines[cur] == '"') { |
| 123 i++; |
| 124 is_quoted = true; |
| 125 } |
| 126 |
| 127 for (; i < defines.size(); i++) { |
| 128 // Handle certain escape sequences: \\, \", \<space>. |
| 129 if (defines[i] == '\\' && i < defines.size() - 1 && |
| 130 (defines[i + 1] == '\\' || |
| 131 defines[i + 1] == ' ' || |
| 132 defines[i + 1] == '=' || |
| 133 defines[i + 1] == '"')) { |
| 134 i++; |
| 135 word->push_back(defines[i]); |
| 136 continue; |
| 137 } |
| 138 if (is_quoted && defines[i] == '"') { |
| 139 // Got to the end of the quoted sequence. |
| 140 return i + 1; |
| 141 } |
| 142 if (!is_quoted && (defines[i] == ' ' || defines[i] == '=')) { |
| 143 return i; |
| 144 } |
| 145 word->push_back(defines[i]); |
| 146 } |
| 147 return i; |
| 148 } |
| 149 |
| 150 // Advances to the beginning of the next word, or the size of the string if |
| 151 // the end was encountered. |
| 152 size_t AdvanceToNextGypDefinesWord(const std::string& defines, size_t cur) { |
| 153 while (cur < defines.size() && defines[cur] == ' ') |
| 154 cur++; |
| 155 return cur; |
| 156 } |
| 157 |
| 158 // The GYP defines looks like: |
| 159 // component=shared_library |
| 160 // component=shared_library foo=1 |
| 161 // component=shared_library foo=1 windows_sdk_dir="C:\Program Files\..." |
| 162 StringStringMap GetGypDefines() { |
| 163 StringStringMap result; |
| 164 |
| 165 scoped_ptr<base::Environment> env(base::Environment::Create()); |
| 166 std::string defines; |
| 167 if (!env->GetVar("GYP_DEFINES", &defines) || defines.empty()) |
| 168 return result; |
| 169 |
| 170 size_t cur = 0; |
| 171 while (cur < defines.size()) { |
| 172 std::string key; |
| 173 cur = AdvanceToNextGypDefinesWord(defines, cur); |
| 174 cur = GetNextGypDefinesWord(defines, cur, &key); |
| 175 |
| 176 // The words should be separated by an equals. |
| 177 cur = AdvanceToNextGypDefinesWord(defines, cur); |
| 178 if (cur == defines.size()) |
| 179 break; |
| 180 if (defines[cur] != '=') |
| 181 continue; |
| 182 cur++; // Skip over '='. |
| 183 |
| 184 std::string value; |
| 185 cur = AdvanceToNextGypDefinesWord(defines, cur); |
| 186 cur = GetNextGypDefinesWord(defines, cur, &value); |
| 187 |
| 188 result[key] = value; |
| 189 } |
| 190 |
| 191 return result; |
| 192 } |
| 193 |
| 194 // Returns a set of args from known GYP define values. |
| 195 Scope::KeyValueMap GetArgsFromGypDefines() { |
| 196 StringStringMap gyp_defines = GetGypDefines(); |
| 197 |
| 198 Scope::KeyValueMap result; |
| 199 |
| 200 if (gyp_defines["component"] == "shared_library") { |
| 201 result["is_component_build"] = Value(NULL, true); |
| 202 } else { |
| 203 result["is_component_build"] = Value(NULL, false); |
| 204 } |
| 205 |
| 206 // Windows SDK path. GYP and the GN build use the same name. |
| 207 const char kWinSdkPath[] = "windows_sdk_path"; |
| 208 if (gyp_defines[kWinSdkPath].empty()) |
| 209 result[kWinSdkPath] = Value(NULL, gyp_defines[kWinSdkPath]); |
| 210 |
| 211 return result; |
| 212 } |
| 213 |
| 214 // Returns the number of targets, number of GYP files. |
| 215 std::pair<int, int> WriteGypFiles( |
| 216 const BuildSettings& debug_settings, |
| 217 const BuildSettings& release_settings, |
| 218 Err* err) { |
| 219 // Group all targets by output GYP file name. |
| 220 std::vector<const Target*> debug_targets; |
| 221 std::vector<const Target*> release_targets; |
| 222 debug_settings.target_manager().GetAllTargets(&debug_targets); |
| 223 release_settings.target_manager().GetAllTargets(&release_targets); |
| 224 |
| 225 // Match up the debug and release version of each target by label. |
| 226 CorrelatedTargetsMap correlated; |
| 227 CorrelateTargets(debug_targets, release_targets, &correlated); |
| 228 |
| 229 GypHelper helper; |
| 230 GroupedTargetsMap grouped_targets; |
| 231 int target_count = 0; |
| 232 for (CorrelatedTargetsMap::iterator i = correlated.begin(); |
| 233 i != correlated.end(); ++i) { |
| 234 const TargetPair& pair = i->second; |
| 235 if (!EnsureTargetsMatch(pair, err)) |
| 236 return std::make_pair(0, 0); |
| 237 |
| 238 if (!pair.debug->item_node()->should_generate()) |
| 239 continue; // Skip non-generated ones. |
| 240 if (pair.debug->external()) |
| 241 continue; // Skip external ones. |
| 242 if (pair.debug->gyp_file().is_null()) |
| 243 continue; // Skip ones without GYP files. |
| 244 |
| 245 target_count++; |
| 246 grouped_targets[helper.GetGypFileForTarget(pair.debug, err)].push_back( |
| 247 pair); |
| 248 if (err->has_error()) |
| 249 return std::make_pair(0, 0); |
| 250 } |
| 251 |
| 252 // Write each GYP file. |
| 253 for (GroupedTargetsMap::iterator i = grouped_targets.begin(); |
| 254 i != grouped_targets.end(); ++i) { |
| 255 GypTargetWriter::WriteFile(i->first, i->second, err); |
| 256 if (err->has_error()) |
| 257 return std::make_pair(0, 0); |
| 258 } |
| 259 |
| 260 return std::make_pair(target_count, |
| 261 static_cast<int>(grouped_targets.size())); |
| 262 } |
| 263 |
| 264 } // namespace |
| 265 |
31 // Suppress output on success. | 266 // Suppress output on success. |
32 const char kSwitchQuiet[] = "q"; | 267 const char kSwitchQuiet[] = "q"; |
33 | 268 |
34 // Skip actually executing GYP. This is for when you're working on the GN | |
35 // build and don't want to wait for GYP to regenerate. All GN files are | |
36 // regenerated, but the GYP ones are not. | |
37 const char kSwitchNoGyp[] = "no-gyp"; | |
38 | |
39 // Where to have GYP write its outputs. | |
40 const char kDirOut[] = "out.gn"; | |
41 | |
42 // We'll do the GN build to here. | |
43 const char kBuildSourceDir[] = "//out.gn/Debug/"; | |
44 | |
45 // File that GYP will write dependency information to. | |
46 const char kGypDepsSourceFileName[] = "//out.gn/gyp_deps.txt"; | |
47 | |
48 void TargetResolvedCallback(base::subtle::Atomic32* write_counter, | |
49 const Target* target) { | |
50 base::subtle::NoBarrier_AtomicIncrement(write_counter, 1); | |
51 NinjaTargetWriter::RunAndWriteFile(target); | |
52 } | |
53 | |
54 bool SimpleNinjaParse(const std::string& data, | |
55 std::set<std::string>* subninjas, | |
56 size_t* first_subninja_offset) { | |
57 const size_t kSubninjaPrefixLen = 10; | |
58 const char kSubninjaPrefix[kSubninjaPrefixLen + 1] = "\nsubninja "; | |
59 | |
60 *first_subninja_offset = std::string::npos; | |
61 size_t next_subninja = 0; | |
62 while ((next_subninja = data.find(kSubninjaPrefix, next_subninja)) != | |
63 std::string::npos) { | |
64 if (*first_subninja_offset == std::string::npos) | |
65 *first_subninja_offset = next_subninja; | |
66 | |
67 size_t line_end = data.find('\n', next_subninja + 1); | |
68 if (line_end == std::string::npos) | |
69 return false; | |
70 | |
71 std::string filename = data.substr( | |
72 next_subninja + kSubninjaPrefixLen, | |
73 line_end - next_subninja - kSubninjaPrefixLen); | |
74 TrimWhitespaceASCII(filename, TRIM_ALL, &filename); | |
75 #if defined(OS_WIN) | |
76 // We always want our array to use forward slashes. | |
77 std::replace(filename.begin(), filename.end(), '\\', '/'); | |
78 #endif | |
79 subninjas->insert(filename); | |
80 | |
81 next_subninja = line_end; | |
82 } | |
83 return *first_subninja_offset != std::string::npos; | |
84 } | |
85 | |
86 bool FixupBuildNinja(const BuildSettings* build_settings, | |
87 const base::FilePath& buildfile) { | |
88 std::string contents; | |
89 if (!base::ReadFileToString(buildfile, &contents)) { | |
90 Err(Location(), "Could not load " + FilePathToUTF8(buildfile)) | |
91 .PrintToStdout(); | |
92 return false; | |
93 } | |
94 | |
95 std::set<std::string> subninjas; | |
96 size_t first_subninja_offset = 0; | |
97 if (!SimpleNinjaParse(contents, &subninjas, &first_subninja_offset)) { | |
98 Err(Location(), "Could not parse " + FilePathToUTF8(buildfile)) | |
99 .PrintToStdout(); | |
100 return false; | |
101 } | |
102 | |
103 // Write toolchain files. | |
104 std::vector<const Settings*> all_settings; | |
105 if (!NinjaWriter::RunAndWriteToolchainFiles( | |
106 build_settings, subninjas, &all_settings)) | |
107 return false; | |
108 | |
109 // Copy first part of buildfile to the output. | |
110 std::ofstream file; | |
111 file.open(FilePathToUTF8(buildfile).c_str(), | |
112 std::ios_base::out | std::ios_base::binary); | |
113 if (file.fail()) { | |
114 Err(Location(), "Could not write " + FilePathToUTF8(buildfile)) | |
115 .PrintToStdout(); | |
116 return false; | |
117 } | |
118 file.write(contents.data(), first_subninja_offset); | |
119 | |
120 // Add refs for our toolchains to the original build.ninja. | |
121 NinjaHelper helper(build_settings); | |
122 PathOutput path_output(build_settings->build_dir(), ESCAPE_NINJA, true); | |
123 file << "\n# GN-added toolchain files.\n"; | |
124 for (size_t i = 0; i < all_settings.size(); i++) { | |
125 file << "subninja "; | |
126 path_output.WriteFile(file, | |
127 helper.GetNinjaFileForToolchain(all_settings[i])); | |
128 file << std::endl; | |
129 } | |
130 file << "\n# GYP-written subninjas."; | |
131 | |
132 // Write remaining old subninjas from original file. | |
133 file.write(&contents[first_subninja_offset], | |
134 contents.size() - first_subninja_offset); | |
135 return true; | |
136 } | |
137 | |
138 bool RunGyp(const BuildSettings* build_settings) { | |
139 if (!CommandLine::ForCurrentProcess()->HasSwitch(kSwitchQuiet)) | |
140 OutputString("Running GYP...\n"); | |
141 | |
142 const base::FilePath& python_path = build_settings->python_path(); | |
143 | |
144 // Construct the command line. Note that AppendArgPath and AppendSwitchPath | |
145 // don't preserve the relative ordering, and we need the python file to be | |
146 // first, so we have to convert switch values to strings before appending. | |
147 // | |
148 // Note that GYP will get confused if this path is quoted, so don't quote it | |
149 // and hope that there are no spaces! | |
150 CommandLine cmdline(python_path); | |
151 cmdline.AppendArgPath( | |
152 build_settings->GetFullPath(SourceFile("//build/gyp_chromium.py"))); | |
153 | |
154 // Override the default output directory so we can coexist in parallel | |
155 // with a normal Ninja GYP build. | |
156 cmdline.AppendArg("-G"); | |
157 cmdline.AppendArg(std::string("output_dir=") + kDirOut); | |
158 | |
159 // Force the Ninja generator. | |
160 cmdline.AppendArg("-f"); | |
161 cmdline.AppendArg("ninja"); | |
162 | |
163 // Write deps for libraries so we can pick them up. | |
164 cmdline.AppendArg("-G"); | |
165 cmdline.AppendArg("link_deps_file=" + FilePathToUTF8( | |
166 build_settings->GetFullPath(SourceFile(kGypDepsSourceFileName)))); | |
167 | |
168 std::string output; | |
169 if (!base::GetAppOutput(cmdline, &output)) { | |
170 Err(Location(), "GYP execution failed.", output).PrintToStdout(); | |
171 return false; | |
172 } | |
173 return true; | |
174 } | |
175 | |
176 } // namespace | |
177 | |
178 // Converts a GYP qualified target which looks like: | |
179 // "/home/you/src/third_party/icu/icu.gyp:icui18n#target" to a GN label like | |
180 // "//third_party/icu:icui18n". On failure returns an empty label and sets the | |
181 // error. | |
182 Label GypQualifiedTargetToLabel(const std::string& source_root_prefix, | |
183 const base::StringPiece& target, | |
184 Err* err) { | |
185 // Prefix should end in canonical path separator. | |
186 const char kSep = static_cast<char>(base::FilePath::kSeparators[0]); | |
187 DCHECK(source_root_prefix[source_root_prefix.size() - 1] == kSep); | |
188 | |
189 if (!target.starts_with(source_root_prefix)) { | |
190 *err = Err(Location(), "GYP deps parsing failed.", | |
191 "The line was \"" + target.as_string() + "\" and it should have " | |
192 "started with \"" + source_root_prefix + "\""); | |
193 return Label(); | |
194 } | |
195 | |
196 size_t begin = source_root_prefix.size(); | |
197 size_t colon = target.find(':', begin); | |
198 if (colon == std::string::npos) { | |
199 *err = Err(Location(), "Expected :", target.as_string()); | |
200 return Label(); | |
201 } | |
202 | |
203 size_t octothorpe = target.find('#', colon); | |
204 if (octothorpe == std::string::npos) { | |
205 *err = Err(Location(), "Expected #", target.as_string()); | |
206 return Label(); | |
207 } | |
208 | |
209 // This will look like "third_party/icu/icu.gyp" | |
210 base::StringPiece gyp_file = target.substr(begin, colon - begin); | |
211 | |
212 // Strip the file name from the end to get "third_party/icu". | |
213 size_t last_sep = gyp_file.find_last_of(kSep); | |
214 if (last_sep == std::string::npos) { | |
215 *err = Err(Location(), "Expected path separator.", target.as_string()); | |
216 return Label(); | |
217 } | |
218 base::StringPiece path = gyp_file.substr(0, last_sep); | |
219 SourceDir dir("//" + path.as_string()); | |
220 | |
221 base::StringPiece name = target.substr(colon + 1, octothorpe - colon - 1); | |
222 | |
223 return Label(dir, name); | |
224 } | |
225 | |
226 // Parses the link deps file, filling the given map. Returns true on sucess. | |
227 // On failure fills the error and returns false. | |
228 // | |
229 // Example format for each line: | |
230 // /home/you/src/third_party/icu/icu.gyp:icui18n#target lib/libi18n.so | |
231 bool ParseLinkDepsFile(const BuildSettings* build_settings, | |
232 const std::string& contents, | |
233 BuildSettings::AdditionalLibsMap* deps, | |
234 Err* err) { | |
235 std::string source_root_prefix = FilePathToUTF8(build_settings->root_path()); | |
236 source_root_prefix.push_back(base::FilePath::kSeparators[0]); | |
237 | |
238 size_t cur = 0; | |
239 while (cur < contents.size()) { | |
240 // The source file is everything up to the space. | |
241 size_t space = contents.find(' ', cur); | |
242 if (space == std::string::npos) | |
243 break; | |
244 Label source(GypQualifiedTargetToLabel( | |
245 source_root_prefix, | |
246 base::StringPiece(&contents[cur], space - cur), | |
247 err)); | |
248 if (source.is_null()) | |
249 return false; | |
250 | |
251 // The library file is everything between the space and EOL. | |
252 cur = space + 1; | |
253 size_t eol = contents.find('\n', cur); | |
254 if (eol == std::string::npos) { | |
255 *err = Err(Location(), "Expected newline at end of link deps file."); | |
256 return false; | |
257 } | |
258 OutputFile lib(contents.substr(cur, eol - cur)); | |
259 | |
260 deps->insert(std::make_pair(source, lib)); | |
261 cur = eol + 1; | |
262 } | |
263 return true; | |
264 } | |
265 | |
266 const char kGyp[] = "gyp"; | 269 const char kGyp[] = "gyp"; |
267 const char kGyp_HelpShort[] = | 270 const char kGyp_HelpShort[] = |
268 "gyp: Run GYP and then GN."; | 271 "gyp: Make GYP files from GN."; |
269 const char kGyp_Help[] = | 272 const char kGyp_Help[] = "Doooooom.\n"; |
270 "gyp: Run GYP and then GN.\n" | 273 |
271 "\n" | |
272 " Generate a hybrid GYP/GN build where some targets are generated by\n" | |
273 " each of the tools. As long as target names and locations match between\n" | |
274 " the two tools, they can depend on each other.\n" | |
275 "\n" | |
276 " When GN is run in this mode, it will not write out any targets\n" | |
277 " annotated with \"external = true\". Otherwise, GYP targets with the\n" | |
278 " same name and location will be overwritten.\n" | |
279 "\n" | |
280 " References to the GN ninja files will be inserted into the\n" | |
281 " GYP-generated build.ninja file.\n" | |
282 "\n" | |
283 "Option:\n" | |
284 " --no-gyp\n" | |
285 " Don't actually run GYP or modify build.ninja. This is used when\n" | |
286 " working on the GN build when it is known that no GYP files have\n" | |
287 " changed and you want it to run faster.\n"; | |
288 | |
289 // Note: partially duplicated from command_gen.cc. | |
290 int RunGyp(const std::vector<std::string>& args) { | 274 int RunGyp(const std::vector<std::string>& args) { |
291 const CommandLine* cmdline = CommandLine::ForCurrentProcess(); | 275 const CommandLine* cmdline = CommandLine::ForCurrentProcess(); |
292 bool no_gyp = cmdline->HasSwitch(kSwitchNoGyp); | 276 |
| 277 base::TimeTicks begin_time = base::TimeTicks::Now(); |
293 | 278 |
294 // Deliberately leaked to avoid expensive process teardown. | 279 // Deliberately leaked to avoid expensive process teardown. |
295 Setup* setup = new Setup; | 280 Setup* setup_debug = new Setup; |
296 if (!setup->DoSetup()) | 281 if (!setup_debug->DoSetup()) |
297 return 1; | 282 return 1; |
298 | 283 const char kIsDebug[] = "is_debug"; |
299 setup->build_settings().SetBuildDir(SourceDir(kBuildSourceDir)); | 284 setup_debug->build_settings().build_args().AddArgOverrides( |
300 setup->build_settings().set_using_external_generator(true); | 285 GetArgsFromGypDefines()); |
301 | 286 setup_debug->build_settings().build_args().AddArgOverride( |
302 // Provide a way for buildfiles to know we're doing a GYP build. | 287 kIsDebug, Value(NULL, true)); |
303 /* | 288 |
304 Scope::KeyValueMap variable_overrides; | 289 // Make a release build based on the debug one. We use a new directory for |
305 variable_overrides["is_gyp"] = Value(NULL, true); | 290 // the build output so that they don't stomp on each other. |
306 setup->build_settings().build_args().AddArgOverrides(variable_overrides); | 291 DependentSetup* setup_release = new DependentSetup(*setup_debug); |
307 */ | 292 setup_release->build_settings().build_args().AddArgOverride( |
308 | 293 kIsDebug, Value(NULL, false)); |
309 base::FilePath link_deps_file = | 294 setup_release->build_settings().SetBuildDir( |
310 setup->build_settings().GetFullPath(SourceFile(kGypDepsSourceFileName)); | 295 SourceDir(setup_release->build_settings().build_dir().value() + |
311 if (!no_gyp) | 296 "gn_release.tmp/")); |
312 base::DeleteFile(link_deps_file, false); | 297 |
313 | 298 // Run both debug and release builds in parallel. |
314 base::TimeTicks begin_time = base::TimeTicks::Now(); | 299 setup_release->RunPreMessageLoop(); |
315 if (!no_gyp) { | 300 if (!setup_debug->Run()) |
316 if (!RunGyp(&setup->build_settings())) | |
317 return 1; | |
318 } | |
319 base::TimeTicks end_gyp_time = base::TimeTicks::Now(); | |
320 | |
321 // Read in the GYP link dependencies. | |
322 std::string link_deps_contents; | |
323 if (!base::ReadFileToString(link_deps_file, &link_deps_contents)) { | |
324 Err(Location(), "Couldn't load link deps file.", | |
325 FilePathToUTF8(link_deps_file)).PrintToStdout(); | |
326 return 1; | 301 return 1; |
327 } | 302 if (!setup_release->RunPostMessageLoop()) |
| 303 return 1; |
| 304 |
328 Err err; | 305 Err err; |
329 if (!ParseLinkDepsFile(&setup->build_settings(), | 306 std::pair<int, int> counts = WriteGypFiles(setup_debug->build_settings(), |
330 link_deps_contents, | 307 setup_release->build_settings(), |
331 &setup->build_settings().external_link_deps(), &err)) { | 308 &err); |
| 309 if (err.has_error()) { |
332 err.PrintToStdout(); | 310 err.PrintToStdout(); |
333 return 1; | 311 return 1; |
334 } | 312 } |
335 | 313 |
336 if (!cmdline->HasSwitch(kSwitchQuiet)) | |
337 OutputString("Running GN...\n"); | |
338 | |
339 // Cause the load to also generate the ninja files for each target. We wrap | |
340 // the writing to maintain a counter. | |
341 base::subtle::Atomic32 write_counter = 0; | |
342 setup->build_settings().set_target_resolved_callback( | |
343 base::Bind(&TargetResolvedCallback, &write_counter)); | |
344 | |
345 // Do the actual load. This will also write out the target ninja files. | |
346 if (!setup->Run()) | |
347 return 1; | |
348 | |
349 // Integrate with the GYP build. | |
350 if (!no_gyp) { | |
351 base::FilePath ninja_buildfile(setup->build_settings().GetFullPath( | |
352 SourceFile(setup->build_settings().build_dir().value() + | |
353 "build.ninja"))); | |
354 if (!FixupBuildNinja(&setup->build_settings(), ninja_buildfile)) | |
355 return 1; | |
356 } | |
357 | |
358 // Timing info. | 314 // Timing info. |
359 base::TimeTicks end_time = base::TimeTicks::Now(); | 315 base::TimeTicks end_time = base::TimeTicks::Now(); |
360 if (!cmdline->HasSwitch(kSwitchQuiet)) { | 316 if (!cmdline->HasSwitch(kSwitchQuiet)) { |
361 OutputString("Done. ", DECORATION_GREEN); | 317 OutputString("Done. ", DECORATION_GREEN); |
362 | 318 |
363 std::string stats = "Wrote " + | 319 std::string stats = "Wrote " + |
364 base::IntToString(static_cast<int>(write_counter)) + | 320 base::IntToString(counts.first) + " targets to " + |
365 " targets from " + | 321 base::IntToString(counts.second) + " GYP files read from " + |
366 base::IntToString( | 322 base::IntToString( |
367 setup->scheduler().input_file_manager()->GetInputFileCount()) + | 323 setup_debug->scheduler().input_file_manager()->GetInputFileCount()) |
368 " files in " + | 324 + " GN files in " + |
369 base::IntToString((end_time - end_gyp_time).InMilliseconds()) + "ms " + | 325 base::IntToString((end_time - begin_time).InMilliseconds()) + "ms\n"; |
370 "(GYP took " + | |
371 base::IntToString((end_gyp_time - begin_time).InMilliseconds()) + | |
372 "ms)\n"; | |
373 | 326 |
374 OutputString(stats); | 327 OutputString(stats); |
375 } | 328 } |
376 | 329 |
377 return 0; | 330 return 0; |
378 } | 331 } |
379 | 332 |
380 } // namespace commands | 333 } // namespace commands |
OLD | NEW |