Successfully bootstrapped with the GNU userspace

This commit is contained in:
Alexander Hill 2026-01-11 15:48:51 -05:00
parent c496159c26
commit 788a62091e
10 changed files with 122 additions and 154 deletions

123
README.md
View File

@ -1,127 +1,6 @@
# Maple Linux Bootstrap Scripts
This repository contains the scripts used to build Maple Linux from the source code. Most users will want to download a tarball from [here](https://maple.camp/linux/) to install Maple Linux. For everyone else, contributions are welcome!
## System Requirements
- Intel Core i3-6100 or greater
- At least 1 GB of storage space
## Installing Maple Linux (For Most Users)
Once you have a tarball downloaded from the repository above, you can begin installing Maple Linux.
### Building the System Image
First, you will need to start with a storage device that is around 4 GB in size (although, you can probably get away with less if you're brave). The first 512 MB should be an EFI System Partition (FAT32), and the remainder of the disk should be formatted as XFS. The following is an example of formatting the disk with `parted`, where `/dev/sdX` is the disk of your choice:
```
# parted /dev/sdX
GNU Parted 3.6
Using /dev/sdX
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) mklabel gpt
(parted) mkpart fat32 0% 512M
(parted) mkpart xfs 512M 100%
(parted) set 1 esp on
(parted) quit
```
That doesn't actually format the partitions, so we'll need to do that next:
```
# mkfs.fat -F32 /dev/sdX1
mkfs.fat 4.2 (2021-01-31)
# mkfs.xfs /dev/sdX2
meta-data=/dev/sdX2 isize=512 agcount=4, agsize=1017280 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=1, sparse=1, rmapbt=1
= reflink=1 bigtime=1 inobtcount=1 nrext64=1
= exchange=0 metadir=0
data = bsize=4096 blocks=4069120, imaxpct=25
= sunit=0 swidth=0 blks
naming =version 2 bsize=4096 ascii-ci=0, ftype=1, parent=0
log =internal log bsize=4096 blocks=19237, version=2
= sectsz=512 sunit=0 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
= rgcount=0 rgsize=0 extents
= zoned=0 start=0 reserved=0
Discarding blocks...Done.
```
Now that our image has been formatted correctly, we can extract the contents of Maple Linux to the disk. We'll start by creating a mount point for the root partition before mounting it and the boot partition.
```
# mkdir maple
# mount /dev/sdX2 maple
# mkdir maple/boot
# mount /dev/sdX1 maple/boot
```
Now that the image has been formatted properly, we can extract the contents of the tarball onto the system image:
```
# cd maple
# tar xf ../maplelinux-202507201645.tar.xz
```
### Configuring Limine
To configure Limine, simply open `maple/boot/limine` in your text editor of choice. The only thing you *may* need to change is the value of `kernel_cmdline`. By default, it points to `/dev/vda2`, which is the default for a VirtIO disk on KVM. Of course, you can do [much more to customize your system](https://github.com/limine-bootloader/limine/blob/v9.x/CONFIG.md), but it is not required to boot.
### Writing /etc/fstab
Once again, you can change the fstab by opening `maple/etc/fstab` in your favorite text editor. The only two lines I would recommend changing are the first two unless you know what you're doing. The first line references the root partition and the second line references the boot partition. They are set to `/dev/vda2` and `/dev/vda1` respectively, as those are what KVM defaults to with a single VirtIO disk configured. This is what fstab should look like by default:
```
/dev/vda2 / xfs defaults 1 1
/dev/vda1 /boot vfat defaults 0 2
proc /proc proc nosuid,noexec,nodev 0 0
sysfs /sys sysfs nosuid,noexec,nodev 0 0
devpts /dev/pts devpts defaults 0 0
tmpfs /run tmpfs defaults 0 0
devtmpfs /dev devtmpfs mode=0755,nosuid 0 0
tmpfs /dev/shm tmpfs nosuid,nodev 0 0
cgroup2 /sys/fs/cgroup cgroup2 nosuid,noexec,nodev 0 0
```
### Building initramfs
Last, but certainly not least, is the initramfs. We'll start by creating the `maple/etc/tinyramfs` folder, which we'll use to store `maple/etc/tinyramfs/config`. Once again, `root` should be set to the root partition on your system.
```
root=/dev/vda2
root_type=xfs
monolith=true
```
Once the initramfs builder has been configured, we can `chroot` into the image one final time, check the installed kernel version by listing the contents of `/lib/modules` (there should only be one subfolder), and invoking `tinyramfs` to build the initramfs for boot.
```
# chroot maple zsh
hostname# ls /lib/modules
6.15.4-maple
hostname# tinyramfs -k 6.15.4-maple /boot/initramfs
>> creating ramfs structure
>> generating initramfs image
+> done: /boot/initramfs
hostname# exit
```
With that, we can umount the image and boot into Maple Linux!
## Building Maple Linux
Building Maple Linux is mostly an automated process at this point. Here is a summary of the scripts provided in this repository, in order of execution (assuming nothing goes wrong):
- `./fetch-sources.sh` - Downloads the sources and verifies their hashes against what is in `sources.list`
- `./build-bootstrap.sh` - Extracts and builds the bare minimum to build the remainder of the image in a `chroot`
- `./chroot-bootstrap.sh` - `chroot`s into the image with all the appropriate mounts
- `./build-chroot.sh` - Extracts and builds the system image from inside of the `chroot`
This process has been known to take *hours*, so it's best not to do this unless you're sure you want to do it this way. Once you have a working bootstrap, it is recommended to back that up before proceeding with the chroot build so you can easily restore your progress without waiting for hours to get a new bootstrap. Of course, if you need to change LLVM for any reason, you will need to bootstrap the system again...
Once you have a working image, you can follow the steps above to install the image normally. If you have any suggestions to improve the system or its build process, please make a pull request or issue!
This repository contains the scripts used to build Maple Linux from the source code.
## Maple Linux Philosophy

View File

@ -13,17 +13,17 @@ Definitions:
| `bzip2` | Yes | Yes |
| `chrony` |
| `cmake` | Yes | Yes |
| `coreutils` |
| `dash` |
| `coreutils` | Yes |
| `dash` | Yes |
| `dhcpcd` |
| `diffutils` |
| `findutils` |
| `diffutils` | Yes |
| `findutils` | Yes |
| `flex` | Yes | Yes |
| `fortune-mod` |
| `gettext` |
| `grep` |
| `grep` | Yes |
| `groff` | Yes | Yes |
| `gzip` |
| `gzip` | Yes |
| `initramfs-tools` | Yes | Yes |
| `iproute2` |
| `kbd` |
@ -41,7 +41,7 @@ Definitions:
| `llvm` | No | No |
| `m4` | Yes | Yes |
| `make` | Yes | Yes |
| `mawk` |
| `mawk` | Yes |
| `muon` | Yes | Yes |
| `musl` | Yes | Yes |
| `nano` |
@ -49,12 +49,12 @@ Definitions:
| `ncurses` |
| `nftables` |
| `openrc` |
| `patch` |
| `patch` | Yes |
| `perl` | Yes | Yes |
| `pkgconf` | Yes | Yes |
| `sed` |
| `sed` | Yes |
| `shadow` |
| `tar` |
| `tar` | Yes |
| `texinfo` |
| `xlibre-xserver` |
| `xz` | Yes | Yes |

View File

@ -1,14 +1,14 @@
#!/bin/zsh -e
MICROARCH=skylake
TARGET=x86_64-maple-linux-musl
export MICROARCH=skylake
export TARGET=x86_64-maple-linux-musl
# Set the environment up
ARCH=$(echo $TARGET | cut -d"-" -f1)
BOOTSTRAP=$(pwd)/.bootstrap
PROCS=$(nproc)
SOURCES=$(pwd)/.treetap/sources
SPEC=$(pwd)/sources
export ARCH=$(echo $TARGET | cut -d"-" -f1)
export BOOTSTRAP=$(pwd)/.bootstrap
export PROCS=$(nproc)
export SOURCES=$(pwd)/.treetap/sources
export SPEC=$(pwd)/sources
export AR=llvm-ar
export AS=llvm-as
if [ ! -z "$CCACHE" ]; then
@ -35,8 +35,8 @@ $TREETAP fetch sources/llvm/llvm.spec
$TREETAP fetch sources/musl/musl.spec
# Make sure both clang-tblgen and llvm-tblgen are in the PATH. ~ahill
[ -z "$(which clang-tblgen)" ] && (echo "Unable to find clang-tblgen"; exit 1)
[ -z "$(which llvm-tblgen)" ] && (echo "Unable to find llvm-tblgen"; exit 1)
! which clang-tblgen && exit 1
! which llvm-tblgen && exit 1
# Simplified filesystem heirarchy with symlinks for compatibility
mkdir -p $BOOTSTRAP/root/{bin,boot/EFI/BOOT,dev,etc,home,lib,proc,run,sys,tmp,usr/{include,share},var/{cache,lib,log,spool,tmp}}
@ -219,12 +219,12 @@ cmake --build build-llvm --parallel $PROCS
cmake --install build-llvm --parallel $PROCS
# NOTE: LLVM doesn't add symlinks for clang, so we'll make them ourselves.
# ~ahill
ln -s clang $BOOTSTRAP/root/bin/cc
ln -s clang++ $BOOTSTRAP/root/bin/c++
ln -sf clang $BOOTSTRAP/root/bin/cc
ln -sf clang++ $BOOTSTRAP/root/bin/c++
cd ..
# Build remaining software with treetap
SOURCES=(coreutils dash grep findutils gzip make mawk sed tar xz)
SOURCES=(coreutils dash diffutils findutils grep gzip make mawk patch sed tar)
for name in $SOURCES; do
$TREETAP fetch $SPEC/$name/$name.spec
$TREETAP build $SPEC/$name/$name.spec
@ -247,6 +247,7 @@ SOURCES=(
cmake
coreutils
dash
diffutils
findutils
flex
grep
@ -264,6 +265,7 @@ SOURCES=(
muon
musl
nasm
patch
perl
pkgconf
sed

36
rescue.sh Executable file
View File

@ -0,0 +1,36 @@
#!/bin/sh
# This file is made with lines from bootstrap.sh to create a shell in case the
# bootstrap fails to build for whatever reason. Building a sysroot, especially
# with LLVM, takes a stupid amount of time and it makes it unreasonable to
# rebuild after something like diffutils fails to build. ~ahill
# The following script was created with:
# sh -c "grep export bootstrap.sh | sed /CCACHE/d; echo zsh" >> rescue.sh
export MICROARCH=skylake
export TARGET=x86_64-maple-linux-musl
export ARCH=$(echo $TARGET | cut -d"-" -f1)
export BOOTSTRAP=$(pwd)/.bootstrap
export PROCS=$(nproc)
export SOURCES=$(pwd)/.treetap/sources
export SPEC=$(pwd)/sources
export AR=llvm-ar
export AS=llvm-as
export CC=clang
export CXX=clang++
export CFLAGS="-fuse-ld=lld -O3 -march=$MICROARCH -pipe --sysroot=$BOOTSTRAP/root -Wno-unused-command-line-argument"
export CXXFLAGS=$CFLAGS
export RANLIB=llvm-ranlib
export LD=ld.lld
export LDFLAGS="--sysroot=$BOOTSTRAP/root"
export TREETAP=$(pwd)/treetap
export TT_DIR=$(pwd)/.treetap
export TT_MICROARCH=$MICROARCH
export TT_SYSROOT=$BOOTSTRAP/root
export TT_TARGET=$TARGET
export CFLAGS="$CFLAGS -Qunused-arguments -rtlib=compiler-rt -Wl,--dynamic-linker=/lib/ld-musl-$ARCH.so.1"
export CXXFLAGS="$CXXFLAGS -Qunused-arguments -rtlib=compiler-rt -Wl,--dynamic-linker=/lib/ld-musl-$ARCH.so.1"
export CFLAGS="$CFLAGS -unwindlib=libunwind"
export CXXFLAGS="$CXXFLAGS -isystem $BOOTSTRAP/root/usr/include/c++/v1 -nostdinc++ -stdlib=libc++ -unwindlib=libunwind"
export TT_DIR=$BOOTSTRAP/root/maple/.treetap
zsh

View File

@ -33,8 +33,13 @@ echo "Done!"
# NOTE: automake requires m4 to build. ~ahill
# NOTE: groff requires Perl to build. ~ahill
# NOTE: nasm requires autoconf and automake to build. ~ahill
# NOTE: dash requires flex and mawk to build. ~ahill
# NOTE: libelf requires zlib to build. ~ahill
cd /maple
PACKAGES="byacc bzip2 coreutils dash findutils grep gzip libelf libressl m4 make mawk muon musl perl pkgconf sed tar xz zlib autoconf automake flex groff libarchive libtool nasm cmake"
LAYER0="byacc bzip2 coreutils diffutils findutils grep gzip libressl m4 make mawk muon musl patch perl pkgconf sed tar xz zlib"
LAYER1="autoconf automake flex groff libarchive libelf libtool"
LAYER2="dash nasm cmake"
PACKAGES="$LAYER0 $LAYER1 $LAYER2"
for pkg in $PACKAGES; do
treetap fetch /maple/sources/$pkg/$pkg.spec
treetap build /maple/sources/$pkg/$pkg.spec

View File

@ -0,0 +1,23 @@
# Maintainer: Alexander Hill <ahill@breadpudding.dev>
SRC_HASH="7c8b7f9fc8609141fdea9cece85249d308624391ff61dedaf528fcb337727dfd"
SRC_NAME="diffutils"
SRC_URL="https://ftp.gnu.org/gnu/diffutils/diffutils-3.12.tar.xz"
SRC_VERSION="3.12"
build() {
tar xf ../$SRC_FILENAME
cd diffutils-$SRC_VERSION/
# NOTE: GNU Diffutils 3.12 has a bug when cross-compiling, stating that it
# can't run a test because it is cross-compiling. Rather than ignore
# the issue, the configure script simply dies, preventing the build
# from proceeding. Adding gl_cv_func_strcasecmp_works fixes the issue
# without the need for a patch. ~ahill
# See also: https://lists.gnu.org/archive/html/bug-gnulib/2025-04/msg00056.html
./configure $TT_AUTOCONF_COMMON gl_cv_func_strcasecmp_works=y
make -j $TT_PROCS
}
package() {
cd diffutils-$SRC_VERSION/
make -j $TT_PROCS install DESTDIR=$TT_INSTALLDIR
}

View File

@ -1,8 +1,8 @@
# Maintainer: Alexander Hill <ahill@breadpudding.dev>
SRC_HASH="7a8879167b89c4bae077d6f39c4f2130769f05dbdad2aad914adab9afb7d7f9a"
SRC_HASH="f850139ca5f79c1bf6bb8b32f92e212aadca97bdaef8a83a7cf4ac4d6a525fab"
SRC_NAME="linux"
SRC_URL="https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.18.3.tar.xz"
SRC_VERSION="6.18.3"
SRC_URL="https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.18.4.tar.xz"
SRC_VERSION="6.18.4"
build() {
tar xf ../$SRC_FILENAME

View File

@ -1,6 +1,7 @@
# Maintainer: Alexander Hill <ahill@breadpudding.dev>
SRC_HASH="51bcb82d577b141d896d9d9c3077d7aaa209490132e9f2b9573ba8511b3835be"
SRC_NAME="mawk"
SRC_REVISION=1
SRC_URL="https://invisible-island.net/archives/mawk/mawk-1.3.4-20250131.tgz"
SRC_VERSION="1.3.4-20250131"
@ -14,4 +15,5 @@ build() {
package() {
cd mawk-$SRC_VERSION/
make -O -j $TT_PROCS install DESTDIR=$TT_INSTALLDIR
ln -s mawk $TT_INSTALLDIR/bin/awk
}

17
sources/patch/patch.spec Normal file
View File

@ -0,0 +1,17 @@
# Maintainer: Alexander Hill <ahill@breadpudding.dev>
SRC_HASH="f87cee69eec2b4fcbf60a396b030ad6aa3415f192aa5f7ee84cad5e11f7f5ae3"
SRC_NAME="patch"
SRC_URL="https://ftp.gnu.org/gnu/patch/patch-2.8.tar.xz"
SRC_VERSION="2.8"
build() {
tar xf ../$SRC_FILENAME
cd patch-$SRC_VERSION/
./configure $TT_AUTOCONF_COMMON
make -j $TT_PROCS
}
package() {
cd patch-$SRC_VERSION/
make -j $TT_PROCS install DESTDIR=$TT_INSTALLDIR
}

View File

@ -1,14 +1,22 @@
# Maintainer: Alexander Hill <ahill@breadpudding.dev>
SRC_HASH="38ef96b8dfe510d42707d9c781877914792541133e1870841463bfa73f883e32"
SRC_NAME="zlib"
SRC_REVISION=1
SRC_URL="https://www.zlib.net/zlib-1.3.1.tar.xz"
SRC_VERSION="1.3.1"
build() {
tar xf ../$SRC_FILENAME
cd zlib-*/
cd zlib-$SRC_VERSION/
# NOTE: The prefix is set to /usr because man pages are stored under the
# prefix whether you like it or not. ~ahill
# NOTE: Zlib refuses to build a shared library if it can't pass the test.
# Rather than stopping the build, it simply proceeds to build a static
# library, which causes issues when building libarchive. The test is
# failing due to it relying on undefined symbols that are referenced
# in the linker script, which LLVM doesn't like at all. To tell LLVM
# to ignore it, we pass --undefined-version to the linker. ~ahill
CFLAGS="-Wl,--undefined-version $CFLAGS" \
./configure \
--eprefix=$TT_PREFIX \
--includedir=$TT_INCLUDEDIR \
@ -18,11 +26,7 @@ build() {
make -O -j $TT_PROCS
}
clean() {
rm -rf zlib-*/
}
package() {
cd zlib-*/
cd zlib-$SRC_VERSION/
make -O -j $TT_PROCS install DESTDIR=$TT_INSTALLDIR
}