diff options
Diffstat (limited to 'scripts/generate_rust_target.rs')
| -rw-r--r-- | scripts/generate_rust_target.rs | 182 | 
1 files changed, 182 insertions, 0 deletions
| diff --git a/scripts/generate_rust_target.rs b/scripts/generate_rust_target.rs new file mode 100644 index 000000000000..3c6cbe2b278d --- /dev/null +++ b/scripts/generate_rust_target.rs @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! The custom target specification file generator for `rustc`. +//! +//! To configure a target from scratch, a JSON-encoded file has to be passed +//! to `rustc` (introduced in [RFC 131]). These options and the file itself are +//! unstable. Eventually, `rustc` should provide a way to do this in a stable +//! manner. For instance, via command-line arguments. Therefore, this file +//! should avoid using keys which can be set via `-C` or `-Z` options. +//! +//! [RFC 131]: https://rust-lang.github.io/rfcs/0131-target-specification.html + +use std::{ +    collections::HashMap, +    fmt::{Display, Formatter, Result}, +    io::BufRead, +}; + +enum Value { +    Boolean(bool), +    Number(i32), +    String(String), +    Object(Object), +} + +type Object = Vec<(String, Value)>; + +/// Minimal "almost JSON" generator (e.g. no `null`s, no arrays, no escaping), +/// enough for this purpose. +impl Display for Value { +    fn fmt(&self, formatter: &mut Formatter<'_>) -> Result { +        match self { +            Value::Boolean(boolean) => write!(formatter, "{}", boolean), +            Value::Number(number) => write!(formatter, "{}", number), +            Value::String(string) => write!(formatter, "\"{}\"", string), +            Value::Object(object) => { +                formatter.write_str("{")?; +                if let [ref rest @ .., ref last] = object[..] { +                    for (key, value) in rest { +                        write!(formatter, "\"{}\": {},", key, value)?; +                    } +                    write!(formatter, "\"{}\": {}", last.0, last.1)?; +                } +                formatter.write_str("}") +            } +        } +    } +} + +struct TargetSpec(Object); + +impl TargetSpec { +    fn new() -> TargetSpec { +        TargetSpec(Vec::new()) +    } +} + +trait Push<T> { +    fn push(&mut self, key: &str, value: T); +} + +impl Push<bool> for TargetSpec { +    fn push(&mut self, key: &str, value: bool) { +        self.0.push((key.to_string(), Value::Boolean(value))); +    } +} + +impl Push<i32> for TargetSpec { +    fn push(&mut self, key: &str, value: i32) { +        self.0.push((key.to_string(), Value::Number(value))); +    } +} + +impl Push<String> for TargetSpec { +    fn push(&mut self, key: &str, value: String) { +        self.0.push((key.to_string(), Value::String(value))); +    } +} + +impl Push<&str> for TargetSpec { +    fn push(&mut self, key: &str, value: &str) { +        self.push(key, value.to_string()); +    } +} + +impl Push<Object> for TargetSpec { +    fn push(&mut self, key: &str, value: Object) { +        self.0.push((key.to_string(), Value::Object(value))); +    } +} + +impl Display for TargetSpec { +    fn fmt(&self, formatter: &mut Formatter<'_>) -> Result { +        // We add some newlines for clarity. +        formatter.write_str("{\n")?; +        if let [ref rest @ .., ref last] = self.0[..] { +            for (key, value) in rest { +                write!(formatter, "    \"{}\": {},\n", key, value)?; +            } +            write!(formatter, "    \"{}\": {}\n", last.0, last.1)?; +        } +        formatter.write_str("}") +    } +} + +struct KernelConfig(HashMap<String, String>); + +impl KernelConfig { +    /// Parses `include/config/auto.conf` from `stdin`. +    fn from_stdin() -> KernelConfig { +        let mut result = HashMap::new(); + +        let stdin = std::io::stdin(); +        let mut handle = stdin.lock(); +        let mut line = String::new(); + +        loop { +            line.clear(); + +            if handle.read_line(&mut line).unwrap() == 0 { +                break; +            } + +            if line.starts_with('#') { +                continue; +            } + +            let (key, value) = line.split_once('=').expect("Missing `=` in line."); +            result.insert(key.to_string(), value.trim_end_matches('\n').to_string()); +        } + +        KernelConfig(result) +    } + +    /// Does the option exist in the configuration (any value)? +    /// +    /// The argument must be passed without the `CONFIG_` prefix. +    /// This avoids repetition and it also avoids `fixdep` making us +    /// depend on it. +    fn has(&self, option: &str) -> bool { +        let option = "CONFIG_".to_owned() + option; +        self.0.contains_key(&option) +    } +} + +fn main() { +    let cfg = KernelConfig::from_stdin(); +    let mut ts = TargetSpec::new(); + +    // `llvm-target`s are taken from `scripts/Makefile.clang`. +    if cfg.has("X86_64") { +        ts.push("arch", "x86_64"); +        ts.push( +            "data-layout", +            "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", +        ); +        let mut features = "-3dnow,-3dnowa,-mmx,+soft-float".to_string(); +        if cfg.has("RETPOLINE") { +            features += ",+retpoline-external-thunk"; +        } +        ts.push("features", features); +        ts.push("llvm-target", "x86_64-linux-gnu"); +        ts.push("target-pointer-width", "64"); +    } else { +        panic!("Unsupported architecture"); +    } + +    ts.push("emit-debug-gdb-scripts", false); +    ts.push("frame-pointer", "may-omit"); +    ts.push( +        "stack-probes", +        vec![("kind".to_string(), Value::String("none".to_string()))], +    ); + +    // Everything else is LE, whether `CPU_LITTLE_ENDIAN` is declared or not +    // (e.g. x86). It is also `rustc`'s default. +    if cfg.has("CPU_BIG_ENDIAN") { +        ts.push("target-endian", "big"); +    } + +    println!("{}", ts); +} |