Skip to content

Commit 50fc7eb

Browse files
authored
Merge pull request #1132 from bryango/cli-web-bundle
`--web-bundle` for v2 CLI: fix #1002 and allow overrides
2 parents 8456df1 + 61eb46a commit 50fc7eb

6 files changed

Lines changed: 250 additions & 65 deletions

File tree

src/bin/tectonic/compile.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,6 @@ pub struct CompileOptions {
3636
#[structopt(takes_value(true), parse(from_os_str), long, short, name = "file_path")]
3737
bundle: Option<PathBuf>,
3838

39-
/// Use this URL to find resource files instead of the default
40-
#[structopt(takes_value(true), long, short, name = "url")]
41-
// TODO add URL validation
42-
web_bundle: Option<String>,
43-
4439
/// Use only resource files cached locally
4540
#[structopt(short = "C", long)]
4641
only_cached: bool,
@@ -95,7 +90,12 @@ pub struct CompileOptions {
9590
}
9691

9792
impl CompileOptions {
98-
pub fn execute(self, config: PersistentConfig, status: &mut dyn StatusBackend) -> Result<i32> {
93+
pub fn execute(
94+
self,
95+
config: PersistentConfig,
96+
status: &mut dyn StatusBackend,
97+
web_bundle: Option<String>,
98+
) -> Result<i32> {
9999
let unstable = UnstableOptions::from_unstable_args(self.unstable.into_iter());
100100

101101
// Default to allowing insecure since it would be super duper annoying
@@ -193,7 +193,7 @@ impl CompileOptions {
193193
}
194194
if let Some(path) = self.bundle {
195195
sess_builder.bundle(config.make_local_file_provider(path, status)?);
196-
} else if let Some(u) = self.web_bundle {
196+
} else if let Some(u) = web_bundle {
197197
sess_builder.bundle(config.make_cached_url_provider(&u, only_cached, None, status)?);
198198
} else {
199199
sess_builder.bundle(config.default_bundle(only_cached, status)?);

src/bin/tectonic/main.rs

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use std::{env, process, str::FromStr};
66
use structopt::StructOpt;
77
use tectonic_status_base::plain::PlainStatusBackend;
88

9-
use structopt::clap;
109
use tectonic::{
1110
config::PersistentConfig,
1211
errors::SyncError,
@@ -40,18 +39,23 @@ mod v2cli {
4039
#[derive(Debug, StructOpt)]
4140
#[structopt(name = "Tectonic", about = "Process a (La)TeX document")]
4241
struct CliOptions {
43-
/// Use experimental V2 interface (see `tectonic -X --help`); must be the first argument
42+
/// Use experimental V2 interface (see `tectonic -X --help`)
4443
#[structopt(short = "X")]
4544
use_v2: bool,
4645

4746
/// How much chatter to print when running
4847
#[structopt(long = "chatter", short, name = "level", default_value = "default", possible_values(&["default", "minimal"]))]
4948
chatter_level: String,
5049

51-
/// Enable/disable colorful log output.
50+
/// Enable/disable colorful log output
5251
#[structopt(long = "color", name = "when", default_value = "auto", possible_values(&["always", "auto", "never"]))]
5352
cli_color: String,
5453

54+
/// Use this URL to find resource files instead of the default
55+
#[structopt(takes_value(true), long, short, name = "url", overrides_with = "url")]
56+
// TODO add URL validation
57+
web_bundle: Option<String>,
58+
5559
#[structopt(flatten)]
5660
compile: compile::CompileOptions,
5761
}
@@ -78,26 +82,34 @@ fn main() {
7882
unstable_opts::UnstableOptions::from_unstable_args(args.unstable.into_iter());
7983
}
8084

81-
// Migration to the "cargo-style" command-line interface. If the first
82-
// argument is `-X`, or argv[0] contains `nextonic`, we activate the
85+
// Migration to the "cargo-style" command-line interface. If the arguments
86+
// list contains `-X`, or argv[0] contains `nextonic`, we activate the
8387
// alternative operation mode. Once this experimental mode is working OK,
8488
// we'll start printing a message telling people to prefer the `-X` option
8589
// and use `-X compile` for the "classic" ("rustc"-style, current)
8690
// interface. After that's been in place for a while, we'll make V2 mode the
8791
// default.
8892

8993
let mut v2cli_enabled = false;
90-
let mut v2cli_arg_idx = 1;
94+
let mut v2cli_args = os_args[1..].to_vec(); // deep copy
9195

9296
if !os_args.is_empty() && os_args[0].to_str().map(|s| s.contains("nextonic")) == Some(true) {
9397
v2cli_enabled = true;
94-
} else if os_args.len() > 1 && os_args[1] == "-X" {
95-
v2cli_enabled = true;
96-
v2cli_arg_idx = 2;
98+
} else if let Some(index) = v2cli_args
99+
.to_vec()
100+
.iter()
101+
.position(|s| s.to_str().unwrap_or_default() == "-X")
102+
{
103+
// Try to parse as v1 cli first, and when that doesn't work,
104+
// interpret it as v2 cli:
105+
if CliOptions::from_args_safe().is_err() || CliOptions::from_args().use_v2 {
106+
v2cli_enabled = true;
107+
v2cli_args.remove(index);
108+
}
97109
}
98110

99111
if v2cli_enabled {
100-
v2cli::v2_main(&os_args[v2cli_arg_idx..]);
112+
v2cli::v2_main(&v2cli_args);
101113
return;
102114
}
103115

@@ -154,20 +166,11 @@ fn main() {
154166
Box::new(PlainStatusBackend::new(chatter_level)) as Box<dyn StatusBackend>
155167
};
156168

157-
if args.use_v2 {
158-
let err = clap::Error::with_description(
159-
"-X option must be the first argument if given",
160-
clap::ErrorKind::ArgumentConflict,
161-
);
162-
status.report_error(&err.into());
163-
process::exit(1)
164-
}
165-
166169
// Now that we've got colorized output, pass off to the inner function ...
167170
// all so that we can print out the word "error:" in red. This code
168171
// parallels various bits of the `error_chain` crate.
169172

170-
if let Err(e) = args.compile.execute(config, &mut *status) {
173+
if let Err(e) = args.compile.execute(config, &mut *status, args.web_bundle) {
171174
status.report_error(&SyncError::new(e).into());
172175
process::exit(1)
173176
}

src/bin/tectonic/v2cli.rs

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,18 @@ struct V2CliOptions {
6262
)]
6363
cli_color: String,
6464

65+
/// Use this URL to find resource files instead of the default
66+
#[structopt(
67+
takes_value(true),
68+
long,
69+
short,
70+
name = "url",
71+
overrides_with = "url",
72+
global(true)
73+
)]
74+
// TODO add URL validation
75+
web_bundle: Option<String>,
76+
6577
#[structopt(subcommand)]
6678
command: Commands,
6779
}
@@ -138,7 +150,7 @@ pub fn v2_main(effective_args: &[OsString]) {
138150

139151
// Now that we've got colorized output, pass off to the inner function.
140152

141-
let code = match args.command.execute(config, &mut *status) {
153+
let code = match args.command.execute(config, &mut *status, args.web_bundle) {
142154
Ok(c) => c,
143155
Err(e) => {
144156
status.report_error(&SyncError::new(e).into());
@@ -204,14 +216,19 @@ impl Commands {
204216
}
205217
}
206218

207-
fn execute(self, config: PersistentConfig, status: &mut dyn StatusBackend) -> Result<i32> {
219+
fn execute(
220+
self,
221+
config: PersistentConfig,
222+
status: &mut dyn StatusBackend,
223+
web_bundle: Option<String>,
224+
) -> Result<i32> {
208225
match self {
209-
Commands::Build(o) => o.execute(config, status),
226+
Commands::Build(o) => o.execute(config, status, web_bundle),
210227
Commands::Bundle(o) => o.execute(config, status),
211-
Commands::Compile(o) => o.execute(config, status),
228+
Commands::Compile(o) => o.execute(config, status, web_bundle),
212229
Commands::Dump(o) => o.execute(config, status),
213-
Commands::New(o) => o.execute(config, status),
214-
Commands::Init(o) => o.execute(config, status),
230+
Commands::New(o) => o.execute(config, status, web_bundle),
231+
Commands::Init(o) => o.execute(config, status, web_bundle),
215232
Commands::Show(o) => o.execute(config, status),
216233
Commands::Watch(o) => o.execute(config, status),
217234
Commands::External(args) => do_external(args),
@@ -254,7 +271,18 @@ pub struct BuildCommand {
254271
impl BuildCommand {
255272
fn customize(&self, _cc: &mut CommandCustomizations) {}
256273

257-
fn execute(self, config: PersistentConfig, status: &mut dyn StatusBackend) -> Result<i32> {
274+
fn execute(
275+
self,
276+
config: PersistentConfig,
277+
status: &mut dyn StatusBackend,
278+
web_bundle: Option<String>,
279+
) -> Result<i32> {
280+
// `--web-bundle` is not actually used for `-X build`,
281+
// so inform the user instead of ignoring silently.
282+
if let Some(url) = web_bundle {
283+
tt_note!(status, "--web-bundle {} ignored", &url);
284+
tt_note!(status, "using workspace bundle configuration");
285+
}
258286
let ws = Workspace::open_from_environment()?;
259287
let doc = ws.first_document();
260288

@@ -681,7 +709,12 @@ pub struct NewCommand {
681709
impl NewCommand {
682710
fn customize(&self, _cc: &mut CommandCustomizations) {}
683711

684-
fn execute(self, config: PersistentConfig, status: &mut dyn StatusBackend) -> Result<i32> {
712+
fn execute(
713+
self,
714+
config: PersistentConfig,
715+
status: &mut dyn StatusBackend,
716+
web_bundle: Option<String>,
717+
) -> Result<i32> {
685718
tt_note!(
686719
status,
687720
"creating new document in directory `{}`",
@@ -690,7 +723,7 @@ impl NewCommand {
690723

691724
let wc = WorkspaceCreator::new(self.path);
692725
ctry!(
693-
wc.create_defaulted(&config, status);
726+
wc.create_defaulted(config, status, web_bundle);
694727
"failed to create the new Tectonic workspace"
695728
);
696729
Ok(0)
@@ -704,7 +737,12 @@ pub struct InitCommand {}
704737
impl InitCommand {
705738
fn customize(&self, _cc: &mut CommandCustomizations) {}
706739

707-
fn execute(self, config: PersistentConfig, status: &mut dyn StatusBackend) -> Result<i32> {
740+
fn execute(
741+
self,
742+
config: PersistentConfig,
743+
status: &mut dyn StatusBackend,
744+
web_bundle: Option<String>,
745+
) -> Result<i32> {
708746
let path = env::current_dir()?;
709747
tt_note!(
710748
status,
@@ -714,7 +752,7 @@ impl InitCommand {
714752

715753
let wc = WorkspaceCreator::new(path);
716754
ctry!(
717-
wc.create_defaulted(&config, status);
755+
wc.create_defaulted(config, status, web_bundle);
718756
"failed to create the new Tectonic workspace"
719757
);
720758
Ok(0)

src/config.rs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,25 @@ pub fn is_config_test_mode_activated() -> bool {
4444
CONFIG_TEST_MODE_ACTIVATED.load(Ordering::SeqCst)
4545
}
4646

47+
pub fn is_test_bundle_wanted(web_bundle: Option<String>) -> bool {
48+
if !is_config_test_mode_activated() {
49+
return false;
50+
}
51+
match web_bundle {
52+
None => true,
53+
Some(x) if x.contains("test-bundle://") => true,
54+
_ => false,
55+
}
56+
}
57+
58+
pub fn maybe_return_test_bundle(web_bundle: Option<String>) -> Result<Box<dyn Bundle>> {
59+
if is_test_bundle_wanted(web_bundle) {
60+
Ok(Box::<crate::test_util::TestBundle>::default())
61+
} else {
62+
Err(ErrorKind::Msg("not asking for the default test bundle".to_owned()).into())
63+
}
64+
}
65+
4766
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
4867
pub struct PersistentConfig {
4968
default_bundles: Vec<BundleInfo>,
@@ -122,6 +141,10 @@ impl PersistentConfig {
122141
custom_cache_root: Option<&Path>,
123142
status: &mut dyn StatusBackend,
124143
) -> Result<Box<dyn Bundle>> {
144+
if let Ok(test_bundle) = maybe_return_test_bundle(Some(url.to_owned())) {
145+
return Ok(test_bundle);
146+
}
147+
125148
let mut cache = if let Some(root) = custom_cache_root {
126149
Cache::get_for_custom_directory(root)
127150
} else {
@@ -156,9 +179,8 @@ impl PersistentConfig {
156179
) -> Result<Box<dyn Bundle>> {
157180
use std::io;
158181

159-
if CONFIG_TEST_MODE_ACTIVATED.load(Ordering::SeqCst) {
160-
let bundle = crate::test_util::TestBundle::default();
161-
return Ok(Box::new(bundle));
182+
if let Ok(test_bundle) = maybe_return_test_bundle(None) {
183+
return Ok(test_bundle);
162184
}
163185

164186
if self.default_bundles.len() != 1 {
@@ -183,7 +205,7 @@ impl PersistentConfig {
183205
}
184206

185207
pub fn format_cache_path(&self) -> Result<PathBuf> {
186-
if CONFIG_TEST_MODE_ACTIVATED.load(Ordering::SeqCst) {
208+
if is_config_test_mode_activated() {
187209
Ok(crate::test_util::test_path(&[]))
188210
} else {
189211
Ok(app_dirs::ensure_user_cache_dir("formats")?)

src/docmodel.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use crate::{
2828
driver::{OutputFormat, PassSetting, ProcessingSessionBuilder},
2929
errors::{ErrorKind, Result},
3030
status::StatusBackend,
31-
test_util, tt_note,
31+
tt_note,
3232
unstable_opts::UnstableOptions,
3333
};
3434

@@ -111,9 +111,8 @@ impl DocumentExt for Document {
111111
}
112112
}
113113

114-
if config::is_config_test_mode_activated() {
115-
let bundle = test_util::TestBundle::default();
116-
Ok(Box::new(bundle))
114+
if let Ok(test_bundle) = config::maybe_return_test_bundle(None) {
115+
Ok(test_bundle)
117116
} else if let Ok(url) = Url::parse(&self.bundle_loc) {
118117
if url.scheme() != "file" {
119118
let mut cache = Cache::get_user_default()?;
@@ -216,22 +215,25 @@ pub trait WorkspaceCreatorExt {
216215
/// for the main document.
217216
fn create_defaulted(
218217
self,
219-
config: &config::PersistentConfig,
218+
config: config::PersistentConfig,
220219
status: &mut dyn StatusBackend,
220+
web_bundle: Option<String>,
221221
) -> Result<Workspace>;
222222
}
223223

224224
impl WorkspaceCreatorExt for WorkspaceCreator {
225225
fn create_defaulted(
226226
self,
227-
config: &config::PersistentConfig,
227+
config: config::PersistentConfig,
228228
status: &mut dyn StatusBackend,
229+
web_bundle: Option<String>,
229230
) -> Result<Workspace> {
230-
let bundle_loc = if config::is_config_test_mode_activated() {
231+
let bundle_loc = if config::is_test_bundle_wanted(web_bundle.clone()) {
231232
"test-bundle://".to_owned()
232233
} else {
234+
let unresolved_loc = web_bundle.unwrap_or(config.default_bundle_loc().to_owned());
233235
let mut gub = DefaultBackend::default();
234-
gub.resolve_url(config.default_bundle_loc(), status)?
236+
gub.resolve_url(&unresolved_loc, status)?
235237
};
236238

237239
Ok(self.create(bundle_loc)?)

0 commit comments

Comments
 (0)