11 Commits

Author SHA1 Message Date
789a68ff36 read arch.list
Some checks failed
Gentoo Utils / build-oci-image (push) Successful in 10s
Gentoo Utils / grep (push) Failing after 2m2s
Gentoo Utils / check-format (push) Failing after 2m6s
Gentoo Utils / docs (push) Failing after 2m5s
Gentoo Utils / build (push) Failing after 5s
Gentoo Utils / test (push) Has been skipped
Gentoo Utils / fuzz (push) Has been skipped
2025-12-22 08:47:09 +00:00
4969177893 read global package.mask 2025-12-22 08:47:09 +00:00
fd3efced29 add mockrepo tests 2025-12-22 08:47:09 +00:00
106e8de849 read repo_name when opening repos 2025-12-22 08:47:09 +00:00
7e61192fba default to Eapi 0 if no eapi file exists 2025-12-22 08:47:09 +00:00
c34afdbf14 add docs to profile module 2025-12-22 08:47:09 +00:00
18b7a468b2 read deprecated file in profiles 2025-12-22 08:47:09 +00:00
93fc514763 read eapi file in profiles 2025-12-22 08:47:09 +00:00
81a8701581 add profile related source files to sources variable 2025-12-22 08:47:09 +00:00
956fb629cb impl profile evaluation 2025-12-22 08:47:09 +00:00
96708614ba port to meson cargo
All checks were successful
Gentoo Utils / build-oci-image (push) Successful in 22s
Gentoo Utils / grep (push) Successful in 1m1s
Gentoo Utils / check-format (push) Successful in 1m26s
Gentoo Utils / docs (push) Successful in 1m31s
Gentoo Utils / build (push) Successful in 1m43s
Gentoo Utils / test (push) Successful in 36s
Gentoo Utils / fuzz (push) Successful in 1m21s
Use the new unstable meson cargo support. This simplifies the
meson.build script and allows to use crates such as clap that require
picking up features from Cargo.toml.

This also allows us to not embed thiserror in subprojects, and instead
use a wrap file with a custom meson.build and some patches to make it
compile without running its build.rs script.
2025-12-22 08:05:20 +00:00
50 changed files with 1632 additions and 11 deletions

View File

@@ -6,7 +6,7 @@ RUN getuto
RUN emerge-webrsync
RUN emerge --jobs=$(nproc) \
RUN emerge \
=dev-lang/rust-bin-9999 \
llvm-core/clang \
llvm-core/lld \
@@ -14,7 +14,6 @@ RUN emerge --jobs=$(nproc) \
sys-process/parallel \
net-libs/nodejs
RUN git clone https://jturnerusa.dev/cgit/ebuilds/ /var/db/repos/spawns
COPY etc/portage/repos.conf /etc/portage/

View File

@@ -20,7 +20,7 @@ fn main() -> Result<(), Box<dyn Error>> {
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();
for category in repo.categories()? {

View File

@@ -17,7 +17,7 @@ fn main() -> Result<(), Box<dyn Error>> {
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();
for category in repo.categories()? {

View File

@@ -69,7 +69,8 @@ pub mod atom;
/// ```
/// 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") {
/// 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('profile')

View File

@@ -11,14 +11,21 @@ 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::{LineBasedFileExpr, Profile},
},
useflag::IUseFlag,
};
pub mod ebuild;
mod parsers;
pub mod profile;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("invalid repo: {0}")]
Invalid(String),
#[error("io error: {0}")]
Io(PathBuf, io::Error),
#[error("error while reading directory: {0:?}: {1}")]
@@ -27,12 +34,23 @@ pub enum Error {
Unicode(PathBuf),
#[error("parser error: {0}")]
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)]
pub struct Repo {
#[get(kind = "deref")]
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)]
@@ -49,10 +67,39 @@ pub struct Categories(PathBuf, fs::ReadDir);
pub struct Ebuilds(PathBuf, fs::ReadDir);
impl Repo {
pub fn new<P: AsRef<Path>>(path: P) -> Self {
Self {
pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
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(),
}
name,
package_mask,
arch_list,
})
}
pub fn categories(&self) -> Result<Categories, Error> {
@@ -63,6 +110,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 {
@@ -316,6 +367,34 @@ fn read_idepend(input: &str) -> Option<Result<Vec<Depend<Atom>>, Error>> {
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> {
Depend::<Atom>::parser()
.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,
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('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,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;
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()? {
let cat = result?;