Skip to content

Commit 1786aa3

Browse files
committed
Merge remote-tracking branch 'tt/master' into xinslu-master
2 parents 6372457 + a3389b9 commit 1786aa3

11 files changed

Lines changed: 146 additions & 26 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ tempfile = "^3.1"
122122

123123
[package.metadata.vcpkg]
124124
git = "https://github.com/microsoft/vcpkg"
125-
rev = "ea222747b888b8d63df56240b262db38b095c68f"
125+
rev = "63366443439398a62afc9a63b34b9a3ba63b1cae"
126126
overlay-triplets-path = "dist/vcpkg-triplets"
127127

128128
# If other targets start using custom triplets like x86_64-pc-windows-msvc,

crates/bridge_core/src/lib.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ pub struct CoreBridgeLauncher<'a> {
219219
hooks: &'a mut dyn DriverHooks,
220220
status: &'a mut dyn StatusBackend,
221221
security: SecuritySettings,
222+
filesystem_emulation_settings: FsEmulationSettings,
222223
}
223224

224225
impl<'a> CoreBridgeLauncher<'a> {
@@ -242,8 +243,25 @@ impl<'a> CoreBridgeLauncher<'a> {
242243
hooks,
243244
status,
244245
security,
246+
filesystem_emulation_settings: FsEmulationSettings::default(),
245247
}
246248
}
249+
250+
/// While absolute paths are useful (for SyncTeX and external tools that
251+
/// resolve paths to TeX sources), we can disable them for reproducibility.
252+
pub fn with_expose_absolute_paths(&mut self, expose_absolute_paths: bool) -> &mut Self {
253+
self.filesystem_emulation_settings.expose_absolute_paths = expose_absolute_paths;
254+
self
255+
}
256+
257+
/// Ditto for file modification timestamps. In deterministic mode, we return
258+
/// the configured build time (i.e. `SOURCE_DATE_EPOCH`) instead of the
259+
/// modification timestamp reported by the IO subsystem.
260+
pub fn with_mtime_override(&mut self, mtime_override: Option<i64>) -> &mut Self {
261+
self.filesystem_emulation_settings.mtime_override = mtime_override;
262+
self
263+
}
264+
247265
/// Invoke a function to launch a bridged FFI engine with a global mutex
248266
/// held.
249267
///
@@ -262,7 +280,12 @@ impl<'a> CoreBridgeLauncher<'a> {
262280
F: FnOnce(&mut CoreBridgeState<'_>) -> Result<T>,
263281
{
264282
let _guard = ENGINE_LOCK.lock().unwrap();
265-
let mut state = CoreBridgeState::new(self.security.clone(), self.hooks, self.status);
283+
let mut state = CoreBridgeState::new(
284+
self.security.clone(),
285+
self.hooks,
286+
self.status,
287+
self.filesystem_emulation_settings.clone(),
288+
);
266289
let result = callback(&mut state);
267290

268291
if let Err(ref e) = result {
@@ -285,6 +308,9 @@ pub struct CoreBridgeState<'a> {
285308
/// The security settings for this invocation
286309
security: SecuritySettings,
287310

311+
/// The filesystem emulation settings for this invocation.
312+
fs_emulation_settings: FsEmulationSettings,
313+
288314
/// The driver hooks associated with this engine invocation.
289315
hooks: &'a mut dyn DriverHooks,
290316

@@ -312,6 +338,7 @@ impl<'a> CoreBridgeState<'a> {
312338
security: SecuritySettings,
313339
hooks: &'a mut dyn DriverHooks,
314340
status: &'a mut dyn StatusBackend,
341+
fs_emulation_settings: FsEmulationSettings,
315342
) -> CoreBridgeState<'a> {
316343
CoreBridgeState {
317344
security,
@@ -320,6 +347,7 @@ impl<'a> CoreBridgeState<'a> {
320347
output_handles: Vec::new(),
321348
input_handles: Vec::new(),
322349
latest_input_path: None,
350+
fs_emulation_settings,
323351
}
324352
}
325353

@@ -592,6 +620,9 @@ impl<'a> CoreBridgeState<'a> {
592620
}
593621

594622
fn input_get_mtime(&mut self, handle: *mut InputHandle) -> i64 {
623+
if let Some(mtime) = self.fs_emulation_settings.mtime_override {
624+
return mtime;
625+
}
595626
let rhandle: &mut InputHandle = unsafe { &mut *handle };
596627

597628
let maybe_time = match rhandle.get_unix_mtime() {
@@ -773,6 +804,32 @@ impl Default for SecuritySettings {
773804
}
774805
}
775806

807+
/// A type that stores configuration knobs related to filesystem emulation.
808+
/// These options are not security-critical, but are relevant for
809+
/// reproducible document builds. We default to an "accurate" view of the
810+
/// underlying IO subsystem and have options that stub the respective IO
811+
/// functions with fake / stable values.
812+
#[derive(Clone, Debug)]
813+
struct FsEmulationSettings {
814+
/// While absolute paths are useful (for SyncTeX and external tools that
815+
/// resolve paths to TeX sources), we can disable them for reproducibility.
816+
expose_absolute_paths: bool,
817+
818+
/// Ditto for file modification timestamps. In deterministic mode, we return
819+
/// the configured build time (i.e. `SOURCE_DATE_EPOCH`) instead of the
820+
/// modification timestamp reported by the IO subsystem.
821+
mtime_override: Option<i64>,
822+
}
823+
824+
impl Default for FsEmulationSettings {
825+
fn default() -> Self {
826+
Self {
827+
expose_absolute_paths: true,
828+
mtime_override: None,
829+
}
830+
}
831+
}
832+
776833
// The entry points.
777834

778835
/// Issue a warning.
@@ -968,6 +1025,9 @@ pub unsafe extern "C" fn ttbc_get_last_input_abspath(
9681025
buffer: *mut u8,
9691026
len: libc::size_t,
9701027
) -> libc::ssize_t {
1028+
if !es.fs_emulation_settings.expose_absolute_paths {
1029+
return 0;
1030+
}
9711031
match es.latest_input_path {
9721032
None => 0,
9731033

crates/xetex_layout/layout/xetex-XeTeXFontInst.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ XeTeXFontInst::initialize(const char* pathname, int index, int &status)
333333

334334
error = FT_New_Memory_Face(gFreeTypeLibrary, m_backingData, sz, index, &m_ftFace);
335335

336-
if (!FT_IS_SCALABLE(m_ftFace)) {
336+
if (error || !FT_IS_SCALABLE(m_ftFace)) {
337337
status = 1;
338338
return;
339339
}

docs/src/ref/v1cli.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,17 @@ The following are the available flags.
4949
| `-c` | `--chatter <LEVEL>` | How much chatter to print when running [default: default] [possible values: default, minimal] |
5050
| | `--format <PATH>` | The name of the "format" file used to initialize the TeX engine [default: latex] |
5151
| `-h` | `--help` | Prints help information |
52-
| | `--hide <PATH>...` | Tell the engine that no file at `<PATH>` exists, if it tries to read it |
52+
| | `--hide <PATH>...` | Tell the engine that no file at `<PATH>` exists, if it tries to read it |
5353
| `-k` | `--keep-intermediates` | Keep the intermediate files generated during processing |
54-
| | `--keep-logs` | Keep the log files generated during processing |
55-
| | `--makefile-rules <PATH>` | Write Makefile-format rules expressing the dependencies of this run to <PATH> |
54+
| | `--keep-logs` | Keep the log files generated during processing |
55+
| | `--makefile-rules <PATH>` | Write Makefile-format rules expressing the dependencies of this run to `<PATH>` |
5656
| `-C` | `--only-cached` | Use only resource files cached locally |
5757
| `-o` | `--outdir <OUTDIR>` | The directory in which to place output files [default: the directory containing INPUT] |
5858
| | `--outfmt <FORMAT>` | The kind of output to generate [default: pdf] [possible values: pdf, html, xdv, aux, format] |
5959
| | `--pass <PASS>` | Which engines to run [default: default] [possible values: default, tex, bibtex_first] |
6060
| `-p` | `--print` | Print the engine's chatter during processing |
6161
| `-r` | `--reruns <COUNT>` | Rerun the TeX engine exactly this many times after the first |
6262
| | `--synctex` | Generate SyncTeX data |
63+
| | `--untrusted` | Input is untrusted: disable all known-insecure features |
6364
| `-V` | `--version` | Prints version information |
6465
| `-w` | `--web-bundle <URL>` | Use this URL find resource files instead of the default |

docs/src/v2cli/build.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ cause it to look for that file in the online support bundle.
5555
The `--print` option (or `-p` for short) will cause the engine to print the
5656
regular terminal output of the TeX engine. This output is similar to, but not
5757
identical to, the contents of the log file. By default, this output is only
58-
printed if the engine encounteres a fatal error.
58+
printed if the engine encounters a fatal error.
5959

6060
The `--open` option will open the built document using the system handler.
6161

docs/src/v2cli/compile.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,4 @@ the set of unstable options is subject to change at any time.
134134
| `-Z search-path=<path>` | Also look in `<path>` for files (unless `--untrusted` has been specified), like TEXINPUTS. Can be specified multiple times. |
135135
| `-Z shell-escape` | Enable `\write18` (unless `--untrusted` has been specified) |
136136
| `-Z shell-escape-cwd=<path>` | Working directory to use for \write18. Use $(pwd) for same behaviour as most other engines (e.g. for relative paths in \inputminted). Implies -Z shell-escape |
137+
| `-Z deterministic-mode` | Force a deterministic build environment. Note that setting `SOURCE_DATE_EPOCH` is usually sufficient for reproducible builds, and this option makes some extra functionality trade-offs. Specifically, deterministic mode breaks SyncTeX's auxiliary files as they include and rely on absolute file paths |

docs/src/v2cli/dump.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ one of its parents.
4444
[tectonic-toml]: ../ref/tectonic-toml.md
4545

4646
The “partial build” consists of one pass of the TeX engine. Future versions of
47-
this tool might gain options allowing you specify different passes. This command
48-
can be used to dump any file created by TeX during the build (so long as it's
49-
created on the first pass).
47+
this tool might gain options allowing you to specify different passes. This
48+
command can be used to dump any file created by TeX during the build (so long
49+
as it's created on the first pass).
5050

5151
#### Command-Line Options
5252

src/bin/tectonic/compile.rs

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@
66
//! `compile` subcommand of the "V2" / "cargo-like" interface.
77
88
use std::{
9-
env,
109
path::{Path, PathBuf},
1110
str::FromStr,
12-
time,
1311
};
1412
use structopt::StructOpt;
1513
use tectonic_bridge_core::{SecuritySettings, SecurityStance};
@@ -113,6 +111,7 @@ impl CompileOptions {
113111
let mut sess_builder =
114112
ProcessingSessionBuilder::new_with_security(SecuritySettings::new(stance));
115113
let format_path = self.format;
114+
let deterministic_mode = unstable.deterministic_mode;
116115
sess_builder
117116
.unstables(unstable)
118117
.format_name(&format_path)
@@ -199,18 +198,7 @@ impl CompileOptions {
199198
} else {
200199
sess_builder.bundle(config.default_bundle(only_cached, status)?);
201200
}
202-
203-
let build_date_str = env::var("SOURCE_DATE_EPOCH").ok();
204-
let build_date = match build_date_str {
205-
Some(s) => {
206-
let epoch = s.parse::<u64>().expect("invalid build date (not a number)");
207-
time::SystemTime::UNIX_EPOCH
208-
.checked_add(time::Duration::from_secs(epoch))
209-
.expect("time overflow")
210-
}
211-
None => time::SystemTime::now(),
212-
};
213-
sess_builder.build_date(build_date);
201+
sess_builder.build_date_from_env(deterministic_mode);
214202
run_and_report(sess_builder, status).map(|_| 0)
215203
}
216204
}

src/docmodel.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use crate::{
2929
errors::{ErrorKind, Result},
3030
status::StatusBackend,
3131
test_util, tt_note,
32+
unstable_opts::UnstableOptions,
3233
};
3334

3435
/// Options for setting up [`Document`] instances with the driver
@@ -40,6 +41,9 @@ pub struct DocumentSetupOptions {
4041

4142
/// Security settings for engine features.
4243
security: SecuritySettings,
44+
45+
/// Ensure a deterministic build environment.
46+
deterministic_mode: bool,
4347
}
4448

4549
impl DocumentSetupOptions {
@@ -48,6 +52,7 @@ impl DocumentSetupOptions {
4852
pub fn new_with_security(security: SecuritySettings) -> Self {
4953
DocumentSetupOptions {
5054
only_cached: false,
55+
deterministic_mode: false,
5156
security,
5257
}
5358
}
@@ -61,6 +66,12 @@ impl DocumentSetupOptions {
6166
self.only_cached = s;
6267
self
6368
}
69+
70+
/// Specify whether we want to ensure a deterministic build environment.
71+
pub fn deterministic_mode(&mut self, s: bool) -> &mut Self {
72+
self.deterministic_mode = s;
73+
self
74+
}
6475
}
6576

6677
pub trait DocumentExt {
@@ -157,7 +168,11 @@ impl DocumentExt for Document {
157168
sess_builder
158169
.output_format(output_format)
159170
.format_name(&profile.tex_format)
160-
.build_date(std::time::SystemTime::now())
171+
.build_date_from_env(setup_options.deterministic_mode)
172+
.unstables(UnstableOptions {
173+
deterministic_mode: setup_options.deterministic_mode,
174+
..Default::default()
175+
})
161176
.pass(PassSetting::Default)
162177
.primary_input_buffer(input_buffer.as_bytes())
163178
.tex_input_name(output_profile);

src/driver.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use std::{
2626
rc::Rc,
2727
result::Result as StdResult,
2828
str::FromStr,
29-
time::SystemTime,
29+
time::{Duration, SystemTime},
3030
};
3131
use tectonic_bridge_core::{CoreBridgeLauncher, DriverHooks, SecuritySettings, SystemRequestError};
3232
use tectonic_bundles::Bundle;
@@ -985,6 +985,28 @@ impl ProcessingSessionBuilder {
985985
self
986986
}
987987

988+
/// Configures the date and time of the processing session from the environment:
989+
/// If `SOURCE_DATE_EPOCH` is set, it's used as the build date.
990+
/// If `force_deterministic` is set, we fall back to UNIX_EPOCH.
991+
/// Otherwise, we use the current system time.
992+
pub fn build_date_from_env(&mut self, force_deterministic: bool) -> &mut Self {
993+
let build_date_str = std::env::var("SOURCE_DATE_EPOCH").ok();
994+
let build_date = match (force_deterministic, build_date_str) {
995+
(_, Some(s)) => {
996+
let epoch = s
997+
.parse::<u64>()
998+
.expect("invalid SOURCE_DATE_EPOCH (not a number)");
999+
1000+
SystemTime::UNIX_EPOCH
1001+
.checked_add(Duration::from_secs(epoch))
1002+
.expect("time overflow")
1003+
}
1004+
(true, None) => SystemTime::UNIX_EPOCH,
1005+
(false, None) => SystemTime::now(),
1006+
};
1007+
self.build_date(build_date)
1008+
}
1009+
9881010
/// Loads unstable options into the processing session
9891011
pub fn unstables(&mut self, opts: UnstableOptions) -> &mut Self {
9901012
self.unstables = opts;
@@ -1830,6 +1852,18 @@ impl ProcessingSession {
18301852
let mut launcher =
18311853
CoreBridgeLauncher::new_with_security(&mut self.bs, status, self.security.clone());
18321854

1855+
// In deterministic mode, we stub a few aspects of the environment.
1856+
// They default to a "realistic" view, but we override them with static values:
1857+
if self.unstables.deterministic_mode {
1858+
launcher.with_expose_absolute_paths(false);
1859+
launcher.with_mtime_override(Some(
1860+
self.build_date
1861+
.duration_since(SystemTime::UNIX_EPOCH)
1862+
.map(|x| x.as_secs() as i64)
1863+
.expect("invalid build date in deterministic mode"),
1864+
));
1865+
}
1866+
18331867
TexEngine::default()
18341868
.halt_on_error_mode(!self.unstables.continue_on_errors)
18351869
.initex_mode(self.output_format == OutputFormat::Format)

0 commit comments

Comments
 (0)