-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path_development.tex
135 lines (113 loc) · 4.93 KB
/
_development.tex
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
\chapter{Overview}
\label{chap:overview}
The result of this research was the development of |miq|, a package manager and
build system for Linux.
Miq is a single-file executable that handles the full lifecycle of the build
process of the packages it manages. These stages include:
\begin{enumerate}
\item Evaluating the expressions that describe packages
\item Calculating the dependency graph
\item Fetching the necessary source code
\item Performing the described build process
\item Handling the storage and tracking of the installed packages
\end{enumerate}
Therefore, the following sections will all the components that make up miq, and
their interactions.
\section{Unique identifiers}
The development of miq aimed for a modular design, such that
each component didn't have much coupling with the others.
This allows for easy refactoring of parts of the source
code, while leaving the rest of the system untouched. As
such the components of miq can be laid out in figure
\ref{fig:miq-components} .
\begin{figure}[hbtp]
\centerfloat
% \includegraphics{assets/overview.png}
\includesvg[width = 450pt]{assets/overview.svg}
\caption{Overview of the subsystems of miq}
\label{fig:miq-components}
\end{figure}
Miq is presented as a pipeline of stages, where each
subsystem transforms the input data to achieve a desired
state. This design is inspired by other package managers,
which present a similar structure of stages. The main
difference, is that miq works on static package laid out in a file, which describe the desired state of the
system. In contrast to other package managers, like |apt|,
where the final state of the system is a succession of
commands executed in the shell.
On of the main differences of miq to other package managers,
is how the files are laid out in the file system. This is
because each package is given a unique identifier, which in
turns is used for the directory where the package will be
located. This identifier is also unique between different
versions of the same package. And not only that, but it also
encodes the recipe used to build the package itself, and all
of its parents.
To accomplish the tagging of each package with a unique
identifier, a flow of data from package input to package is
visualized on figure \ref{fig:hash}. From a |PackageInput|,
a unique hash is generated, that is the result from hashing
all the fields of the struct. Finally, the hash (an unsigned
32-bit integer) is encoded into text, to form the name of
the package. For this application, the algorithm used is the
\textit{Fowler-Noll-Vo} hash function, which is implemented
in the |fnv| create \cite{FnvRust} . This hash function is
not cryptographically secure, but this was not one of the
design requirements of miq (and can be swapped out for any
other hashing algorithm as needed, as long as it conforms to
the |Hash| trait in Rust).
\begin{minted}{rust}
#[derive(Hash)]
struct PackageInput {
name: String,
version: Option<String>,
script: MetaTextInput,
deps: Option<Vec<Unit>>,
env: Option<BTreeMap<String, MetaTextInput>>,
}
\end{minted}
\begin{figure}[hbtp]
\centerfloat
\includesvg[width = 0.7\paperwidth ]{assets/hash.svg}
\caption{Overview of the hashing algorithm}
\label{fig:hash}
\end{figure}
The implications of this design is that any package gets a
different place on the file system, which is derived from
everything that defines the package itself - its build
script and its dependencies, which in turn are also hashed
by the same rules. The advantage of this design, is that
every package has its dependencies perfectly defined,
instead of relying on automatic detection. Let's say that
package |foo| depends on package |bar|. On a conventional
package manager, if |bar| is changed in any form (for
example, updated), then |foo| is usually not modified. But
in essence, now |foo|, if we consider it as a whole, that is
its whole dependency tree, it has changed. This poses a big
issue for the reproducibility of an operating system. Is
|foo| the same if we swapped |bar| for a different version?
Or if we swapped one of |bar|'s dependencies? (Figure
\ref{fig:depswap}) In miq, it is clear that the packages are
no longer the same, as the hashes of its entire dependency
tree has changed, and therefore the name of the output
package. This means that a package "foo" does not really
exist in miq, but rather a package "foo" with a specific hash.
\begin{figure}[hbtp]
\centerfloat
\includesvg[width = 200pt]{assets/depswap.svg}
\caption{Change of dependencies for a package for a conventional package manager}
\label{fig:depswap}
\end{figure}
% insert depswap-miq.svg
\begin{figure}[hbtp]
\centerfloat
\includesvg[width = 200pt]{assets/depswap_miq.svg}
\caption{Change of dependencies for a package for miq}
\label{fig:depswap_miq}
\end{figure}
\input{_dev_graph.tex}
\input{_dev_immut.tex}
\input{_dev_elf.tex}
\input{_dev_baselinux.tex}
% \section{GNU Autotools}
\input{_impl.tex}