From d1127df296aa7871555293e324d125e6d8a843e1 Mon Sep 17 00:00:00 2001 From: John Turner Date: Sat, 29 Nov 2025 20:43:28 +0000 Subject: [PATCH] impl profile evaluation --- check_commands.txt | 2 +- src/repo/mod.rs | 12 +- src/repo/profile/make_defaults/mod.rs | 135 ++++++ src/repo/profile/make_defaults/parsers.rs | 88 ++++ src/repo/profile/mod.rs | 157 +++++++ src/repo/profile/package/mod.rs | 95 +++++ src/repo/profile/package/parsers.rs | 13 + src/repo/profile/package_use/mod.rs | 125 ++++++ src/repo/profile/package_use/parsers.rs | 36 ++ src/repo/profile/packages/mod.rs | 75 ++++ src/repo/profile/packages/parsers.rs | 14 + src/repo/profile/parsers.rs | 35 ++ src/repo/profile/useflags/mod.rs | 94 +++++ tests/meson.build | 2 + tests/profile/meson.build | 1 + tests/profile/read_all_profiles.rs | 483 ++++++++++++++++++++++ 16 files changed, 1365 insertions(+), 2 deletions(-) create mode 100644 src/repo/profile/make_defaults/mod.rs create mode 100644 src/repo/profile/make_defaults/parsers.rs create mode 100644 src/repo/profile/mod.rs create mode 100644 src/repo/profile/package/mod.rs create mode 100644 src/repo/profile/package/parsers.rs create mode 100644 src/repo/profile/package_use/mod.rs create mode 100644 src/repo/profile/package_use/parsers.rs create mode 100644 src/repo/profile/packages/mod.rs create mode 100644 src/repo/profile/packages/parsers.rs create mode 100644 src/repo/profile/parsers.rs create mode 100644 src/repo/profile/useflags/mod.rs create mode 100644 tests/profile/meson.build create mode 100644 tests/profile/read_all_profiles.rs diff --git a/check_commands.txt b/check_commands.txt index 1d1b424..8e6ff5c 100644 --- a/check_commands.txt +++ b/check_commands.txt @@ -1,4 +1,4 @@ /usr/bin/meson format --recursive --check-only rustfmt --edition 2024 --check $(find src -type f -name '*.rs') ninja clippy -C build -meson test unittests '*repo*' '*porthole*' -C build +meson test unittests '*repo*' '*porthole*' '*profile*' -C build diff --git a/src/repo/mod.rs b/src/repo/mod.rs index 46dd895..eb769f8 100644 --- a/src/repo/mod.rs +++ b/src/repo/mod.rs @@ -10,11 +10,15 @@ use mon::{Parser, ParserIter, ascii_whitespace1, input::InputIter, tag}; use crate::{ Parseable, atom::{self, Atom}, - repo::ebuild::{Depend, Eapi, Ebuild, Eclass, License, SrcUri}, + repo::{ + ebuild::{Depend, Eapi, Ebuild, Eclass, License, SrcUri}, + profile::Profile, + }, useflag::IUseFlag, }; pub mod ebuild; +pub mod profile; #[derive(Debug, thiserror::Error)] pub enum Error { @@ -26,6 +30,8 @@ pub enum Error { Unicode(PathBuf), #[error("parser error: {0}")] Parser(String), + #[error("profile error: {0}")] + Profile(profile::Error), } #[derive(Debug, Clone, Get)] @@ -62,6 +68,10 @@ impl Repo { fs::read_dir(&path).map_err(|e| Error::Io(path, e))?, )) } + + pub fn evaluate_profile>(&self, path: P) -> Result { + Profile::evaluate(self.path.join("profiles").join(path)).map_err(Error::Profile) + } } impl Category { diff --git a/src/repo/profile/make_defaults/mod.rs b/src/repo/profile/make_defaults/mod.rs new file mode 100644 index 0000000..334f165 --- /dev/null +++ b/src/repo/profile/make_defaults/mod.rs @@ -0,0 +1,135 @@ +use std::{ + collections::{HashMap, HashSet}, + fs, io, + path::{Path, PathBuf}, +}; + +use mon::{Parser, ParserIter, ascii_whitespace1, input::InputIter}; + +use crate::{ + Parseable, + repo::profile::{LineBasedFileExpr, Profile}, +}; + +mod parsers; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("{0}: io error: {1}")] + Io(PathBuf, io::Error), + #[error("parser error: {0}")] + Parser(String), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +struct Key(String); + +#[derive(Debug, Clone)] +struct Literal(String); + +#[derive(Debug, Clone)] +struct Interpolation(String); + +#[derive(Debug, Clone)] +enum Segment { + Literal(Literal), + Interpolation(Interpolation), +} + +#[derive(Debug, Clone)] +struct Assignment(Key, Vec); + +pub(super) fn evaluate>( + parents: &[Profile], + path: P, +) -> Result, Error> { + let parsed = match fs::read_to_string(path.as_ref().join("make.defaults")) { + Ok(contents) => parse(&contents)?, + Err(e) if matches!(e.kind(), io::ErrorKind::NotFound) => HashMap::new(), + Err(e) => return Err(Error::Io(path.as_ref().to_path_buf(), e)), + }; + + let mut vars = interpolate(parents, parsed); + + incremental(parents, &mut vars); + + Ok(vars) +} + +fn incremental(parents: &[Profile], vars: &mut HashMap) { + for key in [ + "USE", + "USE_EXPAND", + "USE_EXPAND_HIDDEN", + "CONFIG_PROTECT", + "CONFIG_PROTECT_MASK", + ] { + let mut accumulated = Vec::new(); + + for parent in parents { + if let Some(values) = parent.make_defaults().get(key) { + accumulated.extend(values.split_ascii_whitespace()); + } + } + + if let Some(values) = vars.get(key) { + accumulated.extend(values.split_ascii_whitespace()); + } + + let mut final_values = Vec::new(); + + for var in accumulated { + if var == "-*" { + final_values.clear(); + } else if let Some(stripped) = var.strip_prefix("-") { + final_values.retain(|v| *v != stripped); + } else { + final_values.push(var); + } + } + + let mut seen = HashSet::new(); + final_values.retain(|v| seen.insert(*v)); + + if !final_values.is_empty() { + vars.insert(key.to_string(), final_values.join(" ")); + } + } +} + +fn interpolate(parents: &[Profile], vars: HashMap>) -> HashMap { + let parent_vars = parents + .iter() + .flat_map(|parent| parent.make_defaults().clone().into_iter()) + .collect::>(); + + vars.into_iter() + .map(|(key, segments)| { + let interpolated = segments + .into_iter() + .map(|segment| match segment { + Segment::Interpolation(i) => parent_vars.get(&i.0).cloned().unwrap_or_default(), + Segment::Literal(literal) => literal.0.trim().to_string(), + }) + .collect::>(); + + let joined = interpolated.join(""); + + (key.0, joined) + }) + .collect::>() +} + +fn parse(contents: &str) -> Result>, Error> { + Ok(LineBasedFileExpr::::parser() + .separated_by_with_opt_trailing(ascii_whitespace1()) + .many() + .parse_finished(InputIter::new(contents)) + .map_err(|e| Error::Parser(dbg!(e).rest().to_string()))? + .into_iter() + .filter_map(|expr| match expr { + LineBasedFileExpr::Comment => None, + LineBasedFileExpr::Expr(Assignment(key, value)) => Some((key, value)), + }) + .collect()) +} diff --git a/src/repo/profile/make_defaults/parsers.rs b/src/repo/profile/make_defaults/parsers.rs new file mode 100644 index 0000000..4d27791 --- /dev/null +++ b/src/repo/profile/make_defaults/parsers.rs @@ -0,0 +1,88 @@ +use mon::{Parser, ParserIter, ascii_alpha, ascii_alphanumeric, r#if, one_of, tag}; + +use crate::{ + Parseable, + repo::profile::make_defaults::{Assignment, Interpolation, Key, Literal, Segment}, +}; + +impl<'a> Parseable<'a, &'a str> for Key { + type Parser = impl Parser<&'a str, Output = Self>; + + fn parser() -> Self::Parser { + let start = ascii_alpha(); + let rest = ascii_alphanumeric() + .or(one_of("_".chars())) + .repeated() + .many(); + + start + .followed_by(rest) + .recognize() + .map(|output: &str| Key(output.to_string())) + } +} + +impl<'a> Parseable<'a, &'a str> for Literal { + type Parser = impl Parser<&'a str, Output = Self>; + + fn parser() -> Self::Parser { + r#if(|c: &char| *c != '"') + .and_not(Interpolation::parser()) + .repeated() + .at_least(1) + .recognize() + .map(|output: &str| Literal(output.to_string())) + } +} + +impl<'a> Parseable<'a, &'a str> for Interpolation { + type Parser = impl Parser<&'a str, Output = Self>; + + fn parser() -> Self::Parser { + Key::parser() + .recognize() + .delimited_by(tag("{"), tag("}")) + .preceded_by(tag("$")) + .map(|output: &str| Interpolation(output.to_string())) + } +} + +impl<'a> Parseable<'a, &'a str> for Segment { + type Parser = impl Parser<&'a str, Output = Self>; + + fn parser() -> Self::Parser { + Literal::parser() + .map(Segment::Literal) + .or(Interpolation::parser().map(Segment::Interpolation)) + } +} + +impl<'a> Parseable<'a, &'a str> for Assignment { + type Parser = impl Parser<&'a str, Output = Self>; + + fn parser() -> Self::Parser { + Key::parser() + .followed_by(tag("=")) + .and( + Segment::parser() + .repeated() + .many() + .delimited_by(tag("\""), tag("\"")), + ) + .map(|(key, value)| Assignment(key, value)) + } +} + +#[cfg(test)] +mod test { + use mon::input::InputIter; + + use super::*; + + #[test] + fn test_parse_value() { + let it = InputIter::new(r#"KEY="foo ${bar}""#); + + Assignment::parser().check_finished(it).unwrap(); + } +} diff --git a/src/repo/profile/mod.rs b/src/repo/profile/mod.rs new file mode 100644 index 0000000..3edd170 --- /dev/null +++ b/src/repo/profile/mod.rs @@ -0,0 +1,157 @@ +use std::{ + collections::HashMap, + fs::{self, File}, + io::{self, Read}, + path::{Path, PathBuf}, +}; + +use get::Get; +use itertools::Itertools; + +use crate::{atom::Atom, useflag::UseFlag}; + +mod make_defaults; +mod package; +mod package_use; +mod packages; +mod parsers; +mod useflags; + +#[derive(Debug, Clone)] +enum LineBasedFileExpr { + Comment, + Expr(T), +} + +#[derive(Debug, Clone)] +enum FlagOperation { + Add(UseFlag), + Remove(UseFlag), +} + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("{0}: io error: {1}")] + Io(PathBuf, io::Error), + #[error("error evaluating make.defaults settings: {0}")] + MakeDefaults(#[from] make_defaults::Error), + #[error("error evaluating packages settings: {0}")] + Packages(#[from] packages::Error), + #[error("error evaluating package settings: {0}")] + Package(#[from] package::Error), + #[error("error evaluating package.use settings: {0}")] + PackageUse(#[from] package_use::Error), + #[error("error evaluating use settings: {0}")] + Use(#[from] useflags::Error), +} + +#[derive(Debug, Clone, Get)] +pub struct Profile { + #[get(kind = "deref")] + path: PathBuf, + #[get(kind = "deref")] + parents: Vec, + make_defaults: HashMap, + #[get(kind = "deref")] + packages: Vec, + #[get(kind = "deref")] + package_mask: Vec, + #[get(kind = "deref")] + package_provided: Vec, + package_use: HashMap>, + package_use_force: HashMap>, + package_use_mask: HashMap>, + package_use_stable_force: HashMap>, + package_use_stable_mask: HashMap>, + #[get(kind = "deref")] + use_force: Vec, + #[get(kind = "deref")] + use_mask: Vec, + #[get(kind = "deref")] + use_stable_force: Vec, + #[get(kind = "deref")] + use_stable_mask: Vec, +} + +impl Profile { + pub(super) fn evaluate>(path: P) -> Result { + let parents_path = path.as_ref().join("parent"); + + let parents = match fs::read_to_string(&parents_path) { + Ok(parents) => parents + .lines() + .map(|line| path.as_ref().join(line)) + .map(Profile::evaluate) + .collect::>()?, + Err(e) if matches!(e.kind(), io::ErrorKind::NotFound) => Vec::new(), + Err(e) => return Err(Error::Io(parents_path, e)), + }; + + let make_defaults = make_defaults::evaluate(&parents, &path)?; + + let packages = packages::evaluate(&parents, &path)?; + + let package_mask = package::evaluate(&parents, package::Kind::Mask, &path)?; + let package_provided = package::evaluate(&parents, package::Kind::Provided, &path)?; + + let package_use = package_use::evaluate(&parents, package_use::Kind::Use, &path)?; + let package_use_force = package_use::evaluate(&parents, package_use::Kind::Force, &path)?; + let package_use_mask = package_use::evaluate(&parents, package_use::Kind::Mask, &path)?; + let package_use_stable_force = + package_use::evaluate(&parents, package_use::Kind::StableForce, &path)?; + let package_use_stable_mask = + package_use::evaluate(&parents, package_use::Kind::StableMask, &path)?; + + let use_force = useflags::evaluate(&parents, useflags::Kind::Force, &path)?; + let use_mask = useflags::evaluate(&parents, useflags::Kind::Mask, &path)?; + let use_stable_force = useflags::evaluate(&parents, useflags::Kind::StableForce, &path)?; + let use_stable_mask = useflags::evaluate(&parents, useflags::Kind::StableMask, &path)?; + + Ok(Self { + path: path.as_ref().to_path_buf(), + parents, + make_defaults, + packages, + package_mask, + package_provided, + package_use, + package_use_force, + package_use_mask, + package_use_stable_force, + package_use_stable_mask, + use_force, + use_mask, + use_stable_force, + use_stable_mask, + }) + } +} + +fn read_config_files>(path: P) -> Result { + let metadata = fs::metadata(&path)?; + + if metadata.is_file() { + fs::read_to_string(&path) + } else if metadata.is_dir() { + let mut buffer = String::new(); + let paths = fs::read_dir(&path)? + .collect::, _>>()? + .into_iter() + .map(|entry| entry.path()) + .filter(|path| path.starts_with(".")) + .sorted() + .collect::>(); + + for path in &paths { + let mut file = File::open(path)?; + + file.read_to_string(&mut buffer)?; + } + + Ok(buffer) + } else { + let path = fs::canonicalize(&path)?; + + read_config_files(path) + } +} diff --git a/src/repo/profile/package/mod.rs b/src/repo/profile/package/mod.rs new file mode 100644 index 0000000..facf9ea --- /dev/null +++ b/src/repo/profile/package/mod.rs @@ -0,0 +1,95 @@ +use std::{ + io, + path::{Path, PathBuf}, +}; + +use mon::{Parser, ParserIter, ascii_whitespace1, input::InputIter}; + +use crate::{ + Parseable, + atom::Atom, + repo::profile::{LineBasedFileExpr, Profile, read_config_files}, +}; + +mod parsers; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("{0}: io error: {1}")] + Io(PathBuf, io::Error), + #[error("parser error: {0}")] + Parser(String), +} + +#[derive(Debug, Clone, Copy)] +pub(super) enum Kind { + Mask, + Provided, +} + +#[derive(Debug, Clone)] +enum Package { + Add(Atom), + Remove(Atom), +} + +pub(super) fn evaluate>( + parents: &[Profile], + kind: Kind, + path: P, +) -> Result, Error> { + let file_path = match kind { + Kind::Mask => "package.mask", + Kind::Provided => "package.provided", + }; + + let parsed = match read_config_files(path.as_ref().join(file_path)) { + Ok(contents) => parse(&contents)?, + Err(e) if matches!(e.kind(), io::ErrorKind::NotFound) => Vec::new(), + Err(e) => return Err(Error::Io(path.as_ref().to_path_buf(), e)), + }; + + Ok(inherit(parents, kind, parsed)) +} + +fn inherit(parents: &[Profile], kind: Kind, packages: Vec) -> Vec { + let mut accumulated = Vec::new(); + + for parent in parents { + let source = match kind { + Kind::Mask => parent.package_mask(), + Kind::Provided => parent.package_provided(), + }; + + for package in source { + accumulated.push(package.clone()); + } + } + + for package in packages { + match package { + Package::Add(package) => { + accumulated.push(package); + } + Package::Remove(package) => { + accumulated.retain(|p| *p != package); + } + } + } + + accumulated +} + +fn parse(contents: &str) -> Result, Error> { + Ok(LineBasedFileExpr::::parser() + .separated_by_with_opt_trailing(ascii_whitespace1()) + .many() + .parse_finished(InputIter::new(contents)) + .map_err(|e| Error::Parser(e.rest().to_string()))? + .into_iter() + .filter_map(|expr| match expr { + LineBasedFileExpr::Comment => None, + LineBasedFileExpr::Expr(package) => Some(package), + }) + .collect()) +} diff --git a/src/repo/profile/package/parsers.rs b/src/repo/profile/package/parsers.rs new file mode 100644 index 0000000..74d9acc --- /dev/null +++ b/src/repo/profile/package/parsers.rs @@ -0,0 +1,13 @@ +use mon::{Parser, tag}; + +use crate::{Parseable, atom::Atom, repo::profile::package::Package}; + +impl<'a> Parseable<'a, &'a str> for Package { + type Parser = impl Parser<&'a str, Output = Self>; + + fn parser() -> Self::Parser { + Atom::parser() + .map(Package::Add) + .or(Atom::parser().preceded_by(tag("-")).map(Package::Remove)) + } +} diff --git a/src/repo/profile/package_use/mod.rs b/src/repo/profile/package_use/mod.rs new file mode 100644 index 0000000..ddad8b3 --- /dev/null +++ b/src/repo/profile/package_use/mod.rs @@ -0,0 +1,125 @@ +use std::{ + collections::HashMap, + io, + path::{Path, PathBuf}, +}; + +use mon::{Parser, ParserIter, ascii_whitespace1, input::InputIter}; + +use crate::{ + Parseable, + atom::Atom, + repo::profile::{FlagOperation, LineBasedFileExpr, Profile, read_config_files}, + useflag::UseFlag, +}; + +mod parsers; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("{0}: io error: {1}")] + Io(PathBuf, io::Error), + #[error("parser error: {0}")] + Parser(String), +} + +#[derive(Debug, Clone)] +struct Expr(Atom, Vec); + +#[derive(Debug, Clone, Copy)] +pub(super) enum Kind { + Use, + Force, + Mask, + StableForce, + StableMask, +} + +pub(super) fn evaluate>( + parents: &[Profile], + kind: Kind, + path: P, +) -> Result>, Error> { + let file_path = match kind { + Kind::Use => "package.use", + Kind::Force => "package.use.force", + Kind::Mask => "package.use.mask", + Kind::StableForce => "package.use.stable.force", + Kind::StableMask => "package.use.stable.mask", + }; + + let parsed = match read_config_files(path.as_ref().join(file_path)) { + Ok(contents) => parse(&contents)?, + Err(e) if matches!(e.kind(), io::ErrorKind::NotFound) => HashMap::new(), + Err(e) => return Err(Error::Io(path.as_ref().to_path_buf(), e)), + }; + + Ok(inherit(parents, kind, parsed)) +} + +fn inherit( + parents: &[Profile], + kind: Kind, + vars: HashMap>, +) -> HashMap> { + let mut accumulated: HashMap> = HashMap::new(); + + for parent in parents { + let source = match kind { + Kind::Use => parent.package_use(), + Kind::Force => parent.package_use_force(), + Kind::Mask => parent.package_use_mask(), + Kind::StableForce => parent.package_use_stable_force(), + Kind::StableMask => parent.package_use_stable_mask(), + }; + + for (atom, flags) in source { + accumulated + .entry(atom.clone()) + .and_modify(|f| f.extend(flags.iter().cloned())) + .or_insert(flags.clone()); + } + } + + for (atom, flags) in vars { + match accumulated.get_mut(&atom) { + Some(accumulated) => { + for flag in flags { + match flag { + FlagOperation::Add(flag) => accumulated.push(flag), + FlagOperation::Remove(flag) => accumulated.retain(|v| *v != flag), + } + } + } + None => { + accumulated.insert( + atom.clone(), + flags + .iter() + .filter_map(|flag| match flag { + FlagOperation::Add(flag) => Some(flag), + FlagOperation::Remove(_) => None, + }) + .cloned() + .collect(), + ); + } + } + } + + accumulated +} + +fn parse(contents: &str) -> Result>, Error> { + Ok(LineBasedFileExpr::::parser() + .separated_by_with_opt_trailing(ascii_whitespace1()) + .many() + .parse_finished(InputIter::new(contents)) + .map_err(|e| Error::Parser(e.rest().to_string()))? + .into_iter() + .filter_map(|expr| match expr { + LineBasedFileExpr::Comment => None, + LineBasedFileExpr::Expr(Expr(atom, operations)) => Some((atom, operations)), + }) + .collect()) +} diff --git a/src/repo/profile/package_use/parsers.rs b/src/repo/profile/package_use/parsers.rs new file mode 100644 index 0000000..f7bc801 --- /dev/null +++ b/src/repo/profile/package_use/parsers.rs @@ -0,0 +1,36 @@ +use mon::{Parser, ParserIter, ascii_whitespace, ascii_whitespace1, tag}; + +use crate::{ + Parseable, + atom::Atom, + repo::profile::{FlagOperation, package_use::Expr}, +}; + +impl<'a> Parseable<'a, &'a str> for Expr { + type Parser = impl Parser<&'a str, Output = Self>; + + fn parser() -> Self::Parser { + Atom::parser() + .followed_by(ascii_whitespace1()) + .and( + FlagOperation::parser() + .separated_by(ascii_whitespace().and_not(tag("\n")).repeated().at_least(1)) + .at_least(1), + ) + .map(|(atom, operations)| Expr(atom, operations)) + } +} + +#[cfg(test)] +mod test { + use mon::input::InputIter; + + use super::*; + + #[test] + fn test_parse_expr() { + let it = InputIter::new("foo/bar a -b"); + + Expr::parser().check_finished(it).unwrap(); + } +} diff --git a/src/repo/profile/packages/mod.rs b/src/repo/profile/packages/mod.rs new file mode 100644 index 0000000..4fd6559 --- /dev/null +++ b/src/repo/profile/packages/mod.rs @@ -0,0 +1,75 @@ +use std::{ + fs, io, + path::{Path, PathBuf}, +}; + +use mon::{Parser, ParserIter, ascii_whitespace1, input::InputIter}; + +use crate::{ + Parseable, + atom::Atom, + repo::profile::{LineBasedFileExpr, Profile}, +}; + +mod parsers; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("{0}: io error: {1}")] + Io(PathBuf, io::Error), + #[error("parser error: {0}")] + Parser(String), +} + +#[derive(Debug, Clone)] +enum Package { + Add(Atom), + Remove(Atom), +} + +pub(super) fn evaluate>(parents: &[Profile], path: P) -> Result, Error> { + let parsed = match fs::read_to_string(path.as_ref().join("packages")) { + Ok(contents) => parse(&contents)?, + Err(e) if matches!(e.kind(), io::ErrorKind::NotFound) => Vec::new(), + Err(e) => return Err(Error::Io(path.as_ref().to_path_buf(), e)), + }; + + Ok(inherit(parents, parsed)) +} + +fn inherit(parents: &[Profile], packages: Vec) -> Vec { + let mut accumulated = Vec::new(); + + for parent in parents { + for package in parent.packages() { + accumulated.push(package.clone()); + } + } + + for package in packages { + match package { + Package::Add(package) => { + accumulated.push(package); + } + Package::Remove(package) => { + accumulated.retain(|p| *p != package); + } + } + } + + accumulated +} + +fn parse(contents: &str) -> Result, Error> { + Ok(LineBasedFileExpr::::parser() + .separated_by_with_opt_trailing(ascii_whitespace1()) + .many() + .parse_finished(InputIter::new(contents)) + .map_err(|e| Error::Parser(e.rest().to_string()))? + .into_iter() + .filter_map(|expr| match expr { + LineBasedFileExpr::Comment => None, + LineBasedFileExpr::Expr(package) => Some(package), + }) + .collect()) +} diff --git a/src/repo/profile/packages/parsers.rs b/src/repo/profile/packages/parsers.rs new file mode 100644 index 0000000..e5dc727 --- /dev/null +++ b/src/repo/profile/packages/parsers.rs @@ -0,0 +1,14 @@ +use mon::{Parser, tag}; + +use crate::{Parseable, atom::Atom, repo::profile::packages::Package}; + +impl<'a> Parseable<'a, &'a str> for Package { + type Parser = impl Parser<&'a str, Output = Self>; + + fn parser() -> Self::Parser { + Atom::parser() + .preceded_by(tag("*")) + .map(Package::Add) + .or(Atom::parser().preceded_by(tag("-*")).map(Package::Remove)) + } +} diff --git a/src/repo/profile/parsers.rs b/src/repo/profile/parsers.rs new file mode 100644 index 0000000..44e767e --- /dev/null +++ b/src/repo/profile/parsers.rs @@ -0,0 +1,35 @@ +use mon::{Parser, ParserIter, any, ascii_whitespace1, tag}; + +use crate::{ + Parseable, + repo::profile::{FlagOperation, LineBasedFileExpr}, + useflag::UseFlag, +}; + +impl<'a, T> Parseable<'a, &'a str> for LineBasedFileExpr +where + T: Parseable<'a, &'a str>, +{ + type Parser = impl Parser<&'a str, Output = Self>; + + fn parser() -> Self::Parser { + let comment = tag("#") + .preceded_by(ascii_whitespace1().opt()) + .followed_by(any().and_not(tag("\n")).repeated().many()) + .map(|_| LineBasedFileExpr::Comment); + let expr = T::parser().map(|expr| LineBasedFileExpr::Expr(expr)); + + comment.or(expr) + } +} + +impl<'a> Parseable<'a, &'a str> for FlagOperation { + type Parser = impl Parser<&'a str, Output = Self>; + + fn parser() -> Self::Parser { + UseFlag::parser() + .preceded_by(tag("-")) + .map(FlagOperation::Remove) + .or(UseFlag::parser().map(FlagOperation::Add)) + } +} diff --git a/src/repo/profile/useflags/mod.rs b/src/repo/profile/useflags/mod.rs new file mode 100644 index 0000000..d7fb96c --- /dev/null +++ b/src/repo/profile/useflags/mod.rs @@ -0,0 +1,94 @@ +use std::{ + io, + path::{Path, PathBuf}, +}; + +use mon::{Parser, ParserIter, ascii_whitespace1, input::InputIter}; + +use crate::{ + Parseable, + repo::profile::{FlagOperation, LineBasedFileExpr, Profile, read_config_files}, + useflag::UseFlag, +}; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("{0}: io error: {1}")] + Io(PathBuf, io::Error), + #[error("parser error: {0}")] + Parser(String), +} + +#[derive(Debug, Clone, Copy)] +pub(super) enum Kind { + Force, + Mask, + StableForce, + StableMask, +} + +#[allow(clippy::unnecessary_wraps)] +pub(super) fn evaluate>( + parents: &[Profile], + kind: Kind, + path: P, +) -> Result, Error> { + let file_path = match kind { + Kind::Force => "use.force", + Kind::Mask => "use.mask", + Kind::StableForce => "use.stable.force", + Kind::StableMask => "use.stable.mask", + }; + + let parsed = match read_config_files(path.as_ref().join(file_path)) { + Ok(contents) => parse(&contents)?, + Err(e) if matches!(e.kind(), io::ErrorKind::NotFound) => Vec::new(), + Err(e) => return Err(Error::Io(path.as_ref().to_path_buf(), e)), + }; + + Ok(inherit(parents, kind, parsed)) +} + +fn inherit(parents: &[Profile], kind: Kind, operations: Vec) -> Vec { + let mut accumulated = Vec::new(); + + for parent in parents { + let source = match kind { + Kind::Force => parent.use_force(), + Kind::Mask => parent.use_mask(), + Kind::StableForce => parent.use_stable_force(), + Kind::StableMask => parent.use_stable_mask(), + }; + + for flag in source { + accumulated.push(flag.clone()); + } + } + + for operation in operations { + match operation { + FlagOperation::Add(flag) => { + accumulated.push(flag); + } + FlagOperation::Remove(flag) => { + accumulated.retain(|v| *v != flag); + } + } + } + + accumulated +} + +fn parse(contents: &str) -> Result, Error> { + Ok(LineBasedFileExpr::::parser() + .separated_by_with_opt_trailing(ascii_whitespace1()) + .many() + .parse_finished(InputIter::new(contents)) + .map_err(|e| Error::Parser(e.rest().to_string()))? + .into_iter() + .filter_map(|expr| match expr { + LineBasedFileExpr::Comment => None, + LineBasedFileExpr::Expr(flag_operation) => Some(flag_operation), + }) + .collect()) +} diff --git a/tests/meson.build b/tests/meson.build index 8ba06e8..d193910 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -2,6 +2,7 @@ tests = {} subdir('porthole') subdir('repo') +subdir('profile') foreach test, test_args : tests stem = fs.stem(test) @@ -15,5 +16,6 @@ foreach test, test_args : tests link_with: [gentoo_utils], ), args: test_args, + timeout: 0, ) endforeach diff --git a/tests/profile/meson.build b/tests/profile/meson.build new file mode 100644 index 0000000..2a2a541 --- /dev/null +++ b/tests/profile/meson.build @@ -0,0 +1 @@ +tests += {meson.current_source_dir() / 'read_all_profiles.rs': []} diff --git a/tests/profile/read_all_profiles.rs b/tests/profile/read_all_profiles.rs new file mode 100644 index 0000000..6295ec8 --- /dev/null +++ b/tests/profile/read_all_profiles.rs @@ -0,0 +1,483 @@ +use gentoo_utils::repo::Repo; + +fn main() { + let profiles = [ + "default/linux/alpha/23.0", + "default/linux/alpha/23.0/systemd", + "default/linux/alpha/23.0/desktop", + "default/linux/alpha/23.0/desktop/gnome", + "default/linux/alpha/23.0/desktop/gnome/systemd", + "default/linux/alpha/23.0/split-usr", + "default/linux/alpha/23.0/split-usr/desktop", + "default/linux/alpha/23.0/split-usr/desktop/gnome", + "default/linux/amd64/23.0", + "default/linux/amd64/23.0/systemd", + "default/linux/amd64/23.0/desktop", + "default/linux/amd64/23.0/desktop/systemd", + "default/linux/amd64/23.0/desktop/gnome", + "default/linux/amd64/23.0/desktop/gnome/systemd", + "default/linux/amd64/23.0/desktop/plasma", + "default/linux/amd64/23.0/desktop/plasma/systemd", + "default/linux/amd64/23.0/no-multilib", + "default/linux/amd64/23.0/no-multilib/systemd", + "default/linux/amd64/23.0/no-multilib/hardened", + "default/linux/amd64/23.0/no-multilib/hardened/systemd", + "default/linux/amd64/23.0/no-multilib/hardened/selinux", + "default/linux/amd64/23.0/no-multilib/hardened/selinux/systemd", + "default/linux/amd64/23.0/no-multilib/prefix", + "default/linux/amd64/23.0/no-multilib/prefix/kernel-2.6.32+", + "default/linux/amd64/23.0/no-multilib/prefix/kernel-2.6.16+", + "default/linux/amd64/23.0/no-multilib/prefix/kernel-3.2+", + "default/linux/amd64/23.0/llvm", + "default/linux/amd64/23.0/llvm/systemd", + "default/linux/amd64/23.0/hardened", + "default/linux/amd64/23.0/hardened/systemd", + "default/linux/amd64/23.0/hardened/selinux", + "default/linux/amd64/23.0/hardened/selinux/systemd", + "default/linux/amd64/23.0/split-usr", + "default/linux/amd64/23.0/split-usr/desktop", + "default/linux/amd64/23.0/split-usr/desktop/gnome", + "default/linux/amd64/23.0/split-usr/desktop/plasma", + "default/linux/amd64/23.0/split-usr/no-multilib", + "default/linux/amd64/23.0/split-usr/no-multilib/selinux", + "default/linux/amd64/23.0/split-usr/no-multilib/hardened", + "default/linux/amd64/23.0/split-usr/no-multilib/hardened/selinux", + "default/linux/amd64/23.0/split-usr/no-multilib/prefix", + "default/linux/amd64/23.0/split-usr/no-multilib/prefix/kernel-2.6.32+", + "default/linux/amd64/23.0/split-usr/no-multilib/prefix/kernel-2.6.16+", + "default/linux/amd64/23.0/split-usr/no-multilib/prefix/kernel-3.2+", + "default/linux/amd64/23.0/split-usr/llvm", + "default/linux/amd64/23.0/split-usr/hardened", + "default/linux/amd64/23.0/split-usr/hardened/selinux", + "default/linux/amd64/23.0/x32", + "default/linux/amd64/23.0/x32/systemd", + "default/linux/amd64/23.0/split-usr/x32", + "default/linux/arm/23.0", + "default/linux/arm/23.0/desktop", + "default/linux/arm/23.0/desktop/gnome", + "default/linux/arm/23.0/desktop/gnome/systemd", + "default/linux/arm/23.0/desktop/plasma", + "default/linux/arm/23.0/desktop/plasma/systemd", + "default/linux/arm/23.0/armv4", + "default/linux/arm/23.0/armv4t", + "default/linux/arm/23.0/armv4t/systemd", + "default/linux/arm/23.0/armv5te", + "default/linux/arm/23.0/armv5te/systemd", + "default/linux/arm/23.0/armv6j_sf", + "default/linux/arm/23.0/armv6j_sf/hardened", + "default/linux/arm/23.0/armv6j_sf/hardened/selinux", + "default/linux/arm/23.0/armv6j_sf/systemd", + "default/linux/arm/23.0/armv6j_hf", + "default/linux/arm/23.0/armv6j_hf/hardened", + "default/linux/arm/23.0/armv6j_hf/hardened/selinux", + "default/linux/arm/23.0/armv6j_hf/systemd", + "default/linux/arm/23.0/armv7a_sf", + "default/linux/arm/23.0/armv7a_sf/hardened", + "default/linux/arm/23.0/armv7a_sf/hardened/selinux", + "default/linux/arm/23.0/armv7a_sf/desktop", + "default/linux/arm/23.0/armv7a_sf/desktop/gnome", + "default/linux/arm/23.0/armv7a_sf/desktop/gnome/systemd", + "default/linux/arm/23.0/armv7a_sf/desktop/plasma", + "default/linux/arm/23.0/armv7a_sf/desktop/plasma/systemd", + "default/linux/arm/23.0/armv7a_sf/systemd", + "default/linux/arm/23.0/armv7a_hf", + "default/linux/arm/23.0/armv7a_hf/hardened", + "default/linux/arm/23.0/armv7a_hf/hardened/selinux", + "default/linux/arm/23.0/armv7a_hf/desktop", + "default/linux/arm/23.0/armv7a_hf/desktop/gnome", + "default/linux/arm/23.0/armv7a_hf/desktop/gnome/systemd", + "default/linux/arm/23.0/armv7a_hf/desktop/plasma", + "default/linux/arm/23.0/armv7a_hf/desktop/plasma/systemd", + "default/linux/arm/23.0/armv7a_hf/systemd", + "default/linux/arm/23.0/split-usr", + "default/linux/arm/23.0/split-usr/desktop", + "default/linux/arm/23.0/split-usr/desktop/gnome", + "default/linux/arm/23.0/split-usr/desktop/plasma", + "default/linux/arm/23.0/split-usr/armv4", + "default/linux/arm/23.0/split-usr/armv4t", + "default/linux/arm/23.0/split-usr/armv5te", + "default/linux/arm/23.0/split-usr/armv6j_sf", + "default/linux/arm/23.0/split-usr/armv6j_sf/hardened", + "default/linux/arm/23.0/split-usr/armv6j_sf/hardened/selinux", + "default/linux/arm/23.0/split-usr/armv6j_hf", + "default/linux/arm/23.0/split-usr/armv6j_hf/hardened", + "default/linux/arm/23.0/split-usr/armv6j_hf/hardened/selinux", + "default/linux/arm/23.0/split-usr/armv7a_sf", + "default/linux/arm/23.0/split-usr/armv7a_sf/hardened", + "default/linux/arm/23.0/split-usr/armv7a_sf/hardened/selinux", + "default/linux/arm/23.0/split-usr/armv7a_sf/desktop", + "default/linux/arm/23.0/split-usr/armv7a_sf/desktop/gnome", + "default/linux/arm/23.0/split-usr/armv7a_sf/desktop/plasma", + "default/linux/arm/23.0/split-usr/armv7a_hf", + "default/linux/arm/23.0/split-usr/armv7a_hf/hardened", + "default/linux/arm/23.0/split-usr/armv7a_hf/hardened/selinux", + "default/linux/arm/23.0/split-usr/armv7a_hf/desktop", + "default/linux/arm/23.0/split-usr/armv7a_hf/desktop/gnome", + "default/linux/arm/23.0/split-usr/armv7a_hf/desktop/plasma", + "default/linux/arm64/23.0", + "default/linux/arm64/23.0/hardened", + "default/linux/arm64/23.0/hardened/systemd", + "default/linux/arm64/23.0/hardened/selinux", + "default/linux/arm64/23.0/hardened/selinux/systemd", + "default/linux/arm64/23.0/desktop", + "default/linux/arm64/23.0/desktop/gnome", + "default/linux/arm64/23.0/desktop/gnome/systemd", + "default/linux/arm64/23.0/desktop/plasma", + "default/linux/arm64/23.0/desktop/plasma/systemd", + "default/linux/arm64/23.0/desktop/systemd", + "default/linux/arm64/23.0/systemd", + "default/linux/arm64/23.0/llvm", + "default/linux/arm64/23.0/llvm/systemd", + "default/linux/arm64/23.0/split-usr", + "default/linux/arm64/23.0/split-usr/hardened", + "default/linux/arm64/23.0/split-usr/hardened/selinux", + "default/linux/arm64/23.0/split-usr/desktop", + "default/linux/arm64/23.0/split-usr/desktop/gnome", + "default/linux/arm64/23.0/split-usr/desktop/plasma", + "default/linux/arm64/23.0/split-usr/llvm", + "default/linux/arm64/23.0/big-endian", + "default/linux/arm64/23.0/big-endian/systemd", + "default/linux/arm64/23.0/split-usr/big-endian", + "default/linux/hppa/23.0/hppa1.1", + "default/linux/hppa/23.0/hppa1.1/systemd", + "default/linux/hppa/23.0/hppa1.1/desktop", + "default/linux/hppa/23.0/hppa1.1/desktop/systemd", + "default/linux/hppa/23.0/hppa1.1/split-usr", + "default/linux/hppa/23.0/hppa1.1/split-usr/desktop", + "default/linux/hppa/23.0/hppa2.0", + "default/linux/hppa/23.0/hppa2.0/systemd", + "default/linux/hppa/23.0/hppa2.0/desktop", + "default/linux/hppa/23.0/hppa2.0/desktop/systemd", + "default/linux/hppa/23.0/hppa2.0/split-usr", + "default/linux/hppa/23.0/hppa2.0/split-usr/desktop", + "default/linux/loong/23.0/la64v100/lp64d", + "default/linux/loong/23.0/la64v100/lp64d/llvm", + "default/linux/loong/23.0/la64v100/lp64d/llvm/systemd", + "default/linux/loong/23.0/la64v100/lp64d/desktop", + "default/linux/loong/23.0/la64v100/lp64d/desktop/gnome", + "default/linux/loong/23.0/la64v100/lp64d/desktop/gnome/systemd", + "default/linux/loong/23.0/la64v100/lp64d/desktop/plasma", + "default/linux/loong/23.0/la64v100/lp64d/desktop/plasma/systemd", + "default/linux/loong/23.0/la64v100/lp64d/desktop/systemd", + "default/linux/loong/23.0/la64v100/lp64d/systemd", + "default/linux/loong/23.0/la64v100/split-usr/lp64d", + "default/linux/loong/23.0/la64v100/split-usr/lp64d/desktop", + "default/linux/loong/23.0/la64v100/split-usr/lp64d/desktop/gnome", + "default/linux/loong/23.0/la64v100/split-usr/lp64d/desktop/plasma", + "default/linux/m68k/23.0", + "default/linux/m68k/23.0/systemd", + "default/linux/m68k/23.0/split-usr", + "default/linux/m68k/23.0/time64", + "default/linux/mips/23.0/mipsel/o32_sf", + "default/linux/mips/23.0/mipsel/o32_sf/systemd", + "default/linux/mips/23.0/mipsel/o32", + "default/linux/mips/23.0/mipsel/o32/systemd", + "default/linux/mips/23.0/mipsel/n32", + "default/linux/mips/23.0/mipsel/n32/systemd", + "default/linux/mips/23.0/mipsel/n64", + "default/linux/mips/23.0/mipsel/n64/systemd", + "default/linux/mips/23.0/mipsel/multilib/n32", + "default/linux/mips/23.0/mipsel/multilib/n32/systemd", + "default/linux/mips/23.0/mipsel/multilib/n64", + "default/linux/mips/23.0/mipsel/multilib/n64/systemd", + "default/linux/mips/23.0/o32_sf", + "default/linux/mips/23.0/o32_sf/systemd", + "default/linux/mips/23.0/o32", + "default/linux/mips/23.0/o32/systemd", + "default/linux/mips/23.0/n32", + "default/linux/mips/23.0/n32/systemd", + "default/linux/mips/23.0/n64", + "default/linux/mips/23.0/n64/systemd", + "default/linux/mips/23.0/multilib/n32", + "default/linux/mips/23.0/multilib/n32/systemd", + "default/linux/mips/23.0/multilib/n64", + "default/linux/mips/23.0/multilib/n64/systemd", + "default/linux/mips/23.0/split-usr/mipsel/o32_sf", + "default/linux/mips/23.0/split-usr/mipsel/o32", + "default/linux/mips/23.0/split-usr/mipsel/n32", + "default/linux/mips/23.0/split-usr/mipsel/n64", + "default/linux/mips/23.0/split-usr/mipsel/multilib/n32", + "default/linux/mips/23.0/split-usr/mipsel/multilib/n64", + "default/linux/mips/23.0/split-usr/o32_sf", + "default/linux/mips/23.0/split-usr/o32", + "default/linux/mips/23.0/split-usr/n32", + "default/linux/mips/23.0/split-usr/n64", + "default/linux/mips/23.0/split-usr/multilib/n32", + "default/linux/mips/23.0/split-usr/multilib/n64", + "default/linux/mips/23.0/time64/mipsel/o32_sf", + "default/linux/mips/23.0/time64/mipsel/o32_sf/systemd", + "default/linux/mips/23.0/time64/mipsel/o32", + "default/linux/mips/23.0/time64/mipsel/o32/systemd", + "default/linux/mips/23.0/time64/mipsel/n32", + "default/linux/mips/23.0/time64/mipsel/n32/systemd", + "default/linux/mips/23.0/time64/mipsel/multilib/n32", + "default/linux/mips/23.0/time64/mipsel/multilib/n32/systemd", + "default/linux/mips/23.0/time64/mipsel/multilib/n64", + "default/linux/mips/23.0/time64/mipsel/multilib/n64/systemd", + "default/linux/mips/23.0/time64/o32_sf", + "default/linux/mips/23.0/time64/o32_sf/systemd", + "default/linux/mips/23.0/time64/o32", + "default/linux/mips/23.0/time64/o32/systemd", + "default/linux/mips/23.0/time64/n32", + "default/linux/mips/23.0/time64/n32/systemd", + "default/linux/mips/23.0/time64/multilib/n32", + "default/linux/mips/23.0/time64/multilib/n32/systemd", + "default/linux/mips/23.0/time64/multilib/n64", + "default/linux/mips/23.0/time64/multilib/n64/systemd", + "default/linux/mips/23.0/time64/split-usr/mipsel/o32_sf", + "default/linux/mips/23.0/time64/split-usr/mipsel/o32", + "default/linux/mips/23.0/time64/split-usr/mipsel/n32", + "default/linux/mips/23.0/time64/split-usr/mipsel/multilib/n32", + "default/linux/mips/23.0/time64/split-usr/mipsel/multilib/n64", + "default/linux/mips/23.0/time64/split-usr/o32_sf", + "default/linux/mips/23.0/time64/split-usr/o32", + "default/linux/mips/23.0/time64/split-usr/n32", + "default/linux/mips/23.0/time64/split-usr/multilib/n32", + "default/linux/mips/23.0/time64/split-usr/multilib/n64", + "default/linux/ppc/23.0", + "default/linux/ppc/23.0/desktop", + "default/linux/ppc/23.0/desktop/gnome", + "default/linux/ppc/23.0/desktop/gnome/systemd", + "default/linux/ppc/23.0/systemd", + "default/linux/ppc/23.0/split-usr", + "default/linux/ppc/23.0/split-usr/desktop", + "default/linux/ppc/23.0/split-usr/desktop/gnome", + "default/linux/ppc/23.0/time64", + "default/linux/ppc/23.0/time64/desktop", + "default/linux/ppc/23.0/time64/desktop/gnome", + "default/linux/ppc/23.0/time64/desktop/gnome/systemd", + "default/linux/ppc/23.0/time64/systemd", + "default/linux/ppc/23.0/time64/split-usr", + "default/linux/ppc/23.0/time64/split-usr/desktop", + "default/linux/ppc/23.0/time64/split-usr/desktop/gnome", + "default/linux/ppc64/23.0", + "default/linux/ppc64/23.0/desktop", + "default/linux/ppc64/23.0/desktop/gnome", + "default/linux/ppc64/23.0/desktop/gnome/systemd", + "default/linux/ppc64/23.0/systemd", + "default/linux/ppc64/23.0/split-usr", + "default/linux/ppc64/23.0/split-usr/desktop", + "default/linux/ppc64/23.0/split-usr/desktop/gnome", + "default/linux/ppc64le/23.0", + "default/linux/ppc64le/23.0/desktop", + "default/linux/ppc64le/23.0/desktop/gnome", + "default/linux/ppc64le/23.0/desktop/gnome/systemd", + "default/linux/ppc64le/23.0/desktop/plasma", + "default/linux/ppc64le/23.0/desktop/plasma/systemd", + "default/linux/ppc64le/23.0/desktop/systemd", + "default/linux/ppc64le/23.0/systemd", + "default/linux/ppc64le/23.0/split-usr", + "default/linux/ppc64le/23.0/split-usr/desktop", + "default/linux/ppc64le/23.0/split-usr/desktop/gnome", + "default/linux/ppc64le/23.0/split-usr/desktop/plasma", + "default/linux/riscv/23.0/rv64/lp64d", + "default/linux/riscv/23.0/rv64/lp64d/desktop", + "default/linux/riscv/23.0/rv64/lp64d/desktop/gnome", + "default/linux/riscv/23.0/rv64/lp64d/desktop/gnome/systemd", + "default/linux/riscv/23.0/rv64/lp64d/desktop/plasma", + "default/linux/riscv/23.0/rv64/lp64d/desktop/plasma/systemd", + "default/linux/riscv/23.0/rv64/lp64d/desktop/systemd", + "default/linux/riscv/23.0/rv64/lp64d/systemd", + "default/linux/riscv/23.0/rv64/lp64", + "default/linux/riscv/23.0/rv64/lp64/desktop", + "default/linux/riscv/23.0/rv64/lp64/desktop/gnome", + "default/linux/riscv/23.0/rv64/lp64/desktop/gnome/systemd", + "default/linux/riscv/23.0/rv64/lp64/desktop/plasma", + "default/linux/riscv/23.0/rv64/lp64/desktop/plasma/systemd", + "default/linux/riscv/23.0/rv64/lp64/desktop/systemd", + "default/linux/riscv/23.0/rv64/lp64/systemd", + "default/linux/riscv/23.0/rv64/multilib", + "default/linux/riscv/23.0/rv64/multilib/systemd", + "default/linux/riscv/23.0/rv32/ilp32d", + "default/linux/riscv/23.0/rv32/ilp32d/systemd", + "default/linux/riscv/23.0/rv32/ilp32", + "default/linux/riscv/23.0/rv32/ilp32/systemd", + "default/linux/riscv/23.0/rv64/split-usr/lp64d", + "default/linux/riscv/23.0/rv64/split-usr/lp64d/desktop", + "default/linux/riscv/23.0/rv64/split-usr/lp64d/desktop/gnome", + "default/linux/riscv/23.0/rv64/split-usr/lp64d/desktop/plasma", + "default/linux/riscv/23.0/rv64/split-usr/lp64", + "default/linux/riscv/23.0/rv64/split-usr/lp64/desktop", + "default/linux/riscv/23.0/rv64/split-usr/lp64/desktop/gnome", + "default/linux/riscv/23.0/rv64/split-usr/lp64/desktop/plasma", + "default/linux/riscv/23.0/rv64/split-usr/multilib", + "default/linux/riscv/23.0/rv32/split-usr/ilp32d", + "default/linux/riscv/23.0/rv32/split-usr/ilp32", + "default/linux/s390/23.0", + "default/linux/s390/23.0/systemd", + "default/linux/s390/23.0/split-usr", + "default/linux/s390/23.0/split-usr/s390x", + "default/linux/s390/23.0/s390x", + "default/linux/s390/23.0/s390x/systemd", + "default/linux/s390/23.0/time64", + "default/linux/s390/23.0/time64/systemd", + "default/linux/s390/23.0/time64/split-usr", + "default/linux/sparc/23.0", + "default/linux/sparc/23.0/desktop", + "default/linux/sparc/23.0/systemd", + "default/linux/sparc/23.0/64ul", + "default/linux/sparc/23.0/64ul/desktop", + "default/linux/sparc/23.0/64ul/systemd", + "default/linux/sparc/23.0/split-usr", + "default/linux/sparc/23.0/split-usr/desktop", + "default/linux/sparc/23.0/split-usr/64ul", + "default/linux/sparc/23.0/split-usr/64ul/desktop", + "default/linux/x86/23.0/i686", + "default/linux/x86/23.0/i686/systemd", + "default/linux/x86/23.0/i686/hardened", + "default/linux/x86/23.0/i686/hardened/selinux", + "default/linux/x86/23.0/i686/desktop", + "default/linux/x86/23.0/i686/desktop/gnome", + "default/linux/x86/23.0/i686/desktop/gnome/systemd", + "default/linux/x86/23.0/i686/desktop/plasma", + "default/linux/x86/23.0/i686/desktop/plasma/systemd", + "default/linux/x86/23.0/i686/prefix", + "default/linux/x86/23.0/i686/prefix/kernel-2.6.32+", + "default/linux/x86/23.0/i686/prefix/kernel-2.6.16+", + "default/linux/x86/23.0/i686/prefix/kernel-3.2+", + "default/linux/x86/23.0/i686/split-usr", + "default/linux/x86/23.0/i686/split-usr/hardened", + "default/linux/x86/23.0/i686/split-usr/hardened/selinux", + "default/linux/x86/23.0/i686/split-usr/desktop", + "default/linux/x86/23.0/i686/split-usr/desktop/gnome", + "default/linux/x86/23.0/i686/split-usr/desktop/plasma", + "default/linux/x86/23.0/i686/split-usr/prefix", + "default/linux/x86/23.0/i686/split-usr/prefix/kernel-2.6.32+", + "default/linux/x86/23.0/i686/split-usr/prefix/kernel-2.6.16+", + "default/linux/x86/23.0/i686/split-usr/prefix/kernel-3.2+", + "default/linux/x86/23.0/i686/time64", + "default/linux/x86/23.0/i686/time64/systemd", + "default/linux/x86/23.0/i686/time64/hardened", + "default/linux/x86/23.0/i686/time64/hardened/selinux", + "default/linux/x86/23.0/i686/time64/desktop", + "default/linux/x86/23.0/i686/time64/desktop/gnome", + "default/linux/x86/23.0/i686/time64/desktop/gnome/systemd", + "default/linux/x86/23.0/i686/time64/desktop/plasma", + "default/linux/x86/23.0/i686/time64/desktop/plasma/systemd", + "default/linux/x86/23.0/i686/time64/split-usr", + "default/linux/x86/23.0/i686/time64/split-usr/hardened", + "default/linux/x86/23.0/i686/time64/split-usr/hardened/selinux", + "default/linux/x86/23.0/i686/time64/split-usr/desktop", + "default/linux/x86/23.0/i686/time64/split-usr/desktop/gnome", + "default/linux/x86/23.0/i686/time64/split-usr/desktop/plasma", + "default/linux/x86/23.0/i486", + "default/linux/x86/23.0/i486/systemd", + "default/linux/x86/23.0/i486/hardened", + "default/linux/x86/23.0/i486/hardened/selinux", + "default/linux/x86/23.0/i486/split-usr", + "default/linux/x86/23.0/i486/split-usr/hardened", + "default/linux/x86/23.0/i486/split-usr/hardened/selinux", + "default/linux/x86/23.0/i486/time64", + "default/linux/x86/23.0/i486/time64/systemd", + "default/linux/x86/23.0/i486/time64/hardened", + "default/linux/x86/23.0/i486/time64/hardened/selinux", + "default/linux/x86/23.0/i486/time64/split-usr", + "default/linux/x86/23.0/i486/time64/split-usr/hardened", + "default/linux/x86/23.0/i486/time64/split-usr/hardened/selinux", + "default/linux/amd64/23.0/musl", + "default/linux/amd64/23.0/musl/llvm", + "default/linux/amd64/23.0/musl/hardened", + "default/linux/amd64/23.0/musl/hardened/selinux", + "default/linux/amd64/23.0/split-usr/musl", + "default/linux/amd64/23.0/split-usr/musl/llvm", + "default/linux/amd64/23.0/split-usr/musl/hardened", + "default/linux/amd64/23.0/split-usr/musl/hardened/selinux", + "default/linux/arm/23.0/armv6j_hf/musl", + "default/linux/arm/23.0/armv6j_hf/musl/hardened", + "default/linux/arm/23.0/armv6j_hf/musl/hardened/selinux", + "default/linux/arm/23.0/armv7a_hf/musl", + "default/linux/arm/23.0/armv7a_hf/musl/hardened", + "default/linux/arm/23.0/armv7a_hf/musl/hardened/selinux", + "default/linux/arm/23.0/split-usr/armv6j_hf/musl", + "default/linux/arm/23.0/split-usr/armv6j_hf/musl/hardened", + "default/linux/arm/23.0/split-usr/armv6j_hf/musl/hardened/selinux", + "default/linux/arm/23.0/split-usr/armv7a_hf/musl", + "default/linux/arm/23.0/split-usr/armv7a_hf/musl/hardened", + "default/linux/arm/23.0/split-usr/armv7a_hf/musl/hardened/selinux", + "default/linux/arm64/23.0/musl", + "default/linux/arm64/23.0/musl/llvm", + "default/linux/arm64/23.0/musl/hardened", + "default/linux/arm64/23.0/musl/hardened/selinux", + "default/linux/arm64/23.0/split-usr/musl", + "default/linux/arm64/23.0/split-usr/musl/llvm", + "default/linux/arm64/23.0/split-usr/musl/hardened", + "default/linux/arm64/23.0/split-usr/musl/hardened/selinux", + "default/linux/m68k/23.0/musl", + "default/linux/m68k/23.0/split-usr/musl", + "default/linux/mips/23.0/mipsel/o32/musl", + "default/linux/mips/23.0/mipsel/n64/musl", + "default/linux/mips/23.0/o32/musl", + "default/linux/mips/23.0/n64/musl", + "default/linux/mips/23.0/split-usr/mipsel/o32/musl", + "default/linux/mips/23.0/split-usr/mipsel/n64/musl", + "default/linux/mips/23.0/split-usr/o32/musl", + "default/linux/mips/23.0/split-usr/n64/musl", + "default/linux/ppc/23.0/musl", + "default/linux/ppc/23.0/musl/hardened", + "default/linux/ppc/23.0/split-usr/musl", + "default/linux/ppc/23.0/split-usr/musl/hardened", + "default/linux/ppc64/23.0/musl", + "default/linux/ppc64/23.0/musl/hardened", + "default/linux/ppc64/23.0/split-usr/musl", + "default/linux/ppc64/23.0/split-usr/musl/hardened", + "default/linux/ppc64le/23.0/musl", + "default/linux/ppc64le/23.0/musl/hardened", + "default/linux/ppc64le/23.0/split-usr/musl", + "default/linux/ppc64le/23.0/split-usr/musl/hardened", + "default/linux/riscv/23.0/rv64/lp64d/musl", + "default/linux/riscv/23.0/rv64/lp64/musl", + "default/linux/riscv/23.0/rv64/split-usr/lp64d/musl", + "default/linux/riscv/23.0/rv64/split-usr/lp64/musl", + "default/linux/riscv/23.0/rv32/ilp32d/musl", + "default/linux/riscv/23.0/rv32/ilp32/musl", + "default/linux/riscv/23.0/rv32/split-usr/ilp32d/musl", + "default/linux/riscv/23.0/rv32/split-usr/ilp32/musl", + "default/linux/x86/23.0/i686/musl", + "default/linux/x86/23.0/i686/musl/selinux", + "default/linux/x86/23.0/i686/split-usr/musl", + "default/linux/x86/23.0/i686/split-usr/musl/selinux", + "default/linux/x86/23.0/i486/musl", + "default/linux/x86/23.0/i486/musl/selinux", + "default/linux/x86/23.0/i486/split-usr/musl", + "default/linux/x86/23.0/i486/split-usr/musl/selinux", + "prefix/linux/amd64", + "prefix/linux/arm", + "prefix/linux/ppc64", + "prefix/linux/ppc64le", + "prefix/linux/riscv", + "prefix/linux/x86", + "prefix/darwin/macos/10.5/ppc/gcc", + "prefix/darwin/macos/10.5/x86/gcc", + "prefix/darwin/macos/10.11/x64", + "prefix/darwin/macos/10.13/x64", + "prefix/darwin/macos/10.13/x64/gcc", + "prefix/darwin/macos/10.14/x64", + "prefix/darwin/macos/10.14/x64/gcc", + "prefix/darwin/macos/10.15/x64", + "prefix/darwin/macos/10.15/x64/gcc", + "prefix/darwin/macos/11.0/x64", + "prefix/darwin/macos/11.0/x64/gcc", + "prefix/darwin/macos/12.0/x64", + "prefix/darwin/macos/12.0/x64/gcc", + "prefix/darwin/macos/13.0/x64/gcc", + "prefix/darwin/macos/14.0/x64/gcc", + "prefix/darwin/macos/15.0/x64/gcc", + "prefix/darwin/macos/26.0/x64/gcc", + "prefix/darwin/macos/11.0/arm64", + "prefix/darwin/macos/11.0/arm64/gcc", + "prefix/darwin/macos/12.0/arm64", + "prefix/darwin/macos/12.0/arm64/gcc", + "prefix/darwin/macos/13.0/arm64/gcc", + "prefix/darwin/macos/14.0/arm64/gcc", + "prefix/darwin/macos/15.0/arm64/gcc", + "prefix/darwin/macos/26.0/arm64/gcc", + "prefix/sunos/solaris/5.11/x64", + ]; + + let repo = Repo::new("/var/db/repos/gentoo"); + + for profile in profiles { + repo.evaluate_profile(profile) + .unwrap_or_else(|e| panic!("failed to evaluate profile: {profile}: {e}")); + } +}