Skip to content

Commit

Permalink
Mimic rustc's logic for finding custom target files.
Browse files Browse the repository at this point in the history
When a TARGET name isn't a known target, follow rustc's rules for
resolving it to a json file to read.
  • Loading branch information
sunfishcode committed Jun 12, 2018
1 parent dfed0c9 commit 5f067ce
Showing 1 changed file with 70 additions and 50 deletions.
120 changes: 70 additions & 50 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@

use serde_json::Value;
use std::env;
use std::ffi::OsString;
use std::fs::File;
use std::io::prelude::*;
use std::io::{self, Write};
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::str::FromStr;

extern crate serde_json;
Expand Down Expand Up @@ -37,61 +38,80 @@ mod parse_error {

use triple::{Endianness, PointerWidth, Triple};

/// Assuming `target` is a path to a custom target json config file, open it
/// and build a `Triple` using its contents.
fn read_target_from_file(path: &Path) -> Triple {
let mut file = File::open(path).expect("error opening target file");
let mut json = String::new();
file.read_to_string(&mut json)
.expect("error reading target file");

let v: Value = serde_json::from_str(&json).expect("error parsing target file as json");
let target = v["llvm-target"]
.as_str()
.expect("error parsing \"llvm-target\" as a string");
let triple = Triple::from_str(target).expect("error parsing host target");

// Check that the JSON describes a known target configuration.
//
// Unfortunately, none of Rust's "arch", "os", "env", nor "vendor"
// fields directly correspond to triple fields, so we can't easily
// check them.
if let Some(endian) = v["target-endian"].as_str() {
assert_eq!(
endian,
match triple.endianness().unwrap() {
Endianness::Little => "little",
Endianness::Big => "big",
},
"\"target-endian\" field disagrees with the target triple"
);
}
if let Some(pointer_width) = v["target-pointer-width"].as_str() {
assert_eq!(
pointer_width,
match triple.pointer_width().unwrap() {
PointerWidth::U16 => "16",
PointerWidth::U32 => "32",
PointerWidth::U64 => "64",
},
"\"target-pointer-width\" field disagrees with the target triple"
);
}

triple
}

/// Assuming `target` is a target identifier, search for an appropriate custom
/// target json config file in the way that rustc does, and then call
/// `read_target_from_file` on that.
fn read_target_from_file_in_path(target: &str) -> Triple {
let mut target_filename = target.to_owned();
target_filename.push_str(".json");
let target_basename = PathBuf::from(target_filename);
let target_path = env::var_os("RUST_TARGET_PATH").unwrap_or_else(|| OsString::new());
for dir in env::split_paths(&target_path) {
let p = dir.join(&target_basename);
if p.is_file() {
return read_target_from_file(&p);
}
}
panic!("can't find custom target {}", target);
}

fn main() {
let out_dir =
PathBuf::from(env::var("OUT_DIR").expect("The OUT_DIR environment variable must be set"));

let target = env::var("TARGET").expect("The TARGET environment variable must be set");

let triple = match Triple::from_str(&target) {
Ok(triple) => {
assert_eq!(target, triple.to_string(), "host is unrecognized");
triple
}
Err(_) => {
let mut file = File::open(&target).expect("error opening target file");
let mut json = String::new();
file.read_to_string(&mut json)
.expect("error reading target file");
let v: Value = serde_json::from_str(&json).expect("error parsing target file as json");
let target = v["llvm-target"]
.as_str()
.expect("error parsing \"llvm-target\" as a string");
let triple = Triple::from_str(target).expect("error parsing host target");
assert_eq!(
target,
triple.to_string(),
"host is unrecognized (as defined in target json file)"
);

// Check that the JSON describes a known target configuration.
//
// Unfortunately, none of Rust's "arch", "os", "env", nor "vendor"
// fields directly correspond to triple fields, so we can't easily
// check them.
if let Some(endian) = v["target-endian"].as_str() {
assert_eq!(
endian,
match triple.endianness().unwrap() {
Endianness::Little => "little",
Endianness::Big => "big",
},
"\"target-endian\" field disagrees with the target triple"
);
}
if let Some(pointer_width) = v["target-pointer-width"].as_str() {
assert_eq!(
pointer_width,
match triple.pointer_width().unwrap() {
PointerWidth::U16 => "16",
PointerWidth::U32 => "32",
PointerWidth::U64 => "64",
},
"\"target-pointer-width\" field disagrees with the target triple"
);
}

triple
// The following intends to match the logic in rustc.
let triple = if target.ends_with(".json") {
read_target_from_file(Path::new(&target))
} else {
match Triple::from_str(&target) {
Ok(triple) => triple,
Err(_) => read_target_from_file_in_path(&target),
}
};

Expand Down

0 comments on commit 5f067ce

Please sign in to comment.