[Toybox] [PATCH] Add makefile rule to build kconfig; fixes clean-tree parallel builds
Rob Landley
rob at landley.net
Thu Mar 5 10:38:15 PST 2026
On 3/3/26 16:38, Avery Terrel wrote:
> Accidentally replied to Rob first...
>
> On Tuesday, March 3rd, 2026 at 14:11, Rob Landley <rob at landley.net> wrote:
>> Sigh. I'm aware that mac (and cygwin?) builds suck. Would a "forkbomb"
>> parallel build of the prereq stuff help?
>
> I've actually had few issues building on Cygwin. The issues I've come
> across have all been in the code, not the build system. I should work
> on finishing and upstreaming my local patches...
Toybox depends on LP64 and windows 64 chose LLP64.
https://landley.net/toybox/design.html#bits
https://devblogs.microsoft.com/oldnewthing/20050131-00/?p=36563
So I suspect a cygwin build is only going to work at 32 bits.
But Windows has had "Windows Subsystem for Linux" for 10 years now, at
which point I stopped paying attention to their native API. If OS/2 2.1
could lose all its native developers to Win-OS/2 (because you didn't
need to write a native OS/2 program if OS/2 could run a windows program)
then I don't need to care about native windows support once Linux
binaries can run in windows.
(Plus when I was doing my own tinycc fork I was paying attention to
mingw not cygwin. And the one I was very barely paying attention to
during toybox development was that musl port to windows, I forget its
name and Google can no longer find anything.)
>> I believe this is posix-2008:
>>
>> for i in lib/*.c $FILES
>> do
>> X=${FILES##*/} X=${X%.c}
>> $BUILD -c $i -o $X.o &
>> done
>> wait
>> $BUILD $LINK *.o -o toybox-prereq && rm *.o || exit 1
Which should be equivalent to:
for i in lib/*.c $FILES; do
X=$(FILES##*/} X=$(X%.c); $BUILD -c $i -o $X.o &
done
wait; $BUILD $LINK *.o -o toybox-prereq && rm *.o || exit 1
The two X= can't be prefix assignments because those don't apply to
command line argument resolution (hence the semicolon), and we don't get
a usable error return from the "wait" but the link should fail if any of
the .o files it expects aren't present.
But probably I should just add a $SKIP before the $BUILD in build.sh so
I can "SKIP=true source scripts/prereq/build.sh" and get the variables
set without actually doing a build, then have my own loop in use.sh
build it in parallel...
>> (The hard part of parallel builds is the rate limiting. If you just want
>> to launch EVERYTHING in parallel then wait and link... well that. Sigh,
>> I need a way to comment out the last line of build.sh so use.sh could do
>> the parallel one instead. "Simple" a moving target when you add
>> features...)
>
> Here's a sample script for limiting jobs. `jobs -p` must print only
> the group leader PID for each job in `%d\n` format
Toybox has been doing parallel builds in bash from the beginning:
https://codeberg.org/landley/toybox/src/tag/0.8.13/scripts/make.sh#L9
https://codeberg.org/landley/toybox/src/tag/0.8.13/scripts/make.sh#L261
I just don't want unnecessary complexity in build.sh. The whole point of
it was to show the simplest way to build this binary.
> #!/bin/sh
> busy() {
> end=$(($(date +%s) + $1))
> while [ $(date +%s) -lt $end ]
> do
> done
> }
>
> i=0
> while [ $i -lt 100 ]
> do
> while [ $(jobs -p | wc -l) -gt 10 ]
> do
> done
> busy $(($i % 10 + 1)) &
> i=$(($i + 1))
> done
> wait
There's a bunch of ways to do it. Off the top of my head (untested):
chocula() { echo ${#*}; }
for i in lib/*.c $FILES; do
$BUILD $i -o $(basename $i .c).o & P+="$! "
[ $(chocula $P) -gt $(nproc) ] && { wait ${P%% *};P="${P#* }";}
done
wait; $BUILD *.o -o toybox
But now you need a portable nproc, which is sort of on
scripts/portability.sh line 44 but I doubt that works on QNX or Fuchsia
or SerenityOS or xv6 or...
You could do CPUS=$(nproc 2>/dev/null || echo 4) which would even fit on
the same line after count(), but that's a handwave. (Arbitrary
imposition of policy.)
Part of my reasoning with the forkbomb was there's only 24 command files
in the prereq binary, plus another 15 in lib/*.c, so launching the whole
mess in parallel (which is what android would be doing anyway if they
went down that path) isn't THAT bad. (Processors aside, each gcc
invocation takes around 64 megs ram, so 40x in parallel is 2.5 gigs high
water mark. The 4 above is assuming any build system has 256 megs free
memory (above what the OS eats), which is _not_ true for my mkroot qemu
invocations (-m 256 each)...)
As always, everything needs better names and better paths...
Rob
More information about the Toybox
mailing list