After unpacking sources, building binaries, and installing into the %(destdir)s, Conary invokes an extensive set of policy objects to normalize file names, contents, locations, and semantics, and to enforce inter-package and intra-package consistency.
Policy objects are invoked serially and have access to the %(destdir)s as well as the %(builddir)s. Early policy objects can modify the %(destdir)s. After all %(destdir)s modification is finished, Conary creates a set of objects that represents the set of files in the destdir. Later policy then has that information available and can modify the packaging, including marking configuration files, marking tagged files, setting up dependencies, and setting file ownership and permissions.
The policy.Policy abstract superclass implements much of the mechanism that policies need. Many policies can simply list some regular expressions that specify (positively and negatively) the files to which they apply by default, and then implement a single method which is called for each matching file. The superclass provides a rich generic exception mechanism (also based on regular expressions) and a file tree walker that honors all the regular expressions. Policies are not required to use that superstructure; they can implement their own actions wherever necessary.
Policies can take additional information, as well as exceptions. For example, policy divides the files in a package into components automatically, but when the automatic assignment makes a bad choice, you can pass additional information to the ComponentSpec policy to change its behavior. Whenever you want to create multiple packages from a single recipe, you have to give the PackageSpec policy information on which files belong to what package. When you use non-root users and groups, you need to assign those users and group using the Ownership, UtilizeUser, or UtilizeGroup policies.
This object-oriented approach is fundamentally different from RPM policy scripts. In contrast to Conary policy, RPM policy scripts are shell scripts (or are driven by shell scripts), and have an all-or-nothing model. If they do not do exactly what you want, you have to disable them and do by hand every action that the scripts would have done. This means that if you do not keep up with changes in the RPM policy scripts, your re-implementation may not keep up with changes in RPM. Also, because this restriction is onerous, RPM policy scripts cannot be very strict or very useful; they have to be limited in power and scope to the least common denominator. By contrast, the rich exception mechanism in Conary policy allows the policy to be quite strict by default, allowing for explicit exceptions where appropriate. This allows Conary to enable a rich array of tests that enable packaging quality assurance, with the tests run before any cooked troves are committed to the repository or even placed in a changeset.
Policies have access to the recipe object, mainly for the macros and a cache of content type identification that works like an object-oriented version of the file program's magic database and is therefore called the magic cache. This makes it easy to predicate policy action on ELF files, ar files, gzip files, bzip2 files, and so forth. The magic objects (one per file) sometimes contain information extracted from the files, such as ELF sonames and compression levels in gzip and bzip2 files.
Some policy, such as packaging policy, is intended to remain a part of the Conary program per se. However, many policies will eventually be defined (or expanded) outside of Conary, by the distribution, via pluggable policy. This will be a set of modules loaded from the filesystem, probably with one policy object per module, and a system to ensure that ordering constraints are honored.
One of the best ways to describe what policy can do is to describe some examples of what it does do. As of this writing, there are 58 policy modules, so these few examples are by no means exhaustive; they are merely illustrative.
The FilesInMandir policy is very simple, and demonstrates how one line of code and several lines of data can implement effective policy. It looks only in %(mandir)s, %(x11prefix)s/man, and %(krbprefix)s/man, does not recurse through subdirectories, and within those directories only considers file entries, not subdirectory entries. Any recipe that needs to actually store a file in one of those directories can run r.FilesInMandir(exceptions='%(mandir)s/somefile') to cause the policy to ignore that file. All of this action specified so far requires only three simple data elements to be initialized as class data in the policy. Then a single-line method reports an error if the policy applies to any files, automatically ignoring any exceptions that have been applied from the recipe.
This simple policy effectively catches a common disagreement about what the --mandir configure argument or the MANDIR make variable specifies; the autotools de-facto standard is /usr/share/man/ but some upstream packages set it instead to be a subdirectory thereof, such as /usr/share/man/man1, which would cause all the man pages to go in the wrong place by default. Having this policy to catch errors makes it feasible for Conary to set --mandir and MANDIR by default, and fix up the exceptional cases when they occur.
The RemoveNonPackageFiles policy modifies the %(destdir)s and is even simpler in implementation than FilesInMandir. It lists as class data a set of regular expressions defining files that (by default) should not be included in the package, such as .cvsignore files, .orig files, libtool .la files, and so forth. It then has a single-line method that removes whatever file it is applied to. Again like FilesInMandir, a simple exception overrides the defaults; a recipe for a package that actually requires the libtool .la files (there are a few) can avoid having them removed by calling RemoveNonPackageFiles(exceptions=r'\.la$')—note the leading r, which tells Python that this is a ``raw'' string and that it should not interpret any \ characters in the string.
Both RemoveNonPackageFiles and FilesInMandir use the built-in directory walking capabilities of the Policy object. Most policy does, but it is not required. The NormalizeManPages policy is different. It implements its own walking over each man directory (the same ones that the FilesInMandir policy looks at), and removes any accidental references to %(destdir)s in the man pages (a common mistake), makes maximal use of symlinks, makes sure that all man pages are compressed with maximal compression, and then makes sure that all symlinks point to the compressed man pages. It uses almost none of the built-in policy mechanism; it merely asks to be called at the right time.
The NormalizeCompression policy ignores man pages and info pages, since they have their compression normalized intrinsically via other policy. It automatically includes all files that end in .gz and .bz2. Then, for each file, it looks in the magic cache (which is self-priming; if no entry exists, it will create one), and if it really is a gzip or bzip2 file and is not maximally compressed, it recompresses the file with maximal compression.
Finally, the DanglingSymlinks policy uses packaging information, looking at which component each file is assigned to. It is not enough to test whether all symlinks in a package resolve; it is also important to know whether a symlink resolves to a different component, since components can be installed separately. There are also special symlinks that are allowed to point outside the package, including consolehelper symlinks (which create an automatic requirement for the usermode:runtime component) and symlinks into the /proc filesystem. The DanglingSymlinks policy simply warns about symlinks that point from one component into another component built from the same package (except for shared libraries, where :devel components are expected to provide symlinks that cross components); symlinks that are not resolved within the package and are not explicitly provided as exceptions cause the cook to fail.