@HEADER@

Programming with Paludis, the Other Package Mangler

You can get a Subversion checkout of trunk/ rather than working off a tarball. If you're planning to submit patches, you'll have to do this.

It's best to start by skimming over the main program to get a feel for how everything fits together. The interface code is kept in the src/ directory, and library code is in paludis/ .

Basics

There are currently two APIs available:

A basic C++ application will look something like:

@BASIC_CPP_APP@

Compile this using g++ -Wall -lpaludis -lpaludisenvironments. If your compiler lacks tr1 support, you may also need -I/usr/include/paludis/compat.

The same application written in Ruby will look something like:

@BASIC_RUBY_APP@

Notice how various Rubyisms (singleton styles, question mark methods, constants for enums) are used. The Ruby interface aims to be natural to Ruby programmers and behave like a Ruby library, rather than being an exact translation.

Make sure you can run both of these examples before reading on.

Common Patterns

A number of common patterns are used throughout the code.

Iterators

STL style iterator pairs crop up in various places. Where this happens, there is usually a member typedef named Iterator or FooIterator, along with members named begin and end or begin_foo and end_foo. See TCppSL if you are unfamiliar with this style.

The underlying iterator format is usually hidden from the library programmer by a libwrapiter::ForwardIterator. This is to speed up compile times and to avoid breaking lots of things when underlying data types change.

Ruby works better with blocks than external iterators. Thus, instead of providing begin_foo and end_foo methods, Ruby classes typically have a single foo member function that returns an array made from the iterator range. This is slower, but much easier.

Pointers

We make extensive use of std::tr1::shared_ptr. Make sure you know how it works. See TCppSLE.

This is all hidden in the Ruby interface. You shouldn't have to care about memory management.

Validated Names

Rather than using std::string for package, category etc names, we have a wrapper class template called paludis::Validated, and a bunch of typedefs (paludis::CategoryNamePart, paludis::PackageNamePart, paludis::SlotName etc). This gives a couple of benefits:

Ruby doesn't do static checking, so it just uses raw String instances for all of these.

Collections

Sometimes we need to pass around a collection of items. The paludis::SortedCollection, paludis::SequentialCollection and paludis::AssociativeCollection wrappers handle this. They are passed around via smart pointers to avoid copying.

The basic classes are abstract. Use paludis::SortedCollection::Concrete etc if you need to make one yourself.

In Ruby these are converted to arrays.

Smart Records

Smart records are a bit smarter than Plain Old Data structs. They might define comparison operators and constructors. They're used in quite a few places. Try not to worry about how these work internally unless you want to get a very sore head.

Some smart records support named parameter constructors. A typical call to one of these looks like:

PackageDatabaseEntry my_pde(PackageDatabaseEntryParams::create()
        .package(QualifiedPackageName("app-editors/vim"))
        .version(VersionSpec("7.0.147"))
        .repository(RepositoryName("gentoo")));

Named parameters can be specified in any order.

Stringify

Many types can be converted to a std::string for display purposes. The paludis::stringify() template function will handle this. It also works for any internal and standard library data type that can be written to a std::ostream.

The Environment

At the heart of the Paludis API is a paludis::Environment subclass instance. All non-trivial clients will use one of the Environment subclasses as their starting point for obtaining data (Environment itself contains abstract members and cannot be used directly).

PaludisEnvironment and PortageEnvironment

The paludis::PaludisEnvironment and paludis::PortageEnvironment classes should be used when user configuration is to be honoured. These classes are not usually created directly; instead, paludis::EnvironmentMaker is used to obtain an instance:

    std::string my_spec_string(...);
    std::tr1::shared_ptr<paludis::Environment> env(
            paludis::EnvironmentMaker::get_instance()-<make_from_spec(my_spec_string));

The specification string should usually be the command line parameter value for -E or --environment. An empty string will give the default PaludisEnvironment or PortageEnvironment.

When using EnvironmentMaker, linking should include -lpaludisenvironments.

In Ruby, instances must be obtained using Paludis::EnvironmentMaker.

NoConfigEnvironment

The paludis::NoConfigEnvironment class should be used when user configuration should not be read, and instead the repository should be from a single particular directory. Multiple instances of this environment can be created if necessary.

When using NoConfigEnvironment, linking should include -lpaludisnoconfigenvironment.

In Ruby the class is Paludis::NoConfigEnvironment.

Other Environments

The paludis::qa::QAEnvironment class is used for QA environments. There is also a paludis::TestEnvironment class that is used in some test cases. These are less useful for most client authors.

In Ruby the class for a QA environment is Paludis::QA::QAEnvironment, there is currently no Ruby wrapper for the test environment.

The Package Database

Every paludis::Environment has a paludis::PackageDatabase, which can be obtained via the paludis::Environment::package_database() method.

The PackageDatabase contains a number of paludis::Repository subclass instances. These can be obtained using the paludis::PackageDatabase::begin_repositories() and paludis::PackageDatabase::end_repositories() pair or the paludis::PackageDatabase::fetch_repository() method.

The PackageDatabase also provides a number of utility functions. paludis::PackageDatabase::query() can be used to fetch a paludis::PackageDatabaseEntryCollection containing packages matching a particular paludis::PackageDepSpec. paludis::PackageDatabase::fetch_unique_qualified_package_name() can be used to convert a paludis::PackageNamePart with no associated paludis::CategoryNamePart into a full paludis::QualifiedPackageName.

In Ruby, the class is Paludis::PackageDatabase and an instance can only be obtained by calling some_environment.package_database. Rather than providing iterator pairs, repositories are available through the repositories method, whose return value behaves like an array of Paludis::Repository subclass instances. The fetch_repository, query and fetch_unique_qualified_package_name methods are available.

Repositories

The paludis::Repository class is an abstract base class representing a repository. Each paludis::Repository subclass provides various core functions, along with various optional others based upon the repository's capabilities.

The commonly used subclasses are:

Others include:

Repository creation is usually handled by the paludis::Environment subclass and its associated paludis::PackageDatabase. In Ruby, this is the only way to gain access to a repository.

All repositories provide some basic functions for querying their contents. Commonly used functions are paludis::Repository::version_metadata(), paludis::Repository::has_category_named(), paludis::Repository::has_package_named(), paludis::Repository::category_names(), paludis::Repository::package_names(), paludis::Repository::version_specs() and paludis::Repository::has_version(). These are available through Ruby; the has_ functions have a question mark suffix as per Ruby convention.

Additional capabilities are available through optional interfaces. These can be accessed via members named paludis::Repository::mask_interface, paludis::Repository::sets_interface and so on -- these members will either be a pointer to the interface or a zero pointer.

A full list of optional capabilities can be seen in the documentation for paludis::RepositoryCapabilities, from which paludis::Repository inherits.

In Ruby, this works a bit differently. Members that return either a self reference or nil are available for each interface. They are named as their C++ equivalents.

@FOOTER@