[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