Patchwork D7450: INCOMPLETE pyoxidizer

login
register
mail settings
Submitter phabricator
Date Nov. 16, 2019, 9:22 p.m.
Message ID <differential-rev-PHID-DREV-nv32gl2twa2j2my46y7i-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/43317/
State Superseded
Headers show

Comments

phabricator - Nov. 16, 2019, 9:22 p.m.
indygreg created this revision.
Herald added subscribers: mercurial-devel, kevincox, durin42.
Herald added a reviewer: hg-reviewers.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D7450

AFFECTED FILES
  contrib/packaging/hgpackaging/pyoxidizer.py
  rust/hgcli/Cargo.lock
  rust/hgcli/Cargo.toml
  rust/hgcli/pyoxidizer.bzl
  rust/hgcli/src/main.rs
  setup.py

CHANGE DETAILS




To: indygreg, #hg-reviewers
Cc: durin42, kevincox, mercurial-devel
phabricator - Nov. 17, 2019, 12:18 a.m.
mharbison72 added inline comments.
mharbison72 added subscribers: martinvonz, mharbison72.

INLINE COMMENTS

> pyoxidizer.bzl:26
> +            # relative to the executable for now.
> +            install_location="app-relative:lib",
> +        ),

For some reason, this caused it to complain about not being able to find hgdemandimport with pyoxidizer v0.4.0-58-gc7d9c79.  I commented it out, and now I can get basic commands running.  I can live with that for now (though I saw the comment about external templates elsewhere, so maybe it needs to work eventually).

CC: @martinvonz

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7450/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7450

To: indygreg, #hg-reviewers
Cc: mharbison72, martinvonz, durin42, kevincox, mercurial-devel
phabricator - Dec. 17, 2019, 10:56 p.m.
martinvonz added a comment.


  `main.rs` and the Cargo files already exist, so this fails to apply.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7450/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7450

To: indygreg, #hg-reviewers
Cc: mharbison72, martinvonz, durin42, kevincox, mercurial-devel
phabricator - Jan. 23, 2020, 4:49 p.m.
marmoute added a comment.


  What;s the status of this? is it both flagged as "need review" and "INCOMPLETE".

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7450/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7450

To: indygreg, #hg-reviewers
Cc: marmoute, mharbison72, martinvonz, durin42, kevincox, mercurial-devel
phabricator - Jan. 26, 2020, 7:22 a.m.
indygreg added a comment.


  This produces a working `hg` executable with templates support on Linux and Windows (I haven't tested macOS). I'd feel comfortable landing if it will unblock others to hack on things.
  
  The test harness currently has a ~25% failure rate with a PyOxidizer `hg` binary. The reason is it is picking up `hg` as the `python` executable and scripts with `python` in the shebang all fail. We'll need to produce a separate executable that functions like `python` for use in the test harness. Or we'll need to teach the test harness to invoke a real Python for scripts there. Ideally we would use an oxidized Python binary in the test harness, otherwise we may fail to test some code in the PyOxidizer environment. I'll probably add a feature to PyOxidizer to enable creation of executables that function like `python`. Then, it should be a few lines of Starlark to produce a second executable binary :)

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7450/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7450

To: indygreg, #hg-reviewers
Cc: marmoute, mharbison72, martinvonz, durin42, kevincox, mercurial-devel
phabricator - Jan. 26, 2020, 7:47 a.m.
mharbison72 added a comment.


  In D7450#118093 <https://phab.mercurial-scm.org/D7450#118093>, @indygreg wrote:
  
  > This produces a working `hg` executable with templates support on Linux and Windows (I haven't tested macOS). I'd feel comfortable landing if it will unblock others to hack on things.
  
  Nice! I won’t get to test this latest revision until tomorrow, but I’d like to get something official landed to make things easier.
  
  I’ve run into the issue where *.py isn’t treated as a resource, which I think is a known issue [1].  Has enough been solidified to get something working yet?  Mostly this affects discovering bundled extensions and displaying help.
  
  @martinvonz and I talked a bit about bundling templates and possibly 3rd party extensions a couple days ago in IRC, in case you missed it.  So having that would be helpful.
  
  [1] https://github.com/indygreg/PyOxidizer/issues/207

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7450/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7450

To: indygreg, #hg-reviewers
Cc: marmoute, mharbison72, martinvonz, durin42, kevincox, mercurial-devel
phabricator - Jan. 26, 2020, 6:33 p.m.
indygreg added a comment.


  I don't have strong opinions about bundling templates and other resources internally versus externally. I do think it would be cool to have a fully self-contained executable. But that would require a command to write out embedded resources to the filesystem so people can modify them. That's a bit obscure.
  
  We may want a policy-like primitive to control where to load resources by default. And/or we can establish a clear order for resolution. e.g. try CLI argument, environment variable, then config option, then user directory, then global directory, etc.
  
  On Windows (which is where we have the biggest pressing concern for PyOxidizer at the moment since we aren't producing Python 3 installers yet), it doesn't much matter since the installer can install multiple files easily. I think installing files next to the executable mimicking today's install layout is the path of least resistance.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7450/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7450

To: indygreg, #hg-reviewers
Cc: marmoute, mharbison72, martinvonz, durin42, kevincox, mercurial-devel
phabricator - Jan. 27, 2020, 12:26 a.m.
indygreg added a comment.


  I just released PyOxidizer 0.5.0 and have updated the instructions in this file accordingly.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7450/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7450

To: indygreg, #hg-reviewers
Cc: marmoute, mharbison72, martinvonz, durin42, kevincox, mercurial-devel
phabricator - Jan. 27, 2020, 4:22 a.m.
This revision now requires changes to proceed.
mharbison72 added a comment.
mharbison72 requested changes to this revision.


  I think this can land after the Windows options are added.
  
  > So we don't need to worry about vendoring any Rust code to initially support PyOxidizer.
  
  Does that mean the parent of this can go away?
  
  For anybody else following along, start by updating Rust, especially on Windows.  IDR what version I had and it scrolled off the screen at this point, but I installed it in late October/early November.  Building PyOxidizer 0.5.0 cause my antivirus to go nuts when it was building some dependencies in MSYS.  (Being totally new to Rust, I'm wondering how we know that we can trust the numerous dependencies).  I switched to cmd.exe, and I started getting popups about CRT issues.  Updating seems to have fixed both of these.  (I've got rustc 1.40.0 now.)
  
  It also looks like something creates paths longer than MAX_PATH, which most tools can't deal with.  (I know there's a registry option to enable support globally, but I wonder if `make clean` should be delegating to a tool smart enough to handle long paths)
  
    $ make clean>/dev/null
    'build\lib.win-amd64-2.7' does not exist -- can't clean it
    'build\bdist.win-amd64' does not exist -- can't clean it
    'build\scripts-2.7' does not exist -- can't clean it
    rm: cannot lstat `build/pyoxidizer/python_distributions/python.86a3260edabeed314c6f32a931e60dd097fa854b1346561443353e1bc90e3edd/python/install/Lib/site-packag
    es/pip-19.3.1-py3.7.egg/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-37.pyc': File or path name too long
    rm: cannot lstat `build/pyoxidizer/python_distributions/python.86a3260edabeed314c6f32a931e60dd097fa854b1346561443353e1bc90e3edd/python/install/Lib/site-packag
    es/pip-19.3.1-py3.7.egg/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-37.pyc': File or path name too long
    rm: cannot lstat `build/pyoxidizer/python_distributions/python.86a3260edabeed314c6f32a931e60dd097fa854b1346561443353e1bc90e3edd/python/install/Lib/site-packag
    es/pip-19.3.1-py3.7.egg/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-37.pyc': File or path name too long
    rm: cannot lstat `build/pyoxidizer/python_distributions/python.86a3260edabeed314c6f32a931e60dd097fa854b1346561443353e1bc90e3edd/python/install/Lib/site-packag
    es/pip-19.3.1-py3.7.egg/pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/_implementation.cpython-37.pyc': File or path name too long
    rm: cannot lstat `build/pyoxidizer/python_distributions/python.86a3260edabeed314c6f32a931e60dd097fa854b1346561443353e1bc90e3edd/python/install/Lib/site-packag
    es/pip-19.3.1-py3.7.egg/pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/__init__.cpython-37.pyc': File or path name too long
    make: *** [cleanbutpackages] Error 1
  
  Another fun thing to handle will be stacktrace differences in tests.  Note the '.' vs '/' separator.  Is this related to the module naming issue that you referenced, and/or the unresolved issues behind the PyOxidizer issue I referenced here earlier?
  
    @@ -646,7 +634,7 @@
     Even though the extension fails during uisetup, hg is still basically usable:
       $ hg --config extensions.baduisetup=$TESTTMP/baduisetup.py version
       Traceback (most recent call last):
    -    File "*/mercurial/extensions.py", line *, in _runuisetup (glob)
    +    File "mercurial.extensions", line 251, in _runuisetup
           uisetup(ui)
         File "$TESTTMP/baduisetup.py", line 2, in uisetup
           1 / 0

INLINE COMMENTS

> pyoxidizer.bzl:25
> +        # We need this to make resourceutil happy, since it looks for sys.frozen.
> +        sys_frozen = True,
> +    )

I'm not sure why this worked for you on Windows, but I also needed to add:

  legacy_windows_stdio = True,
  legacy_windows_fs_encoding = True,

(I'm not sure if fs_encoding is really needed, but that was in the config file I was hacking with back in early December.)  Without the stdio option, I got no output from the `hg config` command.  Maybe because that wants to spin up a pager?

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7450/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7450

To: indygreg, #hg-reviewers, mharbison72
Cc: marmoute, mharbison72, martinvonz, durin42, kevincox, mercurial-devel
phabricator - Jan. 27, 2020, 4:41 a.m.
mharbison72 added a comment.


  In D7450#118095 <https://phab.mercurial-scm.org/D7450#118095>, @indygreg wrote:
  
  > I don't have strong opinions about bundling templates and other resources internally versus externally. I do think it would be cool to have a fully self-contained executable. But that would require a command to write out embedded resources to the filesystem so people can modify them. That's a bit obscure.
  
  Yeah, these were a few of the things we talked about.  I think we're all on the same page here, and I'm hoping for a single executable.  Martin mentioned early in the IRC chat, maybe putting the files under /etc/mercurial/templates (which is fine now that Windows has a global area for hg data), but then we'd minimally need an HGRCPATH analog for templates for running tests.
  
  The other tricky thing that came up is maybe teaching %include how to include resources.  At work, we point to a custom map-cmdline file that includes the default one, and then overrides just enough so that `hg log -v` will put each file on a newline, instead of making them space delimited.
  
  > We may want a policy-like primitive to control where to load resources by default. And/or we can establish a clear order for resolution. e.g. try CLI argument, environment variable, then config option, then user directory, then global directory, etc.
  
  I thought about adding a user level search path, but it will probably get complicated to tell resource based vs user file based files when reading without adding a known prefix to the resource names.  So I set that aside, because I'm not sure how useful it would actually be for users to install templates.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7450/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7450

To: indygreg, #hg-reviewers, mharbison72
Cc: marmoute, mharbison72, martinvonz, durin42, kevincox, mercurial-devel
phabricator - Jan. 27, 2020, 5:17 p.m.
mharbison72 added a comment.


  In D7450#118154 <https://phab.mercurial-scm.org/D7450#118154>, @mharbison72 wrote:
  
  > I think this can land after the Windows options are added.
  >
  >> So we don't need to worry about vendoring any Rust code to initially support PyOxidizer.
  >
  > Does that mean the parent of this can go away?
  
  Nevermind this.  It looks like `phabread --stack` doesn't pay attention to the fact that it was abandoned, so I didn't realize it.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7450/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7450

To: indygreg, #hg-reviewers, mharbison72
Cc: marmoute, mharbison72, martinvonz, durin42, kevincox, mercurial-devel
phabricator - Jan. 27, 2020, 8:50 p.m.
martinvonz added a comment.


  I'd be very happy to queue this as soon as @mharbison72's request for Windows has been addressed (I think that was all, right?). We (Google) would like to use it for macOS packaging too. We may also end up using it on Linux so we don't break when the system `python3` changes from 3.7 of 3.8 (because we currently install `.so` files specific to a minor Python version, IIUC). Thanks a lot for your work on this, @indygreg. And thanks for reviewing and testing it, @mharbison72.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7450/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7450

To: indygreg, #hg-reviewers, mharbison72
Cc: marmoute, mharbison72, martinvonz, durin42, kevincox, mercurial-devel
phabricator - Jan. 28, 2020, 12:41 a.m.
mharbison72 added a comment.


  In D7450#118284 <https://phab.mercurial-scm.org/D7450#118284>, @martinvonz wrote:
  
  > I'd be very happy to queue this as soon as @mharbison72's request for Windows has been addressed (I think that was all, right?).
  
  Yes.
  
  I'm still curious about the *.py-as-resources issue[1], but don't want this held up.
  
  [1] https://phab.mercurial-scm.org/D7450#118094

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7450/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7450

To: indygreg, #hg-reviewers, mharbison72
Cc: marmoute, mharbison72, martinvonz, durin42, kevincox, mercurial-devel
phabricator - Jan. 30, 2020, 6:01 p.m.
martinvonz added a comment.


  In D7450#118285 <https://phab.mercurial-scm.org/D7450#118285>, @mharbison72 wrote:
  
  > In D7450#118284 <https://phab.mercurial-scm.org/D7450#118284>, @martinvonz wrote:
  >
  >> I'd be very happy to queue this as soon as @mharbison72's request for Windows has been addressed (I think that was all, right?).
  >
  > Yes.
  > I'm still curious about the *.py-as-resources issue[1], but don't want this held up.
  > [1] https://phab.mercurial-scm.org/D7450#118094
  
  I think @indygreg doesn't have much time for Mercurial these days. @mharbison72, what do you think about taking this patch as is and then you can send fixes for Windows on top?

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7450/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7450

To: indygreg, #hg-reviewers, mharbison72
Cc: marmoute, mharbison72, martinvonz, durin42, kevincox, mercurial-devel
phabricator - Jan. 30, 2020, 7:28 p.m.
This revision now requires review to proceed.
mharbison72 added a comment.
mharbison72 accepted this revision.


  In D7450#118725 <https://phab.mercurial-scm.org/D7450#118725>, @martinvonz wrote:
  
  > I think @indygreg doesn't have much time for Mercurial these days. @mharbison72, what do you think about taking this patch as is and then you can send fixes for Windows on top?
  
  Sounds good to me.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7450/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7450

To: indygreg, #hg-reviewers, mharbison72
Cc: marmoute, mharbison72, martinvonz, durin42, kevincox, mercurial-devel

Patch

diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -568,7 +568,11 @@ 
 
 class hgbuildscripts(build_scripts):
     def run(self):
-        if os.name != 'nt' or self.distribution.pure:
+        if (
+            os.name != 'nt'
+            or self.distribution.pure
+            or 'PYOXIDIZER' in os.environ
+        ):
             return build_scripts.run(self)
 
         exebuilt = False
diff --git a/rust/hgcli/src/main.rs b/rust/hgcli/src/main.rs
new file mode 100644
--- /dev/null
+++ b/rust/hgcli/src/main.rs
@@ -0,0 +1,30 @@ 
+use pyembed::{default_python_config, MainPythonInterpreter};
+
+fn main() {
+    // The following code is in a block so the MainPythonInterpreter is destroyed in an
+    // orderly manner, before process exit.
+    let code = {
+        // Load the default Python configuration as derived by the PyOxidizer config
+        // file used at build time.
+        let config = default_python_config();
+
+        // Construct a new Python interpreter using that config, handling any errors
+        // from construction.
+        match MainPythonInterpreter::new(config) {
+            Ok(mut interp) => {
+                // And run it using the default run configuration as specified by the
+                // configuration. If an uncaught Python exception is raised, handle it.
+                // This includes the special SystemExit, which is a request to terminate the
+                // process.
+                interp.run_as_main()
+            }
+            Err(msg) => {
+                eprintln!("{}", msg);
+                1
+            }
+        }
+    };
+
+    // And exit the process according to code execution results.
+    std::process::exit(code);
+}
diff --git a/rust/hgcli/pyoxidizer.bzl b/rust/hgcli/pyoxidizer.bzl
new file mode 100644
--- /dev/null
+++ b/rust/hgcli/pyoxidizer.bzl
@@ -0,0 +1,37 @@ 
+# This file controls the PyOxidizer build configuration. See the
+# pyoxidizer crate's documentation for extensive documentation
+# on this file format.
+
+BUILD_PATH = CWD + "/../../build/pyoxidizer"
+
+Config(
+    application_name="hg",
+    python_distribution=default_python_distribution(),
+    embedded_python_config=EmbeddedPythonConfig(
+        legacy_windows_fs_encoding=True,
+        legacy_windows_stdio=True,
+        sys_paths=["$ORIGIN/lib"],
+    ),
+    python_run_mode=python_run_mode_eval("import hgdemandimport; hgdemandimport.enable(); import mercurial.dispatch; mercurial.dispatch.run()"),
+    packaging_rules=[
+        # Mercurial requires a fully featured Python because extensions may use
+        # anything.
+        StdlibExtensionsPolicy("all"),
+        Stdlib(include_source=True),
+        SetupPyInstall(
+            package_path="../..",
+            extra_global_arguments=["clean", "--all", "build"],
+            # Our code doesn't yet work with the in-memory importer. So package
+            # relative to the executable for now.
+            install_location="app-relative:lib",
+        ),
+    ]
+)
+
+# END OF COMMON USER-ADJUSTED SETTINGS.
+#
+# Everything below this is typically managed by PyOxidizer and doesn't need
+# to be updated by people.
+
+PYOXIDIZER_VERSION = "0.5.0"
+PYOXIDIZER_COMMIT = "9132d0d6e089d0f31526b3134d8be078ca586c31"
diff --git a/rust/hgcli/Cargo.toml b/rust/hgcli/Cargo.toml
new file mode 100644
--- /dev/null
+++ b/rust/hgcli/Cargo.toml
@@ -0,0 +1,14 @@ 
+[package]
+name = "hg"
+version = "0.1.0"
+authors = ["Gregory Szorc <gregory.szorc@gmail.com>"]
+license = "GPL-2.0"
+edition = "2018"
+
+[dependencies]
+jemallocator-global = { version = "0.3", optional = true }
+pyembed = { path = "../pyembed" }
+
+[features]
+default = []
+jemalloc = ["jemallocator-global", "pyembed/jemalloc"]
diff --git a/rust/hgcli/Cargo.lock b/rust/hgcli/Cargo.lock
new file mode 100644
--- /dev/null
+++ b/rust/hgcli/Cargo.lock
@@ -0,0 +1,336 @@ 
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "aho-corasick"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "autocfg"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "bitflags"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "byteorder"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "cc"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "cfg-if"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "cloudabi"
+version = "0.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "cpython"
+version = "0.2.1"
+source = "git+https://github.com/indygreg/PyOxidizer.git?tag=v0.3.0#ae305ea8f3aecb6ce558cb0118fd83a4e20c1e81"
+dependencies = [
+ "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "python3-sys 0.2.1 (git+https://github.com/indygreg/PyOxidizer.git?tag=v0.3.0)",
+]
+
+[[package]]
+name = "fs_extra"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "fuchsia-cprng"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "hg"
+version = "0.1.0"
+dependencies = [
+ "jemallocator-global 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pyembed 0.3.0",
+]
+
+[[package]]
+name = "jemalloc-sys"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "jemallocator"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "jemalloc-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "jemallocator-global"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "jemallocator 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libc"
+version = "0.2.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "memchr"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "num-traits"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "pyembed"
+version = "0.3.0"
+dependencies = [
+ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cpython 0.2.1 (git+https://github.com/indygreg/PyOxidizer.git?tag=v0.3.0)",
+ "jemalloc-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
+ "python3-sys 0.2.1 (git+https://github.com/indygreg/PyOxidizer.git?tag=v0.3.0)",
+ "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "python3-sys"
+version = "0.2.1"
+source = "git+https://github.com/indygreg/PyOxidizer.git?tag=v0.3.0#ae305ea8f3aecb6ce558cb0118fd83a4e20c1e81"
+dependencies = [
+ "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "rand_hc"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_isaac"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_jitter"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_os"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_pcg"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rand_xorshift"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rdrand"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "regex"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "thread_local"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "uuid"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[metadata]
+"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
+"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
+"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
+"checksum cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)" = "aa87058dce70a3ff5621797f1506cb837edd02ac4c0ae642b4542dce802908b8"
+"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
+"checksum cpython 0.2.1 (git+https://github.com/indygreg/PyOxidizer.git?tag=v0.3.0)" = "<none>"
+"checksum fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674"
+"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
+"checksum jemalloc-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d3b9f3f5c9b31aa0f5ed3260385ac205db665baa41d49bb8338008ae94ede45"
+"checksum jemallocator 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "43ae63fcfc45e99ab3d1b29a46782ad679e98436c3169d15a167a1108a724b69"
+"checksum jemallocator-global 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "991b61de8365c8b5707cf6cabbff98cfd6eaca9b851948b883efea408c7f581e"
+"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8"
+"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
+"checksum num-traits 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "443c53b3c3531dfcbfa499d8893944db78474ad7a1d87fa2d94d1a2231693ac6"
+"checksum python3-sys 0.2.1 (git+https://github.com/indygreg/PyOxidizer.git?tag=v0.3.0)" = "<none>"
+"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
+"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
+"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
+"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
+"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
+"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
+"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
+"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
+"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
+"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
+"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
+"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd"
+"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716"
+"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
+"checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a"
+"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
+"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/contrib/packaging/hgpackaging/pyoxidizer.py b/contrib/packaging/hgpackaging/pyoxidizer.py
new file mode 100644
--- /dev/null
+++ b/contrib/packaging/hgpackaging/pyoxidizer.py
@@ -0,0 +1,70 @@ 
+# pyoxidizer.py - Functionality for packaging using PyOxidizer.
+#
+# Copyright 2019 Gregory Szorc <gregory.szorc@gmail.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+import os
+import pathlib
+import shutil
+import subprocess
+
+
+def build_pyoxidizer(
+    source_dir: pathlib.Path,
+    build_dir: pathlib.Path,
+    pyoxidizer_bin: pathlib.Path = 'pyoxidizer',
+):
+    """Build Mercurial using PyOxidizer.
+
+    The resulting files will be copied to ``build_dir``.
+    """
+
+    config_path = source_dir / 'rust' / 'hgcli'
+    staging_dir = build_dir / 'staging'
+
+    if staging_dir.exists():
+        shutil.rmtree(staging_dir)
+
+    res = subprocess.run(
+        [str(pyoxidizer_bin), 'app-path', '--release', str(config_path)],
+        check=True,
+        capture_output=True,
+    )
+
+    app_path = pathlib.Path(res.stdout.strip())
+
+    subprocess.run(
+        [str(pyoxidizer_bin), 'build', '--release', str(config_path)],
+        check=True,
+    )
+
+    # PyOxidizer's application staging doesn't currently handle resource
+    # files properly. So we need to massage the file layout so things work.
+    staging_dir.mkdir(parents=True)
+
+    # All files in the root directory are fine.
+    for p in os.listdir(app_path):
+        p = app_path / p
+
+        if p.is_file():
+            dest = staging_dir / p.name
+            shutil.copyfile(p, dest)
+
+    # For the lib directory, only copy .py and .pyc files: we'll manage
+    # the resource files ourselves later.
+    for root, dirs, files in os.walk(app_path / 'lib'):
+        dirs.sort()
+
+        rel_dir = (app_path / 'lib').relative_to(app_path)
+
+        for f in sorted(files):
+            if not f.endswith(('.py', '.pyc')):
+                continue
+
+            dest_dir = staging_dir / rel_dir
+            dest_dir.mkdir(parents=True, exist_ok=True)
+
+            dest = dest_dir / f
+            shutil.copyfile(root / f, dest)