Beeware Packaging: Making Python Apps Ready for Distribution
Until now, we have been operating our application in “Developer mode“, which has allowed us to easily execute it on our own system.
Nevertheless, our ultimate goal is to be able to distribute our application to other users. However, we do not wish to burden our users with instructions on how to install Python, establish a virtual environment, clone a git repository, and run Briefcase in developer mode.
Rather, we prefer to provide them with an installer that enables the application to function seamlessly without requiring any additional steps.
You can utilize Briefcase for packaging your application and distributing it in the following manner.
Create Your Application Scaffold
As this is your initial attempt at packaging your application, you must establish certain configuration files and other scaffolding to facilitate the packaging process.
To accomplish this, execute the following command from the helloworld directory:
(beeware-mrxenv) $ briefcase create [helloworld] Generating application template... Using app template: https://github.com/beeware/briefcase-macOS-app-template.git, branch v0.3.12 ... [helloworld] Installing support package... ... [helloworld] Installing application code... ... [helloworld] Installing requirements... ... [helloworld] Installing application resources... ... [helloworld] Removing unneeded app content... ... [helloworld] Created build/helloworld/macos/app
(beeware-mrxenv) $ briefcase create [helloworld] Finalizing application configuration... Targeting ubuntu:jammy (Vendor base debian) Determining glibc version... done Targeting glibc 2.35 Targeting Python3.10 [helloworld] Generating application template... Using app template: https://github.com/beeware/briefcase-linux-AppImage-template.git, branch v0.3.12 ... [helloworld] Installing support package... ... [helloworld] Building Docker container image... ... [helloworld] Installing application code... ... [helloworld] Installing requirements... ... [helloworld] Entering Docker context... ... [helloworld] Leaving Docker context... Building wheels for app requirements... done [helloworld] Entering Docker context... ... [helloworld] Leaving Docker context... Installing app requirements... done [helloworld] Installing application resources... ... [helloworld] Removing unneeded app content... ... [helloworld] Created build/helloworld/linux/ubuntu
(beeware-mrxenv) C:\...>briefcase create [helloworld] Generating application template... Using app template: https://github.com/beeware/briefcase-windows-app-template.git, branch 0.3.12 ... [helloworld] Installing support package... ... [helloworld] Installing application code... ... [helloworld] Installing requirements... ... [helloworld] Installing application resources... ... [helloworld] Created build\helloworld\windows\app
It is likely that you have observed an extensive amount of content display on your terminal screen. This leads to the question – what just occurred? Briefcase has performed the following actions:
- Briefcase has generated an application template for you. Building a native installer necessitates numerous files and configurations beyond your application code. This additional scaffolding is practically identical for all applications on the same platform, with the exception of the actual application name being constructed. To accommodate the platform you are developing on, Briefcase furnishes an application template for each supported platform. In this step, the template is implemented by substituting the name of your application, bundle ID, and other properties of your configuration file as required. If you are not satisfied with the template provided by Briefcase, you have the option to create your own. However, it is advisable to wait until you have acquired a bit more experience with Briefcase’s default template.
- Briefcase has also downloaded and installed a support package for you. The packaging method employed by Briefcase is considered to be “the simplest thing that could possibly work.” It includes a complete, isolated Python interpreter with every application it constructs. This method is slightly inefficient in terms of space usage. For example, if you package five applications with Briefcase, you will have five copies of the Python interpreter. Nevertheless, this approach ensures that each application is entirely independent, employing a specific Python version that is verified to work with the application. Once more, Briefcase offers a default support package for each platform. Alternatively, you can provide your own support package, which will be included in the build process. You might want to do this if you require specific options enabled in the Python interpreter or if you want to eliminate unnecessary modules from the standard library at runtime. Briefcase maintains a local cache of support packages. As a result, once you have downloaded a specific support package, it will be utilized in future builds.
- It has also installed the requirements for your application. You can indicate any third-party modules that are necessary at runtime in your application. These modules will be installed using pip into your application’s installer.
- Furthermore, it has installed your application code. Your application likely has its own code and resources, such as images that are required at runtime. These files are duplicated into the installer by Briefcase.
- Lastly, it has installed the necessary resources for your application. It also adds any extra resources required by the installer itself. For example, icons that must be attached to the final application and splash screen images are included in this.
After the above process is finished, you can check the project directory to find a directory that corresponds to your platform (macOS, Linux, or Windows). This directory will contain additional files, which are the platform-specific packaging configuration for your application.
Build Your Application
At this point, you have the option to compile your application.
This action will complete any required binary compilation that is necessary for your application to be executed on your target platform.
(beeware-mrxenv) $ briefcase build [helloworld] Adhoc signing app... ... Signing build/helloworld/macos/app/Hello World.app ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% • 00:07 [helloworld] Built build/helloworld/macos/app/Hello World.app
If you are using macOS, the build command doesn’t involve compilation. However, it is necessary to sign the binary contents to enable execution. The signature applied is an ad hoc signature that is only valid on your machine. If you intend to distribute the application to others, you will need to provide a complete signature.
(beeware-mrxenv) $ briefcase build [helloworld] Finalizing application configuration... Targeting ubuntu:jammy (Vendor base debian) Determining glibc version... done Targeting glibc 2.35 Targeting Python3.10 [helloworld] Building application... Build bootstrap binary... make: Entering directory '/home/brutus/beeware-tutorial/helloworld/build/linux/ubuntu/jammy/bootstrap' ... make: Leaving directory '/home/brutus/beeware-tutorial/helloworld/build/linux/ubuntu/jammy/bootstrap' Building bootstrap binary... done Installing license... done Installing changelog... done Installing man page... done Update file permissions... ... Updating file permissions... done Stripping binary... done [helloworld] Built build/helloworld/linux/ubuntu/jammy/helloworld-0.0.1/usr/bin/helloworld
After the previous step is done, you will find a helloworld-0.0.1 folder inside the build directory. This folder mirrors the Linux /usr filesystem and contains a bin folder with a helloworld binary. It also has lib and share folders that are necessary for supporting the binary.
(beeware-mrxenv) C:\...>briefcase build Setting stub app details... done [helloworld] Built build\helloworld\windows\app\src\Toga Test.exe
When executing the build command on Windows, there’s no need for any compilation, but it’s necessary to provide some metadata to the application such as its name and version.
Run Your App
Now, you can use Briefcase to execute your application.
(beeware-mrxenv) $ briefcase run [helloworld] Starting app... =========================================================================== Configuring isolated Python... Pre-initializing Python runtime... PythonHome: /Users/brutus/beeware-tutorial/helloworld/macOS/app/Hello World/Hello World.app/Contents/Resources/support/python-stdlib PYTHONPATH: - /Users/brutus/beeware-tutorial/helloworld/macOS/app/Hello World/Hello World.app/Contents/Resources/support/python311.zip - /Users/brutus/beeware-tutorial/helloworld/macOS/app/Hello World/Hello World.app/Contents/Resources/support/python-stdlib - /Users/brutus/beeware-tutorial/helloworld/macOS/app/Hello World/Hello World.app/Contents/Resources/support/python-stdlib/lib-dynload - /Users/brutus/beeware-tutorial/helloworld/macOS/app/Hello World/Hello World.app/Contents/Resources/app_packages - /Users/brutus/beeware-tutorial/helloworld/macOS/app/Hello World/Hello World.app/Contents/Resources/app Configure argc/argv... Initializing Python runtime... Installing Python NSLog handler... Running app module: helloworld ---------------------------------------------------------------------------
(beeware-mrxenv) $ briefcase run [helloworld] Finalizing application configuration... Targeting ubuntu:jammy (Vendor base debian) Determining glibc version... done Targeting glibc 2.35 Targeting Python3.10 [helloworld] Starting app... =========================================================================== Install path: /home/brutus/beeware-tutorial/helloworld/build/helloworld/linux/ubuntu/jammy/helloworld-0.0.1/usr Pre-initializing Python runtime... PYTHONPATH: - /usr/lib/python3.10 - /usr/lib/python3.10/lib-dynload - /home/brutus/beeware-tutorial/helloworld/build/helloworld/linux/ubuntu/jammy/helloworld-0.0.1/usr/lib/helloworld/app - /home/brutus/beeware-tutorial/helloworld/build/helloworld/linux/ubuntu/jammy/helloworld-0.0.1/usr/lib/helloworld/app_packages Configure argc/argv... Initializing Python runtime... Running app module: helloworld ---------------------------------------------------------------------------
(beeware-mrxenv) C:\...>briefcase run [helloworld] Starting app... =========================================================================== Log started: 2023-04-18 12:32:01Z PreInitializing Python runtime... PythonHome: C:\Users\brutus\beeware-tutorial\helloworld\windows\app\Hello World\src PYTHONPATH: - C:\Users\brutus\beeware-tutorial\helloworld\windows\app\Hello World\src\python39.zip - C:\Users\brutus\beeware-tutorial\helloworld\windows\app\Hello World\src - C:\Users\brutus\beeware-tutorial\helloworld\windows\app\Hello World\src\app_packages - C:\Users\brutus\beeware-tutorial\helloworld\windows\app\Hello World\src\app Configure argc/argv... Initializing Python runtime... Running app module: helloworld ---------------------------------------------------------------------------
When you start running your application using the output of the build command, you may observe some minor changes in the application’s appearance. This is because you are now using the packaged application instead of just running Python code. The operating system considers it as “an app” rather than “a Python program”, which reflects in the application’s display, such as the icons and the name displayed by the operating system.
Build Your Installer
Now, you are able to bundle your application for distribution by executing the package command.
This command will handle any necessary compilation to transform the project into a finished product that can be distributed to end-users.
The exact tasks required for each platform may differ, such as creating an installer, performing code signing, or carrying out other pre-distribution actions.
(beeware-mrxenv) $ briefcase package --adhoc-sign [helloworld] Signing app with adhoc identity... ... Signing build/helloworld/macos/app/Hello World.app ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% • 00:07 [helloworld] Building DMG... Signing dist/Hello World-0.0.1.dmg [helloworld] Packaged dist/Hello World-0.0.1.dmg
Once you use the package command, a file named Hello World-0.0.1.dmg will be created in the dist folder. You can find this file in the Finder, and by double-clicking on its icon, you can mount the DMG, which will give you a copy of the Hello World app and a link to your Applications folder, making it easy to install. To install your application, simply drag the app file into Applications. You can also send the DMG file to a friend, and they should be able to install the application the same way.
In the above example, the –adhoc-sign option was used to sign the application with ad hoc credentials that will only work on your machine. This was done to keep the tutorial simple. If you intend to distribute your application to others, you will need to set up real code signing identities, which can be a bit tricky.
If you’re using Briefcase to package your application on Linux, the output of the package command may vary slightly depending on the specific distribution you’re using. For example, if you’re using a Debian-based distribution, you will observe:
(beeware-mrxenv) $ briefcase package [helloworld] Finalizing application configuration... Targeting ubuntu:jammy (Vendor base debian) Determining glibc version... done Targeting glibc 2.35 Targeting Python3.10 [helloworld] Building .deb package... Write Debian package control file... done dpkg-deb: building package 'helloworld' in 'helloworld-0.0.1.deb'. Building Debian package... done [helloworld] Packaged dist/helloworld_0.0.1-1~ubuntu-jammy_amd64.deb
In the dist folder, you will find the DEB file that has been created.
You will observe the following if you are working with a RHEL-based distribution:
(beeware-mrxenv) $ briefcase package [helloworld] Finalizing application configuration... Targeting fedora:36 (Vendor base rhel) Determining glibc version... done Targeting glibc 2.35 Targeting Python3.10 [helloworld] Building .rpm package... Generating rpmbuild layout... done Write RPM spec file... done Building source archive... done Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.Kav9H7 + umask 022 ... + exit 0 Building RPM package... done [helloworld] Packaged dist/helloworld-0.0.1-1.fc36.x86_64.rpm
If you are looking for the RPM file that was generated, you will find it in the dist folder. Please note that only some Linux distributions are currently supported for packaging. In case you want to create a package for a different Linux distribution, Briefcase can still assist you, but you will need to install Docker. You can find official installers for Docker Engine on various Unix distributions. Please follow the instructions for your specific platform, but be careful not to install Docker in ‘rootless’ mode. After installing Docker, you should be able to initiate a Linux container, such as:
$ docker run -it ubuntu:22.04
After successful installation of Docker, running the specified command will display a Unix prompt (such as root@84444e31cff9:/#) within a Docker container of Ubuntu 22.04. To exit Docker and return to your local shell, simply press Ctrl-D. With Docker in place, you can use Briefcase to generate a package for any of the Linux distributions that Briefcase supports by providing a Docker image as an argument. For instance, if you want to create a DEB package for Ubuntu 22.04 (Jammy), irrespective of your operating system, you can execute the following command:
briefcase package --target ubuntu:jammy
By performing this command, you will download the Docker image for the Linux distribution that you have selected, set up a container capable of running Briefcase builds, and build the application package within that image. After the process is finished, you will find the package for your intended Linux distribution in the dist folder.
(beeware-mrxenv) C:\...>briefcase create [helloworld] Building MSI... ... [helloworld] Packaged dist\Hello_World-0.0.1.msi
After the completion of the above step, you will find a file named Hello_World-0.0.1.msi in the dist folder. To initiate the installation process, you can double-click on this installer, which will prompt a familiar Windows installation procedure. After this installation is complete, you will have a ‘Hello World’ entry in your start menu.
Beeware Packaging Importance
Beeware is an important tool for packaging Python applications for distribution. Packaging refers to the process of preparing your code for distribution, which can involve bundling it with all its dependencies and creating installers for different platforms.
Using Beeware can help simplify the packaging process, particularly for developers who are not familiar with the nuances of packaging Python applications. Beeware provides a suite of tools that allow developers to package their Python code for distribution as standalone applications that can run on various platforms, including Windows, macOS, and Linux.
Packaging your Python application using Beeware can also make it easier for users to install and use your application, as they don’t need to worry about installing and configuring dependencies themselves. This can help increase the adoption and reach of your application.
Moreover, Beeware provides a consistent and standardized approach to packaging Python applications, which can help ensure that your application runs as expected across different platforms. It can also make it easier to maintain and update your application in the future, as packaging is often a one-time task that can be automated using tools like Beeware.
You may find Beeware to be a valuable tool for packaging your Python applications for distribution. Beeware can simplify the packaging process and help you reach a wider audience while making it easier for users to install and use your applications. By using Beeware’s suite of tools and standardized approach to packaging, you can ensure that your Python applications run consistently across different platforms, which can make them easier to maintain and update in the future. Ultimately, if you want to distribute your Python applications to a larger audience, Beeware is an important tool that you should consider using.