##// END OF EJS Templates
rhg: Add support for automatic fallback to Python...
Simon Sapin -
r47425:93e9f448 default
parent child Browse files
Show More
@@ -11,6 +11,7 b' use hg::utils::files::{get_bytes_from_os'
11 use hg::utils::SliceExt;
11 use hg::utils::SliceExt;
12 use std::ffi::OsString;
12 use std::ffi::OsString;
13 use std::path::PathBuf;
13 use std::path::PathBuf;
14 use std::process::Command;
14
15
15 mod blackbox;
16 mod blackbox;
16 mod error;
17 mod error;
@@ -138,9 +139,43 b' fn exit_code(result: &Result<(), Command'
138
139
139 fn exit(
140 fn exit(
140 ui: &Ui,
141 ui: &Ui,
141 on_unsupported: OnUnsupported,
142 mut on_unsupported: OnUnsupported,
142 result: Result<(), CommandError>,
143 result: Result<(), CommandError>,
143 ) -> ! {
144 ) -> ! {
145 if let (
146 OnUnsupported::Fallback { executable },
147 Err(CommandError::UnsupportedFeature { .. }),
148 ) = (&on_unsupported, &result)
149 {
150 let mut args = std::env::args_os();
151 let executable_path = get_path_from_bytes(&executable);
152 let this_executable = args.next().expect("exepcted argv[0] to exist");
153 if executable_path == &PathBuf::from(this_executable) {
154 // Avoid spawning infinitely many processes until resource
155 // exhaustion.
156 let _ = ui.write_stderr(&format_bytes!(
157 b"Blocking recursive fallback. The 'rhg.fallback-executable = {}' config \
158 points to `rhg` itself.\n",
159 executable
160 ));
161 on_unsupported = OnUnsupported::Abort
162 } else {
163 // `args` is now `argv[1..]` since we’ve already consumed `argv[0]`
164 let result = Command::new(executable_path).args(args).status();
165 match result {
166 Ok(status) => std::process::exit(
167 status.code().unwrap_or(exitcode::ABORT),
168 ),
169 Err(error) => {
170 let _ = ui.write_stderr(&format_bytes!(
171 b"tried to fall back to a '{}' sub-process but got error {}\n",
172 executable, format_bytes::Utf8(error)
173 ));
174 on_unsupported = OnUnsupported::Abort
175 }
176 }
177 }
178 }
144 match &result {
179 match &result {
145 Ok(_) => {}
180 Ok(_) => {}
146 Err(CommandError::Abort { message }) => {
181 Err(CommandError::Abort { message }) => {
@@ -160,6 +195,7 b' fn exit('
160 ));
195 ));
161 }
196 }
162 OnUnsupported::AbortSilent => {}
197 OnUnsupported::AbortSilent => {}
198 OnUnsupported::Fallback { .. } => unreachable!(),
163 }
199 }
164 }
200 }
165 }
201 }
@@ -268,18 +304,32 b' enum OnUnsupported {'
268 Abort,
304 Abort,
269 /// Silently exit with code 252.
305 /// Silently exit with code 252.
270 AbortSilent,
306 AbortSilent,
307 /// Try running a Python implementation
308 Fallback { executable: Vec<u8> },
271 }
309 }
272
310
273 impl OnUnsupported {
311 impl OnUnsupported {
312 const DEFAULT: Self = OnUnsupported::Abort;
313 const DEFAULT_FALLBACK_EXECUTABLE: &'static [u8] = b"hg";
314
274 fn from_config(config: &Config) -> Self {
315 fn from_config(config: &Config) -> Self {
275 let default = OnUnsupported::Abort;
316 match config
276 match config.get(b"rhg", b"on-unsupported") {
317 .get(b"rhg", b"on-unsupported")
318 .map(|value| value.to_ascii_lowercase())
319 .as_deref()
320 {
277 Some(b"abort") => OnUnsupported::Abort,
321 Some(b"abort") => OnUnsupported::Abort,
278 Some(b"abort-silent") => OnUnsupported::AbortSilent,
322 Some(b"abort-silent") => OnUnsupported::AbortSilent,
279 None => default,
323 Some(b"fallback") => OnUnsupported::Fallback {
324 executable: config
325 .get(b"rhg", b"fallback-executable")
326 .unwrap_or(Self::DEFAULT_FALLBACK_EXECUTABLE)
327 .to_owned(),
328 },
329 None => Self::DEFAULT,
280 Some(_) => {
330 Some(_) => {
281 // TODO: warn about unknown config value
331 // TODO: warn about unknown config value
282 default
332 Self::DEFAULT
283 }
333 }
284 }
334 }
285 }
335 }
@@ -1,9 +1,10 b''
1 #require rust
1 #require rust
2
2
3 Define an rhg function that will only run if rhg exists
3 Define an rhg function that will only run if rhg exists
4 $ RHG="$RUNTESTDIR/../rust/target/release/rhg"
4 $ rhg() {
5 $ rhg() {
5 > if [ -f "$RUNTESTDIR/../rust/target/release/rhg" ]; then
6 > if [ -f "$RHG" ]; then
6 > "$RUNTESTDIR/../rust/target/release/rhg" "$@"
7 > "$RHG" "$@"
7 > else
8 > else
8 > echo "skipped: Cannot find rhg. Try to run cargo build in rust/rhg."
9 > echo "skipped: Cannot find rhg. Try to run cargo build in rust/rhg."
9 > exit 80
10 > exit 80
@@ -151,6 +152,27 b' Cat copied file should not display copy '
151 $ rhg cat -r 1 copy_of_original
152 $ rhg cat -r 1 copy_of_original
152 original content
153 original content
153
154
155 Fallback to Python
156 $ rhg cat original
157 unsupported feature: `rhg cat` without `--rev` / `-r`
158 [252]
159 $ FALLBACK="--config rhg.on-unsupported=fallback"
160 $ rhg cat original $FALLBACK
161 original content
162
163 $ rhg cat original $FALLBACK --config rhg.fallback-executable=false
164 [1]
165
166 $ rhg cat original $FALLBACK --config rhg.fallback-executable=hg-non-existent
167 tried to fall back to a 'hg-non-existent' sub-process but got error $ENOENT$
168 unsupported feature: `rhg cat` without `--rev` / `-r`
169 [252]
170
171 $ rhg cat original $FALLBACK --config rhg.fallback-executable="$RHG"
172 Blocking recursive fallback. The 'rhg.fallback-executable = */rust/target/release/rhg' config points to `rhg` itself. (glob)
173 unsupported feature: `rhg cat` without `--rev` / `-r`
174 [252]
175
154 Requirements
176 Requirements
155 $ rhg debugrequirements
177 $ rhg debugrequirements
156 dotencode
178 dotencode
General Comments 0
You need to be logged in to leave comments. Login now