##// END OF EJS Templates
hgcli: customize for Mercurial...
Gregory Szorc -
r45129:bc847878 default
parent child Browse files
Show More
@@ -0,0 +1,50
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 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
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 = "hgcli",
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