Posts tagged tutorial
Nix is useful for quickly testing out software and providing a strict environment that can be shared between people.
Today I’m trying out Nix again, this time I want to do it my way.
Installation process
Nix store
I know Nix needs “Nix store” installation on /
(the system root).
Create it manually to prevent the installation script from calling sudo
. 1st I switch to the root account, and then I run:
|
mkdir -p -m 0755 /nix
chown -R xy:xy /nix
|
Running the install script
Download the Nix install script and examine the contents.
|
curl -L https://nixos.org/nix/install > nix_install.sh
|
Then, run it with --no-daemon
to prevent it running as system service.
|
sh ./nix_install.sh --no-daemon
|
performing a single-user installation of Nix...
copying Nix to /nix/store...
installing 'nix-2.20.1'
building '/nix/store/1ahlg3bviy174d6ig1gn393c23sqlki6-user-environment.drv'...
unpacking channels...
modifying /home/xy/.bash_profile...
modifying /home/xy/.zshenv...
placing /home/xy/.config/fish/conf.d/nix.fish...
Installation finished! To ensure that the necessary environment
variables are set, either log in again, or type
. /home/xy/.nix-profile/etc/profile.d/nix.fish
in your shell.
Wait!
modifying /home/xy/.bash_profile...
modifying /home/xy/.zshenv...
placing /home/xy/.config/fish/conf.d/nix.fish...
That’s very rude!
Stopping Nix from making a mess
I need to prevent Nix from mess up with my environment when I do not want it to. Nix puts some code into the Bash, ZSH and Fish initialization files during installation to ease it’s use. I do not want that since I do not want Nix to meddle with my environment without me knowing it.
I keep my .bash_profile
and .zshenv
in a stow-managed git repo so I can just cd
into my repo and do git reset --hard
, but for you will have to revert those files to their old forms manually.
Playing with Nix
We do not have nix
in PATH
but we still can launch it. Nix executables are located inside ~/.nix-profile/bin/
.
By invoking nix-shell
one can create a ephemeral environment containing only packages specified after the -p
flag. I always add -p nix
to have the Nix tools available also inside the spawned environment.
I will test out chibi
(small Scheme interpreter) + rlwrap
(REPL support for software lacking it) inside a Nix ephemeral environment:
|
~/.nix-profile/bin/nix-shell -p nix chibi rlwrap
|
Inside the spawned shell:
In the chibi REPL, let’s see the contents of the PATH
environment variable:
|
(get-environment-variable "PATH")
|
And exit the Scheme REPL:
After the playtime, run garbage collection:
|
~/.nix-profile/bin/nix-collect-garbage
|
Racket executables made by raco exe
are known to be quite large. One of tools that can be used to help reduce the size of produced binaries is the gzexe
program.
gzeze
is a tool that can compress a executable binary. It can be acquired by installing gzip
on most Linux distributions (included in the app-arch/gzip
package on Gentoo).
Creating a hello-world executable with Racket
Write following contents to hello-world.rkt
file:
To make a binary run:
|
raco exe --orig-exe -v -o hello-world hello-world.rkt
|
The file hello-world
will be produced.
This is what file hello-world
says about it:
|
hello-world: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV),
dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2,
for GNU/Linux 3.2.0, stripped
|
This “small” executable weights 46 MB!
In comparison busybox
weights around 2 MB.
Compressing with gzexe
Keep in mind that gzeze
will overwrite the compressed file and create a backup with appended "~".
And this gives us only 8,5 MB. Nice!
In comparison bazel
, which is a single-binary build system written in JAVA, executable takes 33 MB on my Gentoo machine. I tried compressing it with gzexe
and it reduces it only by 10%, to around 29 MB.
gzexe
is not a silver bullet but with Racket exes it works very nicely.
I really wanted to look into the new kernel building solutions for Gentoo and maybe migrate to dracut, but last time I tried, ~1.5 years ago, the initreamfs was now working for me.
And now in 2023 I’m still running genkernel for my personal boxes as well as other servers running Gentoo.
I guess some short term solutions really become defined tools :P
So this is how I rebuild my kernel nowadays:
-
Copy old config
|
cd /usr/src
cp linux-6.1.38-gentoo/.config linux-6.1.41-gentoo/
|
-
Remove old kernel build directories
|
rm -r linux-6.1.31-gentoo
|
-
Run initial preparation
|
( eselect kernel set 1 && cd /usr/src/linux && make olddefconfig )
|
-
Call genkernel
1
2
3
4
5
6
7
8
9
10
11
12 |
genkernel \
--no-menuconfig \
--no-clean \
--no-clear-cachedir \
--no-cleanup \
--no-mrproper \
--lvm \
--luks \
--mdadm \
--nfs \
--kernel-localversion="-$(hostname)-$(date '+%Y.%m.%d')" \
all
|
-
Rebuild the modules
If in your /etc/genkernel.conf
you have MODULEREBUILD
turned off, then also call emerge:
|
emerge -1 @module-rebuild
|
Constructing debugging syntax
I wanted to echo parameter values when I set them in my blog’s frog.rkt
config file.
Nothing simpler in Racket!
First I create this macro for echoing a single parameter value when it is set:
1
2
3
4
5
6
7
8
9
10
11
12 |
(define-syntax-rule (verbose-set-parameter parameter-id parameter-value)
(begin
;; Set the parameter.
(parameter-id parameter-value)
;; Then call the parameter and print is's value.
;; The "'parameter-id" is special syntax
;; for turning a "parameter-id" identifier to a symbol.
;; We can also write it like:
;; > (quote parameter-id)
;; to be less confusing.
(printf "[DEBUG] (~a ~v)\n" 'parameter-id (parameter-id))))
|
then, I create a wrapper for above macro that can take multiple parameter pairs:
|
(define-syntax-rule (verbose-set-parameters (parameter-id parameter-value) ...)
(begin
;; Unpack a chain of "(parameter-id parameter-value)" pairs
;; using the "..." syntax.
(verbose-set-parameter parameter-id parameter-value) ...))
|
Using the macro
Afterwards we can call it like so:
|
(verbose-set-parameters
(current-title "XGQT's blog")
(current-author "Maciej Barć"))
|
Notice that even the form of setting a parameter, that is (parameter-procedure "value")
, remains the same, but in reality it is just similar to how the syntax macro pattern-matches on it.
Inspecting macro expansion
In racket-mode
inside GNU Emacs we can inspect the macro expansion with racket-expand-region
. Stepping through the expansion provided this result:
|
(begin
(begin
(current-title "XGQT's blog")
(printf "[DEBUG] (~a ~v)\n" 'current-title (current-title)))
(begin
(current-author "Maciej Barć")
(printf "[DEBUG] (~a ~v)\n" 'current-author (current-author))))
|
Equality methods
By implementing a method for equality equal-to?
and two extraction methods equal-hash-code-of
and equal-secondary-hash-code-of
we can define our own object comparison rules.
For more info see Object Equality and Hashing.
Consider the following example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 |
|
If we create a new integer%
object we can notice that it is not transparent (we can not inspect values of any of it’s fields).
|
(new integer%)
;; => (object:integer% ...)
|
But if we compare two fresh integer%
objects they will be equal.
Transparent class
A transparent cvlass is a class with the inspect
expression valuye se to #false
.
From Racket documentation Creating Classes:
Just as for structure types, an inspector controls access to the class’s fields, including private fields, and also affects comparisons using equal?.
Consider the following example:
If we create a new integer%
object we can see it’s field values.
|
(new integer%)
;; => (object:integer% 0)
|
And if we compare two fresh integer%
objects they will be equal.
Config
First let’s prepare a suitable nginx configuration file.
This one is pretty bare but it works well for our case:
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 |
worker_processes 1;
daemon off;
pid ./nginx/temp/nginx.pid;
error_log /dev/stdout info;
events {
worker_connections 1024;
}
http {
client_body_temp_path ./nginx/temp/client 1 2;
proxy_temp_path ./nginx/temp/proxy;
fastcgi_temp_path ./nginx/temp/fastcgi;
uwsgi_temp_path ./nginx/temp/uwsgi;
scgi_temp_path ./nginx/temp/scgi;
server {
listen 127.0.0.1:8080;
server_name localhost;
access_log /dev/stdout;
error_log /dev/stdout info;
root ./;
location / {
autoindex on;
}
}
}
|
Server config is set up for serving all static files from the current directory.
Startup
Preparation
Based on how you want to store _temp_path
files it might be necessary to create (or clean up) additional directories, for example:
|
rm -r ./nginx/temp
mkdir -p ./nginx/temp
|
Run in current directory
|
nginx -c ./nginx.conf -p ./
|
BTW, you may want to replace ./
with "$(pwd)"
and occurrences in the config with static paths.
Bonus: other simple servers
Some of no-dependency-except-itself http servers it’s good to know about:
Python http.server
|
python3 -m http.server -b 127.0.0.1 8080
|
Busybox
|
busybox httpd -f -p 127.0.0.1:8080 -v
|
You can read more about configuring busybox’s httpd on OpenWRT docs.
System preparation
Qemu
Emerge qemu
with static-user
USE enabled and your wanted architectures.
|
app-emulation/qemu QEMU_SOFTMMU_TARGETS: aarch64 arm x86_64
app-emulation/qemu QEMU_USER_TARGETS: aarch64 arm x86_64
app-emulation/qemu static-user
dev-libs/glib static-libs
sys-apps/attr static-libs
sys-libs/zlib static-libs
dev-libs/libpcre2 static-libs
|
OpenRC
Enable qemu-binfmt
:
|
rc-update add qemu-binfmt default
|
Start qemu-binfmt
:
|
rc-service qemu-binfmt start
|
Chrooting
- select chroot location (eg
/chroots/gentoo-arm64-musl-stable
)
- unpack the desired rootfs
- create needed directories
mkdir -p /chroots/gentoo-arm64-musl-stable/var/cache/distfiles
- execute
bwrap
- with last
ro-bind
mount the qemu emulator binary (eg qemu-aarch64
)
- execute the mounted emulator binary giving it a shell program (eg
bash
)
Chroot with bwrap
:
|
bwrap \
--bind /chroots/gentoo-arm64-musl-stable / \
--dev /dev \
--proc /proc \
--perms 1777 --tmpfs /dev/shm \
--tmpfs /run \
--ro-bind /etc/resolv.conf /etc/resolv.conf \
--bind /var/cache/distfiles /var/cache/distfiles \
--ro-bind /usr/bin/qemu-aarch64 /usr/bin/qemu-aarch64 \
/usr/bin/qemu-aarch64 /bin/bash -l
|