blob: 174d83e49a089eb016e24b1efc2393d586ed7e5e [file] [log] [blame]
# Copyright 2021 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")
import("$dir_pw_build/python.gni")
import("$dir_pw_build/python_action.gni")
import("$dir_pw_build/zip.gni")
# Builds a directory containing a collection of Python wheels.
#
# Given one or more pw_python_package targets, this target will build their
# .wheel sub-targets along with the .wheel sub-targets of all dependencies,
# direct and indirect, as understood by GN. The resulting .whl files will be
# collected into a single directory called 'python_wheels'.
#
# Args:
# packages: A list of pw_python_package targets whose wheels should be
# included; their dependencies will be pulled in as wheels also.
template("pw_python_wheels") {
_outer_name = target_name
_wheel_paths_path = "${target_gen_dir}/${target_name}_wheel_paths.txt"
_deps = []
if (defined(invoker.deps)) {
_deps = invoker.deps
}
_packages = []
foreach(_pkg, invoker.packages) {
_pkg_name = get_label_info(_pkg, "label_no_toolchain")
_pkg_toolchain = get_label_info(_pkg, "toolchain")
_packages += [ "${_pkg_name}.wheel(${_pkg_toolchain})" ]
}
# Build a list of relative paths containing all the wheels we depend on.
generated_file("${target_name}._wheel_paths") {
data_keys = [ "pw_python_package_wheels" ]
rebase = root_build_dir
deps = _packages
outputs = [ _wheel_paths_path ]
}
pw_python_action(target_name) {
forward_variables_from(invoker, [ "public_deps" ])
deps = _deps + [ ":${_outer_name}._wheel_paths" ]
module = "pw_build.collect_wheels"
args = [
"--prefix",
rebase_path(root_build_dir, root_build_dir),
"--suffix",
rebase_path(_wheel_paths_path, root_build_dir),
"--out_dir",
rebase_path("${target_out_dir}/python_wheels", root_build_dir),
]
stamp = true
}
}
# Builds a .zip containing Python wheels and setup scripts.
#
# The resulting .zip archive will contain a directory with Python wheels for
# all pw_python_package targets listed in 'packages', plus wheels for any
# pw_python_package targets those packages depend on, directly or indirectly,
# as understood by GN.
#
# In addition to Python wheels, the resulting .zip will also contain simple
# setup scripts for Linux, MacOS, and Windows that take care of creating a
# Python venv and installing all the included wheels into it, and a README.md
# file with setup and usage instructions.
#
# Args:
# packages: A list of pw_python_package targets whose wheels should be
# included; their dependencies will be pulled in as wheels also.
# inputs: An optional list of extra files to include in the generated .zip,
# formatted the same was as the 'inputs' argument to pw_zip targets.
# dirs: An optional list of directories to include in the generated .zip,
# formatted the same way as the 'dirs' argument to pw_zip targets.
template("pw_python_zip_with_setup") {
_outer_name = target_name
_zip_path = "${target_out_dir}/${target_name}.zip"
_inputs = []
if (defined(invoker.inputs)) {
_inputs = invoker.inputs
}
_dirs = []
if (defined(invoker.dirs)) {
_dirs = invoker.dirs
}
pw_python_wheels("${target_name}.wheels") {
packages = invoker.packages
forward_variables_from(invoker,
[
"deps",
"public_deps",
])
}
pw_zip("${target_name}") {
forward_variables_from(invoker, [ "public_deps" ])
inputs = _inputs + [
"$dir_pw_build/python_dist/setup.bat > /${target_name}/",
"$dir_pw_build/python_dist/setup.sh > /${target_name}/",
]
dirs =
_dirs +
[ "${target_out_dir}/python_wheels/ > /${target_name}/python_wheels/" ]
output = _zip_path
deps = [ ":${_outer_name}.wheels" ]
}
}
# Generates a directory of Python packages from source files suitable for
# deployment outside of the project developer environment.
#
# The resulting directory contains only files mentioned in each package's
# setup.cfg file. This is useful for bundling multiple Python packages up
# into a single package for distribution to other locations like
# http://pypi.org.
#
# Args:
# packages: A list of pw_python_package targets to be installed into the build
# directory. Their dependencies will be pulled in as wheels also.
#
# include_tests: If true, copy Python package tests to a `tests` subdir.
#
# extra_files: A list of extra files that should be included in the output. The
# format of each item in this list follows this convention:
# //some/nested/source_file > nested/destination_file
template("pw_create_python_source_tree") {
_output_dir = "${target_out_dir}/${target_name}/"
_metadata_json_file_list =
"${target_gen_dir}/${target_name}_metadata_path_list.txt"
# If generating a setup.cfg file a common base file must be provided.
if (defined(invoker.generate_setup_cfg)) {
generate_setup_cfg = invoker.generate_setup_cfg
assert(defined(generate_setup_cfg.common_config_file),
"'common_config_file' is required in generate_setup_cfg")
}
_extra_file_inputs = []
_extra_file_args = []
# Convert extra_file strings to input, outputs and create_python_tree.py args.
if (defined(invoker.extra_files)) {
_delimiter = ">"
_extra_file_outputs = []
foreach(input, invoker.extra_files) {
# Remove spaces before and after the delimiter
input = string_replace(input, " $_delimiter", _delimiter)
input = string_replace(input, "$_delimiter ", _delimiter)
input_list = []
input_list = string_split(input, _delimiter)
# Save the input file
_extra_file_inputs += [ input_list[0] ]
# Save the output file
_this_output = _output_dir + "/" + input_list[1]
_extra_file_outputs += [ _this_output ]
# Compose an arg for passing to create_python_tree.py with properly
# rebased paths.
_extra_file_args +=
[ string_join(" $_delimiter ",
[
rebase_path(input_list[0], root_build_dir),
rebase_path(_this_output, root_build_dir),
]) ]
}
}
_include_tests = defined(invoker.include_tests) && invoker.include_tests
# Build a list of relative paths containing all the python
# package_metadata.json files we depend on.
generated_file("${target_name}._metadata_path_list.txt") {
data_keys = [ "pw_python_package_metadata_json" ]
rebase = root_build_dir
deps = invoker.packages
outputs = [ _metadata_json_file_list ]
}
# Run the python action on the metadata_path_list.txt file
pw_python_action(target_name) {
deps =
invoker.packages + [ ":${invoker.target_name}._metadata_path_list.txt" ]
script = "$dir_pw_build/py/pw_build/create_python_tree.py"
inputs = _extra_file_inputs
args = [
"--tree-destination-dir",
rebase_path(_output_dir, root_build_dir),
"--input-list-files",
rebase_path(_metadata_json_file_list, root_build_dir),
]
# Add required setup.cfg args if we are generating a merged config.
if (defined(generate_setup_cfg)) {
if (defined(generate_setup_cfg.common_config_file)) {
args += [
"--setupcfg-common-file",
rebase_path(generate_setup_cfg.common_config_file, root_build_dir),
]
}
if (defined(generate_setup_cfg.append_git_sha_to_version)) {
args += [ "--setupcfg-version-append-git-sha" ]
}
if (defined(generate_setup_cfg.append_date_to_version)) {
args += [ "--setupcfg-version-append-date" ]
}
}
if (_extra_file_args == []) {
# No known output files - stamp instead.
stamp = true
} else {
args += [ "--extra-files" ]
args += _extra_file_args
# Include extra_files as outputs
outputs = _extra_file_outputs
}
if (_include_tests) {
args += [ "--include-tests" ]
}
}
}