Add arflags to GN

Allows the build to specify flags to the static library archiver tool.

BUG=598599

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

Cr-Original-Commit-Position: refs/heads/master@{#388315}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 440b117bb10d5c7e195a26cf974ccbd5886948b0
diff --git a/tools/gn/config_values.h b/tools/gn/config_values.h
index 823f1df..b6f153b 100644
--- a/tools/gn/config_values.h
+++ b/tools/gn/config_values.h
@@ -30,6 +30,7 @@
     const std::vector<SourceDir>& name() const { return name##_; } \
     std::vector<SourceDir>& name() { return name##_; }
 
+  STRING_VALUES_ACCESSOR(arflags)
   STRING_VALUES_ACCESSOR(asmflags)
   STRING_VALUES_ACCESSOR(cflags)
   STRING_VALUES_ACCESSOR(cflags_c)
@@ -65,6 +66,7 @@
   }
 
  private:
+  std::vector<std::string> arflags_;
   std::vector<std::string> asmflags_;
   std::vector<std::string> cflags_;
   std::vector<std::string> cflags_c_;
diff --git a/tools/gn/config_values_generator.cc b/tools/gn/config_values_generator.cc
index 3cc8235..e92f091 100644
--- a/tools/gn/config_values_generator.cc
+++ b/tools/gn/config_values_generator.cc
@@ -67,6 +67,7 @@
     GetDirList(scope_, #name, config_values_, input_dir_, \
                &ConfigValues::name, err_);
 
+  FILL_STRING_CONFIG_VALUE(arflags)
   FILL_STRING_CONFIG_VALUE(asmflags)
   FILL_STRING_CONFIG_VALUE(cflags)
   FILL_STRING_CONFIG_VALUE(cflags_c)
diff --git a/tools/gn/docs/reference.md b/tools/gn/docs/reference.md
index 38e8ce7..600daea 100644
--- a/tools/gn/docs/reference.md
+++ b/tools/gn/docs/reference.md
@@ -3003,6 +3003,7 @@
         Example: "gen/base/test"
 
   Linker tools have multiple inputs and (potentially) multiple outputs
+  The static library tool ("alink") is not considered a linker tool.
   The following expansions are available:
 
     {{inputs}}
@@ -3062,6 +3063,9 @@
         These should generally be treated the same as libs by your tool.
         Example: "libfoo.so libbar.so"
 
+  The static library ("alink") tool allows {{arflags}} plus the common
+  tool substitutions.
+
   The copy tool allows the common compiler/linker substitutions, plus
   {{source}} which is the source of the copy. The stamp tool allows
   only the common tool substitutions.
@@ -3725,6 +3729,42 @@
 
 
 ```
+## **arflags**: Arguments passed to static_library archiver.
+
+```
+  A list of flags passed to the archive/lib command that creates static
+  libraries.
+
+  arflags are NOT pushed to dependents, so applying arflags to source
+  sets or any other target type will be a no-op. As with ldflags,
+  you could put the arflags in a config and set that as a public or
+  "all dependent" config, but that will likely not be what you want.
+  If you have a chain of static libraries dependent on each other,
+  this can cause the flags to propagate up to other static libraries.
+  Due to the nature of how arflags are typically used, you will normally
+  want to apply them directly on static_library targets themselves.
+
+```
+
+### **Ordering of flags and values**
+
+```
+  1. Those set on the current target (not in a config).
+  2. Those set on the "configs" on the target in order that the
+     configs appear in the list.
+  3. Those set on the "all_dependent_configs" on the target in order
+     that the configs appear in the list.
+  4. Those set on the "public_configs" on the target in order that
+     those configs appear in the list.
+  5. all_dependent_configs pulled from dependencies, in the order of
+     the "deps" list. This is done recursively. If a config appears
+     more than once, only the first occurance will be used.
+  6. public_configs pulled from dependencies, in the order of the
+     "deps" list. If a dependency is public, they will be applied
+     recursively.
+
+
+```
 ## **args**: Arguments passed to an action.
 
 ```
diff --git a/tools/gn/function_toolchain.cc b/tools/gn/function_toolchain.cc
index bdf5385..aaa9b7e 100644
--- a/tools/gn/function_toolchain.cc
+++ b/tools/gn/function_toolchain.cc
@@ -194,8 +194,8 @@
 }
 
 bool IsLinkerTool(Toolchain::ToolType type) {
-  return type == Toolchain::TYPE_ALINK ||
-         type == Toolchain::TYPE_SOLINK ||
+  // "alink" is not counted as in the generic "linker" tool list.
+  return type == Toolchain::TYPE_SOLINK ||
          type == Toolchain::TYPE_SOLINK_MODULE ||
          type == Toolchain::TYPE_LINK;
 }
@@ -690,6 +690,7 @@
     "        Example: \"gen/base/test\"\n"
     "\n"
     "  Linker tools have multiple inputs and (potentially) multiple outputs\n"
+    "  The static library tool (\"alink\") is not considered a linker tool.\n"
     "  The following expansions are available:\n"
     "\n"
     "    {{inputs}}\n"
@@ -749,6 +750,9 @@
     "        These should generally be treated the same as libs by your tool.\n"
     "        Example: \"libfoo.so libbar.so\"\n"
     "\n"
+    "  The static library (\"alink\") tool allows {{arflags}} plus the common\n"
+    "  tool substitutions.\n"
+    "\n"
     "  The copy tool allows the common compiler/linker substitutions, plus\n"
     "  {{source}} which is the source of the copy. The stamp tool allows\n"
     "  only the common tool substitutions.\n"
@@ -859,6 +863,10 @@
   } else if (IsLinkerTool(tool_type)) {
     subst_validator = &IsValidLinkerSubstitution;
     subst_output_validator = &IsValidLinkerOutputsSubstitution;
+  } else if (tool_type == Toolchain::TYPE_ALINK) {
+    subst_validator = &IsValidALinkSubstitution;
+    // ALink uses the standard output file patterns as other linker tools.
+    subst_output_validator = &IsValidLinkerOutputsSubstitution;
   } else if (tool_type == Toolchain::TYPE_COPY ||
              tool_type == Toolchain::TYPE_COPY_BUNDLE_DATA) {
     subst_validator = &IsValidCopySubstitution;
diff --git a/tools/gn/ninja_binary_target_writer.cc b/tools/gn/ninja_binary_target_writer.cc
index 4a923c3..cb48688 100644
--- a/tools/gn/ninja_binary_target_writer.cc
+++ b/tools/gn/ninja_binary_target_writer.cc
@@ -785,6 +785,11 @@
       target_->output_type() == Target::LOADABLE_MODULE) {
     WriteLinkerFlags(optional_def_file);
     WriteLibs();
+  } else if (target_->output_type() == Target::STATIC_LIBRARY) {
+    out_ << "  arflags =";
+    RecursiveTargetConfigStringsToStream(target_, &ConfigValues::arflags,
+                                         GetFlagOptions(), out_);
+    out_ << std::endl;
   }
   WriteOutputSubstitutions();
   WriteSolibs(solibs);
@@ -795,9 +800,8 @@
   out_ << "  ldflags =";
 
   // First the ldflags from the target and its config.
-  EscapeOptions flag_options = GetFlagOptions();
   RecursiveTargetConfigStringsToStream(target_, &ConfigValues::ldflags,
-                                       flag_options, out_);
+                                       GetFlagOptions(), out_);
 
   // Followed by library search paths that have been recursively pushed
   // through the dependency tree.
diff --git a/tools/gn/ninja_binary_target_writer_unittest.cc b/tools/gn/ninja_binary_target_writer_unittest.cc
index d620f94..ab8b641 100644
--- a/tools/gn/ninja_binary_target_writer_unittest.cc
+++ b/tools/gn/ninja_binary_target_writer_unittest.cc
@@ -111,6 +111,7 @@
         // There are no sources so there are no params to alink. (In practice
         // this will probably fail in the archive tool.)
         "build obj/foo/libstlib.a: alink || obj/foo/bar.stamp\n"
+        "  arflags =\n"
         "  output_extension = \n"
         "  output_dir = \n";
     std::string out_str = out.str();
@@ -138,6 +139,7 @@
         "build obj/foo/libstlib.a: alink obj/foo/bar.input1.o "
             "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj "
             "|| obj/foo/bar.stamp\n"
+        "  arflags =\n"
         "  output_extension = \n"
         "  output_dir = \n";
     std::string out_str = out.str();
@@ -145,6 +147,38 @@
   }
 }
 
+TEST(NinjaBinaryTargetWriter, StaticLibrary) {
+  TestWithScope setup;
+  Err err;
+
+  TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
+  target.sources().push_back(SourceFile("//foo/input1.cc"));
+  target.config_values().arflags().push_back("--asdf");
+  ASSERT_TRUE(target.OnResolved(&err));
+
+  std::ostringstream out;
+  NinjaBinaryTargetWriter writer(&target, out);
+  writer.Run();
+
+  const char expected[] =
+      "defines =\n"
+      "include_dirs =\n"
+      "cflags =\n"
+      "cflags_cc =\n"
+      "root_out_dir = .\n"
+      "target_out_dir = obj/foo\n"
+      "target_output_name = libbar\n"
+      "\n"
+      "build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
+      "\n"
+      "build obj/foo/libbar.a: alink obj/foo/libbar.input1.o\n"
+      "  arflags = --asdf\n"
+      "  output_extension = \n"
+      "  output_dir = \n";
+  std::string out_str = out.str();
+  EXPECT_EQ(expected, out_str);
+}
+
 // This tests that output extension and output dir overrides apply, and input
 // dependencies are applied.
 TEST(NinjaBinaryTargetWriter, OutputExtensionAndInputDeps) {
diff --git a/tools/gn/substitution_type.cc b/tools/gn/substitution_type.cc
index 75d8b7a..fcca19d 100644
--- a/tools/gn/substitution_type.cc
+++ b/tools/gn/substitution_type.cc
@@ -47,6 +47,8 @@
   "{{output_extension}}",  // SUBSTITUTION_OUTPUT_EXTENSION
   "{{solibs}}",  // SUBSTITUTION_SOLIBS
 
+  "{{arflags}}",  // SUBSTITUTION_ARFLAGS
+
   "{{bundle_root_dir}}",  // SUBSTITUTION_BUNDLE_ROOT_DIR
   "{{bundle_resources_dir}}",  // SUBSTITUTION_BUNDLE_RESOURCES_DIR
   "{{bundle_executable_dir}}",  // SUBSTITUTION_BUNDLE_EXECUTABLE_DIR
@@ -96,6 +98,8 @@
     "output_extension",  // SUBSTITUTION_OUTPUT_EXTENSION
     "solibs",            // SUBSTITUTION_SOLIBS
 
+    "arflags",           // SUBSTITUTION_ARFLAGS
+
     "bundle_root_dir",        // SUBSTITUTION_BUNDLE_ROOT_DIR
     "bundle_resources_dir",   // SUBSTITUTION_BUNDLE_RESOURCES_DIR
     "bundle_executable_dir",  // SUBSTITUTION_BUNDLE_EXECUTABLE_DIR
@@ -207,6 +211,15 @@
          type == SUBSTITUTION_OUTPUT_EXTENSION;
 }
 
+bool IsValidALinkSubstitution(SubstitutionType type) {
+  return IsValidToolSubstitution(type) ||
+         type == SUBSTITUTION_LINKER_INPUTS ||
+         type == SUBSTITUTION_LINKER_INPUTS_NEWLINE ||
+         type == SUBSTITUTION_ARFLAGS ||
+         type == SUBSTITUTION_OUTPUT_DIR ||
+         type == SUBSTITUTION_OUTPUT_EXTENSION;
+}
+
 bool IsValidCopySubstitution(SubstitutionType type) {
   return IsValidToolSubstitution(type) ||
          type == SUBSTITUTION_SOURCE;
diff --git a/tools/gn/substitution_type.h b/tools/gn/substitution_type.h
index 2e63373..e82ecd6 100644
--- a/tools/gn/substitution_type.h
+++ b/tools/gn/substitution_type.h
@@ -60,6 +60,9 @@
   SUBSTITUTION_OUTPUT_EXTENSION,  // {{output_extension}}
   SUBSTITUTION_SOLIBS,  // {{solibs}}
 
+  // Valid for alink only.
+  SUBSTITUTION_ARFLAGS,  // {{arflags}}
+
   // Valid for bundle_data targets.
   SUBSTITUTION_BUNDLE_ROOT_DIR,  // {{bundle_root_dir}}
   SUBSTITUTION_BUNDLE_RESOURCES_DIR,  // {{bundle_resources_dir}}
@@ -117,6 +120,7 @@
 bool IsValidCompilerOutputsSubstitution(SubstitutionType type);
 bool IsValidLinkerSubstitution(SubstitutionType type);
 bool IsValidLinkerOutputsSubstitution(SubstitutionType type);
+bool IsValidALinkSubstitution(SubstitutionType type);
 bool IsValidCopySubstitution(SubstitutionType type);
 bool IsValidCompileXCassetsSubstitution(SubstitutionType type);
 
diff --git a/tools/gn/variables.cc b/tools/gn/variables.cc
index 46cd57e..dc0316b 100644
--- a/tools/gn/variables.cc
+++ b/tools/gn/variables.cc
@@ -418,6 +418,25 @@
     "    public_deps = [ \":c\" ]\n"
     "  }\n";
 
+const char kArflags[] = "arflags";
+const char kArflags_HelpShort[] =
+    "arflags: [string list] Arguments passed to static_library archiver.";
+const char kArflags_Help[] =
+    "arflags: Arguments passed to static_library archiver.\n"
+    "\n"
+    "  A list of flags passed to the archive/lib command that creates static\n"
+    "  libraries.\n"
+    "\n"
+    "  arflags are NOT pushed to dependents, so applying arflags to source\n"
+    "  sets or any other target type will be a no-op. As with ldflags,\n"
+    "  you could put the arflags in a config and set that as a public or\n"
+    "  \"all dependent\" config, but that will likely not be what you want.\n"
+    "  If you have a chain of static libraries dependent on each other,\n"
+    "  this can cause the flags to propagate up to other static libraries.\n"
+    "  Due to the nature of how arflags are typically used, you will normally\n"
+    "  want to apply them directly on static_library targets themselves.\n"
+    COMMON_ORDERING_HELP;
+
 const char kArgs[] = "args";
 const char kArgs_HelpShort[] =
     "args: [string list] Arguments passed to an action.";
@@ -1638,6 +1657,7 @@
   if (info_map.empty()) {
     INSERT_VARIABLE(AllDependentConfigs)
     INSERT_VARIABLE(AllowCircularIncludesFrom)
+    INSERT_VARIABLE(Arflags)
     INSERT_VARIABLE(Args)
     INSERT_VARIABLE(Asmflags)
     INSERT_VARIABLE(AssertNoDeps)
diff --git a/tools/gn/variables.h b/tools/gn/variables.h
index 2dafdee..2523c1f 100644
--- a/tools/gn/variables.h
+++ b/tools/gn/variables.h
@@ -79,6 +79,10 @@
 extern const char kAllowCircularIncludesFrom_HelpShort[];
 extern const char kAllowCircularIncludesFrom_Help[];
 
+extern const char kArflags[];
+extern const char kArflags_HelpShort[];
+extern const char kArflags_Help[];
+
 extern const char kArgs[];
 extern const char kArgs_HelpShort[];
 extern const char kArgs_Help[];