WIP: impl profile evaluation #7

Draft
jturnerusa wants to merge 10 commits from feature/profiles into master
49 changed files with 1631 additions and 9 deletions

View File

@@ -20,7 +20,7 @@ fn main() -> Result<(), Box<dyn Error>> {
fs::create_dir_all(&corpus_dir)?; fs::create_dir_all(&corpus_dir)?;
let repo = Repo::new("/var/db/repos/gentoo"); let repo = Repo::new("/var/db/repos/gentoo").expect("failed to open repo");
let mut atoms = Vec::new(); let mut atoms = Vec::new();
for category in repo.categories()? { for category in repo.categories()? {

View File

@@ -17,7 +17,7 @@ fn main() -> Result<(), Box<dyn Error>> {
fs::create_dir_all(&corpus_dir)?; fs::create_dir_all(&corpus_dir)?;
let repo = Repo::new("/var/db/repos/gentoo"); let repo = Repo::new("/var/db/repos/gentoo").expect("failed to open repo");
let mut versions = Vec::new(); let mut versions = Vec::new();
for category in repo.categories()? { for category in repo.categories()? {

View File

@@ -69,7 +69,8 @@ pub mod atom;
/// ``` /// ```
/// use gentoo_utils::repo::Repo; /// use gentoo_utils::repo::Repo;
/// ///
/// let repo = Repo::new("/var/db/repos/gentoo"); /// let repo = Repo::new("/var/db/repos/gentoo")
/// .expect("failed to open repo");
/// ///
/// for result in repo.categories().expect("failed to read categories") { /// for result in repo.categories().expect("failed to read categories") {
/// let category = result.expect("failed to read category"); /// let category = result.expect("failed to read category");

View File

@@ -1,3 +1,4 @@
sources += files('mod.rs') sources += files('mod.rs', 'parsers.rs')
subdir('ebuild') subdir('ebuild')
subdir('profile')

View File

@@ -11,14 +11,21 @@ use mon::{Parser, ParserIter, ascii_whitespace1, input::InputIter, tag};
use crate::{ use crate::{
Parseable, Parseable,
atom::{self, Atom}, atom::{self, Atom},
repo::ebuild::{Depend, Eapi, Ebuild, Eclass, License, SrcUri}, repo::{
ebuild::{Depend, Eapi, Ebuild, Eclass, License, SrcUri},
profile::{LineBasedFileExpr, Profile},
},
useflag::IUseFlag, useflag::IUseFlag,
}; };
pub mod ebuild; pub mod ebuild;
mod parsers;
pub mod profile;
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {
#[error("invalid repo: {0}")]
Invalid(String),
#[error("io error: {0}")] #[error("io error: {0}")]
Io(PathBuf, io::Error), Io(PathBuf, io::Error),
#[error("error while reading directory: {0:?}: {1}")] #[error("error while reading directory: {0:?}: {1}")]
@@ -27,12 +34,23 @@ pub enum Error {
Unicode(PathBuf), Unicode(PathBuf),
#[error("parser error: {0}")] #[error("parser error: {0}")]
Parser(String), Parser(String),
#[error("profile error: {0}")]
Profile(profile::Error),
} }
#[derive(Debug, Clone, PartialEq, Eq, Get)]
pub struct Arch(#[get(method = "get", kind = "deref")] String);
#[derive(Debug, Clone, Get)] #[derive(Debug, Clone, Get)]
pub struct Repo { pub struct Repo {
#[get(kind = "deref")] #[get(kind = "deref")]
path: PathBuf, path: PathBuf,
#[get(kind = "deref")]
name: String,
#[get(kind = "deref")]
package_mask: Vec<Atom>,
#[get(kind = "deref")]
arch_list: Vec<Arch>,
} }
#[derive(Debug, Clone, Get)] #[derive(Debug, Clone, Get)]
@@ -49,10 +67,39 @@ pub struct Categories(PathBuf, fs::ReadDir);
pub struct Ebuilds(PathBuf, fs::ReadDir); pub struct Ebuilds(PathBuf, fs::ReadDir);
impl Repo { impl Repo {
pub fn new<P: AsRef<Path>>(path: P) -> Self { pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
Self { let name_path = path.as_ref().join("profiles/repo_name");
let name = match fs::read_to_string(&name_path) {
Ok(repo_name) => repo_name,
Err(e) if matches!(e.kind(), io::ErrorKind::NotFound) => {
return Err(Error::Invalid("missing repo_name".to_string()));
}
Err(e) => return Err(Error::Io(name_path, e)),
};
let package_mask_path = path.as_ref().join("profiles/package.mask");
let package_mask =
match fs::read_to_string(&package_mask_path).map(|s| read_package_mask(&s)) {
Ok(Ok(package_mask)) => package_mask,
Ok(Err(e)) => return Err(e),
Err(e) if matches!(e.kind(), io::ErrorKind::NotFound) => Vec::new(),
Err(e) => return Err(Error::Io(package_mask_path, e)),
};
let arch_list_path = path.as_ref().join("profiles/arch.list");
let arch_list = match fs::read_to_string(&arch_list_path).map(|s| read_arch_list(&s)) {
Ok(Ok(arch_list)) => arch_list,
Ok(Err(e)) => return Err(e),
Err(e) if matches!(e.kind(), io::ErrorKind::NotFound) => Vec::new(),
Err(e) => return Err(Error::Io(arch_list_path, e)),
};
Ok(Self {
path: path.as_ref().to_path_buf(), path: path.as_ref().to_path_buf(),
} name,
package_mask,
arch_list,
})
} }
pub fn categories(&self) -> Result<Categories, Error> { pub fn categories(&self) -> Result<Categories, Error> {
@@ -63,6 +110,10 @@ impl Repo {
fs::read_dir(&path).map_err(|e| Error::Io(path, e))?, 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 { impl Category {
@@ -316,6 +367,34 @@ fn read_idepend(input: &str) -> Option<Result<Vec<Depend<Atom>>, Error>> {
Some(parse_depends(line)) Some(parse_depends(line))
} }
fn read_package_mask(input: &str) -> Result<Vec<Atom>, Error> {
Ok(profile::LineBasedFileExpr::<Atom>::parser()
.separated_by_with_opt_trailing(ascii_whitespace1())
.many()
.parse_finished(InputIter::new(input))
.map_err(|it| Error::Parser(it.rest().to_string()))?
.into_iter()
.filter_map(|expr| match expr {
LineBasedFileExpr::Comment => None,
LineBasedFileExpr::Expr(atom) => Some(atom),
})
.collect())
}
fn read_arch_list(input: &str) -> Result<Vec<Arch>, Error> {
Ok(LineBasedFileExpr::<Arch>::parser()
.separated_by_with_opt_trailing(ascii_whitespace1())
.many()
.parse_finished(InputIter::new(input))
.map_err(|it| Error::Parser(it.rest().to_string()))?
.into_iter()
.filter_map(|expr| match expr {
LineBasedFileExpr::Comment => None,
LineBasedFileExpr::Expr(arch) => Some(arch),
})
.collect())
}
fn parse_depends(line: &str) -> Result<Vec<Depend<Atom>>, Error> { fn parse_depends(line: &str) -> Result<Vec<Depend<Atom>>, Error> {
Depend::<Atom>::parser() Depend::<Atom>::parser()
.separated_by(ascii_whitespace1()) .separated_by(ascii_whitespace1())

20
src/repo/parsers.rs Normal file
View File

@@ -0,0 +1,20 @@
use mon::{Parser, ParserIter, ascii_alphanumeric, one_of};
use crate::{Parseable, repo::Arch};
impl<'a> Parseable<'a, &'a str> for Arch {
type Parser = impl Parser<&'a str, Output = Self>;
fn parser() -> Self::Parser {
let start = ascii_alphanumeric();
let rest = ascii_alphanumeric()
.or(one_of("-".chars()))
.repeated()
.many();
start
.and(rest)
.recognize()
.map(|output: &str| Arch(output.to_string()))
}
}

View File

@@ -0,0 +1 @@
sources += files('mod.rs', 'parsers.rs')

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

View File

@@ -0,0 +1,6 @@
sources += files('mod.rs', 'parsers.rs')
subdir('make_defaults')
subdir('package')
subdir('packages')
subdir('useflags')

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

@@ -0,0 +1,194 @@
//! Evaluate profiles:
//! ```rust
//! use gentoo_utils::repo::Repo;
//!
//! let repo = Repo::new("/var/db/repos/gentoo")
//! .expect("failed to open repo");
//! let profile = repo.evaluate_profile("default/linux/23.0")
//! .expect("failed to evaluate profile");
//!
//! for (key, value) in profile.make_defaults() {
//! println!("{key} = {value}");
//! }
//! ```
use std::{
collections::HashMap,
fs::{self, File},
io::{self, Read},
path::{Path, PathBuf},
};
use get::Get;
use itertools::Itertools;
use crate::{Parseable, atom::Atom, repo::ebuild::Eapi, useflag::UseFlag};
mod make_defaults;
mod package;
mod package_use;
mod packages;
mod parsers;
mod useflags;
#[derive(Debug, Clone)]
pub(super) 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),
#[error("parser error: {0}")]
Parser(String),
}
#[derive(Debug, Clone, Get)]
pub struct Profile {
#[get(kind = "deref")]
path: PathBuf,
eapi: Eapi,
deprecated: Option<String>,
#[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 eapi_path = path.as_ref().join("eapi");
let eapi = match fs::read_to_string(&eapi_path)
.map(|s| Eapi::parse(s.trim()).map_err(str::to_string))
{
Ok(Ok(eapi)) => eapi,
jturnerusa marked this conversation as resolved Outdated

I think the default behavior for no eapi file per the spec is to treat it as eapi 0. Do we do that here, or are we erroring out if there's no eapi file?

Maybe it should assign eapi 0 on failure to parse (no eapi file), then later during evaluation of the parsed profile you throw an error saying you don't support eapi 0. That way eapi 0 (or other versions) could be supported later without having to change parsing logic.

I think the default behavior for no eapi file per the spec is to treat it as eapi 0. Do we do that here, or are we erroring out if there's no eapi file? Maybe it should assign eapi 0 on failure to parse (no eapi file), then later during evaluation of the parsed profile you throw an error saying you don't support eapi 0. That way eapi 0 (or other versions) could be supported later without having to change parsing logic.
Ok(Err(rest)) => return Err(Error::Parser(rest)),
Err(e) if matches!(e.kind(), io::ErrorKind::NotFound) => Eapi::parse("0").unwrap(),
Err(e) => return Err(Error::Io(eapi_path, e)),
};
let deprecated_path = path.as_ref().join("deprecated");
let deprecated = match fs::read_to_string(&deprecated_path) {
Ok(string) => Some(string),
Err(e) if matches!(e.kind(), io::ErrorKind::NotFound) => None,
Err(e) => return Err(Error::Io(deprecated_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,
eapi,
deprecated,
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 @@
sources += files('mod.rs', 'parsers.rs')

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 @@
sources += files('mod.rs', 'parsers.rs')

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 @@
sources += files('mod.rs', 'parsers.rs')

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 @@
sources += files('mod.rs')

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('porthole')
subdir('repo') subdir('repo')
subdir('profile')
foreach test, test_args : tests foreach test, test_args : tests
stem = fs.stem(test) stem = fs.stem(test)
@@ -15,5 +16,6 @@ foreach test, test_args : tests
link_with: [gentoo_utils], link_with: [gentoo_utils],
), ),
args: test_args, args: test_args,
timeout: 0,
) )
endforeach endforeach

View File

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

View File

@@ -0,0 +1,2 @@
amd64
aarch64

View File

@@ -0,0 +1 @@
USE="base"

View File

@@ -0,0 +1 @@
base

View File

@@ -0,0 +1 @@
app-editors/emacs gui

View File

@@ -0,0 +1 @@
..

View File

@@ -0,0 +1 @@
gui

View File

@@ -0,0 +1 @@
USE="emacs"

View File

@@ -0,0 +1 @@
app-editors/vim

View File

@@ -0,0 +1 @@
app-editors/emacs default

View File

@@ -0,0 +1 @@
*app-editors/emacs

View File

@@ -0,0 +1 @@
default

View File

@@ -0,0 +1,2 @@
USE="selinux"
SELINUX_TYPE="sys.subj.portage"

View File

@@ -0,0 +1 @@
*sec-policy/selinux-base

View File

@@ -0,0 +1 @@
caps

View File

@@ -0,0 +1 @@
jit

View File

@@ -0,0 +1 @@
USE="-base"

View File

@@ -0,0 +1 @@
app-editors/emacs -default

View File

@@ -0,0 +1,3 @@
../base
../features/selinux
../features/emacs/gui

View File

@@ -0,0 +1 @@
-jit

View File

@@ -0,0 +1 @@
app-editors/vim

View File

@@ -0,0 +1 @@
mockrepo

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").expect("failed to open repo");
for profile in profiles {
repo.evaluate_profile(profile)
.unwrap_or_else(|e| panic!("failed to evaluate profile: {profile}: {e}"));
}
}

View File

@@ -0,0 +1,89 @@
use std::env::args;
use gentoo_utils::{atom::Atom, repo::Repo, useflag::UseFlag};
use itertools::Itertools;
fn main() {
let repo_path = args()
.nth(1)
.expect("expected path to mockrepo as first argument");
let repo = Repo::new(&repo_path).expect("failed to read repo");
let global_package_mask = repo
.package_mask()
.iter()
.map(Atom::to_string)
.sorted()
.collect::<Vec<_>>();
assert_eq!(global_package_mask, vec!["app-editors/vim"]);
assert_eq!(
repo.arch_list()
.iter()
.map(|arch| arch.get())
.collect::<Vec<_>>(),
vec!["amd64", "aarch64"]
);
let profile = repo
.evaluate_profile("gentoo-desktop")
.expect("failed to evaluate profile");
let r#use = profile.make_defaults()["USE"]
.split_ascii_whitespace()
.sorted()
.collect::<Vec<_>>();
assert_eq!(r#use, vec!["emacs", "selinux",]);
let packages = profile
.packages()
.iter()
.map(Atom::to_string)
.sorted()
.collect::<Vec<_>>();
assert_eq!(
packages,
vec!["app-editors/emacs", "sec-policy/selinux-base"]
);
let packages_mask = profile
.package_mask()
.iter()
.map(Atom::to_string)
.sorted()
.collect::<Vec<_>>();
assert_eq!(packages_mask, vec!["app-editors/vim"]);
let emacs_use = profile
.package_use()
.iter()
.find_map(|(atom, flags)| {
if atom.clone().into_cp().to_string() == "app-editors/emacs" {
Some(flags)
} else {
None
}
})
.unwrap()
.iter()
.map(UseFlag::to_string)
.sorted()
.collect::<Vec<_>>();
assert_eq!(emacs_use, vec!["gui"]);
let use_force = profile
.use_force()
.iter()
.map(UseFlag::to_string)
.sorted()
.collect::<Vec<_>>();
assert_eq!(use_force, vec!["base", "caps", "default", "gui"]);
assert!(profile.use_mask().is_empty());
}

View File

@@ -3,7 +3,7 @@ use std::error::Error;
use gentoo_utils::repo::Repo; use gentoo_utils::repo::Repo;
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
let repo = Repo::new("/var/db/repos/gentoo"); let repo = Repo::new("/var/db/repos/gentoo").unwrap();
for result in repo.categories()? { for result in repo.categories()? {
let cat = result?; let cat = result?;