selfe_config/model/
mod.rs

1use std::collections::BTreeMap;
2use std::fmt::{self, Display};
3use std::path::{Path, PathBuf};
4use std::str::FromStr;
5
6pub mod deserialization;
7pub mod serialization;
8
9pub use deserialization::ImportError;
10
11const DEFAULT_CONFIG_CONTENT: &str = include_str!("../default_config.toml");
12
13/// Produce a unique instance of the default config content
14pub fn get_default_config() -> full::Full {
15    DEFAULT_CONFIG_CONTENT
16        .parse()
17        .map_err(|e| format!("{}", e))
18        .expect("Default config content should always be valid.")
19}
20
21/// An enum-ified version of the rust's notion of arch, the first part of a rust target triple
22#[derive(Copy, Clone)]
23pub enum RustArch {
24    Aarch64,
25    Arm,
26    Armebv7r,
27    Armv5te,
28    Armv7,
29    Armv7r,
30    Armv7s,
31    Asmjs,
32    I386,
33    I586,
34    I686,
35    Mips,
36    Mips64,
37    Mips64el,
38    Mipsel,
39    Nvptx64,
40    Powerpc,
41    Powerpc64,
42    Powerpc64le,
43    Riscv32imac,
44    Riscv32imc,
45    Riscv64gc,
46    Riscv64imac,
47    S390x,
48    Sparc64,
49    Sparcv9,
50    Thumbv6m,
51    Thumbv7em,
52    Thumbv7m,
53    Thumbv7neon,
54    Thumbv8mmain,
55    Wasm32,
56    X86_64,
57}
58
59impl FromStr for RustArch {
60    type Err = String;
61
62    fn from_str(s: &str) -> Result<Self, Self::Err> {
63        match s {
64            "aarch64" => Ok(RustArch::Aarch64),
65            "arm" => Ok(RustArch::Arm),
66            "armebv7r" => Ok(RustArch::Armebv7r),
67            "armv5te" => Ok(RustArch::Armv5te),
68            "armv7" => Ok(RustArch::Armv7),
69            "armv7r" => Ok(RustArch::Armv7r),
70            "armv7s" => Ok(RustArch::Armv7s),
71            "asmjs" => Ok(RustArch::Asmjs),
72            "i386" => Ok(RustArch::I386),
73            "i586" => Ok(RustArch::I586),
74            "i686" => Ok(RustArch::I686),
75            "mips" => Ok(RustArch::Mips),
76            "mips64" => Ok(RustArch::Mips64),
77            "mips64el" => Ok(RustArch::Mips64el),
78            "mipsel" => Ok(RustArch::Mipsel),
79            "nvptx64" => Ok(RustArch::Nvptx64),
80            "powerpc" => Ok(RustArch::Powerpc),
81            "powerpc64" => Ok(RustArch::Powerpc64),
82            "powerpc64le" => Ok(RustArch::Powerpc64le),
83            "riscv32imac" => Ok(RustArch::Riscv32imac),
84            "riscv32imc" => Ok(RustArch::Riscv32imc),
85            "riscv64gc" => Ok(RustArch::Riscv64gc),
86            "riscv64imac" => Ok(RustArch::Riscv32imc),
87            "s390x" => Ok(RustArch::S390x),
88            "sparc64" => Ok(RustArch::Sparc64),
89            "sparcv9" => Ok(RustArch::Sparcv9),
90            "thumbv6m" => Ok(RustArch::Thumbv6m),
91            "thumbv7em" => Ok(RustArch::Thumbv7em),
92            "thumbv7m" => Ok(RustArch::Thumbv7m),
93            "thumbv7neon" => Ok(RustArch::Thumbv7neon),
94            "thumbv8m.main" => Ok(RustArch::Thumbv8mmain),
95            "wasm32" => Ok(RustArch::Wasm32),
96            "x86_64" => Ok(RustArch::X86_64),
97            _ => Err("Unrecognized rust arch".to_string()),
98        }
99    }
100}
101
102///  This is sel4's notion of 'sel4_arch'
103#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
104pub enum SeL4Arch {
105    Aarch32,
106    Aarch64,
107    ArmHyp,
108    Ia32,
109    X86_64,
110    Riscv32,
111    Riscv64,
112}
113
114impl FromStr for SeL4Arch {
115    type Err = String;
116
117    fn from_str(s: &str) -> Result<Self, Self::Err> {
118        match s {
119            "aarch32" => Ok(SeL4Arch::Aarch32),
120            "aarch64" => Ok(SeL4Arch::Aarch64),
121            "arm_hyp" => Ok(SeL4Arch::ArmHyp),
122            "ia32" => Ok(SeL4Arch::Ia32),
123            "riscv32" => Ok(SeL4Arch::Riscv32),
124            "riscv64" => Ok(SeL4Arch::Riscv64),
125            "x86_64" => Ok(SeL4Arch::X86_64),
126            _ => Err("Unrecognized sel4_arch".to_string()),
127        }
128    }
129}
130
131impl SeL4Arch {
132    /// Create an Arch from the first part of a rust target triple
133    pub fn from_rust_arch(rust_arch: RustArch) -> Option<SeL4Arch> {
134        match rust_arch {
135            RustArch::Aarch64 => Some(SeL4Arch::Aarch64),
136
137            RustArch::Arm
138            | RustArch::Armebv7r
139            | RustArch::Armv7
140            | RustArch::Armv7r
141            | RustArch::Armv7s => Some(SeL4Arch::Aarch32),
142
143            RustArch::I386 | RustArch::I586 | RustArch::I686 => Some(SeL4Arch::Ia32),
144
145            RustArch::Riscv32imac | RustArch::Riscv32imc => Some(SeL4Arch::Riscv32),
146
147            RustArch::Riscv64gc | RustArch::Riscv64imac => Some(SeL4Arch::Riscv64),
148
149            RustArch::Thumbv6m
150            | RustArch::Thumbv7em
151            | RustArch::Thumbv7m
152            | RustArch::Thumbv7neon => Some(SeL4Arch::Aarch32),
153
154            RustArch::Thumbv8mmain => Some(SeL4Arch::Aarch64),
155
156            RustArch::X86_64 => Some(SeL4Arch::X86_64),
157            _ => None,
158        }
159    }
160}
161
162impl Display for SeL4Arch {
163    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164        let s = match self {
165            SeL4Arch::Aarch32 => "aarch32",
166            SeL4Arch::Aarch64 => "aarch64",
167            SeL4Arch::ArmHyp => "arm_hyp",
168            SeL4Arch::Ia32 => "ia32",
169            SeL4Arch::X86_64 => "x86_64",
170            SeL4Arch::Riscv32 => "riscv32",
171            SeL4Arch::Riscv64 => "riscv64",
172        };
173        write!(f, "{}", s)
174    }
175}
176
177/// This is sel4'x notion of 'arch'
178#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
179pub enum Arch {
180    Arm,
181    X86,
182    Riscv,
183}
184
185impl FromStr for Arch {
186    type Err = String;
187
188    fn from_str(s: &str) -> Result<Self, Self::Err> {
189        match s {
190            "arm" => Ok(Arch::Arm),
191            "x86" => Ok(Arch::X86),
192            "riscv" => Ok(Arch::Riscv),
193            _ => Err("Unrecognized arch".to_string()),
194        }
195    }
196}
197
198impl Arch {
199    pub fn from_sel4_arch(sel4_arch: SeL4Arch) -> Arch {
200        match sel4_arch {
201            SeL4Arch::Aarch32 | SeL4Arch::Aarch64 | SeL4Arch::ArmHyp => Arch::Arm,
202            SeL4Arch::Ia32 | SeL4Arch::X86_64 => Arch::X86,
203            SeL4Arch::Riscv32 | SeL4Arch::Riscv64 => Arch::Riscv,
204        }
205    }
206
207    /// Create an Arch from the first part of a rust target triple
208    pub fn from_rust_arch(rust_arch: RustArch) -> Option<Arch> {
209        SeL4Arch::from_rust_arch(rust_arch).map(Arch::from_sel4_arch)
210    }
211}
212
213impl Display for Arch {
214    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
215        let s = match self {
216            Arch::Arm => "arm",
217            Arch::X86 => "x86",
218            Arch::Riscv => "riscv",
219        };
220        write!(f, "{}", s)
221    }
222}
223
224/// This is sel4's platform, which we pass around in SEL4_PLATFORM
225#[derive(Clone, Debug, Eq, PartialEq, Hash)]
226pub struct Platform(pub String);
227impl Display for Platform {
228    fn fmt(&self, mut f: &mut fmt::Formatter) -> fmt::Result {
229        self.0.fmt(&mut f)
230    }
231}
232
233#[derive(Clone, Debug, PartialOrd, PartialEq, Hash)]
234pub enum SingleValue {
235    String(String),
236    Integer(i64),
237    Boolean(bool),
238}
239
240#[derive(Debug, Clone, Eq, PartialEq, Hash)]
241pub struct SeL4Sources {
242    pub kernel: RepoSource,
243    pub tools: RepoSource,
244    pub util_libs: RepoSource,
245}
246
247impl SeL4Sources {
248    fn relative_to<P: AsRef<Path>>(&self, base_dir: &Option<P>) -> Self {
249        SeL4Sources {
250            kernel: self.kernel.relative_to(base_dir),
251            tools: self.tools.relative_to(base_dir),
252            util_libs: self.util_libs.relative_to(base_dir),
253        }
254    }
255}
256
257#[derive(Debug, Clone, Eq, PartialEq, Hash)]
258pub enum RepoSource {
259    LocalPath(PathBuf),
260    RemoteGit { url: String, target: GitTarget },
261}
262
263impl RepoSource {
264    fn relative_to<P: AsRef<Path>>(&self, base_dir: &Option<P>) -> Self {
265        match self {
266            RepoSource::LocalPath(p) => RepoSource::LocalPath(p.relative_to(base_dir)),
267            s => s.clone(),
268        }
269    }
270}
271
272#[derive(Debug, Clone, Eq, PartialEq, Hash)]
273pub enum GitTarget {
274    Branch(String),
275    Rev(String),
276    Tag(String),
277}
278
279impl GitTarget {
280    pub fn kind(&self) -> &'static str {
281        match self {
282            GitTarget::Branch(_) => "branch",
283            GitTarget::Rev(_) => "rev",
284            GitTarget::Tag(_) => "tag",
285        }
286    }
287    pub fn value(&self) -> &str {
288        match self {
289            GitTarget::Branch(s) | GitTarget::Rev(s) | GitTarget::Tag(s) => s,
290        }
291    }
292}
293
294pub mod full {
295    use super::*;
296    use std::collections::btree_map::BTreeMap;
297
298    #[derive(Debug, Clone, PartialEq)]
299    pub struct Full {
300        pub sel4: SeL4,
301        pub build: BTreeMap<String, PlatformBuild>,
302        pub metadata: Metadata,
303    }
304
305    #[derive(Debug, Clone, PartialEq)]
306    pub struct SeL4 {
307        pub sources: SeL4Sources,
308        pub config: Config,
309    }
310
311    #[derive(Debug, Clone, Eq, PartialEq, Default, Hash)]
312    pub struct PlatformBuild {
313        pub cross_compiler_prefix: Option<String>,
314        pub toolchain_dir: Option<PathBuf>,
315        pub debug_build_profile: Option<PlatformBuildProfile>,
316        pub release_build_profile: Option<PlatformBuildProfile>,
317    }
318
319    #[derive(Debug, Clone, Eq, PartialEq, Default, Hash)]
320    pub struct PlatformBuildProfile {
321        pub make_root_task: Option<String>,
322        pub root_task_image: PathBuf,
323    }
324
325    impl SeL4 {
326        pub fn new(sources: SeL4Sources, config: Config) -> Self {
327            SeL4 { sources, config }
328        }
329    }
330
331    pub type Config = PropertiesTree;
332    pub type Metadata = PropertiesTree;
333
334    /// A repeated structure that includes common/shared properties,
335    /// two optional debug and release sets of properties
336    /// and a named bag of bags of properties.
337    #[derive(Debug, Default, Clone, PartialEq)]
338    pub struct PropertiesTree {
339        pub shared: BTreeMap<String, SingleValue>,
340        pub debug: BTreeMap<String, SingleValue>,
341        pub release: BTreeMap<String, SingleValue>,
342        pub contextual: BTreeMap<String, BTreeMap<String, SingleValue>>,
343    }
344}
345
346trait RelativePath {
347    // If self is relative, and a base is supplied, evaluate self relative to
348    // the base. Otherwise hand back self.
349    fn relative_to<P: AsRef<Path>>(&self, base: &Option<P>) -> PathBuf;
350}
351
352impl RelativePath for Path {
353    fn relative_to<P: AsRef<Path>>(&self, base: &Option<P>) -> PathBuf {
354        if self.is_relative() {
355            match base {
356                Some(p) => p.as_ref().join(self),
357                None => self.to_path_buf(),
358            }
359        } else {
360            self.to_path_buf()
361        }
362    }
363}
364
365pub mod contextualized {
366    use super::*;
367
368    #[derive(Debug, Clone, PartialEq, Hash)]
369    pub struct Contextualized {
370        pub sel4_sources: SeL4Sources,
371        pub context: Context,
372        pub sel4_config: BTreeMap<String, SingleValue>,
373        pub build: Build,
374        pub metadata: BTreeMap<String, SingleValue>,
375    }
376
377    #[derive(Debug, Clone, Eq, PartialEq, Default, Hash)]
378    pub struct Build {
379        pub cross_compiler_prefix: Option<String>,
380        pub toolchain_dir: Option<PathBuf>,
381        pub root_task: Option<RootTask>,
382    }
383
384    #[derive(Debug, Clone, Eq, PartialEq, Default, Hash)]
385    pub struct RootTask {
386        pub make_command: Option<String>,
387        pub image_path: PathBuf,
388    }
389
390    #[derive(Debug, Clone, PartialEq, Hash)]
391    pub struct Context {
392        pub platform: Platform,
393        pub is_debug: bool,
394        pub base_dir: Option<PathBuf>,
395        pub arch: Arch,
396        pub sel4_arch: SeL4Arch,
397    }
398
399    impl Contextualized {
400        pub fn from_str(
401            source_toml: &str,
402            arch: Arch,
403            sel4_arch: SeL4Arch,
404            is_debug: bool,
405            platform: Platform,
406            base_dir: Option<&Path>,
407        ) -> Result<Contextualized, ImportError> {
408            let f: full::Full = source_toml.parse()?;
409            Self::from_full(&f, arch, sel4_arch, is_debug, platform, base_dir)
410        }
411
412        pub fn from_full(
413            f: &full::Full,
414            arch: Arch,
415            sel4_arch: SeL4Arch,
416            is_debug: bool,
417            platform: Platform,
418            base_dir: Option<&Path>,
419        ) -> Result<Contextualized, ImportError> {
420            let context = Context {
421                platform: platform.clone(),
422                arch,
423                sel4_arch,
424                is_debug,
425                base_dir: base_dir.map(Path::to_path_buf),
426            };
427            Contextualized::from_full_context(&f, context)
428        }
429
430        pub fn from_full_context(
431            f: &full::Full,
432            context: Context,
433        ) -> Result<Contextualized, ImportError> {
434            let platform_build = f
435                .build
436                .get(&context.platform.to_string())
437                .ok_or_else(|| ImportError::NoBuildSupplied {
438                    platform: context.platform.to_string(),
439                    profile: if context.is_debug {
440                        "debug"
441                    } else {
442                        "release "
443                    },
444                })?
445                .clone();
446            let build_profile = if context.is_debug {
447                platform_build.debug_build_profile
448            } else {
449                platform_build.release_build_profile
450            };
451            let root_task = build_profile.map(|bp| RootTask {
452                make_command: bp.make_root_task,
453                image_path: bp.root_task_image.relative_to(&context.base_dir),
454            });
455            let build = Build {
456                cross_compiler_prefix: platform_build.cross_compiler_prefix,
457                toolchain_dir: platform_build
458                    .toolchain_dir
459                    .map(|p| p.relative_to(&context.base_dir)),
460                root_task,
461            };
462
463            fn resolve_context(
464                tree: &full::PropertiesTree,
465                context: &Context,
466            ) -> BTreeMap<String, SingleValue> {
467                let mut flat_properties = tree.shared.clone();
468                if context.is_debug {
469                    flat_properties.extend(tree.debug.clone())
470                } else {
471                    flat_properties.extend(tree.release.clone())
472                }
473
474                if let Some(arch_props) = tree.contextual.get(&context.arch.to_string()) {
475                    flat_properties.extend(arch_props.clone());
476                }
477                if let Some(sel4_arch_props) = tree.contextual.get(&context.sel4_arch.to_string()) {
478                    flat_properties.extend(sel4_arch_props.clone());
479                }
480                if let Some(platform_props) = tree.contextual.get(&context.platform.to_string()) {
481                    flat_properties.extend(platform_props.clone());
482                }
483                flat_properties
484            }
485
486            let sel4_config = resolve_context(&f.sel4.config, &context);
487            let metadata = resolve_context(&f.metadata, &context);
488
489            let sel4_sources = f.sel4.sources.relative_to(&context.base_dir);
490
491            Ok(Contextualized {
492                sel4_sources,
493                context,
494                sel4_config,
495                build,
496                metadata,
497            })
498        }
499    }
500}
501
502#[cfg(test)]
503mod tests {
504    use super::*;
505    use std::path::PathBuf;
506
507    impl full::Full {
508        fn empty() -> Self {
509            full::Full {
510                sel4: full::SeL4 {
511                    sources: SeL4Sources {
512                        kernel: RepoSource::LocalPath(PathBuf::from(".")),
513                        tools: RepoSource::LocalPath(PathBuf::from(".")),
514                        util_libs: RepoSource::LocalPath(PathBuf::from(".")),
515                    },
516                    config: Default::default(),
517                },
518                build: Default::default(),
519                metadata: Default::default(),
520            }
521        }
522    }
523
524    #[test]
525    fn default_content_is_valid() {
526        let f: full::Full = get_default_config();
527        // Spot check a known piece of the default config content
528        assert_eq!(
529            RepoSource::RemoteGit {
530                url: "https://github.com/seL4/seL4".to_string(),
531                target: GitTarget::Rev("4d0f02c029560cae0e8d93727eb17d58bcecc2ac".to_string())
532            },
533            f.sel4.sources.kernel
534        )
535    }
536
537    #[test]
538    fn override_default_platform_contextualization() {
539        let mut f = full::Full::empty();
540        let expected = Platform("sabre".to_owned());
541        f.build.insert(
542            expected.to_string(),
543            full::PlatformBuild {
544                cross_compiler_prefix: None,
545                toolchain_dir: None,
546                debug_build_profile: None,
547                release_build_profile: Some(full::PlatformBuildProfile {
548                    make_root_task: Some("cmake".to_string()),
549                    root_task_image: PathBuf::from("over_here"),
550                }),
551            },
552        );
553        let c = contextualized::Contextualized::from_full(
554            &f,
555            Arch::Arm,
556            SeL4Arch::Aarch32,
557            false,
558            expected.clone(),
559            None,
560        )
561        .unwrap();
562        assert_eq!(expected, c.context.platform);
563        assert_eq!(false, c.context.is_debug);
564        assert_eq!(Arch::Arm, c.context.arch);
565        assert_eq!(SeL4Arch::Aarch32, c.context.sel4_arch);
566        assert_eq!(
567            "cmake",
568            c.build
569                .root_task
570                .as_ref()
571                .unwrap()
572                .make_command
573                .as_ref()
574                .unwrap()
575        );
576        assert_eq!(
577            PathBuf::from("over_here"),
578            c.build.root_task.unwrap().image_path
579        );
580    }
581}