Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/package/launcher/src/main.rs
6451 views
1
use std::process::Command;
2
use std::str::FromStr;
3
use std::{env, ffi::OsString, fs, path::Path, path::PathBuf};
4
5
fn main() {
6
// compute executable path (resolving symlinks)
7
let mut exe_file: PathBuf = env::current_exe().expect("failed to get exe path");
8
let exe_metadata = fs::symlink_metadata(exe_file.as_path())
9
.expect("failed to get symlink metadata for exe path");
10
if exe_metadata.is_symlink() {
11
exe_file =
12
fs::read_link(exe_file.as_path()).expect("failed to cananoicalize executable path");
13
}
14
15
// compute bin_dir and share_dir (share_dir may be provided externally)
16
let bin_dir = exe_file
17
.parent()
18
.expect("failed to get executable parent")
19
.to_path_buf();
20
let mut share_dir = path_from_env("QUARTO_SHARE_PATH");
21
if share_dir.as_os_str().is_empty() {
22
share_dir = share_dir_from_bin_dir(&bin_dir);
23
}
24
25
// some other file paths
26
let js_file = bin_dir.join(Path::new("quarto.js"));
27
28
// get command line args (skip first which is the program)
29
let args: Vec<OsString> = env::args_os().skip(1).collect();
30
31
// handle no args at all
32
if args.is_empty() {
33
std::process::exit(0);
34
}
35
36
// handle --version
37
if &args[0] == "--version" || &args[0] == "-v" {
38
let version_path = share_dir.join("version");
39
let version = fs::read_to_string(version_path).expect("failed to read version");
40
println!("{}", version);
41
std::process::exit(0);
42
}
43
44
// handle --paths
45
if &args[0] == "--paths" {
46
println!("{}\n{}", bin_dir.display(), share_dir.display());
47
std::process::exit(0);
48
}
49
50
// compute deno and deno dom locations (allow them to be defined externally)
51
let mut deno_file = path_from_env("QUARTO_DENO");
52
if deno_file.as_os_str().is_empty() {
53
if env::consts::OS == "windows" {
54
deno_file = bin_dir
55
.join("tools")
56
.join("x86_64")
57
.join("deno");
58
} else {
59
deno_file = bin_dir
60
.join("tools")
61
.join(deno_dir())
62
.join("deno");
63
}
64
}
65
let mut deno_dom_file: PathBuf = path_from_env("QUARTO_DENO_DOM");
66
if deno_dom_file.as_os_str().is_empty() {
67
deno_dom_file = bin_dir.join("tools").join("x86_64").join("deno_dom").join(DENO_DOM_LIB);
68
}
69
70
// set environment variables requried by quarto.js
71
std::env::set_var("QUARTO_DENO", &deno_file);
72
std::env::set_var("QUARTO_BIN_PATH", &bin_dir);
73
std::env::set_var("QUARTO_SHARE_PATH", &share_dir);
74
std::env::set_var("DENO_DOM_PLUGIN", &deno_dom_file);
75
std::env::set_var("DENO_NO_UPDATE_CHECK", "1");
76
std::env::set_var("DENO_TLS_CA_STORE","system,mozilla");
77
78
// windows-specific env vars
79
#[cfg(target_os = "windows")]
80
std::env::set_var("NO_COLOR", std::ffi::OsStr::new("TRUE"));
81
82
// Define the base deno options
83
let mut deno_options: Vec<String> = vec![
84
String::from("--unstable-ffi"),
85
String::from("--unstable-kv"),
86
String::from("--no-config"),
87
String::from("--no-lock"),
88
String::from("--cached-only"),
89
// Using --allow-all as there is otherwise an issue in Deno 1.46.3 with --allow-read and --allow-write with network drives
90
// https://github.com/quarto-dev/quarto-cli/issues/11332
91
String::from("--allow-all"),
92
String::from("--no-check"),
93
];
94
95
// # --enable-experimental-regexp-engine is required for /regex/l, https://github.com/quarto-dev/quarto-cli/issues/9737
96
if let Ok(v8_options) = env::var("QUARTO_DENO_V8_OPTIONS") {
97
deno_options.push(format!("{}{}", String::from("--v8-flags=--enable-experimental-regexp-engine,--max-old-space-size=8192,--max-heap-size=8192,"), String::from(v8_options)));
98
} else {
99
deno_options.push(String::from("--v8-flags=--enable-experimental-regexp-engine,--max-old-space-size=8192,--max-heap-size=8192"));
100
}
101
// If there are extra args, include those
102
if let Ok(extra_options) = env::var("QUARTO_DENO_EXTRA_OPTIONS") {
103
deno_options.push(extra_options);
104
};
105
106
// run deno
107
let mut child = Command::new(&deno_file)
108
.arg("run")
109
.args(deno_options)
110
.arg(js_file)
111
.args(args)
112
.spawn()
113
.expect("failed to run deno");
114
115
// forward exit status
116
let status = child.wait().expect("failed to wait on deno");
117
if status.success() {
118
std::process::exit(0)
119
} else {
120
match status.code() {
121
Some(code) => std::process::exit(code),
122
// errors reaping the status code have been observed
123
// (see https://github.com/quarto-dev/quarto-cli/issues/2296)
124
// so in that return a normal exit status -- need further
125
// investigation to figure out if there is more to do here
126
None => std::process::exit(0)
127
}
128
}
129
}
130
131
// return a PathBuf for an environment variable using os encoding
132
// (return empty string if the variable is not found)
133
fn path_from_env(key: &str) -> PathBuf {
134
PathBuf::from(env::var_os(key).unwrap_or(OsString::new()))
135
}
136
137
fn share_dir_from_bin_dir(bin_dir: &PathBuf) -> PathBuf {
138
// if quarto is bundled into an `.app` file (e.g. RStudio) it will be
139
// looking for the share directory over in the resources folder.
140
if bin_dir.ends_with("Contents/MacOS/quarto/bin") {
141
bin_dir
142
.parent()
143
.expect("failed to get bin_dir parent")
144
.parent()
145
.expect("failed to get bin_dir parent")
146
.parent()
147
.expect("failed to get bin_dir parent")
148
.join("Resources")
149
.join("quarto")
150
.join("share")
151
// if using standard linux filesystem local bin folder then
152
// look for 'share' in the right place
153
} else if bin_dir.ends_with("usr/local/bin/quarto") {
154
bin_dir
155
.parent()
156
.expect("failed to get bin_dir parent")
157
.parent()
158
.expect("failed to get bin_dir parent")
159
.join("share")
160
.join("quarto")
161
} else {
162
bin_dir
163
.parent()
164
.expect("failed to get bin path parent")
165
.join("share")
166
}
167
}
168
169
fn deno_dir() -> String {
170
let arch = arch_string();
171
if arch.starts_with("Darwin arm64") {
172
return String::from_str("aarch64").unwrap();
173
} else if arch.starts_with("Darwin x86_64") {
174
return String::from_str("x86_64").unwrap();
175
} else {
176
// TODO: Properly deal with multi-architecture on linux
177
return String::from_str("x86_64").unwrap();
178
}
179
}
180
181
// returns a string describing the architecture. only works on Unix
182
fn arch_string() -> String {
183
let out = Command::new("uname")
184
.args(["-sm"])
185
.output().expect("Failed to run uname").stdout;
186
String::from_utf8(out).expect("Couldn't convert to string")
187
}
188
189
// platform-specific deno dom lib file
190
191
#[cfg(target_os = "windows")]
192
const DENO_DOM_LIB: &str = "plugin.dll";
193
194
#[cfg(target_os = "macos")]
195
const DENO_DOM_LIB: &str = "libplugin.dylib";
196
197
#[cfg(target_os = "linux")]
198
const DENO_DOM_LIB: &str = "libplugin.so";
199
200