Rust Projects¶
PyOxidizer uses Rust projects to build binaries embedding Python.
If you just have a standalone configuration file (such as when running
pyoxidizer init-config-file
), a temporary Rust project will be
created as part of building binaries and the existence of Rust should
be largely invisible (except for the output from building the Rust project).
If you use pyoxidizer init-rust-project
to initialize a
PyOxidizer
application, the Rust project exists side-by-side with
the PyOxidizer
configuration file and can be modified like
any other Rust project.
Either way, the PyOxidizer
configuration file works alongside Rust
to build binaries.
Layout¶
Generated Rust projects all have a similar layout:
$ find pyapp -type f | grep -v .git
Cargo.toml
build.rs
pyoxidizer.bzl
src/main.rs
The Cargo.toml
file is the configuration file for the Rust project.
Read more in
the official Cargo documentation.
The magic lines in this file to enable PyOxidizer are the following:
[package]
build = "build.rs"
[dependencies]
pyembed = ...
These lines declare a dependency on the pyembed
package, which holds
the smarts for embedding Python in a binary.
In addition, the build = "build.rs"
tells runs a script that hooks up
the output of the pyembed
crate with this project.
Next let’s look at src/main.rs
. If you aren’t familiar with Rust
projects, the src/main.rs
file is the default location for the source
file implementing an executable. If we open that file, we see a
fn main() {
line, which declares the main function for our executable.
The file is relatively straightforward. We import some symbols from the
pyembed
crate. We then construct a config object, use that to construct
a Python interpreter, then we run the interpreter and pass its exit code
to exit()
. Succinctly, we instantiate and run an embedded Python
interpreter. That’s our executable.
The pyoxidizer.bzl
is our auto-generated
PyOxidizer configuration file.
Build Artifacts for pyembed
¶
The pyembed
crate needs to reference special artifacts as part of its
build process in order to compile a Python interpreter into a binary.
These special artifacts are ultimately generated by PyOxidizer
. How
exactly can vary.
By default, these special artifacts are generated by the pyembed
crate’s
build.rs
build script. Unless behavior is overridden via environment
variables, this program runs pyoxidizer run-build-script
to generate
these special artifacts. That command will resolve the default build script
target in the found PyOxidizer
configuration file.
The special build artifacts are generated by resolving a configuration file
target returning a PythonEmbeddedData instance. In the
auto-generated configuration file, the embedded
target returns such a
type.
Cargo Features to Control Building¶
The pyembed
crate and generated Rust projects share a set of
build-mode-*
Cargo feature flags to control how build artifacts
are created and consumed.
The features are described in the following sections.
build-mode-standalone
¶
Do not attempt to invoke pyoxidizer
or find artifacts it would have
built. It is possible to build the pyembed
crate in this mode if
the rust-cpython
and python3-sys
crates can find a Python
interpreter. But, the pyembed
crate may not be usable or work in
the way you want it to.
This mode is intended to be used for performing quick testing on the
pyembed
crate. It is quite possible that linking errors will occur
in this mode unless you take additional actions to point Cargo at
appropriate libraries.
build-mode-pyoxidizer-exe
¶
A pyoxidizer
executable will be run to generate build artifacts.
The path to this executable can be defined via the PYOXIDIZER_EXE
environment variable. Otherwise PATH
will be used.
At build time, pyoxidizer run-build-script
will be run. A
PyOxidizer
configuration file will be discovered using the heuristics
described at Finding Configuration Files. OUT_DIR
will
be set if running from cargo
, so a pyoxidizer.bzl
next to the main
Rust project being built should be found and used.
pyoxidizer run-build-script
will resolve the default build script target
by default. To override which target should be resolved, specify the target
name via the PYOXIDIZER_BUILD_TARGET
environment variable. e.g.:
$ PYOXIDIZER_BUILD_TARGET=build-artifacts cargo build
build-mode-prebuilt-artifacts
¶
This mode tells the build script to reuse artifacts that were already built.
(Perhaps you called pyoxidizer build
or pyoxidizer run-build-script
outside the context of a normal cargo build
.)
In this mode, the build script will look for artifacts in the directory
specified by PYOXIDIZER_ARTIFACT_DIR
if set, falling back to OUT_DIR
.
This directory must have a cargo_metadata.txt
file, which will be
printed to stdout by the build script to tell Cargo how to link a Python
library.
cpython-link-unresolved-static
¶
Configures the link mode of the cpython
crate to use a static
pythonXY
library without resolving the symbol at its own build
time. The pyembed
crate or a crate building it will need to emit
cargo:rustc-link-lib=static=pythonXY
and any
cargo:rustc-link-search=native={}
lines to specify an explicit
pythonXY
library to link against.
This is the link mode used to produce self-contained binaries containing
libpython
and pyembed
code.
cpython-link-default
¶
Configures the link mode of the cpython
crate to use default
semantics. The crate’s build script will find a pre-built Python
library by querying the python
defined by PYTHON_SYS_EXECUTABLE
or found on PATH
. See the cpython
crate’s documentation for
more.
This link mode should be used when linking against an existing libpython
that can be found by the cpython
crate’s build script.