Initial Pigweed modules

This change adds Pigweed's first few (incomplete) modules: pw_build,
pw_status, and pw_toolchain. The main scaffolding for Pigweed's GN-based
build system is also included, with a root BUILDCONFIG and BUILD.gn file
defining a top-level modules build target.

Change-Id: I09599443d50696343d6c5b85853cb5cee3ce0026
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a4d2b69
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,27 @@
+# Build artifacts
+out/
+
+# IDE artifacts
+.idea/
+.project
+.cproject
+.vscode
+.clangd/
+
+# Python artifacts
+venv/
+*.pyc
+*.egg/
+*.eggs/
+*.egg-info/
+.cache/
+python-env/
+.mypy_cache/
+
+# Mac artifacts
+.DS_Store
+
+# Vim artifacts
+*.swp
+
+.gdb_history
diff --git a/.gn b/.gn
new file mode 100644
index 0000000..de099cc
--- /dev/null
+++ b/.gn
@@ -0,0 +1,16 @@
+# Copyright 2019 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+buildconfig = "//BUILDCONFIG.gn"
+script_executable = "python3"
diff --git a/BUILD.gn b/BUILD.gn
new file mode 100644
index 0000000..437d6e7
--- /dev/null
+++ b/BUILD.gn
@@ -0,0 +1,20 @@
+# Copyright 2019 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+# All Pigweed modules that can be built using gn.
+group("pw_modules") {
+  deps = [
+    "$dir_pw_status",
+  ]
+}
diff --git a/BUILDCONFIG.gn b/BUILDCONFIG.gn
new file mode 100644
index 0000000..3cd4a1e
--- /dev/null
+++ b/BUILDCONFIG.gn
@@ -0,0 +1,48 @@
+# Copyright 2019 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("modules.gni")
+
+# Default configs to use for all binary build targets.
+_default_common_binary_configs = [
+  "$dir_pw_build:reduced_size",
+  "$dir_pw_build:strict_warnings",
+]
+
+set_defaults("executable") {
+  configs = _default_common_binary_configs
+}
+
+set_defaults("static_library") {
+  configs = _default_common_binary_configs
+}
+
+set_defaults("shared_library") {
+  configs = _default_common_binary_configs
+}
+
+set_defaults("source_set") {
+  configs = _default_common_binary_configs
+}
+
+declare_args() {
+  # The name of the GN template used to build Pigweed executables.
+  pw_executable_target_type = "executable"
+
+  # Path to the .gni file in which the pw_executable_target_type template is
+  # defined. Empty string if using a GN primitive.
+  pw_executable_target_path = ""
+}
+
+set_default_toolchain("$dir_pw_toolchain:arm_gcc_cortex_m4_og")
diff --git a/modules.gni b/modules.gni
new file mode 100644
index 0000000..d6647d7
--- /dev/null
+++ b/modules.gni
@@ -0,0 +1,27 @@
+# Copyright 2019 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+# This file defines a directory variable for each of Pigweed's modules. This
+# allows modules to be moved or swapped out without breaking existing builds.
+# All module variables are prefixed with dir_.
+
+declare_args() {
+  # Location of the Pigweed modules directory (defaults to the directory of
+  # this file).
+  dir_pigweed = get_path_info(get_path_info("BUILD.gn", "abspath"), "dir")
+}
+
+dir_pw_build = "$dir_pigweed/pw_build"
+dir_pw_status = "$dir_pigweed/pw_status"
+dir_pw_toolchain = "$dir_pigweed/pw_toolchain"
diff --git a/pw_build/BUILD.gn b/pw_build/BUILD.gn
new file mode 100644
index 0000000..ebe935d
--- /dev/null
+++ b/pw_build/BUILD.gn
@@ -0,0 +1,40 @@
+# Copyright 2019 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+# Standard compiler flags to reduce output binary size.
+config("reduced_size") {
+  cflags = [
+    "-fno-common",
+    "-fno-exceptions",
+    "-ffunction-sections",
+    "-fdata-sections",
+  ]
+  cflags_cc = [ "-fno-rtti" ]
+}
+
+config("strict_warnings") {
+  cflags = [
+    "-Wall",
+    "-Wextra",
+  ]
+}
+
+config("cpp17") {
+  cflags_cc = [ "-std=c++17" ]
+}
+
+# Default C++ version for Pigweed modules.
+config("pw_default_cpp") {
+  configs = [ ":cpp17" ]
+}
diff --git a/pw_build/README.md b/pw_build/README.md
new file mode 100644
index 0000000..3879b11
--- /dev/null
+++ b/pw_build/README.md
@@ -0,0 +1 @@
+# pw\_build: Definitions for Pigweed's build system.
diff --git a/pw_build/pw_executable.gni b/pw_build/pw_executable.gni
new file mode 100644
index 0000000..9b8666d
--- /dev/null
+++ b/pw_build/pw_executable.gni
@@ -0,0 +1,30 @@
+# Copyright 2019 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+# Wrapper for Pigweed executable build targets which uses a globally-defined,
+# configurable target type.
+template("pw_executable") {
+  assert(defined(pw_executable_target_type),
+         "build argument pw_executable_target_type must be defined")
+  assert(defined(pw_executable_target_path),
+         "build argument pw_executable_target_path must be defined")
+
+  if (pw_executable_target_path != "") {
+    import(invoker.pw_executable_target_path)
+  }
+
+  target(invoker.pw_executable_target_type, target_name) {
+    forward_variables_from(invoker, "*")
+  }
+}
diff --git a/pw_status/BUILD.gn b/pw_status/BUILD.gn
new file mode 100644
index 0000000..c3172af
--- /dev/null
+++ b/pw_status/BUILD.gn
@@ -0,0 +1,28 @@
+# Copyright 2019 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+config("default_config") {
+  include_dirs = [ "public" ]
+}
+
+source_set("pw_status") {
+  public_configs = [
+    "$dir_pw_build:pw_default_cpp",
+    ":default_config",
+  ]
+  public = [
+    "public/pw_status/status.h",
+  ]
+  sources = [ "status.cc" ] + public
+}
diff --git a/pw_status/README.md b/pw_status/README.md
new file mode 100644
index 0000000..4261b75
--- /dev/null
+++ b/pw_status/README.md
@@ -0,0 +1 @@
+# pw\_status: Pigweed error codes.
diff --git a/pw_status/public/pw_status/status.h b/pw_status/public/pw_status/status.h
new file mode 100644
index 0000000..550b10a
--- /dev/null
+++ b/pw_status/public/pw_status/status.h
@@ -0,0 +1,176 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+#pragma once
+
+namespace pw {
+
+class Status {
+ public:
+  // These are the canonical Google error codes (copied from Tensorflow).
+  enum Code {
+    OK = 0,
+
+    // The operation was cancelled (typically by the caller).
+    CANCELLED = 1,
+
+    // Unknown error.  An example of where this error may be returned is
+    // if a Status value received from another address space belongs to
+    // an error-space that is not known in this address space.  Also,
+    // errors raised by APIs that do not return enough error information
+    // may be converted to this error.
+    UNKNOWN = 2,
+
+    // Client specified an invalid argument.  Note that this differs
+    // from FAILED_PRECONDITION.  INVALID_ARGUMENT indicates arguments
+    // that are problematic regardless of the state of the system
+    // (e.g. a malformed file name).
+    INVALID_ARGUMENT = 3,
+
+    // Deadline expired before operation could complete.  For operations
+    // that change the state of the system, this error may be returned
+    // even if the operation has completed successfully.  For example, a
+    // successful response from a server could have been delayed long
+    // enough for the deadline to expire.
+    DEADLINE_EXCEEDED = 4,
+
+    // Some requested entity (e.g. file or directory) was not found.
+    // For privacy reasons, this code *may* be returned when the client
+    // does not have the access right to the entity.
+    NOT_FOUND = 5,
+
+    // Some entity that we attempted to create (e.g. file or directory)
+    // already exists.
+    ALREADY_EXISTS = 6,
+
+    // The caller does not have permission to execute the specified
+    // operation.  PERMISSION_DENIED must not be used for rejections
+    // caused by exhausting some resource (use RESOURCE_EXHAUSTED
+    // instead for those errors).  PERMISSION_DENIED must not be
+    // used if the caller cannot be identified (use UNAUTHENTICATED
+    // instead for those errors).
+    PERMISSION_DENIED = 7,
+
+    // The request does not have valid authentication credentials for the
+    // operation.
+    UNAUTHENTICATED = 16,
+
+    // Some resource has been exhausted, perhaps a per-user quota, or
+    // perhaps the entire filesystem is out of space.
+    RESOURCE_EXHAUSTED = 8,
+
+    // Operation was rejected because the system is not in a state
+    // required for the operation's execution.  For example, directory
+    // to be deleted may be non-empty, an rmdir operation is applied to
+    // a non-directory, etc.
+    //
+    // A litmus test that may help a service implementer in deciding
+    // between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE:
+    //  (a) Use UNAVAILABLE if the client can retry just the failing call.
+    //  (b) Use ABORTED if the client should retry at a higher-level
+    //      (e.g. restarting a read-modify-write sequence).
+    //  (c) Use FAILED_PRECONDITION if the client should not retry until
+    //      the system state has been explicitly fixed.  E.g. if an "rmdir"
+    //      fails because the directory is non-empty, FAILED_PRECONDITION
+    //      should be returned since the client should not retry unless
+    //      they have first fixed up the directory by deleting files from it.
+    //  (d) Use FAILED_PRECONDITION if the client performs conditional
+    //      REST Get/Update/Delete on a resource and the resource on the
+    //      server does not match the condition. E.g. conflicting
+    //      read-modify-write on the same resource.
+    FAILED_PRECONDITION = 9,
+
+    // The operation was aborted, typically due to a concurrency issue
+    // like sequencer check failures, transaction aborts, etc.
+    //
+    // See litmus test above for deciding between FAILED_PRECONDITION,
+    // ABORTED, and UNAVAILABLE.
+    ABORTED = 10,
+
+    // Operation tried to iterate past the valid input range.  E.g. seeking or
+    // reading past end of file.
+    //
+    // Unlike INVALID_ARGUMENT, this error indicates a problem that may
+    // be fixed if the system state changes. For example, a 32-bit file
+    // system will generate INVALID_ARGUMENT if asked to read at an
+    // offset that is not in the range [0,2^32-1], but it will generate
+    // OUT_OF_RANGE if asked to read from an offset past the current
+    // file size.
+    //
+    // There is a fair bit of overlap between FAILED_PRECONDITION and
+    // OUT_OF_RANGE.  We recommend using OUT_OF_RANGE (the more specific
+    // error) when it applies so that callers who are iterating through
+    // a space can easily look for an OUT_OF_RANGE error to detect when
+    // they are done.
+    OUT_OF_RANGE = 11,
+
+    // Operation is not implemented or not supported/enabled in this service.
+    UNIMPLEMENTED = 12,
+
+    // Internal errors.  Means some invariants expected by underlying
+    // system has been broken.  If you see one of these errors,
+    // something is very broken.
+    INTERNAL = 13,
+
+    // The service is currently unavailable.  This is a most likely a
+    // transient condition and may be corrected by retrying with
+    // a backoff.
+    //
+    // See litmus test above for deciding between FAILED_PRECONDITION,
+    // ABORTED, and UNAVAILABLE.
+    UNAVAILABLE = 14,
+
+    // Unrecoverable data loss or corruption.
+    DATA_LOSS = 15,
+
+    // An extra enum entry to prevent people from writing code that
+    // fails to compile when a new code is added.
+    //
+    // Nobody should ever reference this enumeration entry. In particular,
+    // if you write C++ code that switches on this enumeration, add a default:
+    // case instead of a case that mentions this enumeration entry.
+    //
+    // Nobody should rely on the value listed here. It may change in the future.
+    DO_NOT_USE_RESERVED_FOR_FUTURE_EXPANSION_USE_DEFAULT_IN_SWITCH_INSTEAD_,
+  };
+
+  constexpr Status(Code code = Code::OK) : code_(code) {}
+
+  constexpr Status(const Status&) = default;
+  constexpr Status& operator=(const Status&) = default;
+
+  constexpr Status& operator=(Code code) {
+    code_ = code;
+    return *this;
+  }
+
+  constexpr Code code() const { return code_; }
+  constexpr bool ok() const { return code_ == Code::OK; }
+
+  // Returns a string representation of the Status::Code.
+  const char* str() const;
+
+ private:
+  Code code_;
+};
+
+constexpr bool operator==(Status lhs, Status rhs) {
+  return lhs.code() == rhs.code();
+}
+
+constexpr bool operator!=(Status lhs, Status rhs) {
+  return lhs.code() != rhs.code();
+}
+
+}  // namespace pw
diff --git a/pw_status/status.cc b/pw_status/status.cc
new file mode 100644
index 0000000..4c0f49c
--- /dev/null
+++ b/pw_status/status.cc
@@ -0,0 +1,50 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+#include "pw_status/status.h"
+
+namespace pw {
+
+#define PW_CASE_RETURN_ENUM_STRING(value) \
+  case (value):                           \
+    return #value
+
+const char* Status::str() const {
+  // Status codes are ordered by assigned number (UNAUTHENTICATED is last).
+  switch (code_) {
+    PW_CASE_RETURN_ENUM_STRING(OK);
+    PW_CASE_RETURN_ENUM_STRING(CANCELLED);
+    PW_CASE_RETURN_ENUM_STRING(UNKNOWN);
+    PW_CASE_RETURN_ENUM_STRING(INVALID_ARGUMENT);
+    PW_CASE_RETURN_ENUM_STRING(DEADLINE_EXCEEDED);
+    PW_CASE_RETURN_ENUM_STRING(NOT_FOUND);
+    PW_CASE_RETURN_ENUM_STRING(ALREADY_EXISTS);
+    PW_CASE_RETURN_ENUM_STRING(PERMISSION_DENIED);
+    PW_CASE_RETURN_ENUM_STRING(RESOURCE_EXHAUSTED);
+    PW_CASE_RETURN_ENUM_STRING(FAILED_PRECONDITION);
+    PW_CASE_RETURN_ENUM_STRING(ABORTED);
+    PW_CASE_RETURN_ENUM_STRING(OUT_OF_RANGE);
+    PW_CASE_RETURN_ENUM_STRING(UNIMPLEMENTED);
+    PW_CASE_RETURN_ENUM_STRING(INTERNAL);
+    PW_CASE_RETURN_ENUM_STRING(UNAVAILABLE);
+    PW_CASE_RETURN_ENUM_STRING(DATA_LOSS);
+    PW_CASE_RETURN_ENUM_STRING(UNAUTHENTICATED);
+    default:
+      return "INVALID STATUS";
+  }
+}
+
+#undef PW_CASE_RETURN_ENUM_STRING
+
+}  // namespace pw
diff --git a/pw_toolchain/BUILD.gn b/pw_toolchain/BUILD.gn
new file mode 100644
index 0000000..c0743ce
--- /dev/null
+++ b/pw_toolchain/BUILD.gn
@@ -0,0 +1,122 @@
+# Copyright 2019 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("arm_gcc.gni")
+
+# Creates a series of toolchain targets with common compiler options.
+#
+# Args:
+#   toolchain_template: The target template to use to create the toolchains.
+#   common_toolchain_cflags: cflags to be shared by all toolchains.
+#   common_toolchain_ldflags: ldflags to be shared by all toolchains.
+#   toolchains: List of scopes defining each of the desired toolchains.
+#     Each scope contains up to three variables:
+#       toolchain_name: The full target name of the toolchain.
+#       additional_cflags: Optional list of extra cflags for the toolchain.
+#       additional_ldflags: Optional list of extra ldflags for the toolchain.
+template("generate_toolchains") {
+  not_needed([ "target_name" ])
+
+  assert(defined(invoker.toolchain_template),
+         "generate_toolchains requires a toolchain template")
+  assert(defined(invoker.toolchains),
+         "generate_toolchains must be called with a list of toolchains")
+
+  if (defined(invoker.common_toolchain_cflags)) {
+    _common_cflags = invoker.common_toolchain_cflags
+  } else {
+    _common_cflags = []
+  }
+
+  if (defined(invoker.common_toolchain_ldflags)) {
+    _common_ldflags = invoker.common_toolchain_ldflags
+  } else {
+    _common_ldflags = []
+  }
+
+  # Create a target for each of the desired toolchains, appending its own cflags
+  # and ldflags to the common ones.
+  foreach(toolchain_config, invoker.toolchains) {
+    # GN does not allow assigning a non-empty array to a non-empty array.
+    # This must be done as two assignments, first clearing the original value.
+    _toolchain_cflags = []
+    _toolchain_cflags = _common_cflags
+    if (defined(toolchain_config.additional_cflags)) {
+      _toolchain_cflags += toolchain_config.additional_cflags
+    }
+
+    _toolchain_ldflags = []
+    _toolchain_ldflags = _common_ldflags
+    if (defined(toolchain_config.additional_ldflags)) {
+      _toolchain_ldflags += toolchain_config.additional_ldflags
+    }
+
+    target(invoker.toolchain_template, toolchain_config.toolchain_name) {
+      toolchain_cflags = _toolchain_cflags
+      toolchain_ldflags = _toolchain_ldflags
+    }
+  }
+}
+
+generate_toolchains("cortex_m4") {
+  toolchain_template = "arm_gcc_toolchain"
+
+  common_toolchain_cflags = [
+    "-mabi=aapcs",
+    "-mcpu=cortex-m4",
+    "-mfpu=fpv4-sp-d16",
+    "-mfloat-abi=hard",
+    "-mthumb",
+
+    # Disable obnoxious ABI warning.
+    #
+    # GCC 7.1 adds an over-zealous ABI warning with little useful information
+    # on how to resolve the issue. The warning you get is:
+    #
+    #   note: parameter passing for argument of type '...' changed in GCC 7.1
+    #
+    # There is no other information, and searching for the error is needed to
+    # understand what is happening. For upstream Pigweed, we compile from
+    # source so this is irrelevant; so disable it.
+    #
+    # See: https://gcc.gnu.org/gcc-7/changes.html (search for "psabi").
+    #      https://gcc.gnu.org/ml/gcc/2017-05/msg00073.html
+    "-Wno-psabi",
+  ]
+
+  common_toolchain_ldflags = [
+    "--specs=nosys.specs",
+    "-lnosys",
+    "-lc",
+  ]
+
+  toolchains = [
+    {
+      toolchain_name = "arm_gcc_cortex_m4_og"
+      additional_cflags = [ "-Og" ]
+    },
+    {
+      toolchain_name = "arm_gcc_cortex_m4_o1"
+      additional_cflags = [ "-O1" ]
+    },
+    {
+      toolchain_name = "arm_gcc_cortex_m4_o2"
+      additional_cflags = [ "-O2" ]
+    },
+    {
+      toolchain_name = "arm_gcc_cortex_m4_os"
+      additional_cflags = [ "-Os" ]
+    },
+  ]
+}
diff --git a/pw_toolchain/README.md b/pw_toolchain/README.md
new file mode 100644
index 0000000..e73f45d
--- /dev/null
+++ b/pw_toolchain/README.md
@@ -0,0 +1 @@
+# pw\_toolchain: Pigweed's standard build toolchains.
diff --git a/pw_toolchain/arm_gcc.gni b/pw_toolchain/arm_gcc.gni
new file mode 100644
index 0000000..1b20d4f
--- /dev/null
+++ b/pw_toolchain/arm_gcc.gni
@@ -0,0 +1,121 @@
+# Copyright 2019 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+# Generates an arm-eabi-none gcc toolchain for a specific target.
+#
+# Args:
+#   toolchain_cflags: Additional C/C++ compiler flags for the target.
+#   toolchain_ldflags: Additional linker flags for the target.
+template("arm_gcc_toolchain") {
+  _toolchain_cflags = ""
+  if (defined(invoker.toolchain_cflags)) {
+    foreach(flag, invoker.toolchain_cflags) {
+      _toolchain_cflags += " " + flag
+    }
+  }
+
+  _toolchain_ldflags = ""
+  if (defined(invoker.toolchain_ldflags)) {
+    foreach(flag, invoker.toolchain_ldflags) {
+      _toolchain_ldflags += " " + flag
+    }
+  }
+
+  # TODO(frolv): This assumes that the ARM gcc toolchain is in the PATH.
+  # It should be updated to point to the prebuilt path within the source tree
+  # once that is added.
+  _tool_name_root = "arm-none-eabi-"
+
+  _ar = _tool_name_root + "ar"
+  _cc = _tool_name_root + "gcc"
+  _cxx = _tool_name_root + "g++"
+
+  toolchain(target_name) {
+    tool("asm") {
+      depfile = "{{output}}.d"
+      command = "$_cc -MMD -MF $depfile $_toolchain_cflags {{defines}} {{include_dirs}} {{asmflags}} -c {{source}} -o {{output}}"
+      depsformat = "gcc"
+      description = "as {{output}}"
+      outputs = [
+        "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
+      ]
+    }
+
+    tool("cc") {
+      depfile = "{{output}}.d"
+      command = "$_cc -MMD -MF $depfile $_toolchain_cflags {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}"
+      depsformat = "gcc"
+      description = "cc {{output}}"
+      outputs = [
+        "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
+      ]
+    }
+
+    tool("cxx") {
+      depfile = "{{output}}.d"
+      command = "$_cxx -MMD -MF $depfile $_toolchain_cflags {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}"
+      depsformat = "gcc"
+      description = "c++ {{output}}"
+      outputs = [
+        "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
+      ]
+    }
+
+    tool("alink") {
+      command = "rm -f {{output}} && $_ar rcs {{output}} {{inputs}}"
+      description = "ar {{target_output_name}}{{output_extension}}"
+      outputs = [
+        "{{target_out_dir}}/{{target_output_name}}{{output_extension}}",
+      ]
+      default_output_extension = ".a"
+    }
+
+    lib_switch = "-l"
+    lib_dir_switch = "-L"
+
+    _link_outfile = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
+    _link_mapfile = "{{output_dir}}/{{target_output_name}}.map"
+    _link_command = "$_cxx {{ldflags}} -Wl,--gc-sections $_toolchain_cflags $_toolchain_ldflags -Wl,--start-group {{inputs}} -Wl,--end-group {{libs}} -Wl,-Map=$_link_mapfile -o $_link_outfile"
+
+    tool("link") {
+      command = _link_command
+      description = "ld $_link_outfile"
+      outputs = [
+        _link_outfile,
+      ]
+      default_output_dir = "{{target_out_dir}}"
+      default_output_extension = ".elf"
+    }
+
+    tool("solink") {
+      command = _link_command + " -shared"
+      description = "ld -shared $_link_outfile"
+      outputs = [
+        _link_outfile,
+      ]
+      default_output_dir = "{{target_out_dir}}"
+      default_output_extension = ".so"
+    }
+
+    tool("stamp") {
+      command = "touch {{output}}"
+      description = "stamp {{output}}"
+    }
+
+    tool("copy") {
+      command = "cp -af {{source}} {{output}}"
+      description = "cp {{source}} {{output}}"
+    }
+  }
+}