Package Manager Definition Developers Guide

Package manager definitions files contain objects which encapsulate the behaviors of specific package managers. Application definition files are written in an abstract way that is not tied to a specific package manager, and layering a package manager on top of the experiment allows Ramble to manage the software stack for its generated experiments. By default, Ramble does not impose a specific package manager on the experiments it generates, allowing experiments to reference manually created software stacks.

This guide will provide general steps for creating a new package manager definition file.

NOTE: Package managers are considered a more advanced feature of Ramble. Writing a new pcakage manager definition file in Ramble largely follows the same workflow as writing an application definition file, although the intent and behavior are different. It is recommended that you review application-def-guide before writing a modifier.

Preparation

Before writing a package manager definition file, it is helpful to understand how to manually accomplish the following steps with the package manager:

  1. Installation of new software

  2. Environment creation (preferred, but not strictly required depending on the package manager)

  3. Installed package path extraction

  4. Provenance extraction for installed packages

  5. (Optional) Package mirroring

  6. (Optional) Package caching / repository building

Ramble and its documentation cannot help with these steps. It is best to research how to perform these steps (if they are possible) with the package manager you are working with.

Package Manager Definition Creation

Package manager definition files are stored within object repositories. These repositories generally store package managers within a directory named package_managers. Individual repositories can control this through their own config file (repo.yaml or package_manager_repo.yaml).

Within the repository, each package manager definition file is a python module that is stored within a directory named for the package manager. As an example, Ramble comes with a repository named builtin. This repository contains several standard package manager definitions that are provided to the community. One of the package manager definition files provided is spack which inherits from spack-lightweight.

The spack package manager definition file is named package_manager.py and is stored within a directory named spack. Witihn the package_manager.py file, a python class is defined with a similar name to the package manager directory. Ramble’s package manager definition naming syntax follows Spack’s package naming rules.

When creating a new package manager, it is recommended that a runner class is created to encapsulate executing commands under the specific package manager. The runner class for spack is included in the spack-lightweight package manager definition.

Base Classes

Ramble provides base classes which can be inherited when creating new package manager definition files. These encapsulate most of the basic package manager functionality, and allow new package managers to function with only a little syntax. These can be seen in more detail in ramble.package_manager_types, however most package managers inherit from the base package manager class defined in the package manager module.

New package manager definitions can also inherit their behavior from other package manager classes to replicate aspects of their behavior.

Existing package manager classes can be referenced using the: from ramble.pkg_man.builtin.<package_manager_name> import <package_manager_class> syntax.

Writing a package manager definition

After a package manager’s package_manager.py file is created, Ramble’s language features can be used to define the bahvior of the modifier. These language features provide directives which define specific portions of the package managers’s functionality.

There are many language features available to package managers, and the ones you use will change based on the specific package manager you need to implement. Some of the language features are shared with applications and modifiers, but some are specific to package managers. For more information see ramble.language.

The directives from Ramble’s package manager language are placed alongside class variables, as in:

class Spack(PackageManagerBase):
  package_manager_variable(...)
  register_builtin(...)
  register_phase(...)

Package Manager Variables

When aspects of a package manager are able to be parameterized, it can be useful for a package manager to define a variable which users can modify to control the package manager’s behavior. The package_manager_variable directive can be used to define these variables.

Builtins

A builtin is a specific command that is injected within an experiment. Builtins can have dependencies, and can be injected either at the beginning or end of the experiments. Builtins are written as class methods that return a list of strings which are explicit commands to add into an experiment. The register_builtin directive can be used to add a builtin into an experiment.

For more information on builtins, see the advanced topic documentation on Builtins.

Phase Registration

In Ramble there are several different pipelines which are groupings of phases to perform a specific action (such as setup or analyze). Some modifiers need to inject phases into one or more of these pipelines. The register_phase directive can be used to add a phase into a pipeline. Phases are written as class methods with a specific signature, and will be automatically executed as part of the pipeline they belong to.

Package managers will primarily define new phases, and register them into specific experiments to modify the behavior of the different pipelines.

For more information on phases and pipelines, see documentation for Experiment Pipelines and Phases.

Testing a Package Manager

The package manager that is used in specific experiments is defined using the variants configuration section. Specifically variants:package_manager:<package_manager_name>. This quantity can be parameterized, and can refer to a variable which defines which package manager is used.

The behavior of a package manager when executing a dry-run depends on how the runner and package manager were implemented. It is recommended that each individual runner implement its own dry-run behavior to enable exploration with the package manager without having to perform full installation. Existing package managers can be used as a reference.

NOTE: Ramble’s unit tests contain a tests called known_applications. This unit test by default will dry-run every possible application with every possible package manager. As a result, it is unlikely that a package manager without dry-run support would pass Ramble’s unit tests.