Skip to content

Commit

Permalink
Don't require all build script output to be utf-8
Browse files Browse the repository at this point in the history
Build scripts often stream output of native build systems like cmake/make and
those aren't always guaranteed to produce utf-8 output. For example  German
MSVC cmake build has been reported to print non-utf-8 umlauts.

This commit instead only attempts to interpret each line as utf-8 rather than
the entire build script output. All non-utf-8 output is ignored.

Closes rust-lang#2556
  • Loading branch information
alexcrichton committed Apr 11, 2016
1 parent 10ddd7d commit 020db0d
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 9 deletions.
17 changes: 9 additions & 8 deletions src/cargo/ops/cargo_rustc/custom_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::str;
use std::sync::{Mutex, Arc};

use core::PackageId;
use util::{CargoResult, human, Human};
use util::{CargoResult, Human};
use util::{internal, ChainError, profile, paths};
use util::Freshness;

Expand Down Expand Up @@ -213,10 +213,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>)
// This is also the location where we provide feedback into the build
// state informing what variables were discovered via our script as
// well.
let output = try!(str::from_utf8(&output.stdout).map_err(|_| {
human("build script output was not valid utf-8")
}));
let parsed_output = try!(BuildOutput::parse(output, &pkg_name));
let parsed_output = try!(BuildOutput::parse(&output.stdout, &pkg_name));
build_state.insert(id, kind, parsed_output);
Ok(())
});
Expand Down Expand Up @@ -270,21 +267,25 @@ impl BuildState {

impl BuildOutput {
pub fn parse_file(path: &Path, pkg_name: &str) -> CargoResult<BuildOutput> {
let contents = try!(paths::read(path));
let contents = try!(paths::read_bytes(path));
BuildOutput::parse(&contents, pkg_name)
}

// Parses the output of a script.
// The `pkg_name` is used for error messages.
pub fn parse(input: &str, pkg_name: &str) -> CargoResult<BuildOutput> {
pub fn parse(input: &[u8], pkg_name: &str) -> CargoResult<BuildOutput> {
let mut library_paths = Vec::new();
let mut library_links = Vec::new();
let mut cfgs = Vec::new();
let mut metadata = Vec::new();
let mut rerun_if_changed = Vec::new();
let whence = format!("build script of `{}`", pkg_name);

for line in input.lines() {
for line in input.split(|b| *b == b'\n') {
let line = match str::from_utf8(line) {
Ok(line) => line.trim(),
Err(..) => continue,
};
let mut iter = line.splitn(2, ':');
if iter.next() != Some("cargo") {
// skip this line since it doesn't start with "cargo:"
Expand Down
13 changes: 12 additions & 1 deletion src/cargo/util/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ pub fn without_prefix<'a>(a: &'a Path, b: &'a Path) -> Option<&'a Path> {
}

pub fn read(path: &Path) -> CargoResult<String> {
(|| -> CargoResult<String> {
(|| -> CargoResult<_> {
let mut ret = String::new();
let mut f = try!(File::open(path));
try!(f.read_to_string(&mut ret));
Expand All @@ -78,6 +78,17 @@ pub fn read(path: &Path) -> CargoResult<String> {
})
}

pub fn read_bytes(path: &Path) -> CargoResult<Vec<u8>> {
(|| -> CargoResult<_> {
let mut ret = Vec::new();
let mut f = try!(File::open(path));
try!(f.read_to_end(&mut ret));
Ok(ret)
})().map_err(human).chain_error(|| {
human(format!("failed to read `{}`", path.display()))
})
}

pub fn write(path: &Path, contents: &[u8]) -> CargoResult<()> {
(|| -> CargoResult<()> {
let mut f = try!(File::create(path));
Expand Down
33 changes: 33 additions & 0 deletions tests/test_cargo_compile_custom_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1866,3 +1866,36 @@ test!(please_respect_the_dag {
{running} `rustc [..] -L native=foo -L native=bar[..]`
", running = RUNNING)));
});

test!(non_utf8_output {
let p = project("foo")
.file("Cargo.toml", r#"
[project]
name = "foo"
version = "0.5.0"
authors = []
build = "build.rs"
"#)
.file("build.rs", r#"
use std::io::prelude::*;
fn main() {
let mut out = std::io::stdout();
// print something that's not utf8
out.write_all(b"\xff\xff\n").unwrap();
// now print some cargo metadata that's utf8
println!("cargo:rustc-cfg=foo");
// now print more non-utf8
out.write_all(b"\xff\xff\n").unwrap();
}
"#)
.file("src/main.rs", r#"
#[cfg(foo)]
fn main() {}
"#);

assert_that(p.cargo_process("build").arg("-v"),
execs().with_status(0));
});

0 comments on commit 020db0d

Please sign in to comment.