Configuration Files¶
PyOxidizer uses Starlark files to configure run-time behavior.
Starlark is a dialect of Python intended to be used as a configuration language and the syntax should be familiar to any Python programmer.
Finding Configuration Files¶
If the PYOXIDIZER_CONFIG
environment variable is set, the path specified
by this environment variable will be used as the location of the Starlark
configuration file.
If PYOXIDIZER_CONFIG
is not set, PyOxidizer
will look for a
pyoxidizer.bzl
file starting in either the current working directory
or from the directory containing the pyembed
crate and then will traverse
ancestor directories until a file is found.
If no configuration file is found, an error occurs.
File Processing Semantics¶
A configuration file is evaluated in a custom Starlark dialect which provides primitives used by PyOxidizer. This dialect provides some well-defined global variables (defined in UPPERCASE) as well as some types and functions that can be constructed and called. See below for general usage and Configuration File API Reference for a full reference of what’s available to the Starlark environment.
Since Starlark is effectively a subset of Python, executing a PyOxidizer
configuration file is effectively running a sandboxed Python script. It is
conceptually similar to running python setup.py
to build a Python
package. As functions withink the Starlark environment are called,
PyOxidizer
will perform actions as described by those functions.
Targets¶
PyOxidizer
configuration files are composed of functions registered
as named targets. You define a function that does something then
register it was a target by calling the
register_target(name, fn, depends=[], default=False) global function provided by our Starlark
dialect. e.g.:
def get_python_distribution():
return default_python_distribution()
register_target("dist", get_python_distribution)
When a configuration file is evaluated, PyOxidizer
attempts to
resolve an ordered list of targets This list of targets is either
specified by the end-user or is derived from the configuration file.
The first register_target()
target or the last register_target()
call passing default=True
is the default target.
PyOxidizer
calls the registered target functions in order to
resolve the requested set of targets.
Target functions can depend on other targets and dependent target functions will automatically be called and have their return value passed as an argument to the target function depending on it. See register_target(name, fn, depends=[], default=False) for more.
The value returned by a target function is special. If that value is one
of the special types defined by our Starlark dialect (e.g.
PythonDistribution(sha256, local_path=None, url=None) or PythonExecutable),
PyOxidizer
will attempt to invoke special functionality depending
on the run mode. For example, when running pyoxidizer build
to
build a target, PyOxidizer
will invoke any build functionality
on the value returned by a target function, if present. For example,
a PythonExecutable
’s build functionality would compile an
executable binary embedding Python.
Common Operations¶
Obtain a Python Distribution¶
A PythonDistribution type defines a Python distribution from which you can derive binaries, perform packaging actions, etc. Every configuration file will likely utilize this type.
Instances are typically constructed from default_python_distribution() and are registered as their own target, since multiple targets may want to reference the distribution instance:
def make_dist():
return default_python_distribution()
register_target("dist", make_dist)
Creating an Executable File Embedding Python¶
A PythonExecutable type defines an executable file embedding Python.
Instances are derived from a PythonDistribution
instance, usually
by using target dependencies. In this example, we create an executable
that runs a Python REPL on startup:
def make_dist():
return default_python_distribution()
def make_exe(dist):
return dist.to_python_executable(
"myapp",
run_repl=True,
)
register_target("dist", make_dist)
register_target("exe", make_exe, depends=["dist"], default=True)
See Packaging User Guide for more examples.
Copying Files Next To Your Application¶
The :ref:`config_file_manifest type represents a collection of files
and their content. When FileManifest
instances are returned from a
target function, their build action results in their contents being
manifested in a directory having the name of the build target.
FileManifest
instances can be used to construct custom file install
layouts.
Say you have an existing directory tree of files you want to copy next to your application.
The glob(include, exclude=None, strip_prefix=None) function can be used to discover existing files
on the filesystem and turn them into a FileManifest
. You can then
return this FileManifest
directory or overlay it onto another
instance using FileManifest.add_manifest(manifest). Here’s an
example:
def make_install():
m = FileManifest()
templates = glob("/path/to/project/templates/**/*", strip_prefix="/path/to/project/")
m.add_manifest(templates)
return m
This will take all files /path/to/project/templates/
, strip the path
prefix /path/to/project/
from them and then add all those files to your
main FileManifest
. The files should be installed as templates/*
when
the InstallManifest
is materialized.