Work on GYP generation for GN. The hybrid build now works in xcode and ninja.

Write mac tool to output dir in toolchain setup.

BUG=
R=thakis@chromium.org

Review URL: https://codereview.chromium.org/46683002

Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 42b80efc8d8588bf369f428129333c7447070ebd
diff --git a/tools/gn/args.cc b/tools/gn/args.cc
index 2eead42..2d9aa6e 100644
--- a/tools/gn/args.cc
+++ b/tools/gn/args.cc
@@ -135,9 +135,12 @@
     }
 
     // Only set on the current scope to the new value if it hasn't been already
-    // set.
-    if (!scope_to_set->GetValue(i->first))
+    // set. Mark the variable used so the build script can override it in
+    // certain cases without getting unused value errors.
+    if (!scope_to_set->GetValue(i->first)) {
       scope_to_set->SetValue(i->first, i->second, i->second.origin());
+      scope_to_set->MarkUsed(i->first);
+    }
   }
 
   return true;
diff --git a/tools/gn/command_gyp.cc b/tools/gn/command_gyp.cc
index de089d3..3c6bef9 100644
--- a/tools/gn/command_gyp.cc
+++ b/tools/gn/command_gyp.cc
@@ -233,9 +233,6 @@
   for (CorrelatedTargetsMap::iterator i = correlated.begin();
        i != correlated.end(); ++i) {
     const TargetGroup& group = i->second;
-    if (!EnsureTargetsMatch(group, err))
-      return std::make_pair(0, 0);
-
     if (!group.debug->item_node()->should_generate())
       continue;  // Skip non-generated ones.
     if (group.debug->external())
@@ -243,6 +240,9 @@
     if (group.debug->gyp_file().is_null())
       continue;  // Skip ones without GYP files.
 
+    if (!EnsureTargetsMatch(group, err))
+      return std::make_pair(0, 0);
+
     target_count++;
     grouped_targets[helper.GetGypFileForTarget(group.debug, err)].push_back(
         group);
diff --git a/tools/gn/gyp_binary_target_writer.cc b/tools/gn/gyp_binary_target_writer.cc
index 7c4e7a3..bcbe7ee 100644
--- a/tools/gn/gyp_binary_target_writer.cc
+++ b/tools/gn/gyp_binary_target_writer.cc
@@ -10,6 +10,7 @@
 #include "tools/gn/config_values_extractors.h"
 #include "tools/gn/err.h"
 #include "tools/gn/escape.h"
+#include "tools/gn/filesystem_utils.h"
 #include "tools/gn/settings.h"
 #include "tools/gn/target.h"
 
@@ -132,15 +133,12 @@
   WriteName(indent + kExtraIndent);
   WriteType(indent + kExtraIndent);
 
-  if (target_->settings()->IsLinux()) {
+  if (target_->settings()->IsLinux())
     WriteLinuxConfiguration(indent + kExtraIndent);
-  } else if (target_->settings()->IsWin()) {
+  else if (target_->settings()->IsWin())
     WriteVCConfiguration(indent + kExtraIndent);
-  } else if (target_->settings()->IsMac()) {
-    // TODO(brettw) mac.
-    NOTREACHED();
-    //WriteMacConfiguration();
-  }
+  else if (target_->settings()->IsMac())
+    WriteMacConfiguration(indent + kExtraIndent);
   WriteDirectDependentSettings(indent + kExtraIndent);
   WriteAllDependentSettings(indent + kExtraIndent);
 
@@ -256,6 +254,25 @@
   WriteDeps(target_, indent);
 }
 
+void GypBinaryTargetWriter::WriteMacConfiguration(int indent) {
+  Indent(indent) << "'configurations': {\n";
+
+  Indent(indent + kExtraIndent) << "'Debug': {\n";
+  Flags debug_flags(FlagsFromTarget(group_.debug));
+  WriteMacFlags(debug_flags, indent + kExtraIndent * 2);
+  Indent(indent + kExtraIndent) << "},\n";
+
+  Indent(indent + kExtraIndent) << "'Release': {\n";
+  Flags release_flags(FlagsFromTarget(group_.release));
+  WriteMacFlags(release_flags, indent + kExtraIndent * 2);
+  Indent(indent + kExtraIndent) << "},\n";
+
+  Indent(indent) << "},\n";
+
+  WriteSources(target_, indent);
+  WriteDeps(target_, indent);
+}
+
 void GypBinaryTargetWriter::WriteVCFlags(Flags& flags, int indent) {
   // Defines and includes go outside of the msvs settings.
   WriteNamedArray(out_, "defines", flags.defines, indent);
@@ -306,6 +323,78 @@
   Indent(indent) << "},\n";
 }
 
+void GypBinaryTargetWriter::WriteMacFlags(Flags& flags, int indent) {
+  WriteNamedArray(out_, "defines", flags.defines, indent);
+  WriteIncludeDirs(flags, indent);
+
+  // Libraries and library directories.
+  EscapeOptions escape_options;
+  escape_options.mode = ESCAPE_JSON;
+  if (!flags.lib_dirs.empty()) {
+    Indent(indent + kExtraIndent) << "'library_dirs': [";
+    for (size_t i = 0; i < flags.lib_dirs.size(); i++) {
+      out_ << " '";
+      EscapeStringToStream(out_,
+                           helper_.GetDirReference(flags.lib_dirs[i], false),
+                           escape_options);
+      out_ << "',";
+    }
+    out_ << " ],\n";
+  }
+  if (!flags.libs.empty()) {
+    Indent(indent) << "'link_settings': {\n";
+    Indent(indent + kExtraIndent) << "'libraries': [";
+    for (size_t i = 0; i < flags.libs.size(); i++) {
+      out_ << " '-l";
+      EscapeStringToStream(out_, flags.libs[i], escape_options);
+      out_ << "',";
+    }
+    out_ << " ],\n";
+    Indent(indent) << "},\n";
+  }
+
+  Indent(indent) << "'xcode_settings': {\n";
+
+  // C/C++ flags.
+  if (!flags.cflags.empty() || !flags.cflags_c.empty() ||
+      !flags.cflags_objc.empty()) {
+    Indent(indent + kExtraIndent) << "'OTHER_CFLAGS': [";
+    WriteArrayValues(out_, flags.cflags);
+    WriteArrayValues(out_, flags.cflags_c);
+    WriteArrayValues(out_, flags.cflags_objc);
+    out_ << " ],\n";
+  }
+  if (!flags.cflags.empty() || !flags.cflags_cc.empty() ||
+      !flags.cflags_objcc.empty()) {
+    Indent(indent + kExtraIndent) << "'OTHER_CPLUSPLUSFLAGS': [";
+    WriteArrayValues(out_, flags.cflags);
+    WriteArrayValues(out_, flags.cflags_cc);
+    WriteArrayValues(out_, flags.cflags_objcc);
+    out_ << " ],\n";
+  }
+
+  // Ld flags. Don't write these for static libraries. Otherwise, they'll be
+  // passed to the library tool which doesn't expect it (the toolchain does
+  // not use ldflags so these are ignored in the normal build).
+  if (target_->output_type() != Target::STATIC_LIBRARY) {
+    WriteNamedArray(out_, "OTHER_LDFLAGS", flags.ldflags,
+                    indent + kExtraIndent);
+  }
+
+  base::FilePath clang_path =
+      target_->settings()->build_settings()->GetFullPath(SourceFile(
+          "//third_party/llvm-build/Release+Asserts/bin/clang"));
+  base::FilePath clang_pp_path =
+      target_->settings()->build_settings()->GetFullPath(SourceFile(
+          "//third_party/llvm-build/Release+Asserts/bin/clang++"));
+
+  Indent(indent) << "'CC': '" << FilePathToUTF8(clang_path) << "',\n";
+  Indent(indent) << "'LDPLUSPLUS': '"
+                 << FilePathToUTF8(clang_pp_path) << "',\n";
+
+  Indent(indent) << "},\n";
+}
+
 void GypBinaryTargetWriter::WriteLinuxFlagsForTarget(const Target* target,
                                                      int indent) {
   Flags flags(FlagsFromTarget(target));
@@ -398,14 +487,12 @@
   out_ << "'direct_dependent_settings': {\n";
 
   Flags flags(FlagsFromConfigList(target_->direct_dependent_configs()));
-  if (target_->settings()->IsLinux()) {
+  if (target_->settings()->IsLinux())
     WriteLinuxFlags(flags, indent + kExtraIndent);
-  } else if (target_->settings()->IsWin()) {
+  else if (target_->settings()->IsWin())
     WriteVCFlags(flags, indent + kExtraIndent);
-  } else if (target_->settings()->IsMac()) {
-    // TODO(brettw) write mac.
-    NOTREACHED();
-  }
+  else if (target_->settings()->IsMac())
+    WriteMacFlags(flags, indent + kExtraIndent);
   Indent(indent) << "},\n";
 }
 
@@ -415,14 +502,12 @@
   Indent(indent) << "'all_dependent_settings': {\n";
 
   Flags flags(FlagsFromConfigList(target_->all_dependent_configs()));
-  if (target_->settings()->IsLinux()) {
+  if (target_->settings()->IsLinux())
     WriteLinuxFlags(flags, indent + kExtraIndent);
-  } else if (target_->settings()->IsWin()) {
+  else if (target_->settings()->IsWin())
     WriteVCFlags(flags, indent + kExtraIndent);
-  } else if (target_->settings()->IsMac()) {
-    // TODO(brettw) write mac.
-    NOTREACHED();
-  }
+  else if (target_->settings()->IsMac())
+    WriteMacFlags(flags, indent + kExtraIndent);
   Indent(indent) << "},\n";
 }
 
diff --git a/tools/gn/gyp_binary_target_writer.h b/tools/gn/gyp_binary_target_writer.h
index b12f154..226ffb8 100644
--- a/tools/gn/gyp_binary_target_writer.h
+++ b/tools/gn/gyp_binary_target_writer.h
@@ -48,11 +48,13 @@
   // Writes the flags, sources, and deps.
   void WriteVCConfiguration(int indent);
   void WriteLinuxConfiguration(int indent);
+  void WriteMacConfiguration(int indent);
 
-  // Writes the Visual Studio flags, defines, etc. The flags input is non-const
-  // because the cflags will be fixed up to account for things converted to
-  // VC settings (rather than compiler flags).
+  // Writes the flags, defines, etc. The flags input is non-const because the
+  // cflags will be fixed up to account for things converted to VC settings
+  // (rather than compiler flags).
   void WriteVCFlags(Flags& flags, int indent);
+  void WriteMacFlags(Flags& flags, int indent);
 
   // Writes the Linux compiler and linker flags. The first version does the
   // flags for the given target, the second version takes a pregenerted list of
diff --git a/tools/gn/gyp_target_writer.cc b/tools/gn/gyp_target_writer.cc
index 5f35054..8a55bdc 100644
--- a/tools/gn/gyp_target_writer.cc
+++ b/tools/gn/gyp_target_writer.cc
@@ -37,6 +37,19 @@
   file << "# Generated by GN. Do not edit.\n\n";
   file << "{\n";
   file << "  'skip_includes': 1,\n";
+
+  if (targets[0].debug->settings()->IsMac()) {
+    // Global settings for make/ninja. This must match common.gypi :(
+    file << "  'make_global_settings': [\n";
+    file << "    ['CC', 'third_party/llvm-build/Release+Asserts/bin/clang'],\n";
+    file <<
+        "    ['CXX', 'third_party/llvm-build/Release+Asserts/bin/clang++'],\n";
+    file << "    ['CC.host', '$(CC)'],\n";
+    file << "    ['CXX.host', '$(CXX)'],\n";
+    file << "  ],\n";
+  }
+  // TODO(brettw) android.
+
   file << "  'targets': [\n";
 
   for (size_t i = 0; i < targets.size(); i++) {
diff --git a/tools/gn/secondary/build/config/BUILDCONFIG.gn b/tools/gn/secondary/build/config/BUILDCONFIG.gn
index 78a3f27..99e1e91 100644
--- a/tools/gn/secondary/build/config/BUILDCONFIG.gn
+++ b/tools/gn/secondary/build/config/BUILDCONFIG.gn
@@ -79,6 +79,7 @@
   is_nacl = false
   is_posix = true
   is_win = false
+  is_clang = true  # Always use clang on Mac.
 } else if (os == "android") {
   is_android = false
   is_chromeos = false
diff --git a/tools/gn/secondary/build/config/compiler/BUILD.gn b/tools/gn/secondary/build/config/compiler/BUILD.gn
index 6174b2e..ef9d61f 100644
--- a/tools/gn/secondary/build/config/compiler/BUILD.gn
+++ b/tools/gn/secondary/build/config/compiler/BUILD.gn
@@ -116,9 +116,7 @@
 
     # Clang-specific compiler flags setup.
     # ------------------------------------
-    # TODO(brettw) these should be clang-only. For now, make it mac-only since
-    # that's where we always use clang.
-    if (is_mac) { # if (is_clang) {
+    if (is_clang) {
       cflags += [
         "-fcolor-diagnostics",
       ]
@@ -301,7 +299,7 @@
 
     # TODO(brettw) Ones below here should be clang-only when we have a flag
     # for it.
-    if (is_mac) { #if (is_clang) {
+    if (is_clang) {
       cflags += [
         "-Wheader-hygiene",
 
diff --git a/tools/gn/secondary/build/toolchain/mac/BUILD.gn b/tools/gn/secondary/build/toolchain/mac/BUILD.gn
index 2752d5a..b8a3c5e 100644
--- a/tools/gn/secondary/build/toolchain/mac/BUILD.gn
+++ b/tools/gn/secondary/build/toolchain/mac/BUILD.gn
@@ -2,10 +2,19 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+# Should only be running on Mac.
+assert(is_mac)
+
 cc = rebase_path("//third_party/llvm-build/Release+Asserts/bin/clang", ".", root_build_dir)
 cxx = rebase_path("//third_party/llvm-build/Release+Asserts/bin/clang++", ".", root_build_dir)
 ld = cxx
 
+# This will copy the gyp-mac-tool to the build directory. We pass in the source
+# file of the win tool.
+gyp_mac_tool_source =
+  rebase_path("//tools/gyp/pylib/gyp/mac_tool.py", ".", root_build_dir)
+exec_script("setup_toolchain.py", [ gyp_mac_tool_source ], "value")
+
 toolchain("clang") {
   # Make these apply to all tools below.
   lib_prefix = "-l"
diff --git a/tools/gn/secondary/build/toolchain/mac/setup_toolchain.py b/tools/gn/secondary/build/toolchain/mac/setup_toolchain.py
new file mode 100644
index 0000000..431078f
--- /dev/null
+++ b/tools/gn/secondary/build/toolchain/mac/setup_toolchain.py
@@ -0,0 +1,29 @@
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import stat
+import sys
+
+def CopyTool(source_path):
+  """Copies the given tool to the current directory, including a warning not
+  to edit it."""
+  with open(source_path) as source_file:
+    tool_source = source_file.readlines()
+
+  # Add header and write it out to the current directory (which should be the
+  # root build dir).
+  out_path = 'gyp-mac-tool'
+  with open(out_path, 'w') as tool_file:
+    tool_file.write(''.join([tool_source[0],
+                             '# Generated by setup_toolchain.py do not edit.\n']
+                            + tool_source[1:]))
+  st = os.stat(out_path)
+  os.chmod(out_path, st.st_mode | stat.S_IEXEC)
+
+# Find the tool source, it's the first argument, and copy it.
+if len(sys.argv) != 2:
+  print "Need one argument (mac_tool source path)."
+  sys.exit(1)
+CopyTool(sys.argv[1])