.. _rust_projects: ============= 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. .. _rust_project_layout: 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 :ref:`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 :ref:`config_python_embedded_data` 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 :ref:`config_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.