Skip to content

Make remote-test-client and remote-test-server compatible with windows #72672

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/bootstrap/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1762,7 +1762,7 @@ impl Step for Crate {
} else if builder.remote_tested(target) {
cargo.env(
format!("CARGO_TARGET_{}_RUNNER", envify(&target)),
format!("{} run", builder.tool_exe(Tool::RemoteTestClient).display()),
format!("{} run 0", builder.tool_exe(Tool::RemoteTestClient).display()),
);
}

Expand Down
21 changes: 13 additions & 8 deletions src/tools/compiletest/src/runtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1584,29 +1584,34 @@ impl<'test> TestCx<'test> {
//
// into
//
// remote-test-client run program:support-lib.so arg1 arg2
// remote-test-client run program 2 support-lib.so support-lib2.so arg1 arg2
//
// The test-client program will upload `program` to the emulator
// along with all other support libraries listed (in this case
// `support-lib.so`. It will then execute the program on the
// emulator with the arguments specified (in the environment we give
// the process) and then report back the same result.
// `support-lib.so` and `support-lib2.so`. It will then execute
// the program on the emulator with the arguments specified
// (in the environment we give the process) and then report back
// the same result.
_ if self.config.remote_test_client.is_some() => {
let aux_dir = self.aux_output_dir_name();
let ProcArgs { mut prog, args } = self.make_run_args();
let ProcArgs { prog, args } = self.make_run_args();
let mut support_libs = Vec::new();
if let Ok(entries) = aux_dir.read_dir() {
for entry in entries {
let entry = entry.unwrap();
if !entry.path().is_file() {
continue;
}
prog.push_str(":");
prog.push_str(entry.path().to_str().unwrap());
support_libs.push(entry.path());
}
}
let mut test_client =
Command::new(self.config.remote_test_client.as_ref().unwrap());
test_client.args(&["run", &prog]).args(args).envs(env.clone());
test_client
.args(&["run", &support_libs.len().to_string(), &prog])
.args(support_libs)
.args(args)
.envs(env.clone());
self.compose_and_run(
test_client,
self.config.run_lib_path.to_str().unwrap(),
Expand Down
23 changes: 15 additions & 8 deletions src/tools/remote-test-client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,13 @@ fn main() {
args.next().map(|s| s.into()),
),
"push" => push(Path::new(&args.next().unwrap())),
"run" => run(args.next().unwrap(), args.collect()),
"run" => run(
args.next().and_then(|count| count.parse().ok()).unwrap(),
// the last required parameter must remain the executable
// path so that the client works as a cargo runner
args.next().unwrap(),
args.collect(),
),
"help" | "-h" | "--help" => help(),
cmd => {
println!("unknown command: {}", cmd);
Expand Down Expand Up @@ -197,12 +203,14 @@ fn push(path: &Path) {
println!("done pushing {:?}", path);
}

fn run(files: String, args: Vec<String>) {
fn run(support_lib_count: usize, exe: String, all_args: Vec<String>) {
let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or(DEFAULT_ADDR.to_string());
let client = t!(TcpStream::connect(device_address));
let mut client = BufWriter::new(client);
t!(client.write_all(b"run "));

let (support_libs, args) = all_args.split_at(support_lib_count);

// Send over the args
for arg in args {
t!(client.write_all(arg.as_bytes()));
Expand All @@ -227,9 +235,7 @@ fn run(files: String, args: Vec<String>) {
t!(client.write_all(&[0]));

// Send over support libraries
let mut files = files.split(':');
let exe = files.next().unwrap();
for file in files.map(Path::new) {
for file in support_libs.iter().map(Path::new) {
send(&file, &mut client);
}
t!(client.write_all(&[0]));
Expand Down Expand Up @@ -302,7 +308,8 @@ Usage: {0} <command> [<args>]
Sub-commands:
spawn-emulator <target> <server> <tmpdir> [rootfs] See below
push <path> Copy <path> to emulator
run <files> [args...] Run program on emulator
run <support_lib_count> <file> [support_libs...] [args...]
Run program on emulator
help Display help message

Spawning an emulator:
Expand All @@ -321,8 +328,8 @@ specified. The file at <path> is sent to this target.
Executing commands on a running emulator:

First the target emulator/adb session is connected to as for pushing files. Next
the colon separated list of <files> is pushed to the target. Finally, the first
file in <files> is executed in the emulator, preserving the current environment.
the <file> and any specified support libs are pushed to the target. Finally, the
<file> is executed in the emulator, preserving the current environment.
That command's status code is returned.
",
env::args().next().unwrap(),
Expand Down
83 changes: 60 additions & 23 deletions src/tools/remote-test-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,19 @@

#![deny(warnings)]

#[cfg(not(windows))]
use std::fs::Permissions;
#[cfg(not(windows))]
use std::os::unix::prelude::*;

use std::cmp;
use std::env;
use std::fs::{self, File, Permissions};
use std::fs::{self, File};
use std::io::prelude::*;
use std::io::{self, BufReader};
use std::net::{TcpListener, TcpStream};
use std::os::unix::prelude::*;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::process::{Command, ExitStatus, Stdio};
use std::str;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
Expand Down Expand Up @@ -72,21 +76,23 @@ fn main() {

let config = Config::parse_args();

let bind_addr = if cfg!(target_os = "android") || config.remote {
let bind_addr = if cfg!(target_os = "android") || cfg!(windows) || config.remote {
"0.0.0.0:12345"
} else {
"10.0.2.15:12345"
};

let (listener, work) = if cfg!(target_os = "android") {
(t!(TcpListener::bind(bind_addr)), "/data/tmp/work")
let listener = t!(TcpListener::bind(bind_addr));
let work: PathBuf = if cfg!(target_os = "android") {
"/data/tmp/work".into()
} else {
(t!(TcpListener::bind(bind_addr)), "/tmp/work")
let mut temp_dir = env::temp_dir();
temp_dir.push("work");
temp_dir
};
println!("listening!");

let work = Path::new(work);
t!(fs::create_dir_all(work));
t!(fs::create_dir_all(&work));

let lock = Arc::new(Mutex::new(()));

Expand All @@ -99,10 +105,11 @@ fn main() {
if &buf[..] == b"ping" {
t!(socket.write_all(b"pong"));
} else if &buf[..] == b"push" {
handle_push(socket, work);
handle_push(socket, &work);
} else if &buf[..] == b"run " {
let lock = lock.clone();
thread::spawn(move || handle_run(socket, work, &lock));
let work = work.clone();
thread::spawn(move || handle_run(socket, &work, &lock));
} else {
panic!("unknown command {:?}", buf);
}
Expand Down Expand Up @@ -196,17 +203,28 @@ fn handle_run(socket: TcpStream, work: &Path, lock: &Mutex<()>) {
let exe = recv(&path, &mut reader);

let mut cmd = Command::new(&exe);
for arg in args {
cmd.arg(arg);
}
for (k, v) in env {
cmd.env(k, v);
}
cmd.args(args);
cmd.envs(env);

// Support libraries were uploaded to `work` earlier, so make sure that's
// in `LD_LIBRARY_PATH`. Also include our own current dir which may have
// had some libs uploaded.
cmd.env("LD_LIBRARY_PATH", format!("{}:{}", work.display(), path.display()));
if cfg!(windows) {
// On windows, libraries are just searched in the executable directory,
// system directories, PWD, and PATH, in that order. PATH is the only one
// we can change for this.
cmd.env(
"PATH",
env::join_paths(
std::iter::once(work.to_owned())
.chain(std::iter::once(path.clone()))
.chain(env::split_paths(&env::var_os("PATH").unwrap())),
)
.unwrap(),
);
} else {
cmd.env("LD_LIBRARY_PATH", format!("{}:{}", work.display(), path.display()));
}

// Spawn the child and ferry over stdout/stderr to the socket in a framed
// fashion (poor man's style)
Expand All @@ -223,10 +241,9 @@ fn handle_run(socket: TcpStream, work: &Path, lock: &Mutex<()>) {

// Finally send over the exit status.
let status = t!(child.wait());
let (which, code) = match status.code() {
Some(n) => (0, n),
None => (1, status.signal().unwrap()),
};

let (which, code) = get_status_code(&status);

t!(socket.lock().unwrap().write_all(&[
which,
(code >> 24) as u8,
Expand All @@ -236,6 +253,19 @@ fn handle_run(socket: TcpStream, work: &Path, lock: &Mutex<()>) {
]));
}

#[cfg(not(windows))]
fn get_status_code(status: &ExitStatus) -> (u8, i32) {
match status.code() {
Some(n) => (0, n),
None => (1, status.signal().unwrap()),
}
}

#[cfg(windows)]
fn get_status_code(status: &ExitStatus) -> (u8, i32) {
(0, status.code().unwrap())
}

fn recv<B: BufRead>(dir: &Path, io: &mut B) -> PathBuf {
let mut filename = Vec::new();
t!(io.read_until(0, &mut filename));
Expand All @@ -253,10 +283,17 @@ fn recv<B: BufRead>(dir: &Path, io: &mut B) -> PathBuf {
let dst = dir.join(t!(str::from_utf8(&filename[..len])));
let amt = read_u32(io) as u64;
t!(io::copy(&mut io.take(amt), &mut t!(File::create(&dst))));
t!(fs::set_permissions(&dst, Permissions::from_mode(0o755)));
set_permissions(&dst);
dst
}

#[cfg(not(windows))]
fn set_permissions(path: &Path) {
t!(fs::set_permissions(&path, Permissions::from_mode(0o755)));
}
#[cfg(windows)]
fn set_permissions(_path: &Path) {}

fn my_copy(src: &mut dyn Read, which: u8, dst: &Mutex<dyn Write>) {
let mut b = [0; 1024];
loop {
Expand Down