About Package Builds

About Package Builds

For every package that is registered at https://pkgs.racket-lang.org/, the package-build service starts with the current release, periodically checks for package updates, and attempts to build each package that has changed or has a dependency that has changed.

When a package installation succeeds, tests in the package are run with

  raco test --drdr

Packages are built on a 64-bit Linux virtual machine (VM) that is isolated from the network. Each package build starts with a fresh instance of the virtual machine, and packages are re-packaged in built form for use by other packages. Testing of a package starts with a fresh instance of the virtual machine and a fresh installation of the package from its built form.

Built-Package Catalog

The package-build service archives the most recently built form of each package and makes it available through a package catalog at

  https://pkg-build.racket-lang.org/server/built/catalog/

Built packages can install much faster than the original source packages, as long as you're using the current release (i.e., the same version of Racket as the package-build service). Use the built-package catalog by supplying the above URL with raco pkg install --catalog or by adding the URL to the start of your Racket installation's list of catalogs.

Another advantage of a built-package catalog is that it's compatible with “binary library” install mode, as selected with the --binary-lib flag to raco pkg install. In that mode, sources and documentation are stripped away from the package as it is installed, and dependencies that are needed only for compilation or building documentation are not downloaded at all.

Limitations

Only Packages From the Main Catalog Are Supported

The package-build service does not support references to PLaneT packages or to compatibility packages at https://planet-compats.racket-lang.org/. When a package depends on one of those, then the package installation fails, because package builds are performed on a VM without network connectivity.

Few System Libraries Are Installed

Each package is installed on a minimal VM that omits as many system libraries and tools as is practical. If building on the minimal VM fails, the package build is retried on a VM with more tools and libraries, including a C compiler and an X server running at :1. Look for “extra system dependencies” in the result column for packages that don’t work in the minimal environment but do work in the extended one.

The idea behind the minimal VM is that a package generally shouldn’t rely on tools that a Racket user may not have installed—and so it’s worth reporting those problems from the package-build service. At the same time, a package might be intended to work only in a typical Unix setup, and witholding a C compiler, for example, would be especially uncooperative of the package-build service.

Test Capabilities May Be Limited

Limited system libraries, missing network connectivity, or other constraints may prevent the package-build service from straighforwardly running a package’s tests. See Dealing with Test Failures.

Native Libraries Need Special Handling

Even on the extended VM, the available system libraries are limited. See Working with Native Libraries below for information on implementing packages that rely on additional native libraries.

Platform-Specific Compiled Files May Not Be In "compiled"

When the package-build service is meant to generate a catalog of built packages for use with multiple variants of Racket (i.e., CS on different platforms as well as BC), then "compiled" will contain machine-independent compiled code, which loads slowly. Machine-specific compiled code is loaded though a separate filesystem location as specified by the current-compiled-file-roots parameter and PLTCOMPILEDROOTS environment variable. Using the content of "compiled" directly or trying to run raco make or raco setup in a subprocess can go wong without taking extra care.

Dealing with Test Failures

In the absence of any "info.rkt"-based specifications or test submodules, raco test runs each module in a package. Running a particular module might fail if it’s a program-starting module that expects command-line arguments, or a module might start a program that expects input and causes the test to time out.

In the simplest case, you can add a test submodule as

  (module test racket/base)

to make raco test ignore the enclosing module. You can control raco test in various other ways through submodules and "info.rkt" files; see the documentation.

The default timeout on an individual test is 90 seconds, and the overall timeout for testing a package is 10 minutes. You can adjust the former, but the latter is a hard limit for now.

Tests are always run on the extended VM, but even so, sometimes the package-build service cannot run a package’s tests. For example, if a package needs network access for testing, the package-build service can’t help, because it runs on an isolated VM. There’s no way for a package to opt out of testing, but a package author can implement a test suite that skip tests under adverse conditions. In case there’s no other way for a test suite to determine that it can’t run, the package-build service sets the PLT_PKG_BUILD_SERVICE and CI environment variables when running tests; a test suite can explicitly check for the environment variable and skip tests that can’t work.

Working with Native Libraries

The “minimal” versus “extended” VM distinction begs the question of how the package-build service can support a package that relies on a native library—one that is not installed even on the extended VM.

It would be nice to have a bridge between the Racket package system and the OS package manager so that dependencies on OS packages could be declared and installed. One catch is that the bridge would have to work with a package-build VM that is isolated from the network. The networking, permission, and maintenance issues seem complex enough that we haven’t embarked on that direction.

For now, the package-build installation identifies itself as running on the "x86_64-linux-natipkg" platform, as opposed to plain "x86_64-linux". On the plain "x86_64-linux" platform, native libraries as needed by Racket packages are expected to be installed by a user through the OS’s package manager. On the "x86_64-linux-natipkg" platform, however, native libraries are handled as on Windows and Mac OS: they are expected to be provided by platform-specific packages.

For example, on the "x86_64-linux-natipkg" platform, the "math-lib" package depends on the "math-x86_64-linux-natipkg" package, which provides 64-bit Linux builds of GMP and MPFR. You can see that dependency declaration in the "info.rkt" file for the "math-lib" package:

  https://github.com/racket/math/blob/master/math-lib/info.rkt

Other example packages are "draw-x86_64-linux-natipkg-3", which includes Cairo and Pango builds, and "gui-x86_64-linux-natipkg", which includes a Gtk build. These natipkg builds are not better than the ones that are normally part of a Linux installation---in fact, they tend to be compiled in a way that supports fewer features---but they can be installed in a relatively minimal Linux installation for purposes like the package-build service.

If your package depends on a native library, then you currently have two main options:

Accomodate Unavailable Libraries

One option is to make the package behave when the native library is unavailable.

Typically, a native library that is accessed via ffi/unsafe isn’t needed to merely build a package (including its documentation). If possible, delay any use of the native library to run time so that the package can build without it.

For tests, you can either just let them fail, or you can adjust the test suite to avoid failure reports when the native library is unavailable or (if you must) when PLT_PKG_BUILD_SERVICE is defined.

Distribute Native Libraries

Another option is to build a 64-bit Linux version of the library, distribute it as a package, and make the package a platform-specific dependency of your package for the "x86_64-linux-natipkg" platform.

This option is in many ways the best one for users and for testing—especially if Windows and Mac OS native-library packages are also provided—but it’s more work.