Show More
@@ -0,0 +1,50 b'' | |||
|
1 | # Oxidized Mercurial | |
|
2 | ||
|
3 | This project provides a Rust implementation of the Mercurial (`hg`) | |
|
4 | version control tool. | |
|
5 | ||
|
6 | Under the hood, the project uses | |
|
7 | [PyOxidizer](https://github.com/indygreg/PyOxidizer) to embed a Python | |
|
8 | interpreter in a binary built with Rust. At run-time, the Rust `fn main()` | |
|
9 | is called and Rust code handles initial process startup. An in-process | |
|
10 | Python interpreter is started (if needed) to provide additional | |
|
11 | functionality. | |
|
12 | ||
|
13 | # Building | |
|
14 | ||
|
15 | This project currently requires an unreleased version of PyOxidizer | |
|
16 | (0.7.0-pre). For best results, build the exact PyOxidizer commit | |
|
17 | as defined in the `pyoxidizer.bzl` file: | |
|
18 | ||
|
19 | $ git clone https://github.com/indygreg/PyOxidizer.git | |
|
20 | $ cd PyOxidizer | |
|
21 | $ git checkout <Git commit from pyoxidizer.bzl> | |
|
22 | $ cargo build --release | |
|
23 | ||
|
24 | Then build this Rust project using the built `pyoxidizer` executable:: | |
|
25 | ||
|
26 | $ /path/to/pyoxidizer/target/release/pyoxidizer build | |
|
27 | ||
|
28 | If all goes according to plan, there should be an assembled application | |
|
29 | under `build/<arch>/debug/app/` with an `hg` executable: | |
|
30 | ||
|
31 | $ build/x86_64-unknown-linux-gnu/debug/app/hg version | |
|
32 | Mercurial Distributed SCM (version 5.3.1+433-f99cd77d53dc+20200331) | |
|
33 | (see https://mercurial-scm.org for more information) | |
|
34 | ||
|
35 | Copyright (C) 2005-2020 Matt Mackall and others | |
|
36 | This is free software; see the source for copying conditions. There is NO | |
|
37 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
|
38 | ||
|
39 | # Running Tests | |
|
40 | ||
|
41 | To run tests with a built `hg` executable, you can use the `--with-hg` | |
|
42 | argument to `run-tests.py`. But there's a wrinkle: many tests run custom | |
|
43 | Python scripts that need to `import` modules provided by Mercurial. Since | |
|
44 | these modules are embedded in the produced `hg` executable, a regular | |
|
45 | Python interpreter can't access them! To work around this, set `PYTHONPATH` | |
|
46 | to the Mercurial source directory. e.g.: | |
|
47 | ||
|
48 | $ cd /path/to/hg/src/tests | |
|
49 | $ PYTHONPATH=`pwd`/.. python3.7 run-tests.py \ | |
|
50 | --with-hg `pwd`/../rust/hgcli/build/x86_64-unknown-linux-gnu/debug/app/hg |
@@ -4,8 +4,12 b' version = "0.1.0"' | |||
|
4 | 4 | build = "build.rs" |
|
5 | 5 | authors = ["Gregory Szorc <gregory.szorc@gmail.com>"] |
|
6 | 6 | edition = "2018" |
|
7 | license = "GPL-2.0" | |
|
8 | readme = "README.md" | |
|
7 | 9 | |
|
8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |
|
10 | [[bin]] | |
|
11 | name = "hg" | |
|
12 | path = "src/main.rs" | |
|
9 | 13 | |
|
10 | 14 | [dependencies] |
|
11 | 15 | jemallocator-global = { version = "0.3", optional = true } |
@@ -1,147 +1,53 b'' | |||
|
1 | # This file defines how PyOxidizer application building and packaging is | |
|
2 | # performed. See the pyoxidizer crate's documentation for extensive | |
|
3 | # documentation on this file format. | |
|
1 | ROOT = CWD + "/../.." | |
|
4 | 2 | |
|
5 | # Obtain the default PythonDistribution for our build target. We link | |
|
6 | # this distribution into our produced executable and extract the Python | |
|
7 | # standard library from it. | |
|
8 | def make_dist(): | |
|
9 | return default_python_distribution() | |
|
3 | def make_exe(): | |
|
4 | dist = default_python_distribution() | |
|
5 | ||
|
6 | code = "import hgdemandimport; hgdemandimport.enable(); from mercurial import dispatch; dispatch.run()" | |
|
10 | 7 | |
|
11 | # Configuration files consist of functions which define build "targets." | |
|
12 | # This function creates a Python executable and installs it in a destination | |
|
13 | # directory. | |
|
14 | def make_exe(dist): | |
|
15 | # This variable defines the configuration of the | |
|
16 | # embedded Python interpreter. | |
|
17 | python_config = PythonInterpreterConfig( | |
|
18 | # bytes_warning=0, | |
|
19 | # dont_write_bytecode=True, | |
|
20 | # ignore_environment=True, | |
|
21 | # inspect=False, | |
|
22 | # interactive=False, | |
|
23 | # isolated=False, | |
|
24 | # legacy_windows_fs_encoding=False, | |
|
25 | # legacy_windows_stdio=False, | |
|
26 | # no_site=True, | |
|
27 | # no_user_site_directory=True, | |
|
28 | # optimize_level=0, | |
|
29 | # parser_debug=False, | |
|
30 | # stdio_encoding=None, | |
|
31 | # unbuffered_stdio=False, | |
|
32 | # filesystem_importer=False, | |
|
33 | # sys_frozen=False, | |
|
34 | # sys_meipass=False, | |
|
35 | # sys_paths=None, | |
|
36 | # raw_allocator=None, | |
|
37 | # terminfo_resolution="dynamic", | |
|
38 | # terminfo_dirs=None, | |
|
39 | # use_hash_seed=False, | |
|
40 | # verbose=0, | |
|
41 | # write_modules_directory_env=None, | |
|
42 | # run_eval=None, | |
|
43 | # run_module=None, | |
|
44 | # run_noop=False, | |
|
45 | # run_repl=True, | |
|
8 | config = PythonInterpreterConfig( | |
|
9 | raw_allocator = "system", | |
|
10 | run_eval = code, | |
|
11 | # We want to let the user load extensions from the file system | |
|
12 | filesystem_importer = True, | |
|
13 | # We need this to make resourceutil happy, since it looks for sys.frozen. | |
|
14 | sys_frozen = True, | |
|
15 | legacy_windows_stdio = True, | |
|
46 | 16 | ) |
|
47 | 17 | |
|
48 | # The run_eval, run_module, run_noop, and run_repl arguments are mutually | |
|
49 | # exclusive controls over what the interpreter should do once it initializes. | |
|
50 | # | |
|
51 | # run_eval -- Run the specified string value via `eval()`. | |
|
52 | # run_module -- Import the specified module as __main__ and run it. | |
|
53 | # run_noop -- Do nothing. | |
|
54 | # run_repl -- Start a Python REPL. | |
|
55 | # | |
|
56 | # These arguments can be ignored if you are providing your own Rust code for | |
|
57 | # starting the interpreter, as Rust code has full control over interpreter | |
|
58 | # behavior. | |
|
59 | ||
|
60 | # Produce a PythonExecutable from a Python distribution, embedded | |
|
61 | # resources, and other options. The returned object represents the | |
|
62 | # standalone executable that will be built. | |
|
63 | 18 | exe = dist.to_python_executable( |
|
64 |
name = "hg |
|
|
65 | config = python_config, | |
|
66 | # Embed all extension modules, making this a fully-featured Python. | |
|
19 | name = "hg", | |
|
20 | resources_policy = "prefer-in-memory-fallback-filesystem-relative:lib", | |
|
21 | config = config, | |
|
22 | # Extension may depend on any Python functionality. Include all | |
|
23 | # extensions. | |
|
67 | 24 | extension_module_filter = "all", |
|
68 | ||
|
69 | # Only package the minimal set of extension modules needed to initialize | |
|
70 | # a Python interpreter. Many common packages in Python's standard | |
|
71 | # library won't work with this setting. | |
|
72 | #extension_module_filter='minimal', | |
|
73 | ||
|
74 | # Only package extension modules that don't require linking against | |
|
75 | # non-Python libraries. e.g. will exclude support for OpenSSL, SQLite3, | |
|
76 | # other features that require external libraries. | |
|
77 | #extension_module_filter='no-libraries', | |
|
78 | ||
|
79 | # Only package extension modules that don't link against GPL licensed | |
|
80 | # libraries. | |
|
81 | #extension_module_filter='no-gpl', | |
|
82 | ||
|
83 | # Include Python module sources. This isn't strictly required and it does | |
|
84 | # make binary sizes larger. But having the sources can be useful for | |
|
85 | # activities such as debugging. | |
|
86 | include_sources = True, | |
|
87 | ||
|
88 | # Whether to include non-module resource data/files. | |
|
89 | include_resources = False, | |
|
90 | ||
|
91 | # Do not include functionality for testing Python itself. | |
|
92 | include_test = False, | |
|
93 | 25 | ) |
|
94 | 26 | |
|
95 | # Invoke `pip install` with our Python distribution to install a single package. | |
|
96 | # `pip_install()` returns objects representing installed files. | |
|
97 | # `add_in_memory_python_resources()` adds these objects to the binary, | |
|
98 | # marking them for in-memory loading. | |
|
99 | #exe.add_in_memory_python_resources(dist.pip_install(["appdirs"])) | |
|
27 | exe.add_python_resources(dist.pip_install([ROOT])) | |
|
100 | 28 | |
|
101 | # Invoke `pip install` using a requirements file and add the collected resources | |
|
102 | # to our binary. | |
|
103 | #exe.add_in_memory_python_resources(dist.pip_install(["-r", "requirements.txt"])) | |
|
29 | return exe | |
|
30 | ||
|
31 | def make_install(exe): | |
|
32 | m = FileManifest() | |
|
104 | 33 | |
|
105 | # Read Python files from a local directory and add them to our embedded | |
|
106 | # context, taking just the resources belonging to the `foo` and `bar` | |
|
107 | # Python packages. | |
|
108 | #exe.add_in_memory_python_resources(dist.read_package_root( | |
|
109 | # path="/src/mypackage", | |
|
110 | # packages=["foo", "bar"], | |
|
111 | #)) | |
|
34 | # `hg` goes in root directory. | |
|
35 | m.add_python_resource(".", exe) | |
|
112 | 36 | |
|
113 | # Discover Python files from a virtualenv and add them to our embedded | |
|
114 | # context. | |
|
115 | #exe.add_in_memory_python_resources(dist.read_virtualenv(path="/path/to/venv")) | |
|
37 | templates = glob( | |
|
38 | include = [ROOT + "/mercurial/templates/**/*"], | |
|
39 | strip_prefix = ROOT + "/mercurial/", | |
|
40 | ) | |
|
41 | m.add_manifest(templates) | |
|
116 | 42 | |
|
117 | # Filter all resources collected so far through a filter of names | |
|
118 | # in a file. | |
|
119 | #exe.filter_from_files(files=["/path/to/filter-file"])) | |
|
120 | ||
|
121 | # Return our `PythonExecutable` instance so it can be built and | |
|
122 | # referenced by other consumers of this target. | |
|
123 | return exe | |
|
43 | return m | |
|
124 | 44 | |
|
125 | 45 | def make_embedded_resources(exe): |
|
126 | 46 | return exe.to_embedded_resources() |
|
127 | 47 | |
|
128 | def make_install(exe): | |
|
129 | # Create an object that represents our installed application file layout. | |
|
130 | files = FileManifest() | |
|
131 | ||
|
132 | # Add the generated executable to our install layout in the root directory. | |
|
133 | files.add_python_resource(".", exe) | |
|
134 | ||
|
135 | return files | |
|
136 | ||
|
137 | # Tell PyOxidizer about the build targets defined above. | |
|
138 | register_target("dist", make_dist) | |
|
139 | register_target("exe", make_exe, depends = ["dist"], default = True) | |
|
140 | register_target("resources", make_embedded_resources, depends = ["exe"], default_build_script = True) | |
|
141 | register_target("install", make_install, depends = ["exe"]) | |
|
142 | ||
|
143 | # Resolve whatever targets the invoker of this configuration file is requesting | |
|
144 | # be resolved. | |
|
48 | register_target("exe", make_exe) | |
|
49 | register_target("app", make_install, depends = ["exe"], default = True) | |
|
50 | register_target("embedded", make_embedded_resources, depends = ["exe"], default_build_script = True) | |
|
145 | 51 | resolve_targets() |
|
146 | 52 | |
|
147 | 53 | # END OF COMMON USER-ADJUSTED SETTINGS. |
General Comments 0
You need to be logged in to leave comments.
Login now