Skip to content

Commit b04aa05

Browse files
authored
process_wrapper: Apply substitutions to param files (#1565)
1 parent b209b3e commit b04aa05

File tree

3 files changed

+91
-10
lines changed

3 files changed

+91
-10
lines changed

test/process_wrapper/process_wrapper_tester.bzl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,20 @@ def _impl(ctx):
5757
args.add(ctx.attr.test_config)
5858
args.add("--current-dir", "${pwd}")
5959
args.add("--test-subst", "subst key to ${key}")
60+
61+
extra_args = ctx.actions.args()
62+
if combined or ctx.attr.test_config == "subst-pwd":
63+
extra_args.set_param_file_format("multiline")
64+
extra_args.use_param_file("@%s", use_always = True)
65+
extra_args.add("${pwd}")
66+
6067
env = {"CURRENT_DIR": "${pwd}/test_path"}
6168

6269
ctx.actions.run(
6370
executable = ctx.executable._process_wrapper,
6471
inputs = ctx.files.env_files + ctx.files.arg_files,
6572
outputs = outputs,
66-
arguments = [args],
73+
arguments = [args, extra_args],
6774
env = env,
6875
tools = [ctx.executable._process_wrapper_tester],
6976
)
@@ -76,6 +83,7 @@ process_wrapper_tester = rule(
7683
"arg_files": attr.label_list(),
7784
"env_files": attr.label_list(),
7885
"test_config": attr.string(mandatory = True),
86+
"use_param_file": attr.bool(default = False),
7987
"_process_wrapper": attr.label(
8088
default = Label("//util/process_wrapper"),
8189
executable = True,

test/process_wrapper/process_wrapper_tester.cc

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include <cstdlib>
1616
#include <iostream>
17+
#include <fstream>
1718
#include <string>
1819

1920
void basic_part1_test(std::string current_dir_arg) {
@@ -42,11 +43,30 @@ void basic_part2_test(std::string current_dir, const char* envp[]) {
4243
}
4344
}
4445

45-
void subst_pwd_test(std::string current_dir, const char* envp[]) {
46+
void subst_pwd_test(int argc, const char* argv[], const char* envp[]) {
47+
std::string current_dir = argv[3];
4648
if (current_dir.find("${pwd}") != std::string::npos) {
4749
std::cerr << "error: argument ${pwd} substitution failed.\n";
4850
std::exit(1);
4951
}
52+
// find the param file using its "@" prefix
53+
std::string param_file;
54+
for (int i = 1; i < argc; ++i) {
55+
if (argv[i][0] == '@') {
56+
param_file = std::string(argv[i]+1);
57+
break;
58+
}
59+
}
60+
if (param_file.empty()) {
61+
std::cerr << "error: no param file.\n";
62+
std::exit(1);
63+
}
64+
std::string param_file_line;
65+
getline(std::ifstream(param_file), param_file_line);
66+
if (param_file_line != current_dir) {
67+
std::cerr << "error: param file " << param_file << " should contain " << current_dir << ", found " << param_file_line << ".\n";
68+
std::exit(1);
69+
}
5070
bool found = false;
5171
for (int i = 0; envp[i] != nullptr; ++i) {
5272
const std::string env = envp[i];
@@ -147,7 +167,7 @@ int main(int argc, const char* argv[], const char* envp[]) {
147167
}
148168

149169
if (combined || test_config == "subst-pwd") {
150-
subst_pwd_test(argv[3], envp);
170+
subst_pwd_test(argc, argv, envp);
151171
} else if (test_config == "basic") {
152172
basic_part2_test(argv[3], envp);
153173
}

util/process_wrapper/options.rs

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use std::collections::HashMap;
22
use std::env;
33
use std::fmt;
4+
use std::fs::File;
5+
use std::io::{self, Write};
46
use std::process::exit;
57

68
use crate::flags::{FlagParseError, Flags, ParseOutcome};
@@ -185,7 +187,7 @@ pub(crate) fn options() -> Result<Options, OptionError> {
185187
);
186188
// Append all the arguments fetched from files to those provided via command line.
187189
child_args.append(&mut file_arguments);
188-
let child_args = prepare_args(child_args, &subst_mappings);
190+
let child_args = prepare_args(child_args, &subst_mappings)?;
189191
// Split the executable path from the rest of the arguments.
190192
let (exec_path, args) = child_args.split_first().ok_or_else(|| {
191193
OptionError::Generic(
@@ -234,15 +236,66 @@ fn env_from_files(paths: Vec<String>) -> Result<HashMap<String, String>, OptionE
234236
Ok(env_vars)
235237
}
236238

237-
fn prepare_args(mut args: Vec<String>, subst_mappings: &[(String, String)]) -> Vec<String> {
239+
fn prepare_arg(mut arg: String, subst_mappings: &[(String, String)]) -> String {
238240
for (f, replace_with) in subst_mappings {
239-
for arg in args.iter_mut() {
240-
let from = format!("${{{}}}", f);
241-
let new = arg.replace(from.as_str(), replace_with);
242-
*arg = new;
241+
let from = format!("${{{}}}", f);
242+
arg = arg.replace(&from, replace_with);
243+
}
244+
arg
245+
}
246+
247+
/// Apply substitutions to the given param file. Returns the new filename.
248+
fn prepare_param_file(
249+
filename: &str,
250+
subst_mappings: &[(String, String)],
251+
) -> Result<String, OptionError> {
252+
let expanded_file = format!("{}.expanded", filename);
253+
let format_err = |err: io::Error| {
254+
OptionError::Generic(format!(
255+
"{} writing path: {:?}, current directory: {:?}",
256+
err,
257+
expanded_file,
258+
std::env::current_dir()
259+
))
260+
};
261+
let mut out = io::BufWriter::new(File::create(&expanded_file).map_err(&format_err)?);
262+
fn process_file(
263+
filename: &str,
264+
out: &mut io::BufWriter<File>,
265+
subst_mappings: &[(String, String)],
266+
format_err: &impl Fn(io::Error) -> OptionError,
267+
) -> Result<(), OptionError> {
268+
for arg in read_file_to_array(filename).map_err(OptionError::Generic)? {
269+
let arg = prepare_arg(arg, subst_mappings);
270+
if let Some(arg_file) = arg.strip_prefix('@') {
271+
process_file(arg_file, out, subst_mappings, format_err)?;
272+
} else {
273+
writeln!(out, "{}", arg).map_err(format_err)?;
274+
}
243275
}
276+
Ok(())
244277
}
245-
args
278+
process_file(filename, &mut out, subst_mappings, &format_err)?;
279+
Ok(expanded_file)
280+
}
281+
282+
/// Apply substitutions to the provided arguments, recursing into param files.
283+
fn prepare_args(
284+
args: Vec<String>,
285+
subst_mappings: &[(String, String)],
286+
) -> Result<Vec<String>, OptionError> {
287+
args.into_iter()
288+
.map(|arg| {
289+
let arg = prepare_arg(arg, subst_mappings);
290+
if let Some(param_file) = arg.strip_prefix('@') {
291+
// Note that substitutions may also apply to the param file path!
292+
prepare_param_file(param_file, subst_mappings)
293+
.map(|filename| format!("@{}", filename))
294+
} else {
295+
Ok(arg)
296+
}
297+
})
298+
.collect()
246299
}
247300

248301
fn environment_block(

0 commit comments

Comments
 (0)