xref: /wasmtime-44.0.1/cranelift/src/utils.rs (revision 98ef18a2)
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::Generic(err)) => return Err(err.into()),
68         Ok(()) => {}
69     }
70 
71     let mut words = flag_triple.trim().split_whitespace();
72     // Look for `target foo`.
73     if let Some(triple_name) = words.next() {
74         let triple = match Triple::from_str(triple_name) {
75             Ok(triple) => triple,
76             Err(parse_error) => return Err(parse_error.into()),
77         };
78 
79         let mut isa_builder = isa::lookup(triple).map_err(|err| match err {
80             isa::LookupError::SupportDisabled => {
81                 anyhow::anyhow!("support for triple '{}' is disabled", triple_name)
82             }
83             isa::LookupError::Unsupported => anyhow::anyhow!(
84                 "support for triple '{}' is not implemented yet",
85                 triple_name
86             ),
87         })?;
88 
89         // Try to parse system-wide unknown settings as target-specific settings.
90         parse_options(
91             unknown_settings.iter().map(|x| x.as_str()),
92             &mut isa_builder,
93             Location { line_number: 0 },
94         )
95         .map_err(ParseError::from)?;
96 
97         // Apply the ISA-specific settings to `isa_builder`.
98         parse_options(words, &mut isa_builder, Location { line_number: 0 })
99             .map_err(ParseError::from)?;
100 
101         Ok(OwnedFlagsOrIsa::Isa(
102             isa_builder.finish(settings::Flags::new(flag_builder))?,
103         ))
104     } else {
105         if !unknown_settings.is_empty() {
106             anyhow::bail!("unknown settings: '{}'", unknown_settings.join("', '"));
107         }
108         Ok(OwnedFlagsOrIsa::Flags(settings::Flags::new(flag_builder)))
109     }
110 }
111 
112 /// Iterate over all of the files passed as arguments, recursively iterating through directories.
113 pub fn iterate_files<'a>(files: &'a [PathBuf]) -> impl Iterator<Item = PathBuf> + 'a {
114     files
115         .iter()
116         .flat_map(WalkDir::new)
117         .filter(|f| match f {
118             Ok(d) => {
119                 // Filter out hidden files (starting with .).
120                 !d.file_name().to_str().map_or(false, |s| s.starts_with('.'))
121                     // Filter out directories.
122                     && !d.file_type().is_dir()
123             }
124             Err(e) => {
125                 println!("Unable to read file: {}", e);
126                 false
127             }
128         })
129         .map(|f| {
130             f.expect("this should not happen: we have already filtered out the errors")
131                 .into_path()
132         })
133 }
134