Skip to content

Commit e6cca33

Browse files
committed
Rewrite the image builder in Golang
1 parent 23ddc20 commit e6cca33

File tree

13 files changed

+353
-181
lines changed

13 files changed

+353
-181
lines changed

Dockerfile

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,33 +24,27 @@ RUN \
2424
--mount=type=cache,target=/root/.cache/go-build \
2525
CGO_ENABLED=0 go build -o /go/bin/init -v --ldflags '-s -w -extldflags=-static'
2626

27+
# =========================================================
28+
FROM golang:1.24.5-alpine AS imager-build
29+
WORKDIR /go/src
30+
# COPY imager/go.mod imager/go.sum ./
31+
# RUN go mod download
32+
COPY imager .
33+
#COPY --from=init /go/bin/init /usr/share/claylinux/init
34+
RUN \
35+
--mount=type=cache,target=/root/.cache/go-build \
36+
go generate -v ./... && \
37+
CGO_ENABLED=0 go build -o /go/bin/imager -v --ldflags '-s -w -extldflags=-static'
38+
2739
# =========================================================
2840
FROM alpine:3.22.1 AS alpine-base
2941

3042
# =========================================================
3143
FROM alpine-base AS imager
32-
SHELL ["/bin/ash", "-euxo", "pipefail", "-c"]
33-
RUN \
34-
echo "@edge-community https://dl-cdn.alpinelinux.org/alpine/edge/community" >>/etc/apk/repositories && \
35-
apk add --no-cache \
36-
bash \
37-
binutils \
38-
coreutils \
39-
cpio \
40-
dosfstools \
41-
findutils \
42-
mtools \
43-
pigz \
44-
qemu-img \
45-
sfdisk \
46-
xorriso \
47-
zstd \
48-
xz \
49-
systemd-efistub@edge-community
50-
COPY --from=init /go/bin/init /usr/share/claylinux/init
51-
COPY build-image.sh /usr/bin/build-image
44+
RUN apk add --no-cache binutils systemd-efistub
45+
COPY --from=imager-build /go/bin/imager /bin/imager
5246
WORKDIR /out
53-
ENTRYPOINT ["build-image"]
47+
ENTRYPOINT ["/bin/imager"]
5448

5549
# =========================================================
5650
FROM alpine-base AS bootable-alpine-rootfs
@@ -110,7 +104,7 @@ RUN if [ "$UCODE" != "none" ]; then apk add --no-cache "${UCODE}-ucode"; fi
110104
# hadolint ignore=DL3006
111105
FROM imager AS test
112106
ARG FORMAT=efi
113-
RUN --mount=from=test-rootfs,target=/system build-image --format "$FORMAT"
107+
RUN --mount=from=test-rootfs,target=/system /bin/imager --format "$FORMAT"
114108

115109
# =========================================================
116110
# Generate a qemu image running our custom OS image

build-image.sh renamed to imager/build.sh

Lines changed: 1 addition & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -1,135 +1,6 @@
11
#!/bin/bash
22
set -euo pipefail
33

4-
# exit with an error message
5-
die() {
6-
echo "Error: $*" >&2
7-
exit 1
8-
}
9-
10-
# get the size of the file in bytes
11-
get_size() {
12-
stat -c %s "$1"
13-
}
14-
15-
# convert a number of bytes into MiB (i.e. 1024 * 1024 bytes), rounded to the next value
16-
in_mib() {
17-
echo $(( ($1 + (1<<20) - 1) >> 20 ))
18-
}
19-
20-
# get the size of the file in mibytes
21-
get_size_mib() {
22-
in_mib "$(get_size "$1")"
23-
}
24-
25-
# round the value given in $1 to the next multiple of the value given in $2
26-
# e.g. align 9 4 -> 12, align 8 4 -> 8
27-
align() {
28-
echo "$(( ($1 + $2 - 1) / $2 * $2 ))"
29-
}
30-
31-
# build the EFI executable
32-
build_efi() {
33-
local size kernel
34-
35-
pushd "$build_dir" >/dev/null
36-
37-
echo "Building the EFI executable"
38-
build_initramfs
39-
size=$(get_size_mib initramfs)
40-
echo "The size of the initramfs is: $size MiB"
41-
kernel=$(find /system/boot -name 'vmlinu*' -print)
42-
space_separated </system/boot/cmdline >cmdline
43-
basename /system/lib/modules/* >kernel-release
44-
45-
# build the EFI UKI file
46-
# TODO: add .dtb section on ARM?
47-
build_uki <<-EOF
48-
.osrel /system/etc/os-release
49-
.uname kernel-release
50-
.cmdline cmdline
51-
.initrd initramfs
52-
.linux $kernel
53-
EOF
54-
55-
# delete all the temporary files
56-
find . ! -name '*.efi' -delete
57-
58-
popd >/dev/null
59-
}
60-
61-
# build the initramfs
62-
build_initramfs() {
63-
mkdir initramfs_files
64-
65-
# copy the init script
66-
cp /usr/share/claylinux/init initramfs_files
67-
68-
# copy /etc/hosts.target as /etc/hosts
69-
if [[ -f /system/etc/hosts.target ]]; then
70-
mkdir -p initramfs_files/etc
71-
cp /system/etc/hosts.target initramfs_files/etc/hosts
72-
fi
73-
74-
# copy etc/resolv.conf.target as etc/resolv.conf
75-
if [[ -f /system/etc/resolv.conf.target ]]; then
76-
mkdir -p initramfs_files/etc
77-
cp /system/etc/resolv.conf.target initramfs_files/etc/resolv.conf
78-
fi
79-
80-
# create an initramfs with these files
81-
find initramfs_files -mindepth 1 -printf '%P\0' \
82-
| cpio --quiet -o0H newc -D initramfs_files -F initramfs.img
83-
84-
# append the system files into the initramfs image, except /boot, /etc/hosts.target and /etc/resolv.conf.target
85-
find /system \
86-
-path /system/boot -prune -o \
87-
! -path /system/init \
88-
! -path /system/etc/hosts.target \
89-
! -path /system/etc/resolv.conf.target \
90-
-mindepth 1 -printf '%P\0' \
91-
| cpio --quiet -o0AH newc -D /system -F initramfs.img
92-
93-
# compress the initramfs
94-
compress initramfs.img
95-
96-
# build the final initramfs by concatenating the ucode images & our compressed initramfs image
97-
# see https://docs.kernel.org/arch/x86/microcode.html
98-
echo "$(find /system/boot/ -name '*-ucode.img') initramfs.img" | xargs cat >initramfs
99-
100-
# remove the temporary files
101-
find . ! -name initramfs -delete
102-
}
103-
104-
# create a Unified Kernel Image from the sections passed in the standard input
105-
build_uki() {
106-
# the sections addresses should be aligned to PAGE_ALIGN(), i.e. 2<<12 == 4096 bytes
107-
local args=() alignment=4096 size offset
108-
109-
# compute the start offset of the new sections
110-
offset="$(objdump -h -w "$efi_stub" | awk 'END { offset=("0x"$4)+0; size=("0x"$3)+0; print offset + size }')"
111-
offset=$(align "$offset" $alignment)
112-
113-
# compute the objcopy arguments
114-
while read -r section file
115-
do
116-
# add the section to the parameters
117-
args+=(
118-
--add-section
119-
"$section=$file"
120-
--change-section-vma
121-
"$section=$offset"
122-
)
123-
124-
# compute the offset for the next section
125-
size="$(get_size "$file")"
126-
size=$(align "$size" $alignment)
127-
offset=$(( offset + size ))
128-
done
129-
130-
objcopy "${args[@]}" "$efi_stub" "$efi_file"
131-
}
132-
1334
# detect the current EFI architecture
1345
get_efi_arch() {
1356
local machine_arch
@@ -153,34 +24,6 @@ get_efi_arch() {
15324
esac
15425
}
15526

156-
# convert a multi-line input into a space separated list
157-
space_separated() {
158-
paste -d' ' -s
159-
}
160-
161-
# compress the initramfs with the specified scheme
162-
compress() {
163-
case "$compression" in
164-
none)
165-
;;
166-
gz)
167-
pigz -9 "$1"
168-
mv "$1".gz "$1"
169-
;;
170-
xz)
171-
xz -C crc32 -9 -T0 "$1"
172-
mv "$1".xz "$1"
173-
;;
174-
zstd)
175-
zstd -19 -T0 --rm "$1"
176-
mv "$1".zstd "$1"
177-
;;
178-
*)
179-
die "invalid compression scheme '$compression'"
180-
;;
181-
esac
182-
}
183-
18427
# just copy the build files to the output directory
18528
generate_efi() {
18629
echo "Copying the OS files to the output directory"
@@ -312,7 +155,6 @@ format=raw
312155
volume=CLAYLINUX
313156
compression=gz
314157
efi_arch=$(get_efi_arch)
315-
efi_stub="/usr/lib/systemd/boot/efi/linux${efi_arch}.efi.stub"
316158

317159
usage=$(cat <<-EOF
318160
Usage: $(basename "$0") [OPTIONS ...]
@@ -373,7 +215,7 @@ done
373215
build_dir=$(mktemp -d)
374216
efi_file="$build_dir"/claylinux.efi
375217
esp_file="$build_dir"/claylinux.esp
376-
build_efi
218+
imager "$build_dir"
377219
mkdir -p "$(dirname "$output")"
378220
generate_"$format"
379221
rmdir "$build_dir"

imager/efi/arch_386.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
//go:build 386
2+
3+
package efi
4+
5+
const Arch = "IA32"

imager/efi/arch_amd64.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
//go:build amd64
2+
3+
package efi
4+
5+
const Arch = "X64"

imager/efi/arch_arm.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
//go:build arm
2+
3+
package efi
4+
5+
const Arch = "ARM"

imager/efi/arch_arm64.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
//go:build arm64
2+
3+
package efi
4+
5+
const Arch = "AA64"

imager/efi/constants.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package efi
2+
3+
const Extension = ".efi"

imager/efi/initramfs.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package efi
2+
3+
import (
4+
"bytes"
5+
"log"
6+
7+
"github.com/cavaliergopher/cpio"
8+
//"github.com/u-root/u-root/pkg/cpio"
9+
)
10+
11+
func BuildInitramfs(rootfsDir string, buildDir string) {
12+
// Create a buffer to write our archive to.
13+
buf := new(bytes.Buffer)
14+
15+
// Create a new cpio archive.
16+
w := cpio.NewWriter(buf)
17+
18+
// Add some files to the archive.
19+
var files = []struct {
20+
Name, Body string
21+
}{
22+
{"readme.txt", "This archive contains some text files."},
23+
{"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"},
24+
{"todo.txt", "Get animal handling license."},
25+
}
26+
for _, file := range files {
27+
hdr := &cpio.Header{
28+
Name: file.Name,
29+
Mode: 0600,
30+
Size: int64(len(file.Body)),
31+
}
32+
if err := w.WriteHeader(hdr); err != nil {
33+
log.Fatalln(err)
34+
}
35+
if _, err := w.Write([]byte(file.Body)); err != nil {
36+
log.Fatalln(err)
37+
}
38+
}
39+
40+
// Make sure to check the error on Close.
41+
if err := w.Close(); err != nil {
42+
log.Fatalln(err)
43+
}
44+
45+
/*
46+
os.Mkdir("initramfs_files", 0755)
47+
run("cp", "/usr/share/claylinux/init", "initramfs_files")
48+
49+
if fileExists("/system/etc/hosts.target") {
50+
os.MkdirAll("initramfs_files/etc", 0755)
51+
run("cp", "/system/etc/hosts.target", "initramfs_files/etc/hosts")
52+
}
53+
if fileExists("/system/etc/resolv.conf.target") {
54+
os.MkdirAll("initramfs_files/etc", 0755)
55+
run("cp", "/system/etc/resolv.conf.target", "initramfs_files/etc/resolv.conf")
56+
}
57+
// cpio for initramfs_files
58+
run("sh", "-c", `find initramfs_files -mindepth 1 -printf '%P\0' | cpio --quiet -o0H newc -D initramfs_files -F initramfs.img`)
59+
// Add system files except boot, hosts.target, resolv.conf.target
60+
run("sh", "-c", `find /system -path /system/boot -prune -o ! -path /system/init ! -path /system/etc/hosts.target ! -path /system/etc/resolv.conf.target -mindepth 1 -printf '%P\0' | cpio --quiet -o0AH newc -D /system -F initramfs.img`)
61+
compress("initramfs.img")
62+
63+
ucode := runOutput("find", "/system/boot/", "-name", "*-ucode.img")
64+
imgs := "initramfs.img"
65+
if ucode != "" {
66+
imgs = ucode + " " + imgs
67+
}
68+
run("sh", "-c", fmt.Sprintf("cat %s >initramfs", imgs))
69+
run("find", ".", "!", "-name", "initramfs", "-delete")
70+
*/
71+
}
72+
73+
/*
74+
func compress(filename string) {
75+
"none", "gz", "xz", "zstd"
76+
"invalid compression scheme: " + compression
77+
}
78+
*/

0 commit comments

Comments
 (0)