summaryrefslogtreecommitdiffstats
path: root/dev/multiarch.mdwn
blob: d7c6a059a85bb1616347af6534b6063892ec4f1b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
[[!meta title="Multiarch Design"]]

Earlier design proposals are documented in [[another_page|dev/multiarch/design]]
kept for historical purposes.


Background
==========

ProteanOS is a self-hosting binary distribution that builds its own packages.
It is also an embedded distribution, intended to be suitable for small
resource-limited systems.  It may not be practical to natively build all of
ProteanOS on real hardware for every architecture to be supported.  Thus, it
will be preferable to cross-build all of ProteanOS's packages, making ProteanOS
a highly ambitious cross-built self-hosting binary distribution.

Cross-compiling and cross-assembling self-contained code is a solved problem,
however programs typically use separately built libraries, which must be
installed at build time to be available for compiling and linking.  Sometimes,
these dependency libraries will already be installed for use by the native
system.  So it needs to be possible to "coinstall" different architecture builds
of each library.  Package management and build tools also need to be able to
install dependency packages of the correct architecture; for example, libraries
must be of the *host* architecture (the architecture **for** which a package is
being built), but build utilities must be of the *build* architecture (the
architecture **on** which a package is being built).  Further complicating
dependency resolution, such build utilities often themselves depend on
libraries, which must also be of the build architecture (not the host
architecture like libraries that are direct build-dependencies).  These are the
two main problems in cross-building distribution packages: coinstallability and
dependencies.  The solution is a design known as "multiarch".

ProteanOS's multiarch design is inspired by, but different from, that
[specified][wuc-mas] and [documented][wdo-ma] in Debian and Ubuntu.

ProteanOS's design solves the coinstallability and dependency problems without
*any* modifications to its package manager, opkg-lede.  This is desirable
because opkg-lede is a tool used by many distributions separate from ProteanOS,
and its upstream maintainers (OpenWrt) would likely be unwilling to merge and
maintain invasive patches specific to a different distribution (making the
patches impossible to test upstream).

[wdo-ma]: http://wiki.debian.org/Multiarch
[wuc-mas]: https://wiki.ubuntu.com/MultiarchSpec


Design
======

Coinstallability
----------------

opkg-lede doesn't allow multiple packages to provide the same file path, so, in
order to be coinstallable, library packages must install all of their files into
architecture-qualified locations.  [Debian and Ubuntu][wuc-mas-arch-indep-files]
limit this requirement to only files whose contents differ between
architectures.  This is accomplished by the package manager dpkg requiring all
packages of the same name to be the same version, verifying that shared files
have identical contents, and reference counting shared files.  As explained
above, ProteanOS's multiarch design doesn't entail such modifications to the
package manager.

opkg-lede also doesn't allow installation of multiple packages with the same
name and different architectures.  The only way to solve this without modifying
the package manager is to qualify the names of coinstallable packages with
their host architectures.  This will be done by appending a colon followed by
the host architecture to each coinstallable package name, e.g. `libc.6` becomes
`libc.6:amd64-linux-glibc`.  Package names appear in the file system under
`/var/cache/opkg/archives/` and `/var/lib/opkg/info/`, so using a colon in
package names will prevent the use of restricted file systems such as FAT file
systems as the root file system.

Dependencies
------------

opkg-lede allows packages from different architectures to be installed, but
there is no way to select the architecture to install other than blanket
priority values.  Since libraries of different architectures will often need to
be installed during the same installation transaction, as described above, such
a blunt solution is insufficient.

[Debian and Ubuntu][wuc-mas-control-fields] solve this by declaring the kinds of
dependencies each package is able to satisfy (based on the interfaces it
provides: executables and/or libraries).  Again this requires modifications to
the package manager to affect dependency resolution.  ProteanOS's design is
similar, but implemented in a way such that *dependent* binary packages are
responsible for declaring the architectures of *their dependencies* (which can
be done automatically at build time, so package maintainers can focus on
declaring how their packages satisfy dependencies, as in Debian and Ubuntu).

One way to declare dependency semantics is by introducing a new single-purpose
control field like Debian's and Ubuntu's `Multi-Arch`.  However, ProteanOS will
instead use [a control field that serves multiple purposes:
`Section`][spf-fields-bin].  `Section: lib` packages will be automatically made
coinstallable by opkbuild, by architecture-qualifying their names as described
above.  Library dependencies generated by oh-shlibdeps will automatically use
the architecture-qualified names.

ProteanOS systems should have only native architecture feeds for sections
`boot`, `dev`, and `util` but may have multiple feeds for sections `dbg`, `lib`,
and `libdev`.  (Sections `doc`, `locale`, and `share` are
architecture-independent.)

opkg-lede's architecture priorities as described above can be useful in certain
specific ways, through a [wrapper script that adds an `-a`/`--host-architecture`
option][opkg-wrapper] (similar to that of apt-get).  First, since multiple
`Section: dbg` feeds may be downloaded and architecture-qualifying such packages
would be undesirable, such packages could be installed instead via `opkg -a
amd64-linux-glibc foo-dbg`.  Similarly, `Section: libdev` packages as build
dependencies could be automatically installed for the host architecture by
prokit supplying an appropriate `-a` option, allowing maintainers to continue
listing unqualified package names in `Build-Depends` instead of having to add
`-${Host-Arch}` to every `Section: libdev` package in `Build-Depends`.

[wuc-mas-arch-indep-files]: https://wiki.ubuntu.com/MultiarchSpec#Architecture-independent_files_in_multiarch_packages
[wuc-mas-control-fields]: https://wiki.ubuntu.com/MultiarchSpec#Binary_package_control_fields
[spf-fields-bin]: http://specs.proteanos.com/spf-2.0/fields.html#fields-bin
[opkg-wrapper]: http://git.proteanos.com/pkg/opkg-lede.git/tree/opkg


Use Cases
=========

**Run-time dependency on a utility:**  Only the native architecture of the
utility dependency is available in the downloaded feed lists, and the dependency
is not architecture-qualified.  The native architecture will be installed.

**Run-time dependency on a library:**  Multiple architectures of the library
dependency may be available, and the dependency must be qualified with the
native architecture.

**Run-time dependency on a language extension:**  Despite technically being a
shared object file, only the native architecture of the language extension
dependency is available in the downloaded feed lists, and the dependency is not
architecture-qualified.  The language extension dependency in turn depends on a
language interpreter, which is also not architecture-qualified.  The native
architecture of both will be installed.

**Build-time dependency on a utility:**  Only the build architecture of the
utility dependency is available in the downloaded feed lists, and the dependency
is not architecture-qualified.  The build architecture will be installed.

**Build-time dependency on a library:**  Multiple architectures of the library
dependency are available, and the dependency must be qualified with the host
architecture.

**Build-time dependency on a language extension:**  Despite technically being a
shared object file, only the native architecture of the language extension
dependency is available in the downloaded feed lists, and the dependency is not
architecture-qualified.  The language extension dependency in turn depends on a
language interpreter, which is also not architecture-qualified.  The native
architecture of both will be installed.