Linux from Scratch
linuxfromscratch.org
linuxfromscratch.org
My understanding of Linux, of bootstrapping, cross-compilation, and Nix has grown tremendously as a result of the time I took on this project - and I still go back and reference the work from time to time. When I get some time to revisit the Nix-based LFS project, there are quite a few things I would like to clean-up, including setting kernel configs and handling post-build permissions.
Nix-complexities aside, I highly recommend LFS if you like to understand how things work and don't mind a little suffering along the way.
The advantage of this approach is that it gives more control to the distro maintainers and the admins of the computer, taking that control away from the "upstream" maintainers of the software being packaged. For example the software being packaged cannot just call the library bar because bar is not at /usr/lib/bar.so like it is in most Linux distros -- it is at /nix/store/17813e8b97b84e0317813e8b97b84e03-bar/usr/lib/bar.so, but of course the software does not know that unless the person creating the Nix package (the packager) arranges for the software to know it (again sometimes by doing a search-and-replace on binaries).
If the upstream maintainer of foo thinks foo should link to version 6 of library bar, but you think it should link to version 5, NixOS makes it easier for you to arrange for foo to link to version 5 than most distros do (even if version 6 of bar is needed by other packages you have installed which you need to use at the same times as your using foo).
Note that if this separation of packages imposed by NixOS has any beneficial security properties, it is merely security through obscurity because there is nothing preventing a binary from covertly searching through the directory listing of /nix/store/ for the name of the library it wants to call. Nevertheless it turns out the be useful to seize some control away from upstream in this way even if technically upstream could seize the control back if it were willing to complicate the software to do so.
People, including the creator of Nix and NixOS (Dolstra), will tell you that NixOS's main advantage is "declarativeness" (which in the past Dolstra called "purity") or the fact that the compilation / building process is deterministic. I believe both positions (NixOS's advantage is declarativeness and the advantage is deterministic builds) are wrong. Specifically, I believe that although deterministic builds are useful, the separation of packages I described is much more useful to most users and prospective users of NixOS.
Another way to summarize it is that NixOS package maintainers routinely modify the software they are packaging to use less "ambient authority".
I still have no idea how it all works but it seemed prudent for me to at least try.
It's better to instead pip-install Python packages into virtual environments, recent Pythons havr `venv` built in for this purpose. For user-scoped or system-scoped utilities, `pipx` can manage dedicated virtual environments and symlink them into the search path.
I think this is insane. Not only will many packages be missing from Nix, you will also have to wait for the upstream changes to actually propagate to Nix repositories. This all assumes, of course, that there are no packaging issues or incompatibilities in this repackaging.
This is one of the ways that Nix sometimes just gets in your way. I've been using Nix(OS) for several years now, and this still bothers me.
Instead of doing this, For Python specifically I would suggest installing pyenv, which Nix packages. Then enter a nix-shell with a derivation thingie[1,2], and install Python as usual with pyenv. Then you can use any Python version, and with pyenv-virtualenv (which Nix _doesn't_ package...), you can use venvs as you're used to. Sure, you don't get the benefits of the declarative approach and isolation as with "the Nix way", and you may run into other issues, but at least it's a workflow well known to Python devs. Hope it helps!
[1]: https://gist.github.com/imiric/3422258c4df9dacb4128ff94d31e9...
[2]: It took me way longer than I would like to admit to figure this out... This shouldn't be so difficult!
nix-shell -p 'python3.withPackages(ps: [ ps.numpy ])'
In case of Python, you can also go for a simpler option that avoids composing Python with its packages, but that gives worse isolation: nix-shell -p python3 python3Packages.numpy
If other packages were present in that ephemeral environment, aside from python3, they could see NumPy in a global directory. That's why the first option is preferable, as it offers better isolation. In Nix, some languages only let you use libraries with something like the first option. See the Nix user wiki for further information: https://nixos.wiki/wiki/Python.12GB being considered significant makes me feel good, a return to simplicity. The other day I couldn't provision a VM with less than 512GB of storage...
I can't even play my favorite games without giving up >150GBs...
The problem with Nix/NixOS is everything is too isolated to be configurable or usable, and so almost everything is broken because it doesn't work like any other OS. Chasing down all of the oddities and gotcha problems becomes a full-time IT job that slows everything else down, and then with a fragile special snowflake of patches and workarounds, it becomes very expensive and slow to do anything or to count on it being reliable. Furthermore, the syntax and nature of the packaging DSL is also too clever special snowflake that could've made do with declarative data or imperative procedural with something widespread like Python, Bourne shell, or JS to reduce the friction of customization and contribution.
Like Qubes, an over-optimization for particular goal(s) ends up becoming Achille's footguns because of a lack of compatibility and usability with everything else that came before.
In my experience, using & configuring NixOS is much simpler and easier than most distros...until it's not, at which point, yes, the difficulty curve is very steep.
Even in those cases, though, it's got the nice feature that once it's solved, it's solved. All the config is in one place, and you can apply the same config across all your hosts. You don't get those situations where you get something working after tweaking configs, env vars and systems jobs, and can't remember how to reproduce it.
And though some of the conventions around NixOS are pretty arcane and convoluted (though the language itself is pretty simple), it has the advantage that you only need to learn it once and you can use it all over, instead of switching between bash, python, yaml, toml, systemd units and even more obscure formats (nginx, apache, postfix, etc).
IMHO, the tradeoff is completely worth it.
https://github.com/cloudripper/NixLFS/blob/64e333f60cca4bec5...
For why I did not chmod the directories - that is something I should probably do when I get time again (cleaning up permissions handling more broadly). In my case at the time, I had the logs from the wrappers and did a quick filter before directly adding them to the script as a last hurdle to having a bootable machine after a month-long grind.
The kernel config handling needs a good amount of attention too. I have a cleaner approach to that in mind that I haven't had the opportunity to work on.
It will likely be December before I am able to invest much more time into it, but definitely open to any input generally.
https://github.com/cloudripper/NixLFS/blob/64e333f60cca4bec5...
Could you speak a bit about why you did it this way instead of maintaining a complete copy of the kernel .config you wanted?
First, for my initial attempt at the project I wanted to make as close of an adaptation from LFS guidance to Nix that I could. While I did sprinkle in some custom kernel configurations (from BLFS guidance), I otherwise tried to make a direct adaption from the book. The LFS guidance for the kernel build uses `make menuconfig`. That requires user interaction which, as far as I am aware, is not feasible in a Nix derivation (and really wouldn't make sense to achieve reproducibility as far as I understand the term). So I used `make defconfig` within the derivation and attempted to sed the kernel .config to achieve a matching config to that of LFS guidance (coupled with a few sprinkles of custom configs). In retrospect, I don't like how that block currently flows and would like to make it a bit cleaner and easier to declare configuration preferences.
Second, my approach with the entire project was to limit external inputs to the derivations with the goal of trying to be as reproducible as I could. If I recall correctly, the only external inputs used in the whole project were the sources and patches provided by the LFS project. That said, I hadn't considered that it might be a lot simpler to set it up in a "Bring Your Own Config" kind of way. I will stew on that more and would definitely be open to more thoughts on either and/or another approach.
I am actually flabbergasted there's another human being on Earth that has this exact same story as me. I used one of those giant 3 ring legal binders for my printed copy, lmao.
> Between LFS and Stage 1 and 2 Gentoo installs back in the early 2000s
> I ended up printing out the LFS book in segments on one of the printers
LFS and Gentoo is what made me understand Linux system.
It is a fun experience though!
Aren't those two opposites?
In which cases did/do you need to add individual files?
I agree with the comment you are replying to. Having broken my home Linux installs too many times has taught me how to diagnose and fix this sort of issues.
Of course that was not the best and cleanest solution, having things outside the package management system bothered, but it was enough to experiment programs and I kept track of the changes so I could have the prior state of things. Later on, the Slackbuilds project eased the work and I contributed by writing code to automate the creation of a few packages. I learned a lot from these issues.
I had the same experience when I installed Gentoo. In both cases at first I was copy/pasting commands, but through failure I ended up understanding a lot of what the commands did.
And what someone else said below is true as well, actually try and force yourself to understand what you're typing. Even briefly just scanning a man page helps a lot.
Another aspect is probably that watching YT clips always has a feeling of being part of something. Some movement, some bubble, some society, whatever. They don't install Arch bcs they want to learn sth, or make some use of the OS. They do it _because_ they found it on YT and they want to be part of it. Maybe they even write comments or make a 'reaction' video. Today it's Arch, tomorrow it's a special pizza recipe from that other guy on Insta. It doesn't really matter.
I worked with college students fairly recently. They did often reach reflexively for video. But when the written material was good enough, they used it.
College students are the next generation. Most millenials remember dialup or at most a time before mainstream streaming video. College students today have seen their formative years being constantly on with instant access to more material in any format they want than they could ever grasp the concept of.
They handle some things a little differently and sometimes reach for different defaults, but in the end, it isn’t like they are coming from some totally alien planet or anything like that.
And I'm always asking myself what is wrong with me that all that completely does not reflect my practical experiences at all.
One possible skew could be: often it is professors who complain about this stuff, but the type of person who goes on to be a professor tends to be pretty clever and surround themselves with clever friends. So if you are a professor or a highly skilled programmer or something, keep in mind that you probably have a rosy picture of the average competence of the past.
I can clearly see that there are unfortunate patterns, even back then when I was young, and it just got worse and worse. Which is no surprise imho. Of course it propagates. If the parents are already 'social media' addicts, and their friends, and the teachers as well, what shall happen with their children. The issues just 'normalize' - that's what we see, but it doesn't _solve_ them.
The thing is, our societies love to chatter about all kinds of issues and troubles, as long as they are somewhere else or at least the bad guys are far enough away. Something not binding. But as soon as an inconvenient discussion about themselves start, about what they do wrong and what issues we get by them, it turns into silence. So we also do not discuss much about more and more incompetent social media addicts. Since the majority is addicted, and the ground for public discussions is social media only nowadays, who should even start this discussion - and where...
> But when the written material was good enough, they used it.
To some extent, yes, as we all do, they will try to make a good show for you when they feel that there is an audience for that show and it might somehow pay off.
I've read accounts of newspapers and common books rotting people's minds (including the "they aren't talking to each other!" concern,) and ancient Greeks complaining about the next generation.
I can't negate any specific point this way, but I do try to think about history repeating itself whenever I see someone notably younger annoying me.
(Also, the complaints that younger generations have about the older generations are just as ancient.)
What I know is, when I talk to e.g. colleagues, the younger there are, the more they feel like materialized YT clips. And that's not a good thing. Of course I try to compensate for their younger age when I do the comparison. And of course I could be wrong, e.g. biased in some way. But I would sat that I really try to be fair, and I must be veeeery off if you say there are no such issues.
I don't think this is fair to say. Could be said for the generation after me. Maybe not. I think this kind of sweeping generalisation is not fair on any generation though. There are motivated and lazy people in all generations, as there are people with good/bad attention spans.
And what you've said about YouTube might be true, but it wasn't for me. I did not go to YouTube for the community aspect, but only because like I said before I didn't even know that the Arch wiki was an install guide as well.
Sorry to say that, and even more sorry that you'll probably not even understand what I mean, but this actually doesn't need any further comments.
> you have not even considered that there might be some kinds of helpful resources directly from Arch outside of YT.
You're speaking about this as if it happened yesterday. This experience happened over a decade ago. I am not a teenager, and I am now accustomed to reading and actually learning on the internet. Don't fault me or my entire attention span for some minor, naïve behaviour/mistake I made as a teenager.
But I had _just_ ventured into this space. And at the time the wiki format wasn't something I'd ever seen outside of Wikipedia so i never put 2-2 together to figure out the wiki could be used as an install guide. Plus the walls of text were kind of intimidating?
So I jumped to what i knew; YouTube tutorials.
I also wanted to implement an isolated build system and my own package manager (basically tar wrapper with file database to be able to uninstall, no fancy dependency stuff), but containers were not invented back then and my skills were not good enough to properly utilize fakeroot, so it never was implemented, although I spent some time experimenting. Today I would do it with docker, exciting time.
I found it quite educational and worth the little time I spent on it.
I tried several times, and tried again just recently. I share your sentiment. It perhaps gave me a renewed appreciation of the huge benefits Linux ditributions and package managers give us.
https://www.linuxfromscratch.org/lfs/view/stable-systemd/cha...
Every step is explained and every used parameter is documented.
The sed command is not explained at all. Even the description is non-sense if you don't know already what they are talking about. What is this "default directory", why do you need to set it, why there and only there, why that command works? Even the command itself is something, which I need to check the manual what the heck it does, because it's not a simple one.
> -enable-default-pie and --enable-default-ssp
The description is almost unusable. So we don't need it, but it's "cleaner", which in this context means exactly nothing. So what happens if I left out? Nothing? Then why should I care?
> --disable-multilib
Okay, it doesn't support "something". I have no idea what is multilib, or why I should care. Basically the next arguments' description tells me that because it wouldn't work the compilation otherwise. And then..
> --disable-threads, --disable-libatomic, --disable-libgomp, --disable-libquadmath, --disable-libssp, --disable-libvtv, --disable-libstdcxx
But why would they fail? I want to understand what's happening here, and I need to blindly trust the manual because they just tell me, that "they won't work, believe us".
> --enable-languages=c,c++
Why are these the only languages which we need? What are the other languages?
So at the end, descriptions are not really helping to understand what's happening, if you don't know already. The last time when I started LFS (about 10 years ago), that was my main problem. That you already need to know almost everything to understand what's really happening, and why, or reading manuals, or trying to find basically unsearchable information (like why libatomic compiling would fail at this step). So after a while, I started the blind copy-pasting, because I didn't have the patience of literary months, and when I realized that this was pointless, I gave up.
> The sed command is not explained at all. Even the description is non-sense if you don't know already what they are talking about. What is this "default directory", why do you need to set it, why there and only there, why that command works? Even the command itself is something, which I need to check the manual what the heck it does, because it's not a simple one.
By "default directory" they just mean that the upstream GCC source code has a file with default variables and they are modifying those variables with the sed command to use $prefix/lib and $prefix/usr/lib instead of $prefix/lib64 and $prefix/usr/lib64, e.g. lines that contain "m64=" and replacing "lib64" to be "lib". This is what sed is used for: To make string substitutions based on pattern matching. Think through ways of writing the sed command and testing on your own file to see how it behaves. This will lead you to more tools like diff, grep and awk. > > -enable-default-pie and --enable-default-ssp
> The description is almost unusable. So we don't need it, but it's "cleaner", which in this context means exactly nothing. So what happens if I left out? Nothing? Then why should I care?
Go back and re-read the sections up to this point. Make note that you're in Chapter 5, which is the bootstrapping phase for the toolchain cross-compilers. Then look into the features that are mentioned. You can see descriptions by running the ./configure --help most times or looking up the GCC documentation. Those features are for security purposes and if you put that in perspective of the bootstrap phase they aren't needed if the only purpose of the temporary GCC binaries is to compile the final GCC in a later phase. To your point, perform an experiment and enable those features to see if there really is a difference other than time spent to compile. GCC incrementally adds security features like this and they are a big thing in hardened distributions. > > --disable-multilib
> Okay, it doesn't support "something". I have no idea what is multilib, or why I should care. Basically the next arguments' description tells me that because it wouldn't work the compilation otherwise. And then..
A great feature to look up! Check out https://gcc.gnu.org/install/configure.html and search for the option and you'll find that it has to do with supporting a variety of target calling conventions. I can see how that'd be pretty confusing. It has to do with the underlying hardware support for application binary interfaces that GCC can utilize and it turns out you probably only need to support your native hardware (e.g. x86_64). That is, you're compiling your system from scratch and it'll only run on your native hardware (x86_64) but if you were a C/C++ programmer maybe you'd want to have support for other hardware (64-bit ARM systems are pretty common today as an example). So you can save time/space by disabling the defaults and honestly the defaults it includes are just not all that relevant on most systems today. > > --disable-threads, --disable-libatomic, --disable-libgomp, --disable-libquadmath, --disable-libssp, --disable-libvtv, --disable-libstdcxx
> But why would they fail? I want to understand what's happening here, and I need to blindly trust the manual because they just tell me, that "they won't work, believe us".
Try it and find out. I would expect that they would fail due to reliance on other dependencies that may not have been installed or included in this bootstrapped build. Or maybe be/c those components don't behaved well with the LFS-based bootstrap methodology and ultimately aren't needed to bootstrap. Sure trust the LFSers but also think through a way to test your own assertions of the build process and try it out! > > --enable-languages=c,c++
> Why are these the only languages which we need? What are the other languages?
GCC supports many language front-ends. See https://gcc.gnu.org/frontends.html. Only C/C++ is needed be/c you're bootstrapping only C and C++ based package sources. You can validate this as you build the remaining sections. It's conceivable that if you needed other languages in the bootstrap you could include them. > So at the end, descriptions are not really helping to understand what's happening, if you don't know already. The last time when I started LFS (about 10 years ago), that was my main problem. That you already need to know almost everything to understand what's really happening, and why, or reading manuals, or trying to find basically unsearchable information (like why libatomic compiling would fail at this step). So after a while, I started the blind copy-pasting, because I didn't have the patience of literary months, and when I realized that this was pointless, I gave up.
It's a steep learning curve, especially of a bootstrap which by its nature is circular! tbh, that's sort of the utility of LFS, it can take you up to a certain point but there are so many options and pitfalls in building a Linux system (or really any complex piece of software) and the real usefulness is pushing through, picking something and learning about it. Then using what you learned to apply to the unknown. GCC is one of the most important packages too, so there's a lot to unpack in understanding anything about it, but the impact is large.That’s almost the exact opposite of the truth, and your example demonstrates his point. Almost none of the commands—let alone their parameters—are explained, beyond a high level summary of what the goal of the step is.
Is this phenomenon the explanation for why almost all documentation sucks? Experienced developers delude themselves into imagining that their docs have a level of detail and explanation that they simply just don’t have?
With LFS you must put substantial amount of value into the journey itself. It's not about copy-pasting commands, it's about trying to actually understand what and, more importantly, why your are doing each step. My recollection is that LFS docs were good in helping with that. Or maybe it was coming from reading docs of the actual components as I went along? I don't remember, probably some mix of both.
I did set LFS system up once, I think it was 2001 or 2002. I specifically remember that it took many days (due to compilation times), was very interesting, and at the end I was putting final touches to my mutt and slrn configs. With a vague memory that I had some TODO for mutt that I didn't finish, still lingering in my mind, apparently! :)
All in all, great and satisfying learning experience.
I would not use such system as a daily driver, though. I think it's good only if you have time and want to learn. I'm curious if someone here is brave enough to have different approach.
I kept it updated by subscribing to Freshmeat emails for releases, and I'd download and install the tarball.
My review is that I'm glad I did it, it's a terrible idea, and it's debatable (honestly, not facetiously) as to whether the classes I skipped were more valuable.
It also helps to have a fast x86_64 box with zillions of cores and plenty of RAM and SSD, and also a compiler cache like sccache to speed up rebuilds.
Linux From Scratch is a fun way to explore what parts make up a Linux distribution. I did this a few time long ago before switching to Gentoo and it really helped with appreciating the freedom to pick and choose details of your operating system without actually writing it from scratch.
Buildroot uses Kconfig (and associated tools like menuconfig, config, etc.) same as the kernel, to generate a configuration for building an embedded Linux system. This configuration can be committed and patched in the repo for your project. Adding an application or library automatically pulls in dependencies. Configs can be merged with fragments that add specific features.
Buildroot is capable of building its own cross compilation tool chain for many architectures, and enables developers to choose their own libc, and other toolchain configurations. It can also be configured to pull a prebuilt toolchain tarball to save time.
Packages are written in GNU Make, and macros are available for pulling code from GitHub and building packages using autoconf, cmake, meson, golang, Rust, python, and many more.
It works fantastically for anything from building an embedded Linux distro for something like digital signage, to a self-contained network bootable application for large scale automated provisioning of devices, to building minimal tarballs of filesystems to run as containers.
I’m sometimes surprised a fork with better approach to package management doesn’t exist yet.
could you say a few words on how this would avoid building and installing the required software individually? are you thinking of osdev instead?
But on subsequent play-throughs, you get to be creative! Want to ignore the FHS and try putting your apps somewhere weird? Want to use a different libc or init system? Build everything around an esoteric shell? Go for maximum optimisation? It's the replayability that makes Linux from Scratch fun, and yes, totally worth working out what your own distro might look like.
Or, as a base to build a distribution: you can automatize some of the process, slap a pkgsrc on top, etc.
For budding programmers, it's a great exercise: discover software, appreciate dependencies, understand components of a Linux distribution, learn how to build things from source, configure a kernel, etc.
If you want to just build an existing distro from scratch then they all have ways of doing that as they need to rebuild packages regularly. Eg: It’s a lot simpler to build a fedora image from scratch by using Koji/OSBuild vs LFS as Koji/OSBuild basically automate the whole LFS process.
Additionally in order to have fun with LFS you don’t really need to do the whole book. You can make your own little Linux “distro” from scratch with the kernel and statically linked busybox. Busybox comes with all the minimal utilities so you can pick and choose what you want to write and what you want to reuse.
when I was playing with this stuff the hardest part for me was getting my head around:
1. why do I need a cross-compiler if my system has a compiler already??? 2. why do I need to compile the compiler twice??
And well it turns out you don’t really need these steps but they’re required as part of the bootstrap process for GCC/glibc itself.
That combined with my general interest in Linux probably saved me thousands in cloud costs. Linux is a dying art among young software engineers, but alive among young nerds. Even among infra/devops people, maybe if they learned more about linux they wouldn't reach for k8s to solve the simplest problems.
I use it a lot for mapping out of the initial concepts but I find one of the best use cases is after understanding the basics, explaining where I need to learn more and asking for a book recommendation. The quality of my reading list has gone up 10x this way and I find myself working through multiple books a week.
Great for code too obviously, though still feels like early days there to me.
Though back then, I learnt well from Gentoo by setting the system up from stage 1, which was a good knowledge to keep when trying to fix things such as handling partitions in single user mode or modprobe'ing to detect hardware and such.
If you can't do anything beyond relying on GUI, you could get stuck real fast.
Unfortunately, the state of Linux documentation is so poor, you can't do it. You need to reference a number of third-party articles that kernel.org itself sometimes links you to.
I believe kernel.org might also mention Linux from Scratch, but LFS does a very poor job of explaining why you need particular dependencies straight out of initramfs.
You need a functional shell, and you need to get your installation on to an actual drive. Neither of those things are explained in sufficient detail with the current documentation available today.
LFS at best says "throw in these ingredients," and leaves you with no other information. You can probably read man pages and piece this stuff together, but it requires that you, at the very least, hardcode where to install files with fsck/mount/cp, I think.
Edit: LFS also does a really poor job of explaining why it chooses its "Basic System Software," much of which isn't actually required for a common GNU/Linux system.
It came as a shock to me to learn that initramfs is mostly optional. It can be skipped, and you can boot straight into userland.
When trying to learn by copy-pasting from the internet, I sometimes get tripped up over older/newer Linuxes using different versions of commands or best practices. (e.g. ipconfig vs. ifconfig, iptables vs. successors)
Once you get to the BLFS part, you have more choice on whether you're going to innovate or use something that old and in maintenance mode. For example, it has instructions for both Pulseaudio and Pipewire, Xorg and Wayland, SysVinit and systemd. The instructions will rarely be outright incorrect on modern distros unless something is very strange about your environment, since a bulk of the work is done after establishing a more predictable build environment.
The things I learned, especially about cross-compiling, were invaluable, but you do have to invest some time understanding everything (even reading patches that you have to apply on top of build tools, for example) to get the most out of it. If you just copy/paste commands, well then there are faster/easier ways to get Linux up and running
Edit: I just noted that CLFS has been dead for a couple of years now, which is sad. I would have loved to try an updated CLFS
The same is true for all other pieces of software.
Build time will always increase until no one can be bothered to build it any more.
> I wonder, if you were to script all the commands you ran back in the day, and ran that same script on your old 386 and on a modern system with a top-of-the-line AMD Epyc or Intel Xeon, how much faster would it run?
Implies you're compiling the 386 era versions of Linux - so the fact modern Linux is larger is immaterial.
I did not repeat the process :)
[1]: https://clfs.org/
Or is it "You fool! Building your own kitbashed Gundam of an OS is the point."
I often wonder why the existence of long "you also need to do this completely unintuitive thing first" documentation on the open internet isn't shaming more projects into reducing barriers to build their software.
I never intended to end up with a usable system, I was just in it for the learning experience. And I did end up learning a lot.
Void is another minimalist distro offering a choice of glibc or musl, and using runit as an init.
If you want more choices of init and a bleeding edge rolling release, Artix is a derivative of Arch with multiple init options in the core distribution.
Arch and Gentoo are the classics of do-it-yourself minimalism.
I tried to automate the process as much as possible. There's a separate Automated Linux From Scratch project, but I choose to roll my own. This was my first experience with Bash scripts -- I developed skills (and bad habits) that I continue to use this day.
I wasn't able to get very far, mostly because I kept running into obscure compiler and linker errors and 18 year old me wasn't clear how to fix them. Would be fun to see how much is changed since then because it does appear to be at least partially maintained.
Dir listing: https://pastebin.com/JrPkftgr
Dockerfile (it's still missing throwing away intermediate layers b/c it's optimized for caching and dev speed):
FROM quay.io/centos/centos:stream9
COPY files/bashrc /root/.bashrc
COPY files/bash_profile /root/.bash_profile
COPY --chmod=755 files/sysdiff /usr/bin/sysdiff
WORKDIR /mnt/lfs
COPY files/functions.inc files/
COPY --chmod=755 files/run files/
COPY scripts/0000-install-deps scripts/
RUN files/run install 0000-install-deps
# Docker host runs a memcache instance to cache build artifacts in RAM
COPY scripts/0010-install-sccache scripts/
COPY files/sccache-wrapper.c files/
RUN files/run install 0010-install-sccache
COPY scripts/1000-setup scripts/
COPY files/hosts files/passwd files/group files/shells files/inputrc files/fstab.in files/locale.conf.in files/
RUN files/run install 1000-setup
...LFS is utterly different. There is no installer, there is no base system, there is nothing even approaching the power of Slackware's package manager, as barebones as it seems today.
This is compile everything from source in a separate environment and put everything in the right place essentially manually*.
Of course, `make`-and-friends will usually do the actual lifting, but you're in charge of giving direction, there isn't a distro community patching packages and build-scripts to adhere to a consistent FSH or the like.
It builds the bootloader, the kernel, initramfs and rootfs, and then my application(s) all installed in the right place and connected to systemd. The output is a set of images that burn into their respective partitions on eMMC.
If you're a masochist, there's also Yocto. It'll build your compiler and crosstools as well as the entire system image.
A lot of the byproducts are configurable. You could configure the system to not use an initramfs, use only an initramfs/squashfs, etc. It is a really great tool with great documentation.
I agree that Yocto is a bit masochistic by comparison, but vendors like it because its primary purpose is to enable shitty vendor forks.
However, it is still Linux under the hood, so there is a kernel + whatever other applications you want running on there, all as separate processes.
You may also be thinking of busybox, which bakes an implementation of most of gnu coreutils into one file to save space. Many symlinks are often created to that sesame file, and when invoked the process can check what name it was invoked with to determine its behavior.
1. Choose the book you want to read (they call them sub-projects)
2. "Download" or "Read online"
Of course, they could reverse the nav order and provide first a "read online"/"download" and then let you pick which of the books you want to read, yielding ... two clicks to get to the one you want to read.