• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
Accumulator__anon3f561a830111::Accumulator24   Accumulator(std::vector<T>* result_in) : result(result_in) {}
25 
operator ()__anon3f561a830111::Accumulator26   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.
WriteArrayValues(std::ostream & out,const std::vector<std::string> & values)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.
GetVCOptimization(std::vector<std::string> * cflags)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>
FillConfigListValues(const LabelConfigVector & configs,const std::vector<T> & (ConfigValues::* getter)()const,std::vector<T> * result)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 
Flags()86 GypBinaryTargetWriter::Flags::Flags() {}
~Flags()87 GypBinaryTargetWriter::Flags::~Flags() {}
88 
GypBinaryTargetWriter(const TargetGroup & group,const Toolchain * debug_toolchain,const SourceDir & gyp_dir,std::ostream & out)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 
~GypBinaryTargetWriter()98 GypBinaryTargetWriter::~GypBinaryTargetWriter() {
99 }
100 
Run()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 
WriteName(int indent)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 
WriteType(int indent)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 
WriteVCConfiguration(int indent)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 
WriteLinuxConfiguration(int indent)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 
WriteMacConfiguration(int indent)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 
WriteVCFlags(Flags & flags,int indent)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 
WriteMacFlags(Flags & flags,int indent)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 
WriteLinuxFlagsForTarget(const Target * target,int indent)395 void GypBinaryTargetWriter::WriteLinuxFlagsForTarget(const Target* target,
396                                                      int indent) {
397   Flags flags(FlagsFromTarget(target));
398   WriteLinuxFlags(flags, indent);
399 }
400 
WriteLinuxFlags(const Flags & flags,int indent)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 
WriteSources(const Target * target,int indent)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 
WriteDeps(const Target * target,int indent)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 
WriteIncludeDirs(const Flags & flags,int indent)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 
WriteDirectDependentSettings(int indent)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 
WriteAllDependentSettings(int indent)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 
FlagsFromTarget(const Target * target) const511 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 
FlagsFromConfigList(const LabelConfigVector & configs) const545 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 
WriteNamedArray(const char * name,const std::vector<std::string> & values,int indent)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