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 "tools/gn/gyp_binary_target_writer.h"
      6 
      7 #include <set>
      8 
      9 #include "base/logging.h"
     10 #include "tools/gn/builder_record.h"
     11 #include "tools/gn/config_values_extractors.h"
     12 #include "tools/gn/err.h"
     13 #include "tools/gn/escape.h"
     14 #include "tools/gn/filesystem_utils.h"
     15 #include "tools/gn/settings.h"
     16 #include "tools/gn/target.h"
     17 
     18 namespace {
     19 
     20 // This functor is used to capture the output of RecursiveTargetConfigToStream
     21 // in an vector.
     22 template<typename T>
     23 struct Accumulator {
     24   Accumulator(std::vector<T>* result_in) : result(result_in) {}
     25 
     26   void operator()(const T& s, std::ostream&) const {
     27     result->push_back(s);
     28   }
     29 
     30   std::vector<T>* result;
     31 };
     32 
     33 // Writes the given array values. The array should already be declared with the
     34 // opening "[" written to the output. The function will not write the
     35 // terminating "]" either.
     36 void WriteArrayValues(std::ostream& out,
     37                       const std::vector<std::string>& values) {
     38   EscapeOptions options;
     39   options.mode = ESCAPE_JSON;
     40   for (size_t i = 0; i < values.size(); i++) {
     41     out << " '";
     42     EscapeStringToStream(out, values[i], options);
     43     out << "',";
     44   }
     45 }
     46 
     47 // Returns the value from the already-filled in cflags_* for the optimization
     48 // level to set in the GYP file. Additionally, this removes the flag from the
     49 // given vector so we don't get duplicates.
     50 std::string GetVCOptimization(std::vector<std::string>* cflags) {
     51   // Searches for the "/O?" option and returns the corresponding GYP value.
     52   for (size_t i = 0; i < cflags->size(); i++) {
     53     const std::string& cur = (*cflags)[i];
     54     if (cur.size() == 3 && cur[0] == '/' && cur[1] == 'O') {
     55       char level = cur[2];
     56       cflags->erase(cflags->begin() + i);  // Invalidates |cur|!
     57       switch (level) {
     58         case 'd': return "'0'";
     59         case '1': return "'1'";
     60         case '2': return "'2'";
     61         case 'x': return "'3'";
     62         default:  return "'2'";
     63       }
     64     }
     65   }
     66   return "'2'";  // Default value.
     67 }
     68 
     69 // Finds all values from the given getter from all configs in the given list,
     70 // and adds them to the given result vector.
     71 template<typename T>
     72 void FillConfigListValues(
     73     const LabelConfigVector& configs,
     74     const std::vector<T>& (ConfigValues::* getter)() const,
     75     std::vector<T>* result) {
     76   for (size_t config_i = 0; config_i < configs.size(); config_i++) {
     77     const std::vector<T>& values =
     78         (configs[config_i].ptr->config_values().*getter)();
     79     for (size_t val_i = 0; val_i < values.size(); val_i++)
     80       result->push_back(values[val_i]);
     81   }
     82 }
     83 
     84 }  // namespace
     85 
     86 GypBinaryTargetWriter::Flags::Flags() {}
     87 GypBinaryTargetWriter::Flags::~Flags() {}
     88 
     89 GypBinaryTargetWriter::GypBinaryTargetWriter(const TargetGroup& group,
     90                                              const Toolchain* debug_toolchain,
     91                                              const SourceDir& gyp_dir,
     92                                              std::ostream& out)
     93     : GypTargetWriter(group.debug->item()->AsTarget(), debug_toolchain,
     94                       gyp_dir, out),
     95       group_(group) {
     96 }
     97 
     98 GypBinaryTargetWriter::~GypBinaryTargetWriter() {
     99 }
    100 
    101 void GypBinaryTargetWriter::Run() {
    102   int indent = 4;
    103 
    104   Indent(indent) << "{\n";
    105 
    106   WriteName(indent + kExtraIndent);
    107   WriteType(indent + kExtraIndent);
    108 
    109   if (target_->settings()->IsLinux())
    110     WriteLinuxConfiguration(indent + kExtraIndent);
    111   else if (target_->settings()->IsWin())
    112     WriteVCConfiguration(indent + kExtraIndent);
    113   else if (target_->settings()->IsMac())
    114     WriteMacConfiguration(indent + kExtraIndent);
    115   WriteDirectDependentSettings(indent + kExtraIndent);
    116   WriteAllDependentSettings(indent + kExtraIndent);
    117 
    118   Indent(indent) << "},\n";
    119 }
    120 
    121 void GypBinaryTargetWriter::WriteName(int indent) {
    122   std::string name = helper_.GetNameForTarget(target_);
    123   Indent(indent) << "'target_name': '" << name << "',\n";
    124 
    125   std::string product_name;
    126   if (target_->output_name().empty())
    127     product_name = target_->label().name();
    128   else
    129     product_name = name;
    130 
    131   // TODO(brettw) GN knows not to prefix targets starting with "lib" with
    132   // another "lib" on Linux, but GYP doesn't. We need to rename applicable
    133   // targets here.
    134 
    135   Indent(indent) << "'product_name': '" << product_name << "',\n";
    136 }
    137 
    138 void GypBinaryTargetWriter::WriteType(int indent) {
    139   Indent(indent) << "'type': ";
    140   switch (target_->output_type()) {
    141     case Target::EXECUTABLE:
    142       out_ << "'executable',\n";
    143       break;
    144     case Target::STATIC_LIBRARY:
    145       out_ << "'static_library',\n";
    146       break;
    147     case Target::SHARED_LIBRARY:
    148       out_ << "'shared_library',\n";
    149       break;
    150     case Target::SOURCE_SET:
    151       out_ << "'static_library',\n";  // TODO(brettw) fixme.
    152       break;
    153     default:
    154       NOTREACHED();
    155   }
    156 
    157   if (target_->hard_dep())
    158     Indent(indent) << "'hard_dependency': 1,\n";
    159 }
    160 
    161 void GypBinaryTargetWriter::WriteVCConfiguration(int indent) {
    162   Indent(indent) << "'configurations': {\n";
    163 
    164   Indent(indent + kExtraIndent) << "'Debug': {\n";
    165   Flags debug_flags(FlagsFromTarget(group_.debug->item()->AsTarget()));
    166   WriteVCFlags(debug_flags, indent + kExtraIndent * 2);
    167   Indent(indent + kExtraIndent) << "},\n";
    168 
    169   Indent(indent + kExtraIndent) << "'Release': {\n";
    170   Flags release_flags(FlagsFromTarget(group_.release->item()->AsTarget()));
    171   WriteVCFlags(release_flags, indent + kExtraIndent * 2);
    172   Indent(indent + kExtraIndent) << "},\n";
    173 
    174   // Note that we always need Debug_x64 and Release_x64 defined or GYP will get
    175   // confused, but we ca leave them empty if there's no 64-bit target.
    176   Indent(indent + kExtraIndent) << "'Debug_x64': {\n";
    177   if (group_.debug64) {
    178     Flags flags(FlagsFromTarget(group_.debug64->item()->AsTarget()));
    179     WriteVCFlags(flags, indent + kExtraIndent * 2);
    180   }
    181   Indent(indent + kExtraIndent) << "},\n";
    182 
    183   Indent(indent + kExtraIndent) << "'Release_x64': {\n";
    184   if (group_.release64) {
    185     Flags flags(FlagsFromTarget(group_.release64->item()->AsTarget()));
    186     WriteVCFlags(flags, indent + kExtraIndent * 2);
    187   }
    188   Indent(indent + kExtraIndent) << "},\n";
    189 
    190   Indent(indent) << "},\n";
    191 
    192   WriteSources(target_, indent);
    193   WriteDeps(target_, indent);
    194 }
    195 
    196 void GypBinaryTargetWriter::WriteLinuxConfiguration(int indent) {
    197   // The Linux stuff works differently. On Linux we support cross-compiles and
    198   // all ninja generators know to look for target conditions. Other platforms'
    199   // generators don't all do this, so we can't have the same GYP structure.
    200   Indent(indent) << "'target_conditions': [\n";
    201   // The host toolset is configured for the current computer, we will only have
    202   // this when doing cross-compiles.
    203   if (group_.host_debug && group_.host_release) {
    204     Indent(indent + kExtraIndent) << "['_toolset == \"host\"', {\n";
    205     Indent(indent + kExtraIndent * 2) << "'configurations': {\n";
    206     Indent(indent + kExtraIndent * 3) << "'Debug': {\n";
    207     WriteLinuxFlagsForTarget(group_.host_debug->item()->AsTarget(),
    208                              indent + kExtraIndent * 4);
    209     Indent(indent + kExtraIndent * 3) << "},\n";
    210     Indent(indent + kExtraIndent * 3) << "'Release': {\n";
    211     WriteLinuxFlagsForTarget(group_.host_release->item()->AsTarget(),
    212                              indent + kExtraIndent * 4);
    213     Indent(indent + kExtraIndent * 3) << "},\n";
    214     Indent(indent + kExtraIndent * 2) << "}\n";
    215 
    216     // The sources are per-toolset but shared between debug & release.
    217     WriteSources(group_.host_debug->item()->AsTarget(),
    218                  indent + kExtraIndent * 2);
    219 
    220     Indent(indent + kExtraIndent) << "],\n";
    221   }
    222 
    223   // The target toolset is the "regular" one.
    224   Indent(indent + kExtraIndent) << "['_toolset == \"target\"', {\n";
    225   Indent(indent + kExtraIndent * 2) << "'configurations': {\n";
    226   Indent(indent + kExtraIndent * 3) << "'Debug': {\n";
    227   WriteLinuxFlagsForTarget(group_.debug->item()->AsTarget(),
    228                            indent + kExtraIndent * 4);
    229   Indent(indent + kExtraIndent * 3) << "},\n";
    230   Indent(indent + kExtraIndent * 3) << "'Release': {\n";
    231   WriteLinuxFlagsForTarget(group_.release->item()->AsTarget(),
    232                            indent + kExtraIndent * 4);
    233   Indent(indent + kExtraIndent * 3) << "},\n";
    234   Indent(indent + kExtraIndent * 2) << "},\n";
    235 
    236   WriteSources(target_, indent + kExtraIndent * 2);
    237 
    238   Indent(indent + kExtraIndent) << "},],\n";
    239   Indent(indent) << "],\n";
    240 
    241   // Deps in GYP can not vary based on the toolset.
    242   WriteDeps(target_, indent);
    243 }
    244 
    245 void GypBinaryTargetWriter::WriteMacConfiguration(int indent) {
    246   Indent(indent) << "'configurations': {\n";
    247 
    248   Indent(indent + kExtraIndent) << "'Debug': {\n";
    249   Flags debug_flags(FlagsFromTarget(group_.debug->item()->AsTarget()));
    250   WriteMacFlags(debug_flags, indent + kExtraIndent * 2);
    251   Indent(indent + kExtraIndent) << "},\n";
    252 
    253   Indent(indent + kExtraIndent) << "'Release': {\n";
    254   Flags release_flags(FlagsFromTarget(group_.release->item()->AsTarget()));
    255   WriteMacFlags(release_flags, indent + kExtraIndent * 2);
    256   Indent(indent + kExtraIndent) << "},\n";
    257 
    258   Indent(indent) << "},\n";
    259 
    260   WriteSources(target_, indent);
    261   WriteDeps(target_, indent);
    262 }
    263 
    264 void GypBinaryTargetWriter::WriteVCFlags(Flags& flags, int indent) {
    265   // Defines and includes go outside of the msvs settings.
    266   WriteNamedArray("defines", flags.defines, indent);
    267   WriteIncludeDirs(flags, indent);
    268 
    269   // C flags.
    270   Indent(indent) << "'msvs_settings': {\n";
    271   Indent(indent + kExtraIndent) << "'VCCLCompilerTool': {\n";
    272 
    273   // GYP always uses the VC optimization flag to add a /O? on Visual Studio.
    274   // This can produce duplicate values. So look up the GYP value corresponding
    275   // to the flags used, and set the same one.
    276   std::string optimization = GetVCOptimization(&flags.cflags);
    277   WriteNamedArray("AdditionalOptions", flags.cflags, indent + kExtraIndent * 2);
    278   // TODO(brettw) cflags_c and cflags_cc!
    279   Indent(indent + kExtraIndent * 2) << "'Optimization': "
    280                                     << optimization << ",\n";
    281   Indent(indent + kExtraIndent) << "},\n";
    282 
    283   // Linker tool stuff.
    284   Indent(indent + kExtraIndent) << "'VCLinkerTool': {\n";
    285 
    286   // ...Library dirs.
    287   EscapeOptions escape_options;
    288   escape_options.mode = ESCAPE_JSON;
    289   if (!flags.lib_dirs.empty()) {
    290     Indent(indent + kExtraIndent * 2) << "'AdditionalLibraryDirectories': [";
    291     for (size_t i = 0; i < flags.lib_dirs.size(); i++) {
    292       out_ << " '";
    293       EscapeStringToStream(out_,
    294                            helper_.GetDirReference(flags.lib_dirs[i], false),
    295                            escape_options);
    296       out_ << "',";
    297     }
    298     out_ << " ],\n";
    299   }
    300 
    301   // ...Libraries.
    302   WriteNamedArray("AdditionalDependencies", flags.libs,
    303                   indent + kExtraIndent * 2);
    304 
    305   // ...LD flags.
    306   // TODO(brettw) EnableUAC defaults to on and needs to be set. Also
    307   // UACExecutionLevel and UACUIAccess depends on that and defaults to 0/false.
    308   WriteNamedArray("AdditionalOptions", flags.ldflags, 14);
    309   Indent(indent + kExtraIndent) << "},\n";
    310   Indent(indent) << "},\n";
    311 }
    312 
    313 void GypBinaryTargetWriter::WriteMacFlags(Flags& flags, int indent) {
    314   WriteNamedArray("defines", flags.defines, indent);
    315   WriteIncludeDirs(flags, indent);
    316 
    317   // Libraries and library directories.
    318   EscapeOptions escape_options;
    319   escape_options.mode = ESCAPE_JSON;
    320   if (!flags.lib_dirs.empty()) {
    321     Indent(indent + kExtraIndent) << "'library_dirs': [";
    322     for (size_t i = 0; i < flags.lib_dirs.size(); i++) {
    323       out_ << " '";
    324       EscapeStringToStream(out_,
    325                            helper_.GetDirReference(flags.lib_dirs[i], false),
    326                            escape_options);
    327       out_ << "',";
    328     }
    329     out_ << " ],\n";
    330   }
    331   if (!flags.libs.empty()) {
    332     Indent(indent) << "'link_settings': {\n";
    333     Indent(indent + kExtraIndent) << "'libraries': [";
    334     for (size_t i = 0; i < flags.libs.size(); i++) {
    335       out_ << " '-l";
    336       EscapeStringToStream(out_, flags.libs[i], escape_options);
    337       out_ << "',";
    338     }
    339     out_ << " ],\n";
    340     Indent(indent) << "},\n";
    341   }
    342 
    343   Indent(indent) << "'xcode_settings': {\n";
    344 
    345   // C/C++ flags.
    346   if (!flags.cflags.empty() || !flags.cflags_c.empty() ||
    347       !flags.cflags_objc.empty()) {
    348     Indent(indent + kExtraIndent) << "'OTHER_CFLAGS': [";
    349     WriteArrayValues(out_, flags.cflags);
    350     WriteArrayValues(out_, flags.cflags_c);
    351     WriteArrayValues(out_, flags.cflags_objc);
    352     out_ << " ],\n";
    353   }
    354   if (!flags.cflags.empty() || !flags.cflags_cc.empty() ||
    355       !flags.cflags_objcc.empty()) {
    356     Indent(indent + kExtraIndent) << "'OTHER_CPLUSPLUSFLAGS': [";
    357     WriteArrayValues(out_, flags.cflags);
    358     WriteArrayValues(out_, flags.cflags_cc);
    359     WriteArrayValues(out_, flags.cflags_objcc);
    360     out_ << " ],\n";
    361   }
    362 
    363   // Ld flags. Don't write these for static libraries. Otherwise, they'll be
    364   // passed to the library tool which doesn't expect it (the toolchain does
    365   // not use ldflags so these are ignored in the normal build).
    366   if (target_->output_type() != Target::STATIC_LIBRARY)
    367     WriteNamedArray("OTHER_LDFLAGS", flags.ldflags, indent + kExtraIndent);
    368 
    369   // Write the compiler that XCode should use. When we're using clang, we want
    370   // the custom one, otherwise don't add this and the default compiler will be
    371   // used.
    372   //
    373   // TODO(brettw) this is a hack. We could add a way for the GN build to set
    374   // these values but as far as I can see this is the only use for them, so
    375   // currently we manually check the build config's is_clang value.
    376   const Value* is_clang =
    377       target_->settings()->base_config()->GetValue("is_clang");
    378   if (is_clang && is_clang->type() == Value::BOOLEAN &&
    379       is_clang->boolean_value()) {
    380     base::FilePath clang_path =
    381         target_->settings()->build_settings()->GetFullPath(SourceFile(
    382             "//third_party/llvm-build/Release+Asserts/bin/clang"));
    383     base::FilePath clang_pp_path =
    384         target_->settings()->build_settings()->GetFullPath(SourceFile(
    385             "//third_party/llvm-build/Release+Asserts/bin/clang++"));
    386 
    387     Indent(indent) << "'CC': '" << FilePathToUTF8(clang_path) << "',\n";
    388     Indent(indent) << "'LDPLUSPLUS': '"
    389                    << FilePathToUTF8(clang_pp_path) << "',\n";
    390   }
    391 
    392   Indent(indent) << "},\n";
    393 }
    394 
    395 void GypBinaryTargetWriter::WriteLinuxFlagsForTarget(const Target* target,
    396                                                      int indent) {
    397   Flags flags(FlagsFromTarget(target));
    398   WriteLinuxFlags(flags, indent);
    399 }
    400 
    401 void GypBinaryTargetWriter::WriteLinuxFlags(const Flags& flags, int indent) {
    402   WriteIncludeDirs(flags, indent);
    403   WriteNamedArray("defines",      flags.defines,      indent);
    404   WriteNamedArray("cflags",       flags.cflags,       indent);
    405   WriteNamedArray("cflags_c",     flags.cflags_c,     indent);
    406   WriteNamedArray("cflags_cc",    flags.cflags_cc,    indent);
    407   WriteNamedArray("cflags_objc",  flags.cflags_objc,  indent);
    408   WriteNamedArray("cflags_objcc", flags.cflags_objcc, indent);
    409 
    410   // Put libraries and library directories in with ldflags.
    411   Indent(indent) << "'ldflags': ["; \
    412   WriteArrayValues(out_, flags.ldflags);
    413 
    414   EscapeOptions escape_options;
    415   escape_options.mode = ESCAPE_JSON;
    416   for (size_t i = 0; i < flags.lib_dirs.size(); i++) {
    417     out_ << " '-L";
    418     EscapeStringToStream(out_,
    419                          helper_.GetDirReference(flags.lib_dirs[i], false),
    420                          escape_options);
    421     out_ << "',";
    422   }
    423 
    424   for (size_t i = 0; i < flags.libs.size(); i++) {
    425     out_ << " '-l";
    426     EscapeStringToStream(out_, flags.libs[i], escape_options);
    427     out_ << "',";
    428   }
    429   out_ << " ],\n";
    430 }
    431 
    432 void GypBinaryTargetWriter::WriteSources(const Target* target, int indent) {
    433   Indent(indent) << "'sources': [\n";
    434 
    435   const Target::FileList& sources = target->sources();
    436   for (size_t i = 0; i < sources.size(); i++) {
    437     const SourceFile& input_file = sources[i];
    438     Indent(indent + kExtraIndent) << "'" << helper_.GetFileReference(input_file)
    439                                   << "',\n";
    440   }
    441 
    442   Indent(indent) << "],\n";
    443 }
    444 
    445 void GypBinaryTargetWriter::WriteDeps(const Target* target, int indent) {
    446   const LabelTargetVector& deps = target->deps();
    447   if (deps.empty())
    448     return;
    449 
    450   EscapeOptions escape_options;
    451   escape_options.mode = ESCAPE_JSON;
    452 
    453   Indent(indent) << "'dependencies': [\n";
    454   for (size_t i = 0; i < deps.size(); i++) {
    455     Indent(indent + kExtraIndent) << "'";
    456     EscapeStringToStream(out_, helper_.GetFullRefForTarget(deps[i].ptr),
    457                          escape_options);
    458     out_ << "',\n";
    459   }
    460   Indent(indent) << "],\n";
    461 }
    462 
    463 void GypBinaryTargetWriter::WriteIncludeDirs(const Flags& flags, int indent) {
    464   if (flags.include_dirs.empty())
    465     return;
    466 
    467   EscapeOptions options;
    468   options.mode = ESCAPE_JSON;
    469 
    470   Indent(indent) << "'include_dirs': [";
    471   for (size_t i = 0; i < flags.include_dirs.size(); i++) {
    472     out_ << " '";
    473     EscapeStringToStream(out_,
    474                          helper_.GetDirReference(flags.include_dirs[i], false),
    475                          options);
    476     out_ << "',";
    477   }
    478   out_ << " ],\n";
    479 }
    480 
    481 void GypBinaryTargetWriter::WriteDirectDependentSettings(int indent) {
    482   if (target_->direct_dependent_configs().empty())
    483     return;
    484   Indent(indent) << "'direct_dependent_settings': {\n";
    485 
    486   Flags flags(FlagsFromConfigList(target_->direct_dependent_configs()));
    487   if (target_->settings()->IsLinux())
    488     WriteLinuxFlags(flags, indent + kExtraIndent);
    489   else if (target_->settings()->IsWin())
    490     WriteVCFlags(flags, indent + kExtraIndent);
    491   else if (target_->settings()->IsMac())
    492     WriteMacFlags(flags, indent + kExtraIndent);
    493   Indent(indent) << "},\n";
    494 }
    495 
    496 void GypBinaryTargetWriter::WriteAllDependentSettings(int indent) {
    497   if (target_->all_dependent_configs().empty())
    498     return;
    499   Indent(indent) << "'all_dependent_settings': {\n";
    500 
    501   Flags flags(FlagsFromConfigList(target_->all_dependent_configs()));
    502   if (target_->settings()->IsLinux())
    503     WriteLinuxFlags(flags, indent + kExtraIndent);
    504   else if (target_->settings()->IsWin())
    505     WriteVCFlags(flags, indent + kExtraIndent);
    506   else if (target_->settings()->IsMac())
    507     WriteMacFlags(flags, indent + kExtraIndent);
    508   Indent(indent) << "},\n";
    509 }
    510 
    511 GypBinaryTargetWriter::Flags GypBinaryTargetWriter::FlagsFromTarget(
    512     const Target* target) const {
    513   Flags ret;
    514 
    515   // Extracts a vector of the given type and name from the config values.
    516   #define EXTRACT(type, name) \
    517       { \
    518         Accumulator<type> acc(&ret.name); \
    519         RecursiveTargetConfigToStream<type>(target, &ConfigValues::name, \
    520                                             acc, out_); \
    521       }
    522 
    523   EXTRACT(std::string, defines);
    524   EXTRACT(SourceDir,   include_dirs);
    525   EXTRACT(std::string, cflags);
    526   EXTRACT(std::string, cflags_c);
    527   EXTRACT(std::string, cflags_cc);
    528   EXTRACT(std::string, cflags_objc);
    529   EXTRACT(std::string, cflags_objcc);
    530   EXTRACT(std::string, ldflags);
    531 
    532   #undef EXTRACT
    533 
    534   const OrderedSet<SourceDir>& all_lib_dirs = target->all_lib_dirs();
    535   for (size_t i = 0; i < all_lib_dirs.size(); i++)
    536     ret.lib_dirs.push_back(all_lib_dirs[i]);
    537 
    538   const OrderedSet<std::string> all_libs = target->all_libs();
    539   for (size_t i = 0; i < all_libs.size(); i++)
    540     ret.libs.push_back(all_libs[i]);
    541 
    542   return ret;
    543 }
    544 
    545 GypBinaryTargetWriter::Flags GypBinaryTargetWriter::FlagsFromConfigList(
    546     const LabelConfigVector& configs) const {
    547   Flags ret;
    548 
    549   #define EXTRACT(type, name) \
    550       FillConfigListValues<type>(configs, &ConfigValues::name, &ret.name);
    551 
    552   EXTRACT(std::string, defines);
    553   EXTRACT(SourceDir,   include_dirs);
    554   EXTRACT(std::string, cflags);
    555   EXTRACT(std::string, cflags_c);
    556   EXTRACT(std::string, cflags_cc);
    557   EXTRACT(std::string, cflags_objc);
    558   EXTRACT(std::string, cflags_objcc);
    559   EXTRACT(std::string, ldflags);
    560   EXTRACT(SourceDir,   lib_dirs);
    561   EXTRACT(std::string, libs);
    562 
    563   #undef EXTRACT
    564 
    565   return ret;
    566 }
    567 
    568 void GypBinaryTargetWriter::WriteNamedArray(
    569     const char* name,
    570     const std::vector<std::string>& values,
    571     int indent) {
    572   if (values.empty())
    573     return;
    574 
    575   EscapeOptions options;
    576   options.mode = ESCAPE_JSON;
    577 
    578   Indent(indent) << "'" << name << "': [";
    579   WriteArrayValues(out_, values);
    580   out_ << " ],\n";
    581 }
    582 
    583