pijul nest
guest [sign in]

Allow merging two packages based on regular expressions of their name

pmeunier
Jun 29, 2025, 9:00 PM
LIUJQXB752UIHJFF32LKJ7ECUZ2VDIFDDHBLAUVNBRTPDYNCAUYQC

Dependencies

  • [2] 5IT7CXFG Debugging the dependency resolution (Tarjan's SCC algorithm)
  • [3] R7J4254Z Reorganisation of the frontend
  • [4] ODUDDQRY Adding the OCaml interface
  • [5] BDEVQIAU Handle cyclic Ubuntu dependencies
  • [6] UWQB743K First working shell (with ocaml code)
  • [7] SI454P2V Documentation and cleanup

Change contents

  • replacement in src/main.rs at line 226
    [4.1][4.3392:3465](),[4.3392][4.3392:3465]()
    let p = download_extract_deps(&index, &self.deb_client, &r.name)
    [4.1]
    [4.3465]
    let link_extra: Vec<_> = r
    .link_extra
    .into_iter()
    .map(|l| {
    (
    regex::Regex::new(&l.pkg).unwrap(),
    regex::Regex::new(&l.dep).unwrap(),
    )
    })
    .collect();
    let p = download_extract_deps(&index, &self.deb_client, &r.name, &link_extra)
  • replacement in src/lib.rs at line 355
    [4.12451][4.12451:12517]()
    let p = extract::download_extract_deps(&index, self, pkg)
    [4.12451]
    [4.12517]
    let p = extract::download_extract_deps(&index, self, pkg, &[])
  • replacement in src/extract.rs at line 35
    [4.310][4.310:397]()
    transitive_deps: Vec<Arc<PathBuf>>,
    transitive_deps_h: BTreeSet<Arc<PathBuf>>,
    [4.310]
    [4.397]
    last_transitive: (usize, usize),
    transitive_deps: Vec<Arc<(usize, PathBuf)>>,
    transitive_deps_h: BTreeSet<Arc<(usize, PathBuf)>>,
  • edit in src/extract.rs at line 41
    [4.502]
    [4.502]
    v: usize,
  • edit in src/extract.rs at line 50
    [4.19944]
    [4.17694]
    link_extra: &[(regex::Regex, regex::Regex)],
  • edit in src/extract.rs at line 54
    [4.507][4.507:613]()
    let mut seen: HashMap<_, (Vec<&str>, usize)> = HashMap::new();
    let mut stack = vec![(pkg, None)];
  • edit in src/extract.rs at line 55
    [4.648]
    [4.648]
    // We first run a DFS of the dependencies and store them in
    // `vertices` for later.
    let mut stack = vec![(pkg, None)];
    let mut seen: HashMap<_, (Vec<&str>, usize)> = HashMap::new();
  • edit in src/extract.rs at line 67
    [2.127]
    [4.948]
    let v_len = vertices.len();
  • edit in src/extract.rs at line 82
    [4.1608]
    [4.1608]
    last_transitive: (0, 0),
  • edit in src/extract.rs at line 85
    [4.1705]
    [4.1705]
    v: v_len,
  • edit in src/extract.rs at line 97
    [4.2103]
    [2.163]
    // Turn dependencies into their index in `vertices`.
  • edit in src/extract.rs at line 117
    [2.451]
    [4.2259]
    // Two independent missions in one loop:
    // - Hash the context
    // - Collect the `ld_paths` of each package in the SCC.
  • replacement in src/extract.rs at line 122
    [4.2289][4.2289:2883](),[4.2883][4.21419:21437](),[4.18790][4.21419:21437](),[4.21419][4.21419:21437](),[4.18952][4.21705:21719](),[4.21705][4.21705:21719](),[4.21719][4.2884:3089]()
    let mut context_hasher = blake3::Hasher::new();
    context_hasher.update(vertices[*v].pkg.sha256.unwrap().as_bytes());
    for d in vertices[*v].deps.iter() {
    let w = &vertices[*d];
    if w.scc == vertices[*v].scc {
    // If in the same SCC, we can't know anything other than the SHA256.
    context_hasher.update(w.pkg.sha256.unwrap().as_bytes());
    } else {
    context_hasher
    .update(w.final_path.as_ref().unwrap().to_str().unwrap().as_bytes());
    }
    }
    vertices[*v].context_path =
    Some(Arc::new(client.store_path.join(
    data_encoding::HEXLOWER.encode(context_hasher.finalize().as_bytes()),
    )));
    [4.2289]
    [2.452]
    hash_context(&mut vertices, *v, &client.store_path, link_extra);
  • edit in src/extract.rs at line 133
    [4.3099]
    [2.810]
    // Once we have the ld paths of the entire SCC, add them to
    // each vertex of the SCC.
  • edit in src/extract.rs at line 143
    [2.1050]
    [2.1050]
    // Then, find the libraries in the `ld_paths` of each package
    // of the SCC.
  • replacement in src/extract.rs at line 147
    [2.1080][2.1080:1126]()
    // Find the libs in this package.
    [2.1080]
    [2.1126]
    let a = vertices[*v].transitive_deps.len();
  • edit in src/extract.rs at line 149
    [2.1164]
    [2.1164]
    vertices[*v].last_transitive = (a, vertices[*v].transitive_deps.len());
  • edit in src/extract.rs at line 152
    [4.21720]
    [4.3100]
    // And finalize each of the component (patch ELFs etc).
  • replacement in src/extract.rs at line 154
    [4.3130][4.3130:3202]()
    let f = finalize(&mut vertices, client, &files, *v).await?;
    [4.3130]
    [4.3202]
    span!(Level::DEBUG, "Finalize");
    let f = finalize(&mut vertices, client, link_extra, &files, *v).await?;
  • edit in src/extract.rs at line 359
    [4.20959]
    [4.25314]
    fn hash_context(
    vertices: &mut [Vertex],
    v: usize,
    store_path: &Path,
    link_extra: &[(regex::Regex, regex::Regex)],
    ) {
    let mut context_hasher = blake3::Hasher::new();
    context_hasher.update(vertices[v].pkg.sha256.unwrap().as_bytes());
    for d in vertices[v].deps.iter() {
    let w = &vertices[*d];
    if w.scc == vertices[v].scc {
    // If in the same SCC, we can't know anything other than the SHA256.
    context_hasher.update(w.pkg.sha256.unwrap().as_bytes());
    } else {
    context_hasher.update(w.final_path.as_ref().unwrap().to_str().unwrap().as_bytes());
    }
    }
    for (a, b) in link_extra {
    context_hasher.update(a.to_string().as_bytes());
    context_hasher.update(b.to_string().as_bytes());
    }
    vertices[v].context_path = Some(Arc::new(
    store_path.join(data_encoding::HEXLOWER.encode(context_hasher.finalize().as_bytes())),
    ));
    }
  • replacement in src/extract.rs at line 390
    [4.21181][4.21181:21274]()
    async fn link_extra(&mut self, final_path: &Path, extra: &[&str]) -> Result<(), Error> {
    [4.21181]
    [4.21274]
    async fn link_extra<S: AsRef<str> + std::fmt::Debug>(
    &mut self,
    final_path: &Path,
    extra: &[S],
    ) -> Result<(), Error> {
  • edit in src/extract.rs at line 397
    [4.21373]
    [4.21373]
    let path = path.as_ref();
  • replacement in src/extract.rs at line 463
    [4.27399][4.7195:7419]()
    let path = Arc::new(
    self.context_path
    .as_ref()
    .unwrap()
    .join(ld.strip_prefix("/").unwrap()),
    );
    [4.27399]
    [4.23105]
    let path = Arc::new((self.v, ld.strip_prefix("/").unwrap().to_path_buf()));
  • edit in src/extract.rs at line 471
    [4.27622]
    [4.27622]
    }
  • replacement in src/extract.rs at line 473
    [4.27623][4.23224:23365](),[4.23365][4.27719:27899](),[4.27719][4.27719:27899]()
    async fn patch_elf(
    &mut self,
    f: &Path,
    dest_path: &Path,
    files: &Files,
    ) -> Result<bool, Error> {
    use elfedit::*;
    info!("patch_elf {:?}", f);
    let file = std::fs::OpenOptions::new()
    .read(true)
    .write(true)
    .open(&f)?;
    [4.27623]
    [4.27899]
    async fn patch_elf<'a>(
    vertices: &mut [Vertex<'a>],
    v: usize,
    f: &Path,
    dest_path: &Path,
    files: &Files,
    ) -> Result<bool, Error> {
    use elfedit::*;
    info!("patch_elf {:?}", f);
    let file = std::fs::OpenOptions::new()
    .read(true)
    .write(true)
    .open(&f)?;
  • replacement in src/extract.rs at line 487
    [4.27900][4.23366:23573](),[4.23573][4.27980:28122](),[4.27980][4.27980:28122]()
    let mut elf = match Elf::open(&file) {
    Ok(elf) => elf,
    Err(e) => {
    info!("error opening {:?}: {:?}", file, e);
    return Ok(false);
    }
    };
    info!("patching {:?}", f);
    let Some(parsed) = elf.parse().unwrap() else {
    info!("No dynamic section");
    [4.27900]
    [4.28122]
    let mut elf = match Elf::open(&file) {
    Ok(elf) => elf,
    Err(e) => {
    info!("error opening {:?}: {:?}", file, e);
  • replacement in src/extract.rs at line 492
    [4.28152][4.28152:28163](),[4.28163][4.23574:23719](),[4.23719][4.28222:28411](),[4.28222][4.28222:28411]()
    };
    let needed: Vec<_> = parsed
    .needed()
    .map(|x| x.unwrap().to_str().unwrap().to_string())
    .collect();
    let interp = parsed.interpreter();
    if let Some(interp) = interp.unwrap() {
    let interp = interp.to_str().unwrap();
    let files = files.lock().unwrap();
    [4.28152]
    [4.28411]
    }
    };
    info!("patching {:?}", f);
    let Some(parsed) = elf.parse().unwrap() else {
    info!("No dynamic section");
    return Ok(false);
    };
    let needed: Vec<_> = parsed
    .needed()
    .map(|x| x.unwrap().to_str().unwrap().to_string())
    .collect();
    let interp = parsed.interpreter();
    if let Some(interp) = interp.unwrap() {
    let interp = interp.to_str().unwrap();
    let files = files.lock().unwrap();
  • replacement in src/extract.rs at line 508
    [4.28412][4.28412:28478]()
    let p = Path::new(interp).strip_prefix("/").unwrap();
    [4.28412]
    [4.28478]
    let p = Path::new(interp).strip_prefix("/").unwrap();
  • replacement in src/extract.rs at line 510
    [4.28479][4.28479:29348]()
    let subst = if let Some(subst) = files.get(p) {
    subst.join(p)
    } else if interp.starts_with("/usr")
    || interp.starts_with("/lib")
    || interp.starts_with("/bin")
    {
    // https://www.freedesktop.org/wiki/Software/systemd/TheCaseForTheUsrMerge/
    let p2 = "usr".to_string() + interp;
    let p2 = Path::new(&p2);
    debug!("{:?}", p2);
    if let Some(subst) = files.get(p2) {
    subst.join(p2)
    } else {
    error!("No subst for {:?}", p2);
    let mut p2 = p2.to_path_buf();
    while p2.pop() {
    debug!("p2 = {:?} {:?}", p2, files.get(&p2));
    }
    return Ok(false);
    }
    [4.28479]
    [4.29348]
    let subst = if let Some(subst) = files.get(p) {
    subst.join(p)
    } else if interp.starts_with("/usr")
    || interp.starts_with("/lib")
    || interp.starts_with("/bin")
    {
    // https://www.freedesktop.org/wiki/Software/systemd/TheCaseForTheUsrMerge/
    let p2 = "usr".to_string() + interp;
    let p2 = Path::new(&p2);
    debug!("{:?}", p2);
    if let Some(subst) = files.get(p2) {
    subst.join(p2)
  • replacement in src/extract.rs at line 523
    [4.29369][4.29369:29657]()
    // TODO: not sure what else to do here, we
    // might want to set the interpreter to a
    // different value (equivalent to "recompiling
    // everything" on Nix).
    info!("Interpreter is {interp}. Already patched?");
    [4.29369]
    [4.29657]
    error!("No subst for {:?}", p2);
    let mut p2 = p2.to_path_buf();
    while p2.pop() {
    debug!("p2 = {:?} {:?}", p2, files.get(&p2));
    }
  • replacement in src/extract.rs at line 529
    [4.29691][4.29691:29778](),[4.29778][4.23720:23770](),[4.23770][4.29812:29872](),[4.29812][4.29812:29872](),[4.29872][4.23771:23809](),[4.23809][4.29904:29936](),[4.29904][4.29904:29936]()
    };
    let subst = CString::new(subst.to_str().unwrap()).unwrap();
    info!("set interpreter {:?}", subst);
    elf.set_interpreter(subst.to_bytes_with_nul());
    } else if needed.is_empty() {
    // No need to patch
    [4.29691]
    [4.29936]
    }
    } else {
    // TODO: not sure what else to do here, we
    // might want to set the interpreter to a
    // different value (equivalent to "recompiling
    // everything" on Nix).
    info!("Interpreter is {interp}. Already patched?");
  • replacement in src/extract.rs at line 537
    [4.29966][4.29966:30057](),[4.30057][2.2298:2349]()
    }
    let mut deps_h = BTreeSet::new();
    let mut path = String::new();
    debug!("needed: {:?} -> {:?}", f, needed);
    [4.29966]
    [2.2349]
    };
    let subst = CString::new(subst.to_str().unwrap()).unwrap();
    info!("set interpreter {:?}", subst);
    elf.set_interpreter(subst.to_bytes_with_nul());
    } else if needed.is_empty() {
    // No need to patch
    return Ok(false);
    }
  • replacement in src/extract.rs at line 546
    [2.2350][2.2350:2405](),[2.2405][4.23865:23914](),[4.7552][4.23865:23914](),[4.23865][4.23865:23914](),[4.23914][4.30140:30177](),[4.30140][4.30140:30177]()
    for dep in self.transitive_deps.iter().rev() {
    debug!("dep of {:?}: {:?}", f, dep);
    if !deps_h.insert(dep) {
    [2.2350]
    [4.23915]
    let mut deps_h = BTreeSet::new();
    let mut path = String::new();
    debug!("needed: {:?} -> {:?}", f, needed);
    let mut satisfied = HashMap::new();
    // First, prefer local deps.
    for ld in vertices[v].ld_path.iter() {
    let mut p = vertices[v]
    .context_path
    .clone()
    .unwrap()
    .join(ld.strip_prefix("/").unwrap());
    let mut at_least_one = false;
    for n in needed.iter() {
    let e = satisfied.entry(n);
    use std::collections::hash_map::Entry;
    if let Entry::Occupied(_) = e {
  • replacement in src/extract.rs at line 564
    [4.23955][4.23955:24106](),[4.24106][2.2406:2552]()
    let mut dep = dep.to_path_buf();
    let mut is_needed = false;
    for n in needed.iter() {
    dep.push(&n);
    let exists = tokio::fs::metadata(&dep).await.is_ok();
    if exists {
    debug!("exists {:?}", dep);
    [4.23955]
    [2.2552]
    p.push(&n);
    debug!("trying local path {:?}", p);
    if tokio::fs::metadata(&p).await.is_ok() {
    if let Entry::Vacant(e) = e {
    e.insert(());
  • replacement in src/extract.rs at line 571
    [2.2570][2.2570:2607](),[2.2607][4.24176:24203](),[4.24176][4.24176:24203]()
    is_needed |= exists;
    dep.pop();
    [2.2570]
    [4.24203]
    at_least_one = true;
    p.pop();
    break;
    } else {
    p.pop();
  • replacement in src/extract.rs at line 577
    [4.24217][4.24217:24245](),[4.24245][4.30177:30217](),[4.30177][4.30177:30217]()
    if !is_needed {
    continue;
    }
    [4.24217]
    [4.30217]
    }
    if at_least_one {
  • replacement in src/extract.rs at line 582
    [4.30296][4.30296:30346]()
    path.push_str(dep.to_str().unwrap());
    [4.30296]
    [4.30346]
    path.push_str(p.to_str().unwrap())
  • edit in src/extract.rs at line 584
    [4.30356]
    [2.2608]
    }
  • replacement in src/extract.rs at line 586
    [2.2609][2.2609:2800]()
    // Add potential local libs.
    let mut last_added = None;
    for (d, _) in find_files(self.downloaded.path.clone())? {
    if last_added.as_deref() == d.parent() {
    [2.2609]
    [2.2800]
    for dep_ in vertices[v].transitive_deps.iter().rev() {
    let (dep, dep_path) = dep_.as_ref();
    if !deps_h.insert(dep) {
    continue;
    }
    let mut dep = if vertices[*dep].scc == vertices[v].scc {
    vertices[*dep].context_path.clone().unwrap().join(dep_path)
    } else {
    vertices[*dep].final_path.clone().unwrap().join(dep_path)
    };
    debug!("dep of {:?}: {:?}", f, dep);
    let mut is_needed = false;
    for n in needed.iter() {
    let e = satisfied.entry(n);
    use std::collections::hash_map::Entry;
    if let Entry::Occupied(_) = e {
  • replacement in src/extract.rs at line 604
    [2.2840][2.2840:3611]()
    for n in needed.iter() {
    if d.file_name().unwrap().to_str().unwrap() == n {
    last_added = d.parent().map(|x| x.to_path_buf());
    if !path.is_empty() {
    path.push(':')
    }
    let suffix = d
    .parent()
    .unwrap()
    .strip_prefix(&self.downloaded.path)
    .unwrap();
    path.push_str(
    self.context_path
    .as_ref()
    .unwrap()
    .join(suffix)
    .to_str()
    .unwrap(),
    );
    [2.2840]
    [2.3611]
    dep.push(&n);
    let exists = tokio::fs::metadata(&dep).await.is_ok();
    if exists {
    if let Entry::Vacant(e) = e {
    e.insert(());
  • edit in src/extract.rs at line 610
    [2.3629]
    [2.3629]
    debug!("exists {:?}", dep);
    is_needed = true;
  • edit in src/extract.rs at line 613
    [2.3643]
    [2.3643]
    dep.pop();
    }
    if !is_needed {
    continue;
  • edit in src/extract.rs at line 618
    [2.3653]
    [2.3653]
    if !path.is_empty() {
    path.push(':')
    }
    path.push_str(dep.to_str().unwrap());
    }
  • replacement in src/extract.rs at line 624
    [2.3654][4.30356:30423](),[4.30356][4.30356:30423](),[4.30423][4.24246:24274](),[4.24274][4.30447:30494](),[4.30447][4.30447:30494]()
    path.push('\0');
    info!("Setting path {:?}", path);
    if path.len() > 1 {
    elf.set_runpath(&path.as_bytes());
    [2.3654]
    [4.30494]
    /*
    // Add potential local libs.
    let mut last_added = None;
    for (d, _) in find_files(vertices[v].downloaded.path.clone())? {
    if last_added.as_deref() == d.parent() {
    continue;
  • replacement in src/extract.rs at line 631
    [4.30504][4.30504:30626]()
    debug!("patching into {:?}", dest_path);
    Ok(elf.update(Some(dest_path)).unwrap()) // map_err(From::from)
    [4.30504]
    [4.30626]
    for n in needed.iter() {
    if d.file_name().unwrap().to_str().unwrap() == n {
    last_added = d.parent().map(|x| x.to_path_buf());
    if !path.is_empty() {
    path.push(':')
    }
    let suffix = d
    .parent()
    .unwrap()
    .strip_prefix(&vertices[v].downloaded.path)
    .unwrap();
    path.push_str(
    vertices[v]
    .context_path
    .as_ref()
    .unwrap()
    .join(suffix)
    .to_str()
    .unwrap(),
    );
    }
    }
  • edit in src/extract.rs at line 654
    [4.30632]
    [4.7553]
    */
    path.push('\0');
    info!("Setting path {:?}", path);
    if path.len() > 1 {
    elf.set_runpath(&path.as_bytes());
    }
    debug!("patching into {:?}", dest_path);
    Ok(elf.update(Some(dest_path)).unwrap()) // map_err(From::from)
  • edit in src/extract.rs at line 668
    [4.7633]
    [4.7633]
    link_extra: &[(regex::Regex, regex::Regex)],
  • replacement in src/extract.rs at line 683
    [4.8224][4.8224:8273]()
    for dep in vertices[v].deps.clone().iter() {
    [4.8224]
    [2.3655]
    for dep in vertices[v].deps.iter() {
  • edit in src/extract.rs at line 689
    [2.3789]
    [4.8273]
    let direct = if vertices[*dep].scc == vertices[v].scc {
    vertices[*dep].context_path.clone().unwrap()
    } else {
    vertices[*dep].final_path.clone().unwrap()
    };
  • edit in src/extract.rs at line 700
    [4.8425]
    [4.31089]
    let l = ld.strip_prefix("/").unwrap();
    if std::fs::metadata(&direct.join(l)).is_ok() {
    let path = Arc::new((*dep, l.to_path_buf()));
    if transitive_deps_h.insert(path.clone()) {
    transitive_deps.push(path);
    }
    }
  • replacement in src/extract.rs at line 708
    [4.31099][2.3790:3863]()
    debug!("transitive {:?}", vertices[*dep].transitive_deps.len());
    [4.31099]
    [4.8426]
  • replacement in src/extract.rs at line 718
    [4.8760][4.8760:8827]()
    deps_paths.push(if vertices[*dep].scc == vertices[v].scc {
    [4.8760]
    [4.8827]
    debug!(
    "adding direct dep: {:?} {:?} {:?}",
    vertices[*dep].scc, vertices[v].scc, direct
    );
    deps_paths.push(direct)
    }
    debug!("transitive {:#?}", transitive_deps);
    // Direct deps should come after transitive ones, in order to be
    // picked up earlier when patching the ELFs.
    for dep in vertices[v].deps.iter() {
    let direct = if vertices[*dep].scc == vertices[v].scc {
  • replacement in src/extract.rs at line 735
    [4.8956][4.8956:8967]()
    })
    [4.8956]
    [4.8967]
    };
    for ld in vertices[*dep].ld_path.iter() {
    let l = ld.strip_prefix("/").unwrap();
    if std::fs::metadata(&direct.join(l)).is_ok() {
    let path = Arc::new((*dep, l.to_path_buf()));
    // Push even if it already exists since we need direct
    // deps to come after indirect ones.
    transitive_deps.push(path);
    }
    }
  • edit in src/extract.rs at line 747
    [4.8973]
    [4.8973]
  • edit in src/extract.rs at line 758
    [4.31482][4.9394:9456](),[4.9523][4.31725:31726](),[4.31725][4.31725:31726]()
    let initial_deps_len = vertices[v].transitive_deps.len();
  • replacement in src/extract.rs at line 769
    [4.9874][4.9874:10042]()
    info!("create final path for {dest:?}");
    match vertices[v]
    .create_final_path(client, &files, &dest, &base_package_name)
    .await
    [4.9874]
    [4.10042]
    info!(
    "create final path for {dest:?} ({:?})",
    vertices[v].pkg.package
    );
    match create_final_path(
    client,
    &files,
    vertices,
    v,
    &dest,
    &base_package_name,
    link_extra,
    )
    .await
  • edit in src/extract.rs at line 823
    [4.11311][4.11311:11587]()
    // Replace prefix for all library deps we've just added,
    // in order to get a Merkle tree.
    for dep in &mut vertices[v].transitive_deps[initial_deps_len..] {
    let end = dep.strip_prefix(&*dest).unwrap();
    *dep = Arc::new(final_path.join(&end));
    }
  • edit in src/extract.rs at line 846
    [4.12379][4.29808:29809](),[4.29808][4.29808:29809](),[4.29809][4.12380:12402](),[4.12402][4.29809:29908](),[4.29809][4.29809:29908](),[4.29940][4.29940:30415](),[4.30415][4.12403:12542](),[4.12542][4.30544:30579](),[4.30544][4.30544:30579](),[4.30579][4.12543:12613](),[4.12613][4.30644:30699](),[4.30644][4.30644:30699]()
    impl<'a> Vertex<'a> {
    async fn create_final_path(
    &mut self,
    client: &Client,
    files: &Files,
    dest: &Path,
    base_package_name: &str,
    ) -> Result<PathBuf, Error> {
    // Link the required libexec before hashing.
    let tmp = async_tempfile::TempDir::new_in(dest.parent().unwrap()).await?;
    self.link_extra(&tmp.dir_path(), &["usr/libexec", "usr/lib/gcc"])
    .await?;
    // Patch the ELFs, now that we have all the deps.
    let mut hashing = Vec::new();
    let mut hashes = Vec::with_capacity(hashing.len());
    debug!("create_final_path {:?}", self.downloaded.path);
    for (f, meta) in find_files(self.downloaded.path.to_path_buf())? {
    debug!("f = {:?}", f);
    let rel = f.strip_prefix(&self.downloaded.path).unwrap();
    let dest_path = tmp.dir_path().join(&rel);
  • replacement in src/extract.rs at line 847
    [4.30700][4.30700:30831]()
    if meta.is_dir() {
    tokio::fs::create_dir_all(dest_path).await.unwrap_or(());
    continue;
    [4.30700]
    [4.30831]
    async fn copy(from: &Path, to: &Path) -> Result<(), std::io::Error> {
    let mut stack = vec![from.to_path_buf()];
    while let Some(elt) = stack.pop() {
    if let Ok(mut dir) = tokio::fs::read_dir(&elt).await {
    let p = elt.strip_prefix(&from).unwrap();
    tokio::fs::create_dir_all(&to.join(&p)).await.unwrap();
    while let Some(e) = dir.next_entry().await.unwrap() {
    stack.push(e.path());
  • edit in src/extract.rs at line 856
    [4.30845]
    [4.30845]
    } else {
    let p = elt.strip_prefix(&from).unwrap();
    debug!("copy {:?} to {:?}", elt, to.join(&p));
    tokio::fs::hard_link(&elt, &to.join(&p)).await.unwrap_or(());
    }
    }
    Ok(())
    }
  • replacement in src/extract.rs at line 865
    [4.30846][4.30846:32085]()
    if meta.is_symlink() {
    // Relink the file to the subst.
    let target = tokio::fs::read_link(&f).await?;
    debug!("relink {:?} -> {:?} {:?}", f, target, target.is_relative());
    let subst = {
    let l = files.lock().unwrap();
    let target_rel = rel.parent().unwrap().join(&target);
    if let Some(subst) = l.get(&target_rel) {
    Some(subst.join(&target_rel))
    } else {
    None
    }
    };
    if let Some(subst) = subst {
    debug!("relink to {:?}", dest_path);
    tokio::fs::symlink(&subst, &dest_path).await?;
    hashes.push((f, subst.to_str().unwrap().to_string()))
    } else {
    // Leave the symlink untouched
    // tokio::fs::create_dir_all(dest_path.parent().unwrap()).await.unwrap_or(());
    debug!("symlink {:?} {:?} {:?}", target, dest_path, f);
    tokio::fs::symlink(&target, &dest_path).await?;
    hashes.push((f, target.to_str().unwrap().to_string()));
    [4.30846]
    [4.32085]
    async fn create_final_path<'a>(
    client: &Client,
    files: &Files,
    vertices: &mut [Vertex<'a>],
    v: usize,
    dest: &Path,
    base_package_name: &str,
    link_extra: &[(regex::Regex, regex::Regex)],
    ) -> Result<PathBuf, Error> {
    // Link the required libexec before hashing.
    let tmp = async_tempfile::TempDir::new_in(dest.parent().unwrap()).await?;
    vertices[v]
    .link_extra(&tmp.dir_path(), &["usr/libexec", "usr/lib/gcc"])
    .await
    .unwrap();
    for (pkg, dep) in link_extra {
    if pkg.is_match(vertices[v].pkg.package) {
    for d in (0..v).rev() {
    if dep.is_match(vertices[d].pkg.package) {
    debug!(
    "match, copying {:?} to {:?}",
    vertices[d].context_path, vertices[v].context_path
    );
    copy(&vertices[d].context_path.clone().unwrap(), &tmp.dir_path()).await?
  • edit in src/extract.rs at line 891
    [4.32103][4.32103:32129]()
    continue;
  • edit in src/extract.rs at line 892
    [4.32143]
    [4.32143]
    }
    }
  • replacement in src/extract.rs at line 895
    [4.32144][4.32144:32465]()
    if !self
    .patch_elf(&f, &dest_path, &files)
    .await
    .unwrap_or(false)
    {
    // Hard link
    debug!("hard link {:?} {:?}", f, dest_path);
    tokio::fs::hard_link(&f, &dest_path).await.unwrap_or(());
    }
    [4.32144]
    [4.32465]
    // Patch the ELFs, now that we have all the deps.
    let mut hashing = Vec::new();
    let mut hashes = Vec::with_capacity(hashing.len());
    debug!("create_final_path {:?}", vertices[v].downloaded.path);
    for (f, meta) in find_files(vertices[v].downloaded.path.to_path_buf())? {
    debug!("f = {:?}", f);
    let rel = f.strip_prefix(&vertices[v].downloaded.path).unwrap();
    let dest_path = tmp.dir_path().join(&rel);
  • replacement in src/extract.rs at line 904
    [4.32466][4.32466:33026]()
    hashing.push(tokio::spawn(async move {
    // hash + write
    info!("hashing {:?}", f);
    if let Ok(file) = tokio::fs::File::open(&dest_path).await {
    let mut hasher = blake3::Hasher::new();
    hash_reader(file, &mut hasher).await.unwrap();
    let hex = data_encoding::HEXLOWER.encode(hasher.finalize().as_bytes());
    Ok::<_, Error>(Some((f, hex)))
    } else {
    Ok(None)
    }
    }));
    [4.32466]
    [4.33026]
    if meta.is_dir() {
    tokio::fs::create_dir_all(dest_path).await.unwrap_or(());
    continue;
  • edit in src/extract.rs at line 908
    [4.33036][4.33036:33066]()
    info!("patched all");
  • replacement in src/extract.rs at line 909
    [4.33067][4.33067:33194]()
    for h in hashing.into_iter() {
    if let Some(h) = h.await.unwrap().unwrap() {
    hashes.push(h)
    [4.33067]
    [4.33194]
    if meta.is_symlink() {
    // Relink the file to the subst.
    let target = tokio::fs::read_link(&f).await?;
    debug!("relink {:?} -> {:?} {:?}", f, target, target.is_relative());
    let subst = {
    let l = files.lock().unwrap();
    let target_rel = rel.parent().unwrap().join(&target);
    if let Some(subst) = l.get(&target_rel) {
    Some(subst.join(&target_rel))
    } else {
    None
    }
    };
    if let Some(subst) = subst {
    debug!("relink to {:?}", dest_path);
    tokio::fs::symlink(&subst, &dest_path).await?;
    hashes.push((f, subst.to_str().unwrap().to_string()))
    } else {
    // Leave the symlink untouched
    // tokio::fs::create_dir_all(dest_path.parent().unwrap()).await.unwrap_or(());
    debug!("symlink {:?} {:?} {:?}", target, dest_path, f);
    tokio::fs::symlink(&target, &dest_path).await?;
    hashes.push((f, target.to_str().unwrap().to_string()));
  • edit in src/extract.rs at line 933
    [4.33208]
    [4.33208]
    continue;
  • edit in src/extract.rs at line 935
    [4.33218][4.33218:33293]()
    hashes.sort_by(|a, b| a.0.cmp(&b.0));
    info!("hashed all");
  • replacement in src/extract.rs at line 936
    [4.33294][4.33294:33349]()
    let mut output_hasher = blake3::Hasher::new();
    [4.33294]
    [4.33349]
    if !patch_elf(vertices, v, &f, &dest_path, &files)
    .await
    .unwrap_or(false)
  • replacement in src/extract.rs at line 940
    [4.33359][4.33359:33566](),[4.33566][4.12614:12671](),[4.12671][4.33618:33975](),[4.33618][4.33618:33975](),[4.33975][4.37569:37583](),[4.37569][4.37569:37583]()
    let blakesums = tmp.dir_path().join("blake3sums");
    let mut file = tokio::fs::File::create(&blakesums).await?;
    for (path, hash) in hashes {
    let path = path
    .strip_prefix(&self.downloaded.path)
    .unwrap()
    .to_str()
    .unwrap();
    file.write_all(hash.as_bytes()).await?;
    file.write_all(b" ").await?;
    file.write_all(path.as_bytes()).await?;
    file.write_all(b"\n").await?;
    writeln!(output_hasher, "{} {}", hash, path)?;
    }
    [4.33359]
    [4.37583]
    // Hard link
    debug!("hard link {:?} {:?}", f, dest_path);
    tokio::fs::hard_link(&f, &dest_path).await.unwrap_or(());
  • edit in src/extract.rs at line 944
    [4.37593][4.33976:34179]()
    let final_path = client.store_path.join(&format!(
    "{}-{}",
    data_encoding::HEXLOWER.encode(output_hasher.finalize().as_bytes()),
    base_package_name,
    ));
  • replacement in src/extract.rs at line 945
    [4.34180][4.34180:34467]()
    {
    let transitive = tmp.dir_path().join("paths");
    let mut file = tokio::fs::File::create(&transitive).await?;
    for path in self.files.iter() {
    file.write_all(path.as_bytes()).await?;
    file.write_all(b"\n").await?;
    [4.34180]
    [4.34467]
    hashing.push(tokio::spawn(async move {
    // hash + write
    info!("hashing {:?}", f);
    if let Ok(file) = tokio::fs::File::open(&dest_path).await {
    let mut hasher = blake3::Hasher::new();
    hash_reader(file, &mut hasher).await.unwrap();
    let hex = data_encoding::HEXLOWER.encode(hasher.finalize().as_bytes());
    Ok::<_, Error>(Some((f, hex)))
    } else {
    Ok(None)
  • edit in src/extract.rs at line 956
    [4.34481]
    [4.34481]
    }));
    }
    info!("patched all");
    for h in hashing.into_iter() {
    if let Some(h) = h.await.unwrap().unwrap() {
    hashes.push(h)
  • edit in src/extract.rs at line 964
    [4.34491]
    [4.34491]
    }
    hashes.sort_by(|a, b| a.0.cmp(&b.0));
    info!("hashed all");
  • replacement in src/extract.rs at line 968
    [4.34492][4.12672:12813](),[4.12813][4.34623:34733](),[4.34623][4.34623:34733]()
    for (f, _) in find_dirs(self.downloaded.path.to_path_buf())? {
    let rel = f.strip_prefix(&self.downloaded.path).unwrap();
    self.files
    .insert(Arc::new(final_path.join(rel).to_str().unwrap().to_string()));
    [4.34492]
    [4.34733]
    let mut output_hasher = blake3::Hasher::new();
    {
    let blakesums = tmp.dir_path().join("blake3sums");
    let mut file = tokio::fs::File::create(&blakesums).await?;
    for (path, hash) in hashes {
    let path = path
    .strip_prefix(&vertices[v].downloaded.path)
    .unwrap()
    .to_str()
    .unwrap();
    file.write_all(hash.as_bytes()).await?;
    file.write_all(b" ").await?;
    file.write_all(path.as_bytes()).await?;
    file.write_all(b"\n").await?;
    writeln!(output_hasher, "{} {}", hash, path)?;
  • edit in src/extract.rs at line 984
    [4.34743]
    [4.34743]
    }
    let final_path = client.store_path.join(&format!(
    "{}-{}",
    data_encoding::HEXLOWER.encode(output_hasher.finalize().as_bytes()),
    base_package_name,
    ));
  • replacement in src/extract.rs at line 991
    [4.34744][4.34744:34891]()
    info!("rename {:?} to {:?}", tmp.dir_path(), dest);
    tokio::fs::rename(tmp.dir_path(), dest).await?;
    std::mem::forget(tmp);
    [4.34744]
    [4.34891]
    {
    let transitive = tmp.dir_path().join("paths");
    let mut file = tokio::fs::File::create(&transitive).await?;
    for path in vertices[v].files.iter() {
    file.write_all(path.as_bytes()).await?;
    file.write_all(b"\n").await?;
    }
    }
  • replacement in src/extract.rs at line 1000
    [4.34892][4.34892:34915]()
    Ok(final_path)
    [4.34892]
    [4.37593]
    for (f, _) in find_dirs(vertices[v].downloaded.path.to_path_buf())? {
    let rel = f.strip_prefix(&vertices[v].downloaded.path).unwrap();
    vertices[v]
    .files
    .insert(Arc::new(final_path.join(rel).to_str().unwrap().to_string()));
  • edit in src/extract.rs at line 1006
    [4.37599]
    [4.37599]
    info!("rename {:?} to {:?}", tmp.dir_path(), dest);
    tokio::fs::rename(tmp.dir_path(), dest).await.unwrap();
    std::mem::forget(tmp);
    Ok(final_path)
  • edit in elpe/lib/elpegrpc.proto at line 78
    [4.55824]
    [4.55824]
    message LinkExtra {
    string pkg = 1;
    string dep = 2;
    }
  • replacement in elpe/lib/elpegrpc.proto at line 86
    [4.55903][4.63857:63894]()
    repeated string path_patterns = 3;
    [4.55903]
    [4.55903]
    repeated LinkExtra link_extra = 3;
  • edit in elpe/lib/derivation.ml at line 61
    [3.2288]
    [3.2288]
    let link_extra = [ ("libstd-rust-.*-dev", "libstd-rust.*") ]
  • replacement in elpe/lib/derivation.ml at line 65
    [3.2334][3.2334:2405]()
    let req = Elpegrpc.Elpe.UbuntuPackageRequest.make ~index ~name () in
    [3.2334]
    [3.2405]
    let link_extra =
    List.map
    (fun (pkg, dep) -> Elpegrpc.Elpe.LinkExtra.make ~pkg ~dep ())
    link_extra
    in
    let req =
    Elpegrpc.Elpe.UbuntuPackageRequest.make ~index ~name ~link_extra ()
    in