Home | History | Annotate | Download | only in gn
      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