feat: compilation respect SOURCE_DATE_EPOCH (#1631)

* Changes before error encountered

Co-authored-by: Myriad-Dreamin <35292584+Myriad-Dreamin@users.noreply.github.com>

* feat: errors caused by copilot

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
Myriad-Dreamin 2025-07-09 19:09:58 +08:00 committed by GitHub
parent 1258c0f2ca
commit 0849d6c641
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 81 additions and 6 deletions

View file

@ -237,6 +237,8 @@ pub enum Interrupt<F: CompilerFeat> {
ChangeTask(ProjectInsId, TaskInputs),
/// Font changes.
Font(Arc<F::FontResolver>),
/// Creation timestamp changes.
CreationTimestamp(Option<i64>),
/// Memory file changes.
Memory(MemoryEvent),
/// File system event.
@ -253,6 +255,7 @@ impl<F: CompilerFeat> fmt::Debug for Interrupt<F> {
write!(f, "ChangeTask({id:?}, entry={:?})", change.entry.is_some())
}
Interrupt::Font(..) => write!(f, "Font(..)"),
Interrupt::CreationTimestamp(ts) => write!(f, "CreationTimestamp({ts:?})"),
Interrupt::Memory(..) => write!(f, "Memory(..)"),
Interrupt::Fs(..) => write!(f, "Fs(..)"),
}
@ -482,6 +485,7 @@ impl<F: CompilerFeat + Send + Sync + 'static, Ext: Default + 'static> ProjectCom
self.primary.verse.vfs().fork(),
self.primary.verse.registry.clone(),
self.primary.verse.font_resolver.clone(),
self.primary.verse.creation_timestamp,
);
let mut proj =
@ -600,6 +604,18 @@ impl<F: CompilerFeat + Send + Sync + 'static, Ext: Default + 'static> ProjectCom
}
});
}
Interrupt::CreationTimestamp(creation_timestamp) => {
self.projects().for_each(|proj| {
let timestamp_changed = proj.verse.increment_revision(|verse| {
verse.set_creation_timestamp(creation_timestamp);
// Creation timestamp changes affect compilation
verse.creation_timestamp_changed()
});
if timestamp_changed {
proj.reason.see(reason_by_entry_change());
}
});
}
Interrupt::Memory(event) => {
log::debug!("ProjectCompiler: memory event incoming");

View file

@ -76,6 +76,7 @@ impl WorldProvider for CompileOnceArgs {
inputs,
packages,
fonts,
self.creation_timestamp,
))
}
@ -168,6 +169,7 @@ impl WorldProvider for (ProjectInput, ImmutPath) {
Arc::new(LazyHash::new(inputs)),
packages,
Arc::new(fonts),
None, // creation_timestamp - not available in project file context
))
}
@ -215,6 +217,7 @@ impl LspUniverseBuilder {
inputs: ImmutDict,
package_registry: HttpRegistry,
font_resolver: Arc<FontResolverImpl>,
creation_timestamp: Option<i64>,
) -> LspUniverse {
let package_registry = Arc::new(package_registry);
let resolver = Arc::new(RegistryPathMapper::new(package_registry.clone()));
@ -233,6 +236,7 @@ impl LspUniverseBuilder {
Vfs::new(resolver, SystemAccessModel {}),
package_registry,
font_resolver,
creation_timestamp,
)
}

View file

@ -63,6 +63,7 @@ pub fn run_with_sources<T>(source: &str, f: impl FnOnce(&mut LspUniverse, PathBu
Default::default(),
LspUniverseBuilder::resolve_package(None, None),
FONT_RESOLVER.clone(),
None,
);
let sources = source.split("-----");

View file

@ -46,6 +46,7 @@ impl TypstBrowserUniverse {
vfs,
registry,
Arc::new(font_resolver),
None, // creation_timestamp - not used in browser context
)
}
}

View file

@ -33,6 +33,10 @@ pub struct CompileOpts {
#[serde(rename = "withEmbeddedFonts")]
#[serde_as(as = "Vec<AsCowBytes>")]
pub with_embedded_fonts: Vec<Cow<'static, [u8]>>,
/// Fixed creation timestamp for the world.
#[serde(rename = "creationTimestamp")]
pub creation_timestamp: Option<i64>,
}
#[serde_as]

View file

@ -43,6 +43,7 @@ impl TypstSystemUniverse {
let registry: Arc<HttpRegistry> = Arc::default();
let resolver = Arc::new(RegistryPathMapper::new(registry.clone()));
let inputs = std::mem::take(&mut opts.inputs);
let timestamp = opts.creation_timestamp;
// todo: enable html
Ok(Self::new_raw(
@ -52,6 +53,7 @@ impl TypstSystemUniverse {
Vfs::new(resolver, SystemAccessModel {}),
registry,
Arc::new(Self::resolve_fonts(opts)?),
timestamp,
))
}
@ -86,6 +88,7 @@ impl SystemUniverseBuilder {
Vfs::new(resolver, SystemAccessModel {}),
registry,
font_resolver,
None, // creation_timestamp - not used in this context
)
}

View file

@ -64,6 +64,8 @@ pub struct CompilerUniverse<F: CompilerFeat> {
/// The current revision of the universe.
pub revision: NonZeroUsize,
/// The creation timestamp for reproducible builds.
pub creation_timestamp: Option<i64>,
}
/// Creates, snapshots, and manages the compiler universe.
@ -81,6 +83,7 @@ impl<F: CompilerFeat> CompilerUniverse<F> {
vfs: Vfs<F::AccessModel>,
package_registry: Arc<F::Registry>,
font_resolver: Arc<F::FontResolver>,
creation_timestamp: Option<i64>,
) -> Self {
Self {
entry,
@ -92,6 +95,7 @@ impl<F: CompilerFeat> CompilerUniverse<F> {
font_resolver,
registry: package_registry,
vfs,
creation_timestamp,
}
}
@ -168,6 +172,7 @@ impl<F: CompilerFeat> CompilerUniverse<F> {
slots: Default::default(),
},
now: OnceLock::new(),
creation_timestamp: self.creation_timestamp,
};
mutant.map(|m| w.task(m)).unwrap_or(w)
@ -177,6 +182,7 @@ impl<F: CompilerFeat> CompilerUniverse<F> {
pub fn increment_revision<T>(&mut self, f: impl FnOnce(&mut RevisingUniverse<F>) -> T) -> T {
f(&mut RevisingUniverse {
vfs_revision: self.vfs.revision(),
creation_timestamp_changed: false,
font_changed: false,
font_revision: self.font_resolver.revision(),
registry_changed: false,
@ -324,6 +330,7 @@ pub struct RevisingUniverse<'a, F: CompilerFeat> {
view_changed: bool,
vfs_revision: NonZeroUsize,
font_changed: bool,
creation_timestamp_changed: bool,
font_revision: Option<NonZeroUsize>,
registry_changed: bool,
registry_revision: Option<NonZeroUsize>,
@ -392,6 +399,12 @@ impl<F: CompilerFeat> RevisingUniverse<'_, F> {
self.inner.inputs = inputs;
}
/// Set the creation timestamp for reproducible builds.
pub fn set_creation_timestamp(&mut self, creation_timestamp: Option<i64>) {
self.creation_timestamp_changed = creation_timestamp != self.inner.creation_timestamp;
self.inner.creation_timestamp = creation_timestamp;
}
pub fn set_entry_file(&mut self, entry_file: Arc<Path>) -> SourceResult<()> {
self.view_changed = true;
self.inner.set_entry_file_(entry_file)
@ -418,6 +431,10 @@ impl<F: CompilerFeat> RevisingUniverse<'_, F> {
self.font_changed && is_revision_changed(self.font_revision, self.font_resolver.revision())
}
pub fn creation_timestamp_changed(&self) -> bool {
self.creation_timestamp_changed
}
pub fn registry_changed(&self) -> bool {
self.registry_changed
&& is_revision_changed(self.registry_revision, self.registry.revision())
@ -461,6 +478,8 @@ pub struct CompilerWorld<F: CompilerFeat> {
/// The current datetime if requested. This is stored here to ensure it is
/// always the same within one compilation. Reset between compilations.
now: OnceLock<NowStorage>,
/// The creation timestamp for reproducible builds.
creation_timestamp: Option<i64>,
}
impl<F: CompilerFeat> Clone for CompilerWorld<F> {
@ -502,6 +521,7 @@ impl<F: CompilerFeat> CompilerWorld<F> {
revision: self.revision,
source_db: self.source_db.clone(),
now: self.now.clone(),
creation_timestamp: self.creation_timestamp,
};
if root_changed {
@ -756,8 +776,16 @@ impl<F: CompilerFeat> World for CompilerWorld<F> {
#[cfg(any(feature = "web", feature = "system"))]
fn today(&self, offset: Option<i64>) -> Option<Datetime> {
use chrono::{Datelike, Duration};
// todo: typst respects creation_timestamp, but we don't...
let now = self.now.get_or_init(|| tinymist_std::time::now().into());
let now = self.now.get_or_init(|| {
if let Some(timestamp) = self.creation_timestamp {
chrono::DateTime::from_timestamp(timestamp, 0)
.unwrap_or_else(|| tinymist_std::time::now().into())
.into()
} else {
tinymist_std::time::now().into()
}
});
let naive = match offset {
None => now.naive_local(),
@ -781,8 +809,16 @@ impl<F: CompilerFeat> World for CompilerWorld<F> {
#[cfg(not(any(feature = "web", feature = "system")))]
fn today(&self, offset: Option<i64>) -> Option<Datetime> {
use tinymist_std::time::{now, to_typst_time, Duration};
// todo: typst respects creation_timestamp, but we don't...
let now = self.now.get_or_init(|| now().into());
let now = self.now.get_or_init(|| {
if let Some(timestamp) = self.creation_timestamp {
tinymist_std::time::UtcDateTime::from_unix_timestamp(timestamp)
.unwrap_or_else(|_| now().into())
.into()
} else {
now().into()
}
});
let now = offset
.and_then(|offset| {

View file

@ -645,12 +645,14 @@ impl Config {
Option<bool>,
&Vec<PathBuf>,
Option<&CompileFontArgs>,
Option<i64>,
Option<Arc<Path>>,
) {
(
self.system_fonts,
&self.font_paths,
self.typst_extra_args.as_ref().map(|e| &e.font),
self.creation_timestamp(),
self.entry_resolver
.root(self.entry_resolver.resolve_default().as_ref()),
)

View file

@ -191,8 +191,16 @@ impl ServerState {
let fonts = config.fonts();
let packages = LspUniverseBuilder::resolve_package(cert_path.clone(), Some(&package));
let verse =
LspUniverseBuilder::build(entry, export_target, features, inputs, packages, fonts);
let creation_timestamp = config.creation_timestamp();
let verse = LspUniverseBuilder::build(
entry,
export_target,
features,
inputs,
packages,
fonts,
creation_timestamp,
);
// todo: unify filesystem watcher
let (dep_tx, dep_rx) = mpsc::unbounded_channel();