1# Copyright 2019 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14 15import("python_action.gni") 16 17# Runs a program which isn't in Python. 18# 19# This is provided to avoid having to write a new Python wrapper script every 20# time a program needs to be run from GN. 21# 22# Args: 23# program: The program to run. Can be a full path or just a name (in which case 24# $PATH is searched). 25# 26# args: Optional list of arguments to the program. 27# 28# deps: Dependencies for this target. 29# 30# public_deps: Public dependencies for this target. In addition to outputs from 31# this target, outputs generated by public dependencies can be used as inputs 32# from targets that depend on this one. This is not the case for private 33# deps. 34# 35# inputs: Optional list of build inputs to the program. 36# 37# outputs: Optional list of artifacts produced by the program's execution. 38# 39# env: Optional list of key-value pairs defining environment variables for 40# the program. 41# 42# env_file: Optional path to a file containing a list of newline-separated 43# key-value pairs defining environment variables for the program. 44# 45# args_file: Optional path to a file containing additional positional arguments 46# to the program. Each line of the file is appended to the invocation. Useful 47# for specifying arguments from GN metadata. 48# 49# skip_empty_args: If args_file is provided, boolean indicating whether to skip 50# running the program if the file is empty. Used to avoid running commands 51# which error when called without arguments. 52# 53# capture_output: If true, output from the program is hidden unless the program 54# exits with an error. Defaults to true. 55# 56# working_directory: The working directory to execute the subprocess with. If 57# not specified it will not be set and the subprocess will have whatever the 58# parent current working directory is. 59# 60# venv: Python virtualenv to pass along to the underlying pw_python_action. 61# 62# visibility: GN visibility to apply to the underlying target. 63# 64# Example: 65# 66# pw_exec("hello_world") { 67# program = "/bin/sh" 68# args = [ 69# "-c", 70# "echo hello \$WORLD", 71# ] 72# env = [ 73# "WORLD=world", 74# ] 75# } 76# 77template("pw_exec") { 78 assert(defined(invoker.program), "pw_exec requires a program to run") 79 80 _script_args = [ 81 "--target", 82 target_name, 83 ] 84 85 if (defined(invoker.env_file)) { 86 _script_args += [ 87 "--env-file", 88 rebase_path(invoker.env_file, root_build_dir), 89 ] 90 } 91 92 if (defined(invoker.args_file)) { 93 _script_args += [ 94 "--args-file", 95 rebase_path(invoker.args_file, root_build_dir), 96 ] 97 98 if (defined(invoker.skip_empty_args) && invoker.skip_empty_args) { 99 _script_args += [ "--skip-empty-args" ] 100 } 101 } 102 103 if (defined(invoker.env)) { 104 foreach(_env, invoker.env) { 105 _script_args += [ 106 "--env", 107 _env, 108 ] 109 } 110 } 111 112 if (!defined(invoker.capture_output) || invoker.capture_output) { 113 _script_args += [ "--capture-output" ] 114 _capture_output = true 115 } else { 116 _capture_output = false 117 } 118 119 if (defined(invoker.working_directory)) { 120 _script_args += [ 121 "--working-directory", 122 invoker.working_directory, 123 ] 124 } 125 126 _script_args += [ 127 "--", 128 invoker.program, 129 ] 130 if (defined(invoker.args)) { 131 _script_args += invoker.args 132 } 133 134 pw_python_action(target_name) { 135 script = "$dir_pw_build/py/pw_build/exec.py" 136 args = _script_args 137 capture_output = _capture_output 138 139 forward_variables_from(invoker, 140 [ 141 "deps", 142 "inputs", 143 "pool", 144 "public_deps", 145 "venv", 146 "visibility", 147 ]) 148 149 if (!defined(inputs)) { 150 inputs = [] 151 } 152 if (defined(invoker.env_file)) { 153 inputs += [ invoker.env_file ] 154 } 155 156 if (defined(invoker.outputs)) { 157 outputs = invoker.outputs 158 } else { 159 stamp = true 160 } 161 } 162} 163