1 //! Utility functions. 2 3 use anyhow::Context; 4 use cranelift_codegen::isa; 5 use cranelift_codegen::isa::TargetIsa; 6 use cranelift_codegen::settings::{self, FlagsOrIsa}; 7 use cranelift_reader::{parse_options, Location, ParseError, ParseOptionError}; 8 use std::fs::File; 9 use std::io::{self, Read}; 10 use std::path::{Path, PathBuf}; 11 use std::str::FromStr; 12 use target_lexicon::Triple; 13 use walkdir::WalkDir; 14 15 /// Read an entire file into a string. 16 pub fn read_to_string<P: AsRef<Path>>(path: P) -> anyhow::Result<String> { 17 let mut buffer = String::new(); 18 let path = path.as_ref(); 19 if path == Path::new("-") { 20 let stdin = io::stdin(); 21 let mut stdin = stdin.lock(); 22 stdin 23 .read_to_string(&mut buffer) 24 .context("failed to read stdin to string")?; 25 } else { 26 let mut file = File::open(path)?; 27 file.read_to_string(&mut buffer) 28 .with_context(|| format!("failed to read {} to string", path.display()))?; 29 } 30 Ok(buffer) 31 } 32 33 /// Like `FlagsOrIsa`, but holds ownership. 34 pub enum OwnedFlagsOrIsa { 35 Flags(settings::Flags), 36 Isa(Box<dyn TargetIsa>), 37 } 38 39 impl OwnedFlagsOrIsa { 40 /// Produce a FlagsOrIsa reference. 41 pub fn as_fisa(&self) -> FlagsOrIsa { 42 match *self { 43 Self::Flags(ref flags) => FlagsOrIsa::from(flags), 44 Self::Isa(ref isa) => FlagsOrIsa::from(&**isa), 45 } 46 } 47 } 48 49 /// Parse "set" and "triple" commands. 50 pub fn parse_sets_and_triple( 51 flag_set: &[String], 52 flag_triple: &str, 53 ) -> anyhow::Result<OwnedFlagsOrIsa> { 54 let mut flag_builder = settings::builder(); 55 56 // Collect unknown system-wide settings, so we can try to parse them as target specific 57 // settings, if a target is defined. 58 let mut unknown_settings = Vec::new(); 59 match parse_options( 60 flag_set.iter().map(|x| x.as_str()), 61 &mut flag_builder, 62 Location { line_number: 0 }, 63 ) { 64 Err(ParseOptionError::UnknownFlag { name, .. }) => { 65 unknown_settings.push(name); 66 } 67 Err(ParseOptionError::UnknownValue { name, value, .. }) => { 68 unknown_settings.push(format!("{}={}", name, value)); 69 } 70 Err(ParseOptionError::Generic(err)) => return Err(err.into()), 71 Ok(()) => {} 72 } 73 74 let mut words = flag_triple.trim().split_whitespace(); 75 // Look for `target foo`. 76 if let Some(triple_name) = words.next() { 77 let triple = match Triple::from_str(triple_name) { 78 Ok(triple) => triple, 79 Err(parse_error) => return Err(parse_error.into()), 80 }; 81 82 let mut isa_builder = isa::lookup(triple).map_err(|err| match err { 83 isa::LookupError::SupportDisabled => { 84 anyhow::anyhow!("support for triple '{}' is disabled", triple_name) 85 } 86 isa::LookupError::Unsupported => anyhow::anyhow!( 87 "support for triple '{}' is not implemented yet", 88 triple_name 89 ), 90 })?; 91 92 // Try to parse system-wide unknown settings as target-specific settings. 93 parse_options( 94 unknown_settings.iter().map(|x| x.as_str()), 95 &mut isa_builder, 96 Location { line_number: 0 }, 97 ) 98 .map_err(ParseError::from)?; 99 100 // Apply the ISA-specific settings to `isa_builder`. 101 parse_options(words, &mut isa_builder, Location { line_number: 0 }) 102 .map_err(ParseError::from)?; 103 104 Ok(OwnedFlagsOrIsa::Isa( 105 isa_builder.finish(settings::Flags::new(flag_builder))?, 106 )) 107 } else { 108 if !unknown_settings.is_empty() { 109 anyhow::bail!("unknown settings: '{}'", unknown_settings.join("', '")); 110 } 111 Ok(OwnedFlagsOrIsa::Flags(settings::Flags::new(flag_builder))) 112 } 113 } 114 115 /// Iterate over all of the files passed as arguments, recursively iterating through directories. 116 pub fn iterate_files<'a>(files: &'a [PathBuf]) -> impl Iterator<Item = PathBuf> + 'a { 117 files 118 .iter() 119 .flat_map(WalkDir::new) 120 .filter(|f| match f { 121 Ok(d) => { 122 // Filter out hidden files (starting with .). 123 !d.file_name().to_str().map_or(false, |s| s.starts_with('.')) 124 // Filter out directories. 125 && !d.file_type().is_dir() 126 } 127 Err(e) => { 128 println!("Unable to read file: {}", e); 129 false 130 } 131 }) 132 .map(|f| { 133 f.expect("this should not happen: we have already filtered out the errors") 134 .into_path() 135 }) 136 } 137