pw_build: Add deps support to pw_linker_script

To support includes and defines from deps and transitive deps, build up
the preprocessing command line using the merged deps compilation
context. This allows headers, include paths, and defines to be sent in
to linker script preprocessing.

Bug: b/331927492
Change-Id: I7bfa92abacbb658c0242a6b2f3096819dea5b9df
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/211194
Lint: Lint 🤖 <android-build-ayeaye@system.gserviceaccount.com>
Commit-Queue: Austin Foxley <afoxley@google.com>
Presubmit-Verified: CQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Armando Montanez <amontanez@google.com>
Reviewed-by: Ted Pudlik <tpudlik@google.com>
diff --git a/pw_build/bazel.rst b/pw_build/bazel.rst
index 971a779..167f860 100644
--- a/pw_build/bazel.rst
+++ b/pw_build/bazel.rst
@@ -35,6 +35,14 @@
          "PW_BOOT_VECTOR_TABLE_BEGIN=0x08000000",
          "PW_BOOT_VECTOR_TABLE_SIZE=512",
      ],
+     deps = [":some_header_library"],
+   )
+
+   # You can include headers provided by targets specified in deps.
+   cc_library(
+     name = "some_header_library",
+     hdrs = ["test_header.h"],
+     includes = ["."],
    )
 
    # You can include the linker script in the deps.
diff --git a/pw_build/bazel_internal/BUILD.bazel b/pw_build/bazel_internal/BUILD.bazel
index 822ee7d..5773400 100644
--- a/pw_build/bazel_internal/BUILD.bazel
+++ b/pw_build/bazel_internal/BUILD.bazel
@@ -14,6 +14,12 @@
 
 load("//pw_build:pigweed.bzl", "pw_linker_script")
 
+cc_library(
+    name = "header_test",
+    hdrs = ["header_test.h"],
+    includes = ["."],
+)
+
 pw_linker_script(
     name = "linker_script_test",
     defines = [
@@ -27,6 +33,7 @@
         "PW_BOOT_VECTOR_TABLE_SIZE=1M",
     ],
     linker_script = "linker_script.ld",
+    deps = [":header_test"],
 )
 
 # Use cc_binary to build the test to avoid duplicating the linker script in the
diff --git a/pw_build/bazel_internal/BUILD.gn b/pw_build/bazel_internal/BUILD.gn
index ef76ebd..04ddbb0 100644
--- a/pw_build/bazel_internal/BUILD.gn
+++ b/pw_build/bazel_internal/BUILD.gn
@@ -17,5 +17,6 @@
 # This is target is to keep the presubmit happy. But is not actually used.
 pw_source_set("tests") {
   public_deps = [ dir_pw_preprocessor ]
+  headers = [ "header_test.h" ]
   sources = [ "test.cc" ]
 }
diff --git a/pw_build/bazel_internal/header_test.h b/pw_build/bazel_internal/header_test.h
new file mode 100644
index 0000000..6481f14
--- /dev/null
+++ b/pw_build/bazel_internal/header_test.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2024 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
+#define SOME_USEFUL_LINKER_SCRIPT_MACRO 0
diff --git a/pw_build/bazel_internal/linker_script.ld b/pw_build/bazel_internal/linker_script.ld
index 126c647..809f1c6 100644
--- a/pw_build/bazel_internal/linker_script.ld
+++ b/pw_build/bazel_internal/linker_script.ld
@@ -21,6 +21,11 @@
 #error "PW_BOOT_VECTOR_TABLE_BEGIN is not defined, and is required to use pw_boot_cortex_m"
 #endif  // PW_BOOT_VECTOR_TABLE_BEGIN
 
+#include "header_test.h"
+
+#ifndef SOME_USEFUL_LINKER_SCRIPT_MACRO
+#error "Header include test failed"
+#endif
 
  /* Note: This technically doesn't set the firmware's entry point. Setting the
  *       firmware entry point is done by setting vector_table[1]
diff --git a/pw_build/pigweed.bzl b/pw_build/pigweed.bzl
index 7dd850e..42b2d85 100644
--- a/pw_build/pigweed.bzl
+++ b/pw_build/pigweed.bzl
@@ -425,10 +425,22 @@
         feature_configuration = feature_configuration,
         action_name = C_COMPILE_ACTION_NAME,
     )
+    compilation_context = cc_common.merge_compilation_contexts(
+        compilation_contexts = [dep[CcInfo].compilation_context for dep in ctx.attr.deps],
+    )
     c_compile_variables = cc_common.create_compile_variables(
         feature_configuration = feature_configuration,
         cc_toolchain = cc_toolchain,
         user_compile_flags = ctx.fragments.cpp.copts + ctx.fragments.cpp.conlyopts,
+        include_directories = compilation_context.includes,
+        quote_include_directories = compilation_context.quote_includes,
+        system_include_directories = compilation_context.system_includes,
+        preprocessor_defines = depset(ctx.attr.defines, transitive = [compilation_context.defines]),
+    )
+    cmd_line = cc_common.get_memory_inefficient_command_line(
+        feature_configuration = feature_configuration,
+        action_name = C_COMPILE_ACTION_NAME,
+        variables = c_compile_variables,
     )
     env = cc_common.get_environment_variables(
         feature_configuration = feature_configuration,
@@ -439,25 +451,17 @@
         outputs = [output_script],
         inputs = depset(
             [ctx.file.linker_script],
-            transitive = [cc_toolchain.all_files],
+            transitive = [compilation_context.headers, cc_toolchain.all_files],
         ),
         executable = cxx_compiler_path,
         arguments = [
             "-E",
             "-P",
-            # TODO: b/296928739 - This flag is needed so cc1 can be located
-            # despite the presence of symlinks. Normally this is provided
-            # through copts inherited from the toolchain, but since those are
-            # not pulled in here the flag must be explicitly added.
-            "-no-canonical-prefixes",
             "-xc",
             ctx.file.linker_script.path,
             "-o",
             output_script.path,
-        ] + [
-            "-D" + d
-            for d in ctx.attr.defines
-        ] + ctx.attr.copts,
+        ] + cmd_line,
         env = env,
     )
     linker_input = cc_common.create_linker_input(
@@ -478,6 +482,12 @@
     attrs = {
         "copts": attr.string_list(doc = "C compile options."),
         "defines": attr.string_list(doc = "C preprocessor defines."),
+        "deps": attr.label_list(
+            providers = [CcInfo],
+            doc = """Dependencies of this linker script. Can be used to provide
+                     header files and defines. Only the CompilationContext of
+                     the provided dependencies are used.""",
+        ),
         "linker_script": attr.label(
             mandatory = True,
             allow_single_file = True,