Bring most of "user documentation" from rpm.org to our reference manual

The rest need more massaging and/or delicate merging to avoid losing
content added in one place but not the other.
This commit is contained in:
Panu Matilainen 2020-10-07 14:58:04 +03:00
parent 0a91e77ef7
commit f5b24da36d
9 changed files with 680 additions and 7 deletions

View File

@ -67,6 +67,13 @@ EXTRA_DIST += $(man_sk_man8_DATA)
EXTRA_DIST += \
manual/index.md \
manual/arch_dependencies.md \
manual/autosetup.md \
manual/boolean_dependencies.md \
manual/file_triggers.md \
manual/lua.md \
manual/more_dependencies.md \
manual/scriptlet_expansion.md \
manual/builddependencies.md \
manual/buildroot.md \
manual/conditionalbuilds.md \

View File

@ -0,0 +1,112 @@
# Architecture-specific Dependencies
On multiarch systems such as x86_64 it would be often desireable to express that a package of compatible architecture is
needed to satisfy a dependency. In most of the cases this is already handled by the automatically extracted soname dependencies,
but this is not always the case: sometimes it's necessary to disable the automatic dependency generation, and then there
are cases where the information cannot be automatically generated, such as -devel package dependencies on other -devel packages
and build dependencies. Consider the following:
```
Name: foo
...
BuildRequires: libbar-devel >= 2.2
%package devel
Requires: libbar-devel >= 2.2
...
```
This works fine on single-arch systems such as i386, but it's not sufficient on multiarch systems: when
building a 32bit package on a 64bit system, a 32bit version of the libbar-devel would be needed, but the above lets
libbar-devel.x86_64 satisfy the build dependency too, leading to obscure build failure. Similarly
a 32bit libbar-devel would incorrectly satisfy the dependency for a 64bit package.
## ISA Dependencies
In rpm 4.6.0, the concept of ISA (Instruction Set Architecture) was introduced to permit
differentiating between 32- and 64-bit versions without resorting to file dependencies on obscure and/or
library-version dependent paths. To declare a dependency on a package name architecture specific,
append %{?_isa} to the dependency name, eg
```
Requires: libbar-devel%{?_isa} >= 2.2
```
This will expand to libbar-devel(archfamily-bitness) depending on the build target architecture,
for example a native build on x86_64 would give
```
Requires: libbar-devel(x86-64) >= 2.2
```
but with --target i386 (or i586, i686 etc) it would become
```
Requires: libbar-devel(x86-32) >= 2.2
```
Note that this requires all the involved packages must have been built with rpm >= 4.6.0,
older versions do not add the necessary name(isa) provides to packages.
## Anatomy of ISA macros
The ISA of a system in rpm consists of two parts: the generic
architecture family, and the bitness of the architecture. These are declared in the platform
specific macro files: %{!__isa_name} holds the architecture family and %{!__isa_bits} is the bitness.
The %{_isa} macro is just a convenience wrapper to format this to "(arch-bits)", and using the conditional
format %{?_isa} allows for backwards compatible use in spec files.
Besides the common-case use of just "Requires: foo%{_isa} >= 1.2.3", this two-part scheme permits
some rare special cases such as 64bit package also requiring 32bit version of the same architecture family:
```
Requires: foo%{_isa}
%if %{__isa_bits} == 64
Requires: foo(%{__isa_name}-32)
%endif
```
Note that there are systems with 64bit ISA which are not multiarch, so simply testing for %{!__isa_bits} in
the above example is not correct in all cases.
## Rationale
The ISA-scheme is not what people typically think of when speaking of architecture specific dependencies, instead
the general expectation is something like:
```
Requires: %{name}.%{_target_cpu}
```
While this would appear to be an obvious choice, it's not as useful as it initially seems: many architecture families
have several "sub-architectures" (such as i386, i586 and i686) and it's sometimes desirable to offer more than one
version of a package, optimized for a different architecture. For example, a package might come in i386 and i686 flavors.
You'll want the best version for a given system, but if some other package had hardwired the literal %{_target_cpu} in a
dependency, this could cause a) suboptimal package to be pulled in b) failure to install the package at all. "But make
it a regular expression" doesn't fly either: you would end up with specs full of constructs like
```
%ifarch %ix86
Requires: %{name}.(i?86|athlon|geode)
%endif
%ifarch x86_64 amd64 ia32e
Requires: %{name}.(x86_64|amd64|ia32e)
%endif
...
```
So why not just use the "base arch", ie %{_arch} for it? The problem with this is that eg. i386 is already overloaded
with meanings: it is used to mean the x86-32 architecture family, and also the physical i386 CPU. So if you had
i386 and i686 flavors of a package, the "base architecture" of the i686 variant would be i386, and ... oops, it's
not possible to tell whether it means an actual i386 CPU or i386-compatible architecture. The ISA naming scheme avoids
this ambiguity: ISA always means architecture family, not any specific architecture flavor.
The other important aspect of the implementation is compatibility: by adding the ISA information as additional
regular provides, packages remain 100% compatible with older rpm versions and depsolvers such as yum and urpmi without
any code changes. This wouldn't have been the case if the implementation relied on %{arch} tag from headers. Also
the conditional macro syntax permits spec files to be backwards compatible: when built with an rpm without ISA support,
it'll just fall back to former behavior.
It's possible that the expected "Requires: %{name}.%{_target_cpu}" style dependencies become supported too at some point,
but the need for this is rare and can be already accomplished by adding manual arch-specific provides+requires to the
packages that need it (or by arranging arch-specific filename(s) in packages and depending on those).

64
doc/manual/autosetup.md Normal file
View File

@ -0,0 +1,64 @@
# Automating patch application in specs
## %autosetup description
Starting with version 4.11, RPM has a set of new macros to further automate source unpacking and patch application. Previously one had to manually specify each patch to be applied, eg
```
%prep
%setup -q
%patch0
%patch1
%patch2
...
%patch149
```
This can get rather tedious when the number of patches is large. The new %autosetup macro allows taking care of all this in a single step: the following applies all the patches declared in the spec, ordered by the patch number:
```
%prep
%autosetup
```
In addition to automating plain old "patch" command invocations, %autosetup allows utilizing various version control systems such as git, mercurial (aka hg), quilt and bzr for managing the build directory source. For example this unpacks the vanilla source, initializes a git repository in the build directory and then applies all the patches defined in the spec using individual git apply + commits:
```
%autosetup -S git
```
The resulting build directory can be used for bisecting problems introduced in patches, and developing new patches from the build directory is more natural than with gendiff.
## %autosetup options
Generally %autosetup accepts the same arguments as %setup does. The notable exceptions are
* %autosetup defaults to quiet operation, so -q is not needed or accepted. Use -v to enable verbose source unpacking if needed.
* -N disables automatic patch application if necessary for some reason. If %autosetup is called with -N, the patch-application phase can be manually invoked with %autopatch macro.
* -S<vcs> specifies the VCS to use. Currently supported VCSes are: "git", "hg" (for mercurial), "bzr", "quilt" and "patch", "git_am" (rpm >= 4.12) and "gendiff" (rpm >= 4.14). If -S is omitted, %autosetup defaults to "patch"
* -p<number> argument to control patch prefix stripping (same as -p to %patch)
* -b (for creating patch backups) is accepted but currently ignored - this is not meaningful for a full-blown VCS anyway. If you need backups for gendiff use, use "gendiff" backend.
Note that the exact behavior of -S option depends on the used VCS: for example quilt only controls patches whereas git and mercurial control the entire source repository.
## Automating patch (and source) declarations
While typically patch and source names tend to be descriptive for humans, making automating the declarations impossible, some upstreams (for example bash and vim) provide bugfixes by serially numbered patches. In such cases automation can be taken one step further by programmatically generating the patch declarations as well. As of this writing there are no specific helper macros for performing this, but for example the embedded Lua interpreter can be used for the purpose:
```
%{lua:for i=1,45 do print(string.format("Patch%u: bash42-%03u\n", i, i)) end}
```
On spec parse, the above expands to as many patch declarations (best inspected with `rpmspec --parse <spec>`):
```
Patch1: bash42-001
Patch2: bash42-002
Patch3: bash42-003
Patch4: bash42-004
...
Patch45: bash42-045
```
Combined with %autosetup, this can eliminate a very large number of repetitive spec lines, making package maintenance that little bit easier.

View File

@ -0,0 +1,78 @@
## Boolean Dependencies
Starting with rpm-4.13, RPM is able to process boolean expressions in all dependencies (Requires, Recommends, Suggests, Supplements, Enhances, Conflicts). Boolean Expressions are always enclosed with parenthesis. They are build out of "normal" dependencies: either name only or name, comparison and version description.
## Boolean Operators
The following operators were introduced in RPM 4.13:
* `and` - requires all operands to be fulfilled for the term to be True.
* `Conflicts: (pkgA and pkgB)`
* `or` - requires one of the operands to be fulfilled
* `Requires: (pkgA >= 3.2 or pkgB)`
* `if` - requires the first operand to be fulfilled if the second is (reverse implication)
* `Recommends: (myPkg-langCZ if langsupportCZ)`
* `if else` - same as above but requires the third operand to be fulfilled if the second is not
* `Requires: (myPkg-backend-mariaDB if mariaDB else sqlite)`
The following additional operators were introduced in RPM 4.14:
* `with` - requires all operands to be fulfilled by the same package for the term to be True.
* `Requires: (pkgA-foo with pkgA-bar)`
* `without` - requires a single package that satisfies the first operand but not the second (set subtraction)
* `Requires: (pkgA-foo without pkgA-bar)`
* `unless` - requires the first operand to be fulfilled if the second is not (reverse negative implication)
* `Conflicts: (myPkg-driverA unless driverB)`
* `unless else` - same as above but requires the third operand to be fulfilled if the second is
* `Conflicts: (myPkg-backend-SDL1 unless myPkg-backend-SDL2 else SDL2)`
The `if` operator cannot be used in the same context with `or` and the `unless` operator cannot be used in the same context with `and`.
## Nesting
Operands can be Boolean Expressions themselves. They also need to be surrounded by parenthesis. `and` and `or` operators may be chained together repeating the same operator with only one set of surrounding parenthesis.
Examples:
`Requires: (pkgA or pkgB or pkgC)`
`Requires: (pkgA or (pkgB and pkgC))`
`Supplements: (foo and (lang-support-cz or lang-support-all))`
`Requires: ((pkgA with capB) or (pkgB without capA))`
`Supplements: ((driverA and driverA-tools) unless driverB)`
`Recommends: ((myPkg-langCZ and (font1-langCZ or font2-langCZ)) if langsupportCZ)`
## Semantics
The Semantic of the dependencies stay unchanged but instead checking for one match all names are checked and the Boolean value of there being a match is then aggregated over the Boolean operators. For a conflict to not prevent an install the result has to be False for everything else it has to be True.
Note that '''Provides''' are not dependencies and '''cannot contain Boolean Expressions'''.
### Incompatible nesting of operators
Note that the `if` operator is also returning a Boolean value. This is close to what the intuitive reading in most cases. E.g:
`Requires: (pkgA if pkgB)`
is True (and everything is OK) if pkgB is not installed. But if the same term is used where the "default" is False things become complicated:
`Conflicts: (pkgA if pkgB)`
is a Conflict unless pkgB is installed and pkgA is not. So one might rather want to use
`Conflicts: (pkgA and pkgB)`
in this case. The same is true if the `if` operator is nested in `or` terms.
`Requires: ((pkgA if pkgB) or pkgC or pkg)`
As the `if` term is True if pkgB is not installed this also makes the whole term True. If pkgA only helps if pkgB is installed you should use `and` instead:
`Requires: ((pkgA and pkgB) or pkgC or pkg)`
To avoid this confusion rpm refuses nesting `if` in such `or` like contexts (including `Conflicts:`) and `unless` in `and` like contexts.

View File

@ -0,0 +1,51 @@
## File triggers
File triggers are kind of rpm scriptlets. They could be defined in spec file of a package. Syntax of file trigger is following:
```
%file_trigger_tag [FILE_TRIGGER_OPTIONS] -- PATHPREFIX...
body_of_script
```
file_trigger_tag defines type of filetrigger. Allowed types are filetriggerin, filetriggerun, filetriggerpostun, transfiletriggerin, transfiletriggerun, transfiletriggerpostun. FILE_TRIGGER_OPTIONS are the same as options for other rpm scriptlets except for option -P. The priority of trigger is defined with number, the bigger number the sooner a file trigger script will be executed. Triggers with priority greater than 100000 will be executed before standard scriptlets and the other triggers will be executed after standard scriptlets. If you don't specify priority, the default priority is 1000000. Every file trigger of each type must contain one or more path prefixes and script. So example of file trigger can be:
```
%filetriggerin -- /usr/lib /lib
/usr/sbin/ldconfig
```
This file trigger will execute `/usr/bin/ldconfig` right after installation of a package that contains a file having a path starting with `/usr/lib` or `/lib`. The file trigger will be executed just once for one package no matter how many files in package starts with `/usr/lib` or `/lib`. But all file names starting with `/usr/lib` or `/lib` will be passed to standard input of trigger script so that you can do some filtering inside of your script:
```
%filetriggerin -- /usr/lib
grep "foo" && /usr/sbin/ldconfig
```
This file trigger will execute `/usr/bin/ldconfig` for each package containing files starting with `/usr/lib` and containing "foo" at the same time. Note that
the prefix-matched files includes all types of files - regular files,
directories, symlinks etc.
The file triggers are defined in spec files of packages. E.g. file trigger executing `ldconfig` could be defined in glibc package.
As was mentioned above there are more types of file triggers. We have two main types. File triggers execute once for package and file triggers executed once for whole transaction a.k.a transaction file triggers. Further file triggers are dived according to time of execution: before/after installation or erasure of a package or before/after a transaction.
Here is a list of all possible types:
### Excuted once per package ###
%filetriggerin: Executed after installation of a package if that package contained file(s) that matches prefix of this trigger. Also executed after installation of a package that contains this file trigger and there is/are some file(s) matching prefix of this file trigger in rpmdb.
%filetriggerun: Executed before uninstallation of a package if that package contained file(s) that matches prefix of this trigger. Also executed before uninstallation of a package that contains this file trigger and there is/are some file(s) matching prefix of this file trigger in rpmdb.
%filetriggerpostun: Executed after uninstallation of a package if that package contained file(s) that matches prefix of this trigger.
### Excuted once per transaction
%transfiletriggerin: Executed once after transaction for all installed packages that contained file(s) that matches prefix of this trigger. Also executed after transaction if there was a package containing this file trigger in that transaction and there is/are some files(s) matching prefix of this trigger in rpmdb.
%transfiletriggerun: Executed once before transaction for all packages that will be uninstalled in this transaction and that contains file(s) that matches prefix of this trigger. Also execute before transaction if there is a package containing this file trigger in that transaction and there is/are some files(s) matching prefix of this trigger in rpmdb.
%transfiletriggerpostun: Executed once after transaction for all uninstalled packages that contained file(s) that matches prefix of this trigger. Note: the list of triggering files is not available in this trigger type.

View File

@ -14,16 +14,25 @@
## Macro subsystem
* [Macro syntax](macros.md)
* [Embedded Lua](lua.md)
## Package Building
* [Spec Syntax](spec.md)
* [Conditional Builds](conditionalbuilds.md)
* [Dependencies](dependencies.md)
* [Installation Order](tsort.md)
* [Build Dependencies](builddependencies.md)
* [Relocatable Packages](relocatable.md)
* [Triggers](triggers.md)
* [Buildroot](buildroot.md) and [Multiple build areas](multiplebuilds.md)
* [Autosetup](autosetup.md)
* [Dependencies](dependencies.md)
* [More on Dependencies](more_dependencies.md)
* [Boolean Dependencies](boolean_dependencies.md)
* [Build Dependencies](builddependencies.md)
* [Architecture Dependencies](arch_dependencies.md)
* [Installation Order](tsort.md)
* Install scriptlets
* [Triggers](triggers.md)
* [File Triggers](file_triggers.md)
* [Scriptlet Expansion](scriptlet_expansion.md)
* [Conditional Builds](conditionalbuilds.md)
* [Relocatable Packages](relocatable.md)
* [Buildroot](buildroot.md) and [Multiple build areas](multiplebuilds.md)
* [Dependency Generation](dependency_generators.md)
## Developer Information

124
doc/manual/lua.md Normal file
View File

@ -0,0 +1,124 @@
# Lua in RPM
A fairly unknown feature of RPM is that it comes with an embedded Lua interpreter. This page attempts to document the possibilities of the embedded Lua. Note that Lua-support is a compile-time option, the following assumes that your RPM is was built using --with-lua option.
## Lua scriptlets
The internal Lua can be used as the interpreter of rpm any scriptlets (%pre, %post etc):
```
%pre -p <lua>
print("Hello from Lua")
```
The point? Remember, Lua is embedded in rpm. This means the Lua scriptlets run in the rpm process context, instead of forking a new process to execute something. This has a number of advantages over, say, using /bin/sh as scriptlet interpreter:
* No forking involved means it runs faster. Lua itself is fairly fast as an interpreter too.
* No external dependencies introduced to packages.
* The internal interpreter can run when there's nothing at all installed yet, because it doesn't need to be exec()'ed. Consider the initial install phase: before even /bin/sh is available to execute the simplest shell built-in commands, the shell's dependencies will have to be installed. What if some of those need scriptlets?
Internal Lua is the only thing that can reliably run in %pretrans. On initial system installation, there's absolutely nothing in the environment where %pretrans scriptlets execute. This is a condition you cannot even detect with any other means: testing for existence of a file or directory would otherwise require a shell, which is not there.
* Syntax errors in <lua> scripts are detected at package build time.
* As it runs in within the rpm process context, it can do things that external process cannot do, such as define new macros.
Scriptlet arguments are accessible from a global 'arg' table. Note: in Lua, indexes customarily start at 1 (one) instead of 0 (zero), and for the better or worse, the rpm implementation follows this practise. Thus the scriptlet arg indexes are off by one from general expectation based on traditional scriptlet arguments. The argument containing number of installed package instances is arg[2] and the similar argument for trigger targets is arg[3], whereas traditionally they are 1 and 2 (eg $1 and $2 in shell scripts).
While scriptlets shouldn't be allowed to fail normally, you can signal scriptlet failure status by using Lua's error(msg, [level]) function if you need to. As <lua> scriptlets run within the rpm process itself, care needs to be taken within the scripts - eg os.exit() must not be called (see ticket #167). In newer rpm versions (>= 4.9.0) this is not an issue as rpm protects itself by returning errors on unsafe os.exit() and posix.exec().
## Lua macros
The internal Lua interpreter can be used for dynamic macro content creation:
```
%{lua: print("Requires: foo >= 1.2")}
```
The above is a silly example and doesn't even begin to show how powerful a feature this is. For a slightly more complex example, RPM itself uses this to implement %patches and %sources macros (new in RPM 4.6.0):
```
%patches %{lua: for i, p in ipairs(patches) do print(p.." ") end}
%sources %{lua: for i, s in ipairs(sources) do print(s.." ") end}
```
## Available Lua extensions in RPM
In addition to all Lua standard libraries (subject to the Lua version rpm is linked to), a few custom extensions are available in the RPM internal Lua interpreter. These can be used in all contexts where the internal Lua can be used.
### rpm extension
The following RPM specific functions are available:
| Function | Explanation | Example |
|----------|-------------|---------|
|define(arg) | Define a global macro. | rpm.define("foo 1") |
|expand(arg) | Perform macro expansion. | rpm.expand("%{_libdir}")
|register() | Register an RPM hook |
|unregister() | Unregister an RPM hook |
|call() |Call an RPM hook |
|interactive() | Launch interactive session (only for testing + debugging) | rpm --eval "%{lua: rpm.interactive()}"|
|vercmp() | Perform RPM version comparison (rpm >= 4.7.0) | rpm.vercmp("1.2-1", "2.0-1")|
|b64encode() | Perform base64 encoding (rpm >= 4.8.0) |
|b64decode() | Perform base64 decoding (rpm >= 4.8.0) |
### posix extension
Lua standard library offers fairly limited set of io operations. The posix extension greatly enhances what can be done from Lua. The following functions are available in "posix" namespace, ie to call them use posix.function().
| Function | Explanation | Example |
|-----------|---------------|-----------|
|access(path, [mode]) | Test file/directory accessibility | if posix.access("/bin/rpm", "x") then ... end|
|chdir(path) | Change directory | posix.chdir("/tmp")
|chmod(path, modestring) | Change file/directory mode |
|chown(path, uid, gid) | Change file/directory owner/group |
|ctermid() |
|dir([path]) | Get directory contents - like readdir() | for i,p in pairs(posix.dir("/tmp")) do print(p.."\n") end|
|errno() | Get errno value and message print(posix.errno())|
|exec(path, [args...]) | Exec a program |
|files([path]) | Iterate over directory contents | for f in posix.files("/tmp") do print(f..'\n') end|
|fork() |Fork a new process |
|getcwd() | Get current directory |
|getenv(name) | Get environment variable |
|getgroup(gid) | Get group members (gid is number or string) |
|getlogin() | Get login name |
|getpasswd(uid, selector) | Get passwd information (username, uid, gid, shell, gecos...) | posix.getpasswd(posix.getlogin(), "shell")|
|getprocessid(selector) | Get process ID information (gid, uid, pid, ppid...) | posix.getprocessid("pid")|
|kill(pid, signal) | Send a signal to a process |
|link(oldpath, newpath) |Create a hard link |
|mkdir(path) | Create a new directory |
|mkfifo(path) | Create a FIFO |
|pathconf() | Get pathconf values |
|putenv() | Change or add an environment variable |
|readlink(path) | Read symlink value |
|rmdir(path) | Remove a directory |posix.rmdir("/tmp")|
|setgid(gid) | Set group identity |
|setuid(uid) | Set user identity |
|sleep(seconds) | Sleep for specified number of seconds | posix.sleep(5)|
|stat(path, selector) | Perform stat() on a file/directory (mode, ino, dev, nlink, uid, gid...) | posix.stat("/tmp", "mode")|
|symlink(oldpath, newpath) | Create a symlink |
|sysconf(name) | Access sysconf values | posix.sysconf("open_max")|
|times() | Get process times |
|ttyname() | Get current tty name |
|umask() | Get current umask |
|uname([format]) | Get information about current kernel | posix.uname("%r")|
|utime() | Change access/modification time of an inode |
|wait() | Wait for a child process |
|setenv() | Change or add an environment variable |
|unsetenv() | Remove a variable from environment |
### rex extension
This extension adds regular expression matching to Lua.
A simple example:
```
expr = rex.new(<regex<)
if expr:match(<arg<) then
... do stuff ...
end
```
TODO: fully document
## Extending and customizing
On initialization, RPM executes a global init.lua Lua initialization script, typically located in /usr/lib/rpm/init.lua. This can be used for performing custom runtime configuration of RPM and adding global functions and variables to the RPM Lua environment without recompiling rpm.

View File

@ -0,0 +1,132 @@
# More on Dependencies
## Boolean Dependencies
Starting with rpm 4.13.0, rpm supports boolean expressions in all
dependencies. For example:
```
Requires: (pkgA or (pkgB and pkgC))
```
More details in a [separate article](boolean_dependencies.html).
## Architecture-specific Dependencies
Starting with rpm 4.6.0, it's possible to specify dependencies to be specific to a given architecture family:
```
Requires: somepackage%{?_isa} = version
```
More details in a [separate article](arch_dependencies.html).
## Scriptlet Dependencies
Often package scriptlets need various other packages in order to execute correctly, and sometimes those dependencies aren't even needed at runtime, only for installation. Such install/erase-time dependencies can be expressed with "Requires(<scriptlet>): <dependency>" notation, for example the following tells rpm that useradd program is needed by the package %pre scriptlet (often the case if a package uses a custom group/username for its files):
```
Requires(pre): /usr/sbin/useradd
```
This has two "side-effects":
* It ensures that the package providing /usr/sbin/useradd is installed before this package. In presence of dependency loops, scriptlet dependencies are the only way to ensure correct install order.
* If there are no other dependencies on the package providing /usr/sbin/useradd, that package is permitted to be removed from the system after installation(!)
It's a fairly common mistake to replace legacy PreReq dependencies with Requires(pre), but this is not the same, due to the latter point above!
## Automatic Dependencies
To reduce the amount of work required by the package builder, RPM scans the file list of a package when it is being built. Any files in the file list which require shared libraries to work (as determined by ldd) cause that package to require the shared library.
For example, if your package contains /bin/vi, RPM will add dependencies for both libtermcap.so.2 and libc.so.5. These are treated as virtual packages, so no version numbers are used.
A similar process allows RPM to add Provides information automatically. Any shared library in the file list is examined for its soname (the part of the name which must match for two shared libraries to be considered equivalent) and that soname is automatically provided by the package. For example, the libc-5.3.12 package has provides information added for libm.so.5 and libc.so.5. We expect this automatic dependency generation to eliminate the need for most packages to use explicit Requires: lines.
## Custom Automatic Dependency
Customizing automatic dependency generation is covered in [dependency generator documentation]().
## Interpreters and Shells
Modules for interpreted languages like perl and tcl impose additional dependency requirements on packages. A script written for an interpreter often requires language specific modules to be installed in order to execute correctly. In order to automatically detect language specific modules, each interpreter may have its own [generators](dependency_generators.html). To prevent module name collisions between interpreters, module names are enclosed within parentheses and a conventional interpreter specific identifier is prepended:
```
Provides: perl(MIME-Base64), perl(Mail-Header)-1-09
Requires: perl(Carp), perl(IO-Wrap) = 4.5
```
The output of a per-interpreter dependency generator (notice in this example the first requirement is a package and the rest are language specific modules)
```
Mail-Header >= 1.01
perl(Carp) >= 3.2
perl(IO-Wrap) == 4.5 or perl(IO-Wrap)-4.5
```
the output from dependency generator is
```
Foo-0.9
perl(Widget)-0-1
```
## Installing and Erasing Packages with Dependencies
For the most part, dependencies should be transparent to the user. However, a few things will change.
First, when packages are added or upgraded, all of their dependencies must be satisfied. If they are not, an error message like this appears:
```
failed dependencies:
libICE.so.6 is needed by somepackage-2.11-1
libSM.so.6 is needed by somepackage-2.11-1
libc.so.5 is needed by somepackage-2.11-1
```
Similarly, when packages are removed, a check is made to ensure that no installed packages will have their dependency conditions break due to the packages being removed. If you wish to turn off dependency checking for a particular command, use the --nodeps flag.
## Querying for Dependencies
Two new query information selection options are now available. The first, --provides, prints a list of all of the capabilities a package provides. The second, --requires, shows the other packages that a package requires to be installed, along with any version number checking.
There are also two new ways to search for packages. Running a query with --whatrequires \<item\> queries all of the packages that require \<item\>. Similarly, running --whatprovides \<item\> queries all of the packages that provide the \<item\> virtual package. Note that querying for package that provides "python" will not return anything, as python is a package, not a virtual package.
## Verifying Dependencies
As of RPM 2.2.2, -V (aka --verify) verifies package dependencies by default. You can tell rpm to ignore dependencies during system verification with the --nodeps. If you want RPM to verify just dependencies and not file attributes (including file existence), use the --nofiles flag. Note that "rpm -Va --nofiles --nodeps" will not verify anything at all, nor generate an error message.
## Branching Version
It is quite common to need to branch a set of sources in version control. It is not so obvious how those branches should be represented in the package version numbers. Here is one solution.
You have a bag of features that are injected into a package in a non-ordered fashion, and you want to have the package name-version-release be able to:
```
1) identify the "root version" of the source code.
2) identify the handful of features that are in that
branch of the package.
3) preserve sufficient ordering so that packages upgrade
without the use of --oldpackage.
```
A simple (but possibly not adequate) scheme to achieve this is:
```
Name: foo
Version: <the "root version" of the source code>
Release: <release instance>.<branch>
```
where the release instance is something like YYYMMMDD or some linear record of the number of builds with the current tar file, it is used to preserve ordering when necessary.
Another alternative scheme might be:
```
Name: foo
Epoch: <branch>
Version: <the branch specific version of the code>
Release: <release instance>
```
## Build dependencies
The following dependencies need to be fullfilled at build time. These are similar to the install time version but these apply only during package creation and are specified in the specfile. They end up as "regular" dependencies of the source package (SRPM) (BuildRequires? become Requires) but are not added to the binary package at all.
```
BuildRequires:
BuildConflicts:
BuildPreReq:
```

View File

@ -0,0 +1,96 @@
# Runtime scriptlet expansion (DRAFT)
Traditionally rpm scriptlets are macro-expanded at build-time like everything else in specs, but beyond that they are "static". Usually this is just what you want, but there are some cases where one would rather leave some decisions until install-time. For example a noarch package might want to interact with an arch-specific package in its scriptlets, which can involve determining the correct value of %{_libdir}. Or one might want to only perform some actions depending on macros defined on the system. Macros can be evaluated at runtime by using the built-in Lua-interpreter (ie -p <lua> scriptlets) with rpm.expand() but rewriting scriptlets in Lua is not always feasible. Starting with version 4.9.0, rpm supports two generic runtime scriptlet expansion mechanisms that are available to all scriptlets regardless of the language they are written in: macro- and queryformat-expansion.
## Macro expansion
Taking the example above where noarch package wants to create a symlink to a arch-dependent location from its scriptlet, traditionally one might use something like this - figure out whether /usr/lib64 or /usr/lib should be used by looking at existing paths:
```
%post
if [ -d /usr/lib64/foo ]; then
ln -s %{_datadir}/bar/some.ext /usr/lib64/foo/some.ext
elif [ -d /usr/lib/foo ]; then
ln -s %{_datadir}/bar/some.ext /usr/lib/foo/some.ext
fi
```
Using runtime macro expansion this could be written as:
```
%post -e
ln -s %{_datadir}/bar/some.ext %%{_libdir}/foo/some.ext
```
The -e argument to %post (or any other scriptlet) enables the runtime macro expansion for this particular script. Also note that %%{_libdir} is escaped with an extra %, otherwise it would be expanded at build-time along with %{_datadir} which is not what you want here. What the scriptlet does now directly depends on the system its being installed on - architecture and overall rpm configuration:
On a single-lib system (such as most 32bit architectures) it would typically expand to:
```
ln -s /usr/share/bar/some.ext /usr/lib/foo/some.ext
```
...and on a multilib-capable 64bit system it would expand to:
```
ln -s /usr/share/bar/some.ext /usr/lib64/foo/some.ext
```
For fancier tricks, all rpm macros including built-ins such as %{lua:...} can be used in this manner. However the effects of %define, %global and %undefine are limited to the particular scriptlet being expanded: the scriptlet expansion occurs in the fork()'ed child process, not in the main rpm process.
## Queryformat expansion
The other form of runtime expansion is queryformat expansion, which can be rather cumbersome to use but allows for something not otherwise possible at all: access to the package header contents at runtime. Much of the data that is static by nature, such as %{name}, %{version} and %{_arch}, is of course available as build-time macros, but there's plenty of data which is simply not available at build-time. Such as the exact file list of the package, not to mention the files that actually are installed when considering effects of --nodocs, %{_netshareddir}, %{_installangs} etc mechanisms. Consider the following simple "hello world" example package with a queryformat-expanded %post scriptlet:
```
%files
%defattr(-,root,root)
%doc FAQ README COPYING
%{_bindir}/hello
%post -q
for f in [%%{instfilenames} ]; do
echo $f
done
```
The -q argument to %post (or any other scriptlet) enables the runtime queryformat expansion for this particular script. The entire script now needs to be a legal --queryformat argument, which is what makes this form of runtime expansion harder to use: be prepared for lots of annoying escapes for common shell syntaxes such as {} and [].
Here's what a typical installation of the above example would output:
```
[root@localhost ~]# rpm -Uvh /tmp/hello-2.0-1.x86_64.rpm
Preparing... ################################# [100%]
Updating / installing...
1:hello-2.0-1 ################################# [100%]
/usr/bin/hello
/usr/share/doc/hello-2.0
/usr/share/doc/hello-2.0/COPYING
/usr/share/doc/hello-2.0/FAQ
/usr/share/doc/hello-2.0/README
[root@localhost ~]#
```
With --nodocs the output from %post looks quite different:
```
[root@localhost ~]# rpm -Uvh --nodocs /tmp/hello-2.0-1.x86_64.rpm
Preparing... ################################# [100%]
Updating / installing...
1:hello-2.0-1 ################################# [100%]
/usr/bin/hello
[root@localhost ~]#
```
## Other notes
It's possible to combine both forms of runtime scriptlet expansion, in which case macros are expanded first and the result is used as the queryformat string. However given all the conflicts between macro and queryformat syntaxes, on top of all all the shell-escaping involved, this is perhaps best avoided.
As mentioned earlier, only rpm >= 4.9.0 supports runtime scriptlet expansion, older versions can not correctly be used to install packages utilizing it. The following rpmlib() dependency is automatically added to guard against installation with rpm versions which do not support it:
```
rpmlib(ScriptletExpansion) <= 4.9.0-1
```