1 Commits

Author SHA1 Message Date
John Turner
d1127df296 impl profile evaluation 2025-11-29 20:50:59 +00:00
20 changed files with 1370 additions and 92 deletions

View File

@@ -5,18 +5,16 @@ source /lib/gentoo/functions.sh
export PATH="${HOME}/.local/bin:${PATH}" CC=clang CXX=clang++
lld=$(command -v lld)
ldd=$(command -v ldd)
if [[ -n ${ldd} ]]; then
export LDFLAGS=-fuse-ld=${lld}
export LDFLAGS=-fuse-ld=${ldd}
fi
if [[ ! -d build ]]; then
meson setup -Dfuzz=enabled -Dtests=enabled -Dbuildtype=debugoptimized -Ddocs=enabled build || exit $?
meson setup -Dfuzz=enabled -Dtests=enabled build || exit $?
fi
meson compile -C build || exit $?
ebegin "running check commands"
parallel --halt soon,fail=1 --keep-order -j$(nproc) < check_commands.txt
eend $? || exit $?

View File

@@ -1,5 +1,4 @@
/usr/bin/meson format --recursive --check-only
rustfmt --edition 2024 --check $(find src -type f -name '*.rs')
ninja rustdoc -C build
ninja clippy -C build
meson test unittests doctests '*repo*' '*porthole*' -C build
meson test unittests '*repo*' '*porthole*' '*profile*' -C build

View File

@@ -28,13 +28,3 @@ endif
if get_option('fuzz').enabled()
subdir('fuzz')
endif
if get_option('docs').enabled()
rust.doctest(
'doctests',
gentoo_utils,
dependencies: [mon, get, itertools],
link_with: [thiserror],
args: ['--nocapture'],
)
endif

View File

@@ -1,3 +1,2 @@
option('fuzz', type: 'feature', value: 'disabled')
option('tests', type: 'feature', value: 'disabled')
option('docs', type: 'feature', value: 'disabled')

View File

@@ -1,17 +1,3 @@
//! Gentoo and PMS related utils.
//!
//! Currently implements:
//! - parsers for atoms and DEPEND expressions
//! - strongly typed representations of atoms, versions, etc
//! - version comparison and equality impls
//! - iterator over repos categories and ebuilds
//!
//! Planned features
//! - profile evaluation
//! - vdb reader
//! - sourcing ebuilds with bash
//!
#![deny(clippy::pedantic, unused_imports)]
#![allow(
dead_code,
@@ -21,71 +7,14 @@
)]
#![feature(impl_trait_in_assoc_type)]
use mon::{
Parser,
input::{Input, InputIter},
};
use mon::{Parser, input::Input};
pub trait Parseable<'a, I: Input + 'a> {
type Parser: Parser<I, Output = Self>;
fn parser() -> Self::Parser;
fn parse(input: I) -> Result<Self, I>
where
Self: Sized,
{
Self::parser()
.parse_finished(InputIter::new(input))
.map_err(|e| e.rest())
}
}
/// Strongly typed atom and cpv representations.
///
/// Create atoms from parsers:
/// ```
/// use gentoo_utils::{Parseable, atom::Atom};
///
/// let emacs = Atom::parse("=app-editors/emacs-31.0-r1")
/// .expect("failed to parse atom");
///
/// assert_eq!(emacs.to_string(), "=app-editors/emacs-31.0-r1");
/// ````
///
/// Compare versions:
/// ```
/// use gentoo_utils::{Parseable, atom::Cpv};
///
/// let a = Cpv::parse("foo/bar-1.0").unwrap();
/// let b = Cpv::parse("foo/bar-2.0").unwrap();
///
/// assert!(a < b);
/// ```
pub mod atom;
/// Access to repos and ebuilds.
///
/// ```
/// use gentoo_utils::repo::Repo;
///
/// let repo = Repo::new("/var/db/repos/gentoo");
///
/// for result in repo.categories().expect("failed to read categories") {
/// let category = result.expect("failed to read category");
///
/// for result in category.ebuilds().expect("failed to read ebuilds") {
/// let ebuild = result.expect("failed to read ebuild");
///
/// println!(
/// "{}-{}: {}",
/// ebuild.name(),
/// ebuild.version(),
/// ebuild.description().clone().unwrap_or("no description available".to_string())
/// );
/// }
/// }
///
/// ```
pub mod repo;
pub mod useflag;

View File

@@ -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<P: AsRef<Path>>(&self, path: P) -> Result<Profile, Error> {
Profile::evaluate(self.path.join("profiles").join(path)).map_err(Error::Profile)
}
}
impl Category {

View File

@@ -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<Segment>);
pub(super) fn evaluate<P: AsRef<Path>>(
parents: &[Profile],
path: P,
) -> Result<HashMap<String, String>, 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<String, String>) {
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<Key, Vec<Segment>>) -> HashMap<String, String> {
let parent_vars = parents
.iter()
.flat_map(|parent| parent.make_defaults().clone().into_iter())
.collect::<HashMap<_, _>>();
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::<Vec<_>>();
let joined = interpolated.join("");
(key.0, joined)
})
.collect::<HashMap<String, String>>()
}
fn parse(contents: &str) -> Result<HashMap<Key, Vec<Segment>>, Error> {
Ok(LineBasedFileExpr::<Assignment>::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())
}

View File

@@ -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();
}
}

157
src/repo/profile/mod.rs Normal file
View File

@@ -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<T> {
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<Profile>,
make_defaults: HashMap<String, String>,
#[get(kind = "deref")]
packages: Vec<Atom>,
#[get(kind = "deref")]
package_mask: Vec<Atom>,
#[get(kind = "deref")]
package_provided: Vec<Atom>,
package_use: HashMap<Atom, Vec<UseFlag>>,
package_use_force: HashMap<Atom, Vec<UseFlag>>,
package_use_mask: HashMap<Atom, Vec<UseFlag>>,
package_use_stable_force: HashMap<Atom, Vec<UseFlag>>,
package_use_stable_mask: HashMap<Atom, Vec<UseFlag>>,
#[get(kind = "deref")]
use_force: Vec<UseFlag>,
#[get(kind = "deref")]
use_mask: Vec<UseFlag>,
#[get(kind = "deref")]
use_stable_force: Vec<UseFlag>,
#[get(kind = "deref")]
use_stable_mask: Vec<UseFlag>,
}
impl Profile {
pub(super) fn evaluate<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
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::<Result<_, _>>()?,
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<P: AsRef<Path>>(path: P) -> Result<String, io::Error> {
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::<Result<Vec<_>, _>>()?
.into_iter()
.map(|entry| entry.path())
.filter(|path| path.starts_with("."))
.sorted()
.collect::<Vec<_>>();
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)
}
}

View File

@@ -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<P: AsRef<Path>>(
parents: &[Profile],
kind: Kind,
path: P,
) -> Result<Vec<Atom>, 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<Package>) -> Vec<Atom> {
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<Vec<Package>, Error> {
Ok(LineBasedFileExpr::<Package>::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())
}

View File

@@ -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))
}
}

View File

@@ -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<FlagOperation>);
#[derive(Debug, Clone, Copy)]
pub(super) enum Kind {
Use,
Force,
Mask,
StableForce,
StableMask,
}
pub(super) fn evaluate<P: AsRef<Path>>(
parents: &[Profile],
kind: Kind,
path: P,
) -> Result<HashMap<Atom, Vec<UseFlag>>, 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<Atom, Vec<FlagOperation>>,
) -> HashMap<Atom, Vec<UseFlag>> {
let mut accumulated: HashMap<Atom, Vec<UseFlag>> = 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<HashMap<Atom, Vec<FlagOperation>>, Error> {
Ok(LineBasedFileExpr::<Expr>::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())
}

View File

@@ -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();
}
}

View File

@@ -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<P: AsRef<Path>>(parents: &[Profile], path: P) -> Result<Vec<Atom>, 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<Package>) -> Vec<Atom> {
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<Vec<Package>, Error> {
Ok(LineBasedFileExpr::<Package>::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())
}

View File

@@ -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))
}
}

View File

@@ -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<T>
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))
}
}

View File

@@ -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<P: AsRef<Path>>(
parents: &[Profile],
kind: Kind,
path: P,
) -> Result<Vec<UseFlag>, 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<FlagOperation>) -> Vec<UseFlag> {
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<Vec<FlagOperation>, Error> {
Ok(LineBasedFileExpr::<FlagOperation>::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())
}

View File

@@ -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

View File

@@ -0,0 +1 @@
tests += {meson.current_source_dir() / 'read_all_profiles.rs': []}

View File

@@ -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}"));
}
}