2022-08-04 13:20:37 +03:00
// 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`.
2022-08-04 13:16:44 +03:00
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 " ) ;
2022-08-04 13:20:37 +03:00
} 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 ) ;
}