Skip to content

Commit

Permalink
Bring most of "user documentation" from rpm.org to our reference manual
Browse files Browse the repository at this point in the history
The rest need more massaging and/or delicate merging to avoid losing
content added in one place but not the other.
  • Loading branch information
pmatilai committed Oct 7, 2020
1 parent f72d3c4 commit 019e80e
Show file tree
Hide file tree
Showing 9 changed files with 680 additions and 7 deletions.
7 changes: 7 additions & 0 deletions doc/Makefile.am
Expand Up @@ -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 \
Expand Down
112 changes: 112 additions & 0 deletions doc/manual/arch_dependencies.md
@@ -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 changes: 64 additions & 0 deletions doc/manual/autosetup.md
@@ -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.
78 changes: 78 additions & 0 deletions doc/manual/boolean_dependencies.md
@@ -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.
51 changes: 51 additions & 0 deletions doc/manual/file_triggers.md
@@ -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.

23 changes: 16 additions & 7 deletions doc/manual/index.md
Expand Up @@ -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
Expand Down

0 comments on commit 019e80e

Please sign in to comment.