Skip to content

Commit 28077fd

Browse files
committed
Remove the half-backed lockfile resolution
1 parent a49154c commit 28077fd

File tree

4 files changed

+69
-166
lines changed

4 files changed

+69
-166
lines changed

core/src/cache.rs

+8-37
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::eval::cache::Cache as EvalCache;
55
use crate::eval::Closure;
66
#[cfg(feature = "nix-experimental")]
77
use crate::nix_ffi;
8-
use crate::package::{self, LockFile, LockedPackageSource};
8+
use crate::package::{self, LockedPackageSource, ResolvedLockFile};
99
use crate::parser::{lexer::Lexer, ErrorTolerantParser};
1010
use crate::position::TermPos;
1111
use crate::program::FieldPath;
@@ -100,7 +100,7 @@ pub struct Cache {
100100
error_tolerance: ErrorTolerance,
101101
import_paths: Vec<PathBuf>,
102102

103-
lock_file: LockFile,
103+
lock_file: ResolvedLockFile,
104104

105105
#[cfg(debug_assertions)]
106106
/// Skip loading the stdlib, used for debugging purpose
@@ -350,7 +350,7 @@ impl Cache {
350350
stdlib_ids: None,
351351
error_tolerance,
352352
import_paths: Vec::new(),
353-
lock_file: LockFile::default(),
353+
lock_file: ResolvedLockFile::default(),
354354

355355
#[cfg(debug_assertions)]
356356
skip_stdlib: false,
@@ -364,7 +364,7 @@ impl Cache {
364364
self.import_paths.extend(paths.map(PathBuf::from));
365365
}
366366

367-
pub fn set_lock_file(&mut self, lock_file: LockFile) {
367+
pub fn set_lock_file(&mut self, lock_file: ResolvedLockFile) {
368368
self.lock_file = lock_file;
369369
}
370370

@@ -1346,39 +1346,10 @@ impl ImportResolver for Cache {
13461346
pos: &TermPos,
13471347
) -> Result<(ResolvedTerm, FileId), ImportError> {
13481348
let (possible_parents, pkg_id) = if let Some(pkg) = pkg {
1349-
let pkg_id = if let Some(parent_pkg) = parent.and_then(|p| self.package.get(&p)) {
1350-
// The parent package should have come from the lock file.
1351-
let parent = self
1352-
.lock_file
1353-
.data
1354-
.packages
1355-
.get(parent_pkg)
1356-
.ok_or_else(|| ImportError::InternalError {
1357-
msg: format!("unknown parent package {parent_pkg:?}"),
1358-
pos: *pos,
1359-
})?;
1360-
parent
1361-
.dependencies
1362-
.get(pkg)
1363-
.ok_or_else(|| ImportError::MissingDependency {
1364-
parent: Some(Box::new((parent.name.clone(), parent_pkg.clone()))),
1365-
missing: pkg.clone(),
1366-
pos: *pos,
1367-
})?
1368-
.clone()
1369-
} else {
1370-
self.lock_file
1371-
.data
1372-
.dependencies
1373-
.get(pkg)
1374-
.ok_or_else(|| ImportError::MissingDependency {
1375-
parent: None,
1376-
missing: pkg.clone(),
1377-
pos: *pos,
1378-
})?
1379-
.clone()
1380-
};
1381-
(vec![pkg_id.local_path()], Some(pkg_id))
1349+
let pkg_id = self
1350+
.lock_file
1351+
.get(parent.and_then(|p| self.package.get(&p)), pkg, pos)?;
1352+
(vec![pkg_id.local_path()], Some(pkg_id.clone()))
13821353
} else {
13831354
// `parent` is the file that did the import. We first look in its containing directory, followed by
13841355
// the directories in the import path.

core/src/error/mod.rs

-15
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
//!
33
//! Define error types for different phases of the execution, together with functions to generate a
44
//! [codespan](https://crates.io/crates/codespan-reporting) diagnostic from them.
5-
use std::path::PathBuf;
65
76
pub use codespan::{FileId, Files};
87
pub use codespan_reporting::diagnostic::{Diagnostic, Label, LabelStyle};
@@ -583,14 +582,6 @@ pub enum ImportError {
583582
missing: Name,
584583
pos: TermPos,
585584
},
586-
/// A package tried to import a file from a path it is not permitted to access.
587-
/// (For example, a git dependency tried to import a path from a parent directory.)
588-
InvalidPath {
589-
/// The path that they tried to import.
590-
attempted: PathBuf,
591-
/// The places they're allowed to import from.
592-
restriction: PathBuf,
593-
},
594585
}
595586

596587
/// An error occurred during serialization.
@@ -2587,12 +2578,6 @@ impl IntoDiagnostics<FileId> for ImportError {
25872578

25882579
vec![Diagnostic::error().with_message(msg).with_labels(labels)]
25892580
}
2590-
ImportError::InvalidPath {
2591-
attempted,
2592-
restriction,
2593-
} => {
2594-
todo!()
2595-
}
25962581
}
25972582
}
25982583
}

core/src/package.rs

+59-112
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
//! This module contains the part of package management that's needed by the evaluator.
2+
//!
3+
//! In particular, it doesn't contain any support for version resolution or
4+
//! dependency fetching, but defines lockfiles and allows them to be used to
5+
//! resolve package dependencies to paths.
6+
17
use std::{
28
collections::HashMap,
39
path::{Path, PathBuf},
@@ -7,10 +13,7 @@ use std::{
713
use directories::ProjectDirs;
814
use serde::{Deserialize, Serialize};
915

10-
use crate::{
11-
cache::{normalize_abs_path, normalize_path},
12-
error::ImportError,
13-
};
16+
use crate::{error::ImportError, position::TermPos};
1417

1518
const ID_LEN: usize = 20;
1619

@@ -134,8 +137,16 @@ impl From<Name> for String {
134137
/// A locked package source uniquely identifies the source of the package (with a specific version).
135138
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
136139
pub enum LockedPackageSource {
137-
Git { repo: String, tree: ObjectId },
138-
Path { path: PathBuf },
140+
Git {
141+
repo: String,
142+
tree: ObjectId,
143+
/// Path of the package relative to the git repo root.
144+
#[serde(default)]
145+
path: PathBuf,
146+
},
147+
Path {
148+
path: PathBuf,
149+
},
139150
}
140151

141152
impl LockedPackageSource {
@@ -158,31 +169,6 @@ impl LockedPackageSource {
158169
matches!(self, LockedPackageSource::Path { .. })
159170
}
160171

161-
fn canonicalize(
162-
self,
163-
parent_path: &Path,
164-
restriction: Option<&Path>,
165-
) -> Result<Self, ImportError> {
166-
assert!(parent_path.is_absolute());
167-
assert!(restriction.map_or(true, Path::is_absolute));
168-
169-
match self {
170-
LockedPackageSource::Git { .. } => Ok(self),
171-
LockedPackageSource::Path { path } => {
172-
let path = normalize_abs_path(&parent_path.join(path));
173-
if let Some(restriction) = restriction {
174-
if !path.starts_with(restriction) {
175-
return Err(ImportError::InvalidPath {
176-
attempted: path.to_owned(),
177-
restriction: restriction.to_owned(),
178-
});
179-
}
180-
}
181-
Ok(LockedPackageSource::Path { path })
182-
}
183-
}
184-
}
185-
186172
/// Is this locked package available offline? If not, it needs to be fetched.
187173
pub fn is_available_offline(&self) -> bool {
188174
// We consider path-dependencies to be always available offline, even if they don't exist.
@@ -197,109 +183,70 @@ impl LockedPackageSource {
197183
}
198184
}
199185

186+
/// A lock file that's been fully resolved, including path dependencies.
200187
#[derive(Clone, Debug, Default)]
201-
pub struct LockFile {
188+
pub struct ResolvedLockFile {
202189
/// Absolute path to the lock file's parent directory.
203190
///
204-
/// Path dependencies at the top-level are resolved relative to this. (Path dependencies
205-
/// of dependencies are resolved relative to the dependency that imported them.)
191+
/// Path dependencies at the top-level are resolved relative to this.
206192
pub path: PathBuf,
207-
pub data: LockFileData,
193+
/// The inner lockfile, which is now guaranteed to have closed dependencies.
194+
pub inner: LockFile,
208195
}
209196

210197
/// A lock file, specifying versions and names for all recursive dependencies.
198+
///
199+
/// This defines the on-disk format for lock files.
211200
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
212-
pub struct LockFileData {
201+
pub struct LockFile {
213202
/// The dependencies of the current (top-level) package.
214203
pub dependencies: HashMap<Name, LockedPackageSource>,
215204
/// All packages that we know about, and the dependencies of each one.
216205
///
217-
/// We guarantee that this package list is closed, in the sense that everything that is
218-
/// a dependency of some package here has its own entry in the map.
206+
/// Note that the package list is not guaranteed to be closed: path dependencies
207+
/// cannot have their dependencies resolved in the on-disk lockfile because they
208+
/// can change at any time. *Some* path dependencies (for example, path dependencies
209+
/// that are local to a git depencency repo) may have resolved dependencies.
219210
pub packages: HashMap<LockedPackageSource, LockFileEntry>,
220211
}
221212

222-
impl LockFileData {
213+
impl ResolvedLockFile {
223214
/// Are all the packages mentioned in this lockfile available offline?
224215
pub fn is_available_offline(&self) -> bool {
225-
self.packages
216+
self.inner
217+
.packages
226218
.keys()
227219
.all(LockedPackageSource::is_available_offline)
228220
}
229-
}
230221

231-
impl LockFile {
232-
pub fn canonicalize_paths(&mut self) -> Result<(), ImportError> {
233-
let mut new = LockFile {
234-
path: self.path.clone(),
235-
data: LockFileData::default(),
222+
pub fn get(
223+
&self,
224+
parent: Option<&LockedPackageSource>,
225+
pkg: &Name,
226+
pos: &TermPos,
227+
) -> Result<&LockedPackageSource, ImportError> {
228+
// The parent package should have come from the lock file.
229+
let (deps, parent_name) = if let Some(parent) = parent {
230+
let parent_pkg =
231+
self.inner
232+
.packages
233+
.get(parent)
234+
.ok_or_else(|| ImportError::InternalError {
235+
msg: format!("unknown parent package {parent:?}"),
236+
pos: *pos,
237+
})?;
238+
239+
(&parent_pkg.dependencies, Some(&parent_pkg.name))
240+
} else {
241+
(&self.inner.dependencies, None)
236242
};
237-
238-
struct StackEntry<'a> {
239-
abs_parent: LockedPackageSource,
240-
restriction: Option<PathBuf>,
241-
entry: &'a LockFileEntry,
242-
}
243-
let mut stack = Vec::new();
244-
245-
fn add_dep<'stack, 'a: 'stack>(
246-
stack: &'stack mut Vec<StackEntry<'a>>,
247-
source: &LockedPackageSource,
248-
parent: &Path,
249-
restriction: Option<&Path>,
250-
orig_packages: &'a HashMap<LockedPackageSource, LockFileEntry>,
251-
) -> Result<LockedPackageSource, ImportError> {
252-
let abs_source = source.clone().canonicalize(parent, restriction)?;
253-
254-
if let Some(entry) = orig_packages.get(source) {
255-
let parent_path = abs_source.local_path();
256-
stack.push(StackEntry {
257-
restriction: if abs_source.is_path() {
258-
restriction.map(Path::to_owned)
259-
} else {
260-
Some(parent_path.clone())
261-
},
262-
abs_parent: abs_source.clone(),
263-
entry,
264-
});
265-
}
266-
Ok(abs_source)
267-
}
268-
269-
for (name, source) in &self.data.dependencies {
270-
let abs_source = add_dep(&mut stack, source, &self.path, None, &self.data.packages)?;
271-
new.data.dependencies.insert(name.clone(), abs_source);
272-
}
273-
274-
while let Some(StackEntry {
275-
abs_parent,
276-
restriction,
277-
entry,
278-
}) = stack.pop()
279-
{
280-
let mut abs_dependencies = HashMap::new();
281-
for (name, source) in &entry.dependencies {
282-
let abs_source = add_dep(
283-
&mut stack,
284-
source,
285-
&abs_parent.local_path(),
286-
restriction.as_deref(),
287-
&self.data.packages,
288-
)?;
289-
290-
// FIXME: insist that if there's overlap then everything agrees
291-
abs_dependencies.insert(name.clone(), abs_source);
292-
}
293-
new.data.packages.insert(
294-
abs_parent,
295-
LockFileEntry {
296-
name: entry.name.clone(),
297-
dependencies: abs_dependencies,
298-
},
299-
);
300-
}
301-
302-
Ok(())
243+
deps.get(pkg).ok_or_else(|| ImportError::MissingDependency {
244+
parent: parent
245+
.zip(parent_name)
246+
.map(|(p, n)| Box::new((n.clone(), p.clone()))),
247+
missing: pkg.clone(),
248+
pos: *pos,
249+
})
303250
}
304251
}
305252

core/src/program.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use crate::{
3030
identifier::LocIdent,
3131
label::Label,
3232
metrics::increment,
33-
package::LockFile,
33+
package::ResolvedLockFile,
3434
term::{
3535
make as mk_term, make::builder, record::Field, BinaryOp, MergePriority, RichTerm, Term,
3636
},
@@ -309,7 +309,7 @@ impl<EC: EvalCache> Program<EC> {
309309
self.vm.import_resolver_mut().add_import_paths(paths);
310310
}
311311

312-
pub fn set_lock_file(&mut self, lock: LockFile) {
312+
pub fn set_lock_file(&mut self, lock: ResolvedLockFile) {
313313
self.vm.import_resolver_mut().set_lock_file(lock)
314314
}
315315

0 commit comments

Comments
 (0)