##// END OF EJS Templates
rust: implementation of `hg`...
Gregory Szorc -
r35587:96421278 default
parent child Browse files
Show More
@@ -0,0 +1,7 b''
1 # Rust builds with a modern MSVC and uses a newer CRT.
2 # Python 2.7 has a shared library dependency on an older CRT (msvcr90.dll).
3 # We statically link the modern CRT to avoid multiple msvcr*.dll libraries
4 # being loaded and Python possibly picking up symbols from the newer runtime
5 # (which would be loaded first).
6 [target.'cfg(target_os = "windows")']
7 rustflags = ["-Ctarget-feature=+crt-static"]
@@ -0,0 +1,2 b''
1 [workspace]
2 members = ["hgcli"]
@@ -0,0 +1,78 b''
1 ===================
2 Mercurial Rust Code
3 ===================
4
5 This directory contains various Rust code for the Mercurial project.
6
7 The top-level ``Cargo.toml`` file defines a workspace containing
8 all primary Mercurial crates.
9
10 Building
11 ========
12
13 To build the Rust components::
14
15 $ cargo build
16
17 If you prefer a non-debug / release configuration::
18
19 $ cargo build --release
20
21 Features
22 --------
23
24 The following Cargo features are available:
25
26 localdev (default)
27 Produce files that work with an in-source-tree build.
28
29 In this mode, the build finds and uses a ``python2.7`` binary from
30 ``PATH``. The ``hg`` binary assumes it runs from ``rust/target/<target>hg``
31 and it finds Mercurial files at ``dirname($0)/../../../``.
32
33 Build Mechanism
34 ---------------
35
36 The produced ``hg`` binary is *bound* to a CPython installation. The
37 binary links against and loads a CPython library that is discovered
38 at build time (by a ``build.rs`` Cargo build script). The Python
39 standard library defined by this CPython installation is also used.
40
41 Finding the appropriate CPython installation to use is done by
42 the ``python27-sys`` crate's ``build.rs``. Its search order is::
43
44 1. ``PYTHON_SYS_EXECUTABLE`` environment variable.
45 2. ``python`` executable on ``PATH``
46 3. ``python2`` executable on ``PATH``
47 4. ``python2.7`` executable on ``PATH``
48
49 Additional verification of the found Python will be performed by our
50 ``build.rs`` to ensure it meets Mercurial's requirements.
51
52 Details about the build-time configured Python are built into the
53 produced ``hg`` binary. This means that a built ``hg`` binary is only
54 suitable for a specific, well-defined role. These roles are controlled
55 by Cargo features (see above).
56
57 Running
58 =======
59
60 The ``hgcli`` crate produces an ``hg`` binary. You can run this binary
61 via ``cargo run``::
62
63 $ cargo run --manifest-path hgcli/Cargo.toml
64
65 Or directly::
66
67 $ target/debug/hg
68 $ target/release/hg
69
70 You can also run the test harness with this binary::
71
72 $ ./run-tests.py --with-hg ../rust/target/debug/hg
73
74 .. note::
75
76 Integration with the test harness is still preliminary. Remember to
77 ``cargo build`` after changes because the test harness doesn't yet
78 automatically build Rust code.
@@ -0,0 +1,127 b''
1 [[package]]
2 name = "aho-corasick"
3 version = "0.5.3"
4 source = "registry+https://github.com/rust-lang/crates.io-index"
5 dependencies = [
6 "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
7 ]
8
9 [[package]]
10 name = "cpython"
11 version = "0.1.0"
12 source = "git+https://github.com/indygreg/rust-cpython.git?rev=c90d65cf84abfffce7ef54476bbfed56017a2f52#c90d65cf84abfffce7ef54476bbfed56017a2f52"
13 dependencies = [
14 "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
15 "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
16 "python27-sys 0.1.2 (git+https://github.com/indygreg/rust-cpython.git?rev=c90d65cf84abfffce7ef54476bbfed56017a2f52)",
17 ]
18
19 [[package]]
20 name = "hgcli"
21 version = "0.1.0"
22 dependencies = [
23 "cpython 0.1.0 (git+https://github.com/indygreg/rust-cpython.git?rev=c90d65cf84abfffce7ef54476bbfed56017a2f52)",
24 "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
25 "python27-sys 0.1.2 (git+https://github.com/indygreg/rust-cpython.git?rev=c90d65cf84abfffce7ef54476bbfed56017a2f52)",
26 ]
27
28 [[package]]
29 name = "kernel32-sys"
30 version = "0.2.2"
31 source = "registry+https://github.com/rust-lang/crates.io-index"
32 dependencies = [
33 "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
34 "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
35 ]
36
37 [[package]]
38 name = "libc"
39 version = "0.2.34"
40 source = "registry+https://github.com/rust-lang/crates.io-index"
41
42 [[package]]
43 name = "memchr"
44 version = "0.1.11"
45 source = "registry+https://github.com/rust-lang/crates.io-index"
46 dependencies = [
47 "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
48 ]
49
50 [[package]]
51 name = "num-traits"
52 version = "0.1.41"
53 source = "registry+https://github.com/rust-lang/crates.io-index"
54
55 [[package]]
56 name = "python27-sys"
57 version = "0.1.2"
58 source = "git+https://github.com/indygreg/rust-cpython.git?rev=c90d65cf84abfffce7ef54476bbfed56017a2f52#c90d65cf84abfffce7ef54476bbfed56017a2f52"
59 dependencies = [
60 "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
61 "regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)",
62 ]
63
64 [[package]]
65 name = "regex"
66 version = "0.1.80"
67 source = "registry+https://github.com/rust-lang/crates.io-index"
68 dependencies = [
69 "aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
70 "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
71 "regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
72 "thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
73 "utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
74 ]
75
76 [[package]]
77 name = "regex-syntax"
78 version = "0.3.9"
79 source = "registry+https://github.com/rust-lang/crates.io-index"
80
81 [[package]]
82 name = "thread-id"
83 version = "2.0.0"
84 source = "registry+https://github.com/rust-lang/crates.io-index"
85 dependencies = [
86 "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
87 "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
88 ]
89
90 [[package]]
91 name = "thread_local"
92 version = "0.2.7"
93 source = "registry+https://github.com/rust-lang/crates.io-index"
94 dependencies = [
95 "thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
96 ]
97
98 [[package]]
99 name = "utf8-ranges"
100 version = "0.1.3"
101 source = "registry+https://github.com/rust-lang/crates.io-index"
102
103 [[package]]
104 name = "winapi"
105 version = "0.2.8"
106 source = "registry+https://github.com/rust-lang/crates.io-index"
107
108 [[package]]
109 name = "winapi-build"
110 version = "0.1.1"
111 source = "registry+https://github.com/rust-lang/crates.io-index"
112
113 [metadata]
114 "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66"
115 "checksum cpython 0.1.0 (git+https://github.com/indygreg/rust-cpython.git?rev=c90d65cf84abfffce7ef54476bbfed56017a2f52)" = "<none>"
116 "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
117 "checksum libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)" = "36fbc8a8929c632868295d0178dd8f63fc423fd7537ad0738372bd010b3ac9b0"
118 "checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
119 "checksum num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cacfcab5eb48250ee7d0c7896b51a2c5eec99c1feea5f32025635f5ae4b00070"
120 "checksum python27-sys 0.1.2 (git+https://github.com/indygreg/rust-cpython.git?rev=c90d65cf84abfffce7ef54476bbfed56017a2f52)" = "<none>"
121 "checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f"
122 "checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
123 "checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
124 "checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
125 "checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
126 "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
127 "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
@@ -0,0 +1,35 b''
1 [package]
2 name = "hgcli"
3 version = "0.1.0"
4 authors = ["Gregory Szorc <gregory.szorc@gmail.com>"]
5 license = "GPL-2.0"
6
7 build = "build.rs"
8
9 [[bin]]
10 name = "hg"
11 path = "src/main.rs"
12
13 [features]
14 # localdev: detect Python in PATH and use files from source checkout.
15 default = ["localdev"]
16 localdev = []
17
18 [dependencies]
19 libc = "0.2.34"
20
21 # We currently use a custom build of cpython and python27-sys with the
22 # following changes:
23 # * GILGuard call of prepare_freethreaded_python() is removed.
24 # TODO switch to official release when our changes are incorporated.
25 [dependencies.cpython]
26 version = "0.1"
27 default-features = false
28 features = ["python27-sys"]
29 git = "https://github.com/indygreg/rust-cpython.git"
30 rev = "c90d65cf84abfffce7ef54476bbfed56017a2f52"
31
32 [dependencies.python27-sys]
33 version = "0.1.2"
34 git = "https://github.com/indygreg/rust-cpython.git"
35 rev = "c90d65cf84abfffce7ef54476bbfed56017a2f52"
@@ -0,0 +1,128 b''
1 // build.rs -- Configure build environment for `hgcli` Rust package.
2 //
3 // Copyright 2017 Gregory Szorc <gregory.szorc@gmail.com>
4 //
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
7
8 use std::collections::HashMap;
9 use std::env;
10 use std::path::Path;
11 #[cfg(target_os = "windows")]
12 use std::path::PathBuf;
13
14 use std::process::Command;
15
16 struct PythonConfig {
17 python: String,
18 config: HashMap<String, String>,
19 }
20
21 fn get_python_config() -> PythonConfig {
22 // The python27-sys crate exports a Cargo variable defining the full
23 // path to the interpreter being used.
24 let python = env::var("DEP_PYTHON27_PYTHON_INTERPRETER").expect(
25 "Missing DEP_PYTHON27_PYTHON_INTERPRETER; bad python27-sys crate?",
26 );
27
28 if !Path::new(&python).exists() {
29 panic!(
30 "Python interpreter {} does not exist; this should never happen",
31 python
32 );
33 }
34
35 // This is a bit hacky but it gets the job done.
36 let separator = "SEPARATOR STRING";
37
38 let script = "import sysconfig; \
39 c = sysconfig.get_config_vars(); \
40 print('SEPARATOR STRING'.join('%s=%s' % i for i in c.items()))";
41
42 let mut command = Command::new(&python);
43 command.arg("-c").arg(script);
44
45 let out = command.output().unwrap();
46
47 if !out.status.success() {
48 panic!(
49 "python script failed: {}",
50 String::from_utf8_lossy(&out.stderr)
51 );
52 }
53
54 let stdout = String::from_utf8_lossy(&out.stdout);
55 let mut m = HashMap::new();
56
57 for entry in stdout.split(separator) {
58 let mut parts = entry.splitn(2, "=");
59 let key = parts.next().unwrap();
60 let value = parts.next().unwrap();
61 m.insert(String::from(key), String::from(value));
62 }
63
64 PythonConfig {
65 python: python,
66 config: m,
67 }
68 }
69
70 #[cfg(not(target_os = "windows"))]
71 fn have_shared(config: &PythonConfig) -> bool {
72 match config.config.get("Py_ENABLE_SHARED") {
73 Some(value) => value == "1",
74 None => false,
75 }
76 }
77
78 #[cfg(target_os = "windows")]
79 fn have_shared(config: &PythonConfig) -> bool {
80 // python27.dll should exist next to python2.7.exe.
81 let mut dll = PathBuf::from(&config.python);
82 dll.pop();
83 dll.push("python27.dll");
84
85 return dll.exists();
86 }
87
88 const REQUIRED_CONFIG_FLAGS: [&'static str; 2] = ["Py_USING_UNICODE", "WITH_THREAD"];
89
90 fn main() {
91 let config = get_python_config();
92
93 println!("Using Python: {}", config.python);
94 println!("cargo:rustc-env=PYTHON_INTERPRETER={}", config.python);
95
96 let prefix = config.config.get("prefix").unwrap();
97
98 println!("Prefix: {}", prefix);
99
100 // TODO Windows builds don't expose these config flags. Figure out another
101 // way.
102 #[cfg(not(target_os = "windows"))]
103 for key in REQUIRED_CONFIG_FLAGS.iter() {
104 let result = match config.config.get(*key) {
105 Some(value) => value == "1",
106 None => false,
107 };
108
109 if !result {
110 panic!("Detected Python requires feature {}", key);
111 }
112 }
113
114 // We need a Python shared library.
115 if !have_shared(&config) {
116 panic!("Detected Python lacks a shared library, which is required");
117 }
118
119 let ucs4 = match config.config.get("Py_UNICODE_SIZE") {
120 Some(value) => value == "4",
121 None => false,
122 };
123
124 if !ucs4 {
125 #[cfg(not(target_os = "windows"))]
126 panic!("Detected Python doesn't support UCS-4 code points");
127 }
128 }
@@ -0,0 +1,222 b''
1 // main.rs -- Main routines for `hg` program
2 //
3 // Copyright 2017 Gregory Szorc <gregory.szorc@gmail.com>
4 //
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
7
8 extern crate libc;
9 extern crate cpython;
10 extern crate python27_sys;
11
12 use cpython::{NoArgs, ObjectProtocol, PyModule, PyResult, Python};
13 use libc::{c_char, c_int};
14
15 use std::env;
16 use std::path::PathBuf;
17 use std::ffi::CString;
18 #[cfg(target_family = "unix")]
19 use std::os::unix::ffi::OsStringExt;
20
21 #[derive(Debug)]
22 struct Environment {
23 _exe: PathBuf,
24 python_exe: PathBuf,
25 python_home: PathBuf,
26 mercurial_modules: PathBuf,
27 }
28
29 /// Run Mercurial locally from a source distribution or checkout.
30 ///
31 /// hg is <srcdir>/rust/target/<target>/hg
32 /// Python interpreter is detected by build script.
33 /// Python home is relative to Python interpreter.
34 /// Mercurial files are relative to hg binary, which is relative to source root.
35 #[cfg(feature = "localdev")]
36 fn get_environment() -> Environment {
37 let exe = env::current_exe().unwrap();
38
39 let mut mercurial_modules = exe.clone();
40 mercurial_modules.pop(); // /rust/target/<target>
41 mercurial_modules.pop(); // /rust/target
42 mercurial_modules.pop(); // /rust
43 mercurial_modules.pop(); // /
44
45 let python_exe: &'static str = env!("PYTHON_INTERPRETER");
46 let python_exe = PathBuf::from(python_exe);
47
48 let mut python_home = python_exe.clone();
49 python_home.pop();
50
51 // On Windows, python2.7.exe exists at the root directory of the Python
52 // install. Everywhere else, the Python install root is one level up.
53 if !python_exe.ends_with("python2.7.exe") {
54 python_home.pop();
55 }
56
57 Environment {
58 _exe: exe.clone(),
59 python_exe: python_exe,
60 python_home: python_home,
61 mercurial_modules: mercurial_modules.to_path_buf(),
62 }
63 }
64
65 // On UNIX, argv starts as an array of char*. So it is easy to convert
66 // to C strings.
67 #[cfg(target_family = "unix")]
68 fn args_to_cstrings() -> Vec<CString> {
69 env::args_os()
70 .map(|a| CString::new(a.into_vec()).unwrap())
71 .collect()
72 }
73
74 // TODO Windows support is incomplete. We should either use env::args_os()
75 // (or call into GetCommandLineW() + CommandLinetoArgvW()), convert these to
76 // PyUnicode instances, and pass these into Python/Mercurial outside the
77 // standard PySys_SetArgvEx() mechanism. This will allow us to preserve the
78 // raw bytes (since PySys_SetArgvEx() is based on char* and can drop wchar
79 // data.
80 //
81 // For now, we use env::args(). This will choke on invalid UTF-8 arguments.
82 // But it is better than nothing.
83 #[cfg(target_family = "windows")]
84 fn args_to_cstrings() -> Vec<CString> {
85 env::args().map(|a| CString::new(a).unwrap()).collect()
86 }
87
88 fn set_python_home(env: &Environment) {
89 let raw = CString::new(env.python_home.to_str().unwrap())
90 .unwrap()
91 .into_raw();
92 unsafe {
93 python27_sys::Py_SetPythonHome(raw);
94 }
95 }
96
97 fn update_encoding(_py: Python, _sys_mod: &PyModule) {
98 // Call sys.setdefaultencoding("undefined") if HGUNICODEPEDANTRY is set.
99 let pedantry = env::var("HGUNICODEPEDANTRY").is_ok();
100
101 if pedantry {
102 // site.py removes the sys.setdefaultencoding attribute. So we need
103 // to reload the module to get a handle on it. This is a lesser
104 // used feature and we'll support this later.
105 // TODO support this
106 panic!("HGUNICODEPEDANTRY is not yet supported");
107 }
108 }
109
110 fn update_modules_path(env: &Environment, py: Python, sys_mod: &PyModule) {
111 let sys_path = sys_mod.get(py, "path").unwrap();
112 sys_path
113 .call_method(py, "insert", (0, env.mercurial_modules.to_str()), None)
114 .expect("failed to update sys.path to location of Mercurial modules");
115 }
116
117 fn run() -> Result<(), i32> {
118 let env = get_environment();
119
120 //println!("{:?}", env);
121
122 // Tell Python where it is installed.
123 set_python_home(&env);
124
125 // Set program name. The backing memory needs to live for the duration of the
126 // interpreter.
127 //
128 // Yes, we use the path to the Python interpreter not argv[0] here. The
129 // reason is because Python uses the given path to find the location of
130 // Python files. Apparently we could define our own ``Py_GetPath()``
131 // implementation. But this may require statically linking Python, which is
132 // not desirable.
133 let program_name = CString::new(env.python_exe.to_str().unwrap())
134 .unwrap()
135 .as_ptr();
136 unsafe {
137 python27_sys::Py_SetProgramName(program_name as *mut i8);
138 }
139
140 unsafe {
141 python27_sys::Py_Initialize();
142 }
143
144 // https://docs.python.org/2/c-api/init.html#c.PySys_SetArgvEx has important
145 // usage information about PySys_SetArgvEx:
146 //
147 // * It says the first argument should be the script that is being executed.
148 // If not a script, it can be empty. We are definitely not a script.
149 // However, parts of Mercurial do look at sys.argv[0]. So we need to set
150 // something here.
151 //
152 // * When embedding Python, we should use ``PySys_SetArgvEx()`` and set
153 // ``updatepath=0`` for security reasons. Essentially, Python's default
154 // logic will treat an empty argv[0] in a manner that could result in
155 // sys.path picking up directories it shouldn't and this could lead to
156 // loading untrusted modules.
157
158 // env::args() will panic if it sees a non-UTF-8 byte sequence. And
159 // Mercurial supports arbitrary encodings of input data. So we need to
160 // use OS-specific mechanisms to get the raw bytes without UTF-8
161 // interference.
162 let args = args_to_cstrings();
163 let argv: Vec<*const c_char> = args.iter().map(|a| a.as_ptr()).collect();
164
165 unsafe {
166 python27_sys::PySys_SetArgvEx(args.len() as c_int, argv.as_ptr() as *mut *mut i8, 0);
167 }
168
169 let result;
170 {
171 // These need to be dropped before we call Py_Finalize(). Hence the
172 // block.
173 let gil = Python::acquire_gil();
174 let py = gil.python();
175
176 // Mercurial code could call sys.exit(), which will call exit()
177 // itself. So this may not return.
178 // TODO this may cause issues on Windows due to the CRT mismatch.
179 // Investigate if we can intercept sys.exit() or SystemExit() to
180 // ensure we handle process exit.
181 result = match run_py(&env, py) {
182 // Print unhandled exceptions and exit code 255, as this is what
183 // `python` does.
184 Err(err) => {
185 err.print(py);
186 Err(255)
187 }
188 Ok(()) => Ok(()),
189 };
190 }
191
192 unsafe {
193 python27_sys::Py_Finalize();
194 }
195
196 result
197 }
198
199 fn run_py(env: &Environment, py: Python) -> PyResult<()> {
200 let sys_mod = py.import("sys").unwrap();
201
202 update_encoding(py, &sys_mod);
203 update_modules_path(&env, py, &sys_mod);
204
205 // TODO consider a better error message on failure to import.
206 let demand_mod = py.import("hgdemandimport")?;
207 demand_mod.call(py, "enable", NoArgs, None)?;
208
209 let dispatch_mod = py.import("mercurial.dispatch")?;
210 dispatch_mod.call(py, "run", NoArgs, None)?;
211
212 Ok(())
213 }
214
215 fn main() {
216 let exit_code = match run() {
217 Err(err) => err,
218 Ok(()) => 0,
219 };
220
221 std::process::exit(exit_code);
222 }
@@ -56,6 +56,8 b' i18n/hg.pot'
56 locale/*/LC_MESSAGES/hg.mo
56 locale/*/LC_MESSAGES/hg.mo
57 hgext/__index__.py
57 hgext/__index__.py
58
58
59 rust/target/
60
59 # Generated wheels
61 # Generated wheels
60 wheelhouse/
62 wheelhouse/
61
63
@@ -2435,12 +2435,27 b' class TestRunner(object):'
2435 self._tmpbindir = os.path.join(self._hgtmp, b'install', b'bin')
2435 self._tmpbindir = os.path.join(self._hgtmp, b'install', b'bin')
2436 os.makedirs(self._tmpbindir)
2436 os.makedirs(self._tmpbindir)
2437
2437
2438 # This looks redundant with how Python initializes sys.path from
2438 normbin = os.path.normpath(os.path.abspath(whg))
2439 # the location of the script being executed. Needed because the
2439 normbin = normbin.replace(os.sep.encode('ascii'), b'/')
2440 # "hg" specified by --with-hg is not the only Python script
2440
2441 # executed in the test suite that needs to import 'mercurial'
2441 # Other Python scripts in the test harness need to
2442 # ... which means it's not really redundant at all.
2442 # `import mercurial`. If `hg` is a Python script, we assume
2443 self._pythondir = self._bindir
2443 # the Mercurial modules are relative to its path and tell the tests
2444 # to load Python modules from its directory.
2445 with open(whg, 'rb') as fh:
2446 initial = fh.read(1024)
2447
2448 if re.match(b'#!.*python', initial):
2449 self._pythondir = self._bindir
2450 # If it looks like our in-repo Rust binary, use the source root.
2451 # This is a bit hacky. But rhg is still not supported outside the
2452 # source directory. So until it is, do the simple thing.
2453 elif re.search(b'|/rust/target/[^/]+/hg', normbin):
2454 self._pythondir = os.path.dirname(self._testdir)
2455 # Fall back to the legacy behavior.
2456 else:
2457 self._pythondir = self._bindir
2458
2444 else:
2459 else:
2445 self._installdir = os.path.join(self._hgtmp, b"install")
2460 self._installdir = os.path.join(self._hgtmp, b"install")
2446 self._bindir = os.path.join(self._installdir, b"bin")
2461 self._bindir = os.path.join(self._installdir, b"bin")
General Comments 0
You need to be logged in to leave comments. Login now