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 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. 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. 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). 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 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