]>
git.proxmox.com Git - rustc.git/blob - src/tools/qemu-test-client/src/main.rs
1 // Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 /// This is a small client program intended to pair with `qemu-test-server` in
12 /// this repository. This client connects to the server over TCP and is used to
13 /// push artifacts and run tests on the server instead of locally.
15 /// Here is also where we bake in the support to spawn the QEMU emulator as
20 use std
::io
::prelude
::*;
21 use std
::io
::{self, BufWriter}
;
22 use std
::net
::TcpStream
;
24 use std
::process
::{Command, Stdio}
;
26 use std
::time
::Duration
;
29 ($e
:expr
) => (match $e
{
31 Err(e
) => panic
!("{} failed with {}", stringify
!($e
), e
),
36 let mut args
= env
::args().skip(1);
38 match &args
.next().unwrap()[..] {
40 spawn_emulator(Path
::new(&args
.next().unwrap()),
41 Path
::new(&args
.next().unwrap()))
44 push(Path
::new(&args
.next().unwrap()))
47 run(args
.next().unwrap(), args
.collect())
49 cmd
=> panic
!("unknown command: {}", cmd
),
53 fn spawn_emulator(rootfs
: &Path
, tmpdir
: &Path
) {
54 // Generate a new rootfs image now that we've updated the test server
55 // executable. This is the equivalent of:
57 // find $rootfs -print 0 | cpio --null -o --format=newc > rootfs.img
58 let rootfs_img
= tmpdir
.join("rootfs.img");
59 let mut cmd
= Command
::new("cpio");
63 .stdin(Stdio
::piped())
64 .stdout(Stdio
::piped())
66 let mut child
= t
!(cmd
.spawn());
67 let mut stdin
= child
.stdin
.take().unwrap();
68 let rootfs
= rootfs
.to_path_buf();
69 thread
::spawn(move || add_files(&mut stdin
, &rootfs
, &rootfs
));
70 t
!(io
::copy(&mut child
.stdout
.take().unwrap(),
71 &mut t
!(File
::create(&rootfs_img
))));
72 assert
!(t
!(child
.wait()).success());
74 // Start up the emulator, in the background
75 let mut cmd
= Command
::new("qemu-system-arm");
76 cmd
.arg("-M").arg("vexpress-a15")
77 .arg("-m").arg("1024")
78 .arg("-kernel").arg("/tmp/zImage")
79 .arg("-initrd").arg(&rootfs_img
)
80 .arg("-dtb").arg("/tmp/vexpress-v2p-ca15-tc1.dtb")
81 .arg("-append").arg("console=ttyAMA0 root=/dev/ram rdinit=/sbin/init init=/sbin/init")
83 .arg("-redir").arg("tcp:12345::12345");
86 // Wait for the emulator to come online
88 let dur
= Duration
::from_millis(100);
89 if let Ok(mut client
) = TcpStream
::connect("127.0.0.1:12345") {
90 t
!(client
.set_read_timeout(Some(dur
)));
91 t
!(client
.set_write_timeout(Some(dur
)));
92 if client
.write_all(b
"ping").is_ok() {
94 if client
.read_exact(&mut b
).is_ok() {
102 fn add_files(w
: &mut Write
, root
: &Path
, cur
: &Path
) {
103 for entry
in t
!(cur
.read_dir()) {
104 let entry
= t
!(entry
);
105 let path
= entry
.path();
106 let to_print
= path
.strip_prefix(root
).unwrap();
107 t
!(write
!(w
, "{}\u{0}", to_print
.to_str().unwrap()));
108 if t
!(entry
.file_type()).is_dir() {
109 add_files(w
, root
, &path
);
115 fn push(path
: &Path
) {
116 let client
= t
!(TcpStream
::connect("127.0.0.1:12345"));
117 let mut client
= BufWriter
::new(client
);
118 t
!(client
.write_all(b
"push"));
119 t
!(client
.write_all(path
.file_name().unwrap().to_str().unwrap().as_bytes()));
120 t
!(client
.write_all(&[0]));
121 let mut file
= t
!(File
::open(path
));
122 t
!(io
::copy(&mut file
, &mut client
));
124 println
!("done pushing {:?}", path
);
127 fn run(files
: String
, args
: Vec
<String
>) {
128 let client
= t
!(TcpStream
::connect("127.0.0.1:12345"));
129 let mut client
= BufWriter
::new(client
);
130 t
!(client
.write_all(b
"run "));
132 // Send over the args
134 t
!(client
.write_all(arg
.as_bytes()));
135 t
!(client
.write_all(&[0]));
137 t
!(client
.write_all(&[0]));
139 // Send over env vars
140 for (k
, v
) in env
::vars() {
141 if k
!= "PATH" && k
!= "LD_LIBRARY_PATH" {
142 t
!(client
.write_all(k
.as_bytes()));
143 t
!(client
.write_all(&[0]));
144 t
!(client
.write_all(v
.as_bytes()));
145 t
!(client
.write_all(&[0]));
148 t
!(client
.write_all(&[0]));
150 // Send over support libraries
151 let mut files
= files
.split('
:'
);
152 let exe
= files
.next().unwrap();
153 for file
in files
.map(Path
::new
) {
154 t
!(client
.write_all(file
.file_name().unwrap().to_str().unwrap().as_bytes()));
155 t
!(client
.write_all(&[0]));
156 send(&file
, &mut client
);
158 t
!(client
.write_all(&[0]));
160 // Send over the client executable as the last piece
161 send(exe
.as_ref(), &mut client
);
163 println
!("uploaded {:?}, waiting for result", exe
);
165 // Ok now it's time to read all the output. We're receiving "frames"
166 // representing stdout/stderr, so we decode all that here.
167 let mut header
= [0; 5];
168 let mut stderr_done
= false;
169 let mut stdout_done
= false;
170 let mut client
= t
!(client
.into_inner());
171 let mut stdout
= io
::stdout();
172 let mut stderr
= io
::stderr();
173 while !stdout_done
|| !stderr_done
{
174 t
!(client
.read_exact(&mut header
));
175 let amt
= ((header
[1] as u64) << 24) |
176 ((header
[2] as u64) << 16) |
177 ((header
[3] as u64) << 8) |
178 ((header
[4] as u64) << 0);
183 t
!(io
::copy(&mut (&mut client
).take(amt
), &mut stdout
));
190 t
!(io
::copy(&mut (&mut client
).take(amt
), &mut stderr
));
196 // Finally, read out the exit status
197 let mut status
= [0; 5];
198 t
!(client
.read_exact(&mut status
));
199 let code
= ((status
[1] as i32) << 24) |
200 ((status
[2] as i32) << 16) |
201 ((status
[3] as i32) << 8) |
202 ((status
[4] as i32) << 0);
204 std
::process
::exit(code
);
206 println
!("died due to signal {}", code
);
207 std
::process
::exit(3);
211 fn send(path
: &Path
, dst
: &mut Write
) {
212 let mut file
= t
!(File
::open(&path
));
213 let amt
= t
!(file
.metadata()).len();
220 t
!(io
::copy(&mut file
, dst
));