Finish Installing Every Arch Package

This commit is contained in:
Anthony Wang 2022-01-31 19:59:28 -06:00
parent 38ebdbe445
commit c9fa01362f
Signed by untrusted user: a
GPG key ID: BC96B00AEC5F2D76
5 changed files with 207 additions and 6 deletions

View file

@ -34,26 +34,28 @@ We could resolve all the conflicts manually with an hour of work... or we could
![Automation](https://imgs.xkcd.com/comics/automation.png)
## Time for some algorithms!
It's time to put our algorithms knowledge to good use. This is *just* a graph We can think of each package as a node in a graph and each conflict is an edge. Since we don't care about dependency checks (which would make for a likely broken system), we don't need to add any other edges to the graph.
It's time to put our algorithms knowledge to good use. This is *just* a graph We can think of each package as a node in a graph and each conflict is an edge. Since we don't care about dependency checks (which would make for a likely broken system but who cares), we don't need to add any other edges to the graph.
For each edge, we need to pick at most one package, but not both. That sounds a lot like a [maximum independent set](https://en.wikipedia.org/wiki/Maximum_independent_set)!
Wait... it's NP hard though? And we have up to 12000 nodes, so we'll never be able to find the answer before the heat death of the universe, right?
Well, do we have 12000 *connected* nodes? No, since the largest connected component is probably only a few nodes. We aren't going to have hundreds or thousands of packages all conflicting with each other. Therefore, we don't need to solve [P vs. NP](https://en.wikipedia.org/wiki/P_versus_NP_problem) to be able to find the maximum independent set of this particular graph. Phew!
Well, do we have 12000 *connected* nodes? No, since the largest connected component is probably only a few nodes. We aren't going to have hundreds or thousands of packages all conflicting with each other. Therefore, we don't need to prove [P = NP](https://en.wikipedia.org/wiki/P_versus_NP_problem) to be able to find the maximum independent set of this particular graph. Phew!
## Implementing this in Julia
We're going to use [Julia](https://julialang.org/) for implementing this algorithm, since Julia is Python but better. We first need to get a list of all packages:
We're going to use [Julia](https://julialang.org/) for implementing this algorithm, because Julia is Python but better. We first need to get a list of all packages:
```jl
pkgname = split(read(`pacman -Sql`, String))
N = length(pkgname)
```
Now, we'll get info about each package, using multithreading to speed things up:
Now, we'll get info about each package, using multithreading to speed things up: (this is the slowest part of the program, actually)
```jl
struct Package
provides::Vector{String}
@ -104,7 +106,7 @@ for i = 1:N
end
```
Now we can find each connected component using [BFS](https://en.wikipedia.org/wiki/Breadth-first_search), and brute-force the maximum independent set by trying every subset of the nodes in that component. It's implemented here using some bit manipulation trickery.
Now we can find each connected component using [BFS](https://en.wikipedia.org/wiki/Breadth-first_search), and brute-force the maximum independent set by trying every subset of the nodes in that component. It's implemented here using some bit manipulation trickery. Since the components are all very small, the brute-force finishes really quickly!
```jl
ans = BitSet(1:N)
@ -166,6 +168,9 @@ open("out", "w") do f
end
```
## Installing everything
Alright, time to install everything! It'll take about 30 minutes for everything to download, depending on your internet connection. Make sure you have the `multilib` repository enabled. We can also speed this up by disabling package signature checking.
```sh
sudo pacman -Sdd $(cat out)
@ -218,4 +223,48 @@ Now let's try again:
sudo pacman -Sdd $(cat out) --overwrite '*' --needed
```
Success! This command takes forever to run too, and there's *52* `pacman` hooks at the end.
Success! Now, time for yet another 30 minute wait, and even better, there's *52* `pacman` hooks at the end. Yeah, we'll just have to wait as every DKMS module is compiled for each of the four kernels we have installed.
## Exploring the system
Alright, let's reboot and check out our new and improved Arch Linux system!
The first obstacle that we encounter is that most display managers, with the exception of [LXDM](https://wiki.archlinux.org/title/LXDM). So, let's `sudo systemctl start lxdm` and choose one of the 44 desktop sessions to explore.
![A very bloated LXDM](/images/bloated-lxdm.png)
Surprisingly, KDE Plasma starts up pretty snappily, but 10 different background services also popped up in the system tray. Memory usage is pretty high, but what can you expect? We also have over 30 different terminals to choose from for our `neofetch` screenshot.
![A very bloated KDE](/images/bloated-kde.png)
Now let's have some real fun with this system:
![All apps](/images/all-apps.png)
Endless fun!
# So is this system usable?
Surprisingly, yes! It's hard to judge how bad the performance really is, since it's in a virtual machine, but all the software that I tested was definitely usable. It's somewhat slow, but that's exactly what you'd expect. As we used a lot of unsafe hacks (disabling dependency and file conflict checking, for instance) to get this to actually work, I wouldn't recommend using this system for anything other than proving it's possible.
Now is this useful? The short answer is no. The long answer is also no. I can think of exactly zero uses of this experiment (and I must be pretty crazy for doing it).
# I want to try this out!
Uh, don't do it, but if you insist, you need a fresh new Arch Linux system, 250 GB of free disk space, Julia, a boatload of patience, and a healthy dose of insanity. You can download the Julia code as a [Pluto notebook](/src/solveconflicts.jl) and run it with `julia --threads=auto solveconflicts.jl`. I'd recommend doing the actual installation (`sudo pacman -Sdd $(cat out) --overwrite '*' --needed`) in a VM since this completely destroys an Arch Linux system, and there's no going back once you finish.
And before you start, please ask yourself three times, why am I doing this???
## Further potential explorations
- Install every package of a different distro? In particular, I'd like to see someone do this for Ubuntu, Debian, and NixOS and watch them suffer. This was painful enough.
- Investigate the file conflicts and file some Arch Linux [bugs](https://bugs.archlinux.org/task/73574).
- Figure out how to make `pacman` skip all package conflict checks, possibly by editing the `pacman` source code and disabling the relevant code that does these checks, so we can *really* install every single Arch package.
- Build a time machine so I can get back the 10 hours that I wasted by doing this project. (I also destroyed 6 testing VMs) Oh well. Now we know what really happens when you install all Arch Linux packages!

BIN
static/images/all-apps.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

View file

@ -0,0 +1,152 @@
### A Pluto.jl notebook ###
# v0.17.7
using Markdown
using InteractiveUtils
# ╔═╡ 462b98b8-8182-11ec-085a-850a7141205b
pkgname = split(read(`pacman -Sql`, String))
# ╔═╡ 86789bd1-393d-480d-a5bc-43f545906cb8
N = length(pkgname)
# ╔═╡ e100eae8-502a-4570-ab54-1bd1a26db7d5
struct Package
provides::Vector{String}
conflicts::Vector{String}
size::Float64
end
# ╔═╡ e014fa4c-f82b-470e-b3bd-fde76b4673a6
pkginfo = Vector{Package}(undef, N)
# ╔═╡ 016da011-4300-40ac-a99b-682380d64ef9
Threads.@threads for i = 1:N
pkg = pkgname[i]
info = map(x -> split(replace(split(x, "\n")[1], "None" => "")), split(read(`pacman -Si $pkg`, String), " : "))
push!(info[10], pkg)
pkginfo[i] = Package(info[10], info[13], parse(Float64, info[16][1]))
end
# ╔═╡ 53be9679-e41e-4f35-83e6-9760f5196bf0
providedby = Dict{String, Vector{Int}}()
# ╔═╡ b7f48d11-f7ba-4490-9493-66dd2425f359
for i = 1:N
for p in pkginfo[i].provides
p = split(p, "=")[1]
if !(p in keys(providedby))
providedby[p] = Vector{Int}()
end
push!(providedby[p], i)
end
end
# ╔═╡ 714946d5-1990-4c28-a9ca-07bd233b8441
G = [Set{Int}() for i = 1:N]
# ╔═╡ 9cb5db2f-728f-4d33-9eb9-7c9330a83bdf
for i = 1:N
for p in pkginfo[i].conflicts
if p in keys(providedby)
for j in providedby[p]
if j != i
push!(G[i], j)
push!(G[j], i)
end
end
end
end
end
# ╔═╡ ff60f482-5e78-406c-a1dd-cae826266fac
ans = BitSet(1:N)
# ╔═╡ 599c20b1-2922-4f5f-b67b-b4a72b558b21
used = BitSet()
# ╔═╡ 1ed271ee-5995-42e2-8033-85a9a34bb151
for i = 1:N
if !(i in used)
push!(used, i)
component = Vector{Int}()
queue = Vector{Int}([i])
while !isempty(queue)
u = popfirst!(queue)
push!(component, u)
for v in G[u]
if !(v in used)
push!(used, v)
push!(queue, v)
end
end
end
M = length(component)
best = (0, 0.0, 0)
for m = 1:(1<<M)-1
good = true
for j = 1:M
if (m>>(j-1))&1 == 1
for k = j+1:M
if (m>>(k-1))&1 == 1 && component[j] in G[component[k]]
good = false
end
end
end
end
if !good
continue
end
cnt = length([j for j = 1:M if (m>>(j-1))&1 == 1])
size = sum([pkginfo[component[j]].size for j = 1:M if (m>>(j-1))&1 == 1])
best = max((cnt, size, m), best)
end
for j = 1:M
if (best[3]>>(j-1))&1 != 1
delete!(ans, component[j])
end
end
end
end
# ╔═╡ 2492b63a-9d4b-4e2e-964e-71f52fb2765d
open("out", "w") do f
for i in ans
println(f, pkgname[i])
end
end
# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
"""
# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised
julia_version = "1.7.1"
manifest_format = "2.0"
[deps]
"""
# ╔═╡ Cell order:
# ╠═462b98b8-8182-11ec-085a-850a7141205b
# ╠═86789bd1-393d-480d-a5bc-43f545906cb8
# ╠═e100eae8-502a-4570-ab54-1bd1a26db7d5
# ╠═e014fa4c-f82b-470e-b3bd-fde76b4673a6
# ╠═016da011-4300-40ac-a99b-682380d64ef9
# ╠═53be9679-e41e-4f35-83e6-9760f5196bf0
# ╠═b7f48d11-f7ba-4490-9493-66dd2425f359
# ╠═714946d5-1990-4c28-a9ca-07bd233b8441
# ╠═9cb5db2f-728f-4d33-9eb9-7c9330a83bdf
# ╠═ff60f482-5e78-406c-a1dd-cae826266fac
# ╠═599c20b1-2922-4f5f-b67b-b4a72b558b21
# ╠═1ed271ee-5995-42e2-8033-85a9a34bb151
# ╠═2492b63a-9d4b-4e2e-964e-71f52fb2765d
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002