third_party.pigweed.src/pw_build/python_action.gni
Michael Spang c8b939009b Remove absolute paths from build command lines
The GN convention is to specify paths in command lines relative to the
build directory. Unfortunately and contrary to pigweed's expectations
this is not what rebase_path(path) does; that outputs an absolute path.

Absolute paths are not desirable in most circumstances as they contain
sources of nondeterminism such as the developer's home directory. Using
them can for example reduce hit rate in build caches.

Replace rebase_path(path) with rebase_path(path, root_build_dir) which
is the correct idiom and matches GN's builtin behavior (e.g. for
sources, include_dirs, etc).

This also removes the --directory argument to python_action(). Changing
the directory during the build while using relative paths is likely to
result in confusion and should be discouraged.

There's a couple more things to do on top of this for identical
binaries between build directories / machines / developers:

- pass options to avoid embedding the working directory
- pass options to use relative paths for the vendored clang & libc++

See [1]-[2] for how to do that.

[1] https://source.chromium.org/chromium/chromium/src/+/main:build/config/compiler/BUILD.gn;l=1170-1239;drc=ab531c265c533cba1c2f6d8240cc0bf7679f605a
[2] https://cs.opensource.google/fuchsia/fuchsia/+/main:build/config/BUILD.gn;l=145-216;drc=f6d705f0937c778d5d5f807a4580113612b02f5a

Change-Id: I17708102c03d6488d68c8571b6e9343191fd47de
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/47461
Reviewed-by: Wyatt Hepler <hepler@google.com>
Commit-Queue: Michael Spang <spang@google.com>
2021-06-09 19:15:51 +00:00

203 lines
6.4 KiB
Plaintext

# Copyright 2020 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("//build_overrides/pigweed.gni")
# Defines an action that runs a Python script.
#
# This wraps a regular Python script GN action with an invocation of a script-
# runner script that adds useful features. pw_python_action() uses the same
# actions as GN's action(), with the following additions or changes:
#
# module May be used in place of the script argument to run the
# provided Python module with `python -m` instead of a script.
# Either script or module must be provided.
#
# capture_output If true, script output is hidden unless the script fails
# with an error. Defaults to true.
#
# stamp File to touch if the script is successful. Actions that
# don't create output files can use this stamp file instead of
# creating their own placeholder file. If true, a generic file
# is used. If false or not set, no file is touched.
#
# environment Environment variables to set, passed as a list of NAME=VALUE
# strings.
#
# args Same as the standard action args, except special expressions
# may be used to extract information not normally accessible
# in GN. These include the following:
#
# <TARGET_FILE(//some/label:here)> - expands to the
# output file (such as a .a or .elf) from a GN target
# <TARGET_FILE_IF_EXISTS(//some/label:here)> - expands to
# the output file if the target exists, or nothing
# <TARGET_OBJECTS(//some/label:here)> - expands to the
# object files produced by the provided GN target
#
# python_deps Dependencies on pw_python_package or related Python targets.
#
template("pw_python_action") {
assert(defined(invoker.script) != defined(invoker.module),
"pw_python_action requires either 'script' or 'module'")
_script_args = [
# GN root directory relative to the build directory (in which the runner
# script is invoked).
"--gn-root",
rebase_path("//", root_build_dir),
# Current directory, used to resolve relative paths.
"--current-path",
rebase_path(".", root_build_dir),
"--default-toolchain=$default_toolchain",
"--current-toolchain=$current_toolchain",
]
if (defined(invoker.environment)) {
foreach(variable, invoker.environment) {
_script_args += [ "--env=$variable" ]
}
}
if (defined(invoker.inputs)) {
_inputs = invoker.inputs
} else {
_inputs = []
}
# List the script to run as an input so that the action is re-run when it is
# modified.
if (defined(invoker.script)) {
_inputs += [ invoker.script ]
}
if (defined(invoker.outputs)) {
_outputs = invoker.outputs
} else {
_outputs = []
}
# If a stamp file is requested, add it as an output of the runner script.
if (defined(invoker.stamp) && invoker.stamp != false) {
if (invoker.stamp == true) {
_stamp_file = "$target_gen_dir/$target_name.pw_pystamp"
} else {
_stamp_file = invoker.stamp
}
_outputs += [ _stamp_file ]
_script_args += [
"--touch",
rebase_path(_stamp_file, root_build_dir),
]
}
# Capture output or not (defaults to true).
if (!defined(invoker.capture_output) || invoker.capture_output) {
_script_args += [ "--capture-output" ]
}
if (defined(invoker.module)) {
_script_args += [
"--module",
invoker.module,
]
}
# "--" indicates the end of arguments to the runner script.
# Everything beyond this point is interpreted as the command and arguments
# of the Python script to run.
_script_args += [ "--" ]
if (defined(invoker.script)) {
_script_args += [ rebase_path(invoker.script, root_build_dir) ]
}
if (defined(invoker.args)) {
_script_args += invoker.args
}
if (defined(invoker._pw_action_type)) {
_action_type = invoker._pw_action_type
} else {
_action_type = "action"
}
if (defined(invoker.deps)) {
_deps = invoker.deps
} else {
_deps = []
}
if (defined(invoker.python_deps)) {
foreach(dep, invoker.python_deps) {
_deps += [ get_label_info(dep, "label_no_toolchain") + ".install(" +
get_label_info(dep, "toolchain") + ")" ]
}
# Add the base target as a dep so the action reruns when any source files
# change, even if the package does not have to be reinstalled.
_deps += invoker.python_deps
}
target(_action_type, target_name) {
_ignore_vars = [
"script",
"args",
"deps",
"inputs",
"outputs",
]
forward_variables_from(invoker, "*", _ignore_vars)
script = "$dir_pw_build/py/pw_build/python_runner.py"
args = _script_args
inputs = _inputs
outputs = _outputs
deps = _deps
}
}
# Runs pw_python_action once per file over a set of sources.
#
# This template brings pw_python_action's features to action_foreach. Usage is
# the same as pw_python_action, except that sources must be provided and source
# expansion (e.g. "{{source}}") may be used in args and outputs.
#
# See the pw_python_action and action_foreach documentation for full details.
template("pw_python_action_foreach") {
assert(defined(invoker.sources) && invoker.sources != [],
"pw_python_action_foreach requires a list of one or more sources")
pw_python_action(target_name) {
if (defined(invoker.stamp) && invoker.stamp != false) {
if (invoker.stamp == true) {
# Use source file names in the generated stamp file path so they are
# unique for each source.
stamp = "$target_gen_dir/{{source_file_part}}.pw_pystamp"
} else {
stamp = invoker.stamp
}
} else {
stamp = false
}
forward_variables_from(invoker, "*", [ "stamp" ])
_pw_action_type = "action_foreach"
}
}