Commit Graph
70946 Commits
Author SHA1 Message Date
John Crispin ec4768fbed yafut: use a reproducible timestamp
yaffsfs_CurrentTime() returns the wall clock, and Yaffs writes that value
to the access, modification and change time of every object header. The
RouterBOARD images of ipq40xx/mikrotik therefore differ on every build,
and so does the sysupgrade metadata checksum that follows them.

Add a patch that reads SOURCE_DATE_EPOCH, which the build already exports.

Tested with two images written one second apart. They are identical with
SOURCE_DATE_EPOCH set and differ without it.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:46:30 +02:00
John Crispin 1e7c8fbc29 trusted-firmware-a: use a reproducible build timestamp
TF-A embeds __TIME__ and __DATE__ in its build message, so bl2 and bl31
differ on every build, and so do all images that contain them.

Pass BUILD_MESSAGE_TIMESTAMP, derived from the source date of the
package, in the same format.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:46:30 +02:00
John Crispin e923974fc1 apk: use deterministic ECDSA signatures
The host apk signs with LibreSSL, which uses a random ECDSA nonce. A
package that is signed again has different bytes and often a different
size, even if its content did not change. The rootfs records the
package size in /lib/apk/db/installed, so a rebuild of one package
changes all images.

Add a patch that derives the nonce as specified in RFC 6979. Signed
packages and images are now identical after a rebuild.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:46:30 +02:00
John Crispin cd322612cc tools: cache the configure results of ten autotools tools
These ten hold up the wall clock on their own. Their configure stages
run while nothing else does, because the tools build in dependency
order. Measured over a full build, cold cache against warm:

  sed        21.68 s -> 1.22 s
  m4         21.48 s -> 1.22 s
  findutils  19.47 s -> 1.56 s
  tar        17.75 s -> 1.47 s
  cpio       14.28 s -> 1.26 s
  bison      12.81 s -> 1.29 s
  patch      12.16 s -> 1.31 s
  elfutils   26.58 s -> 15.47 s

Each of the ten configures the same way with a warm cache as it does
without one. The check runs configure three times, without a cache, with
a cold cache and with a warm cache, and compares every file that
configure generates between the first and the last. ncurses fails that
check; these do not.

The only difference left is ac_ct_AR and ac_ct_RANLIB, which a cold run
fills through the fallback path of AC_CHECK_TOOL and a warm run leaves
empty. AR and RANLIB are correct in both, and no generated Makefile
reads the ac_ct_ form.

tools/cmake keeps its 33 s, the longest configure stage in the build. It
bootstraps with its own script rather than autoconf, so this does not
reach it.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin a0b036985a build: cache the autoconf results of a package that opts in
A configure run spends its time on the compiler invocations behind its
checks, not on the shell. tools/m4 runs the compiler 633 times and takes
24 seconds. ccache does not help, because it refuses to cache an
autoconf conftest compile at all; it counts these separately as
"Autoconf compile/link" under its uncacheable calls.

A package that sets PKG_CONFIGURE_CACHE or HOST_CONFIGURE_CACHE keeps a
config.cache under tmp/, so a package that is cleaned and built again
reads the answers instead of running the checks. The configure stage of
tools/m4 drops from 24.5 s to 1.5 s. dirclean removes tmp/, so a full
reset of the tree starts from cold answers.

A package has to opt in, because a cache is only correct for a configure
script that keeps its side effects outside the AC_CACHE_VAL body.
ncurses appends -D_XOPEN_SOURCE and -D_DEFAULT_SOURCE to CPPFLAGS inside
that body, so a cached run restores the value, skips the append, and
then fails to compile wcwidth. ncurses_cfg.h is identical in both cases;
the difference is in the generated Makefile.

The path covers everything else that changes an answer. A target package
is keyed on the toolchain directory, which carries the gcc version. A
host package is keyed on the version string of the host compiler,
because autoconf cannot detect a new compiler behind an unchanged name;
gcc 14 turned an implicit function declaration into an error and changed
many answers that way. Both are keyed on a hash of the configure
arguments and the build flags, and autoconf refuses a cache written with
different flags on top of that.

A cached program path outlives a wipe of staging_dir, so ac_cv_path_ and
ac_cv_prog_ answers are dropped when the cache loads and configure
searches PATH again. Without that, a cache written before staging_dir
held sed pins /usr/bin/sed for good. A search runs no compiler, so this
costs nothing that matters.

The cache arrives through a site script rather than --cache-file, so a
hand written configure script never sees a new argument. The site script
removes the file of the previous arguments, so a different configuration
gets a cold cache instead of an autoconf error. It also unsets the
variable, and make exports it for the configure stage alone, so a nested
configure run keeps its own cache.

Set CONFIGURE_CACHE_DIR to keep the cache elsewhere, or to the empty
string to run every check again.

Host builds now read a site script on Linux as well. Before this change
only Darwin had one.

CONFIG_SITE holds one path name: autoconf before 2.70 reads the whole
value as that one name, so a second path would silence both scripts. It
names the cache script alone, and the cache script loads the site script
of the architecture from CONFIG_SITE_BASE.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 48c1e36bf2 package: reuse the rootfs if its inputs did not change
The rootfs is installed from scratch on every build: apk add, then the
post-install and init scripts of every package, one after the other.
This takes about 1.5 s, even if no package changed.

Build a key from the package index, which holds a hash of every
package, the install list, .config, SOURCE_DATE_EPOCH, the files
overlay, apk, tar, gzip and the Makefiles that create the rootfs. Save
a copy of the rootfs with the key, and restore it from the copy if the
key did not change. Later build steps modify the rootfs, so it is
always restored from the copy and never reused in place.

prepare_rootfs runs tar and gzip over the apk script archive, so the
key holds those two host tools as well. With mklibs it also runs
rstrip.sh, which the key then holds too.

The install list now holds the base-files, libc and kernel versions
too, so that the key can use it. Only the APK path uses the cache.

The copy costs disk: the build directory carries the root filesystem
twice, once as root and root.orig and once in the cache.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 5b3be62345 build: reuse the output of Build/libdeflate-gzip
libdeflate-gzip -12 of a kernel takes about a second, and its input
usually did not change. Run it through cache-run.sh, as Build/lzma.

libdeflate-gzip stores no mtime or file name, and -k -S .new gives the
same output as -c.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin c6a1bf5966 build: reuse the output of slow compression steps
On a rebuild, the initrd and the initramfs kernel image usually have
the same content as before, but xz and lzma compress them again on
every build. For the OpenWrt One this takes about 6 s of the build.

Add scripts/cache-run.sh. It runs a command that creates one file from
another, and stores the output together with a hash of the input, the
command line and the tool. If they are the same in the next run, it
copies the stored output instead. The commands are deterministic, so
the result is identical.

On a hit the script compares the stored output with the file that is
already there and copies only when the two differ. The output then keeps
its timestamp, so a later step that compares timestamps does not treat it
as new work.

Use it for the initrd compression and for Build/lzma. bzip2 and lzop
stay as they are: bzip2 writes to stdout, and lzop stores the mtime of
its input. xz and gzip now keep the cpio, as the other tools do, so a
cache hit leaves the same files behind as a normal run.

The slot name follows the path of the output below $(KDIR), because two
targets in different directories can share a basename. cache-run.sh holds
a lock over the slot and swaps the stored output in with a rename, so two
image recipes on one slot never read a half written pair.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin f7adeacb62 build: hash downloaded files only after they changed
check_download_integrity hashes the downloaded file on every parse of
every Makefile that uses it. With large archives this costs seconds per
build, for example about 1.2 s for linux-firmware, which is on the
critical path of the package build.

Remember the identity (inode, size, mtime, ctime) of a file that
matched its expected hash, and hash it again only after it changed.

The identity comes from the stat of tools/coreutils, which a Linux host
does not build. Fall back to the stat of the host, and drop the cache
when neither one answers, so that a missing stat costs time and never
trust.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin b5c4b58764 build: let find answer the rebuild question
The -n caller of timestamp.pl only needs to know whether the stamp is the
newest file. The script reads the timestamp of every file to answer that,
which costs 0.09 s for the build directory of U-Boot with its 44000 files.
The rebuild check runs the script twice per package and per build.

Collect the paths first, and for -n ask find for the first file that is
newer than the stamp. find stops at that file, so the walk ends early when
there is work to do. The other modes keep the old code, and so does -n with
-follow, because find and the script treat a symbolic link to a file
differently there.

The check over the U-Boot build directory now takes 0.02 s. The result is
the same for all 44 build directories of the OpenWrt One. The time of a full
no-op build does not change on a machine with 24 cores, where the package
step waits for the dependency chain rather than for the checks.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin ac4f1dd36c build: write the build info files only when they change
buildversion, feedsversion and json_overview_image_info rewrite
version.buildinfo, feeds.buildinfo and profiles.json on every build. The
content is the same, but the new timestamp makes the checksum step hash
every image again, which takes 0.18 s for the OpenWrt One and grows with the
number of devices.

Write the three files to the temporary directory and copy them over the
result of the last build only when the content differs.

This takes the time of the index, image info and checksum step from 0.36 s
to 0.10 s, and the time of a full no-op build from 3.25 s to 3.00 s.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 0e5a0bd558 build: scan the package and target metadata only after a change
prepare-tmpinfo runs the metadata scan for the packages and for the targets
on every build. Each run starts two more make processes and walks the whole
directory, which takes 0.15 s per scan even when it finds nothing to do.

Keep a stamp per scan and skip the scan while no Makefile, no .mk file and
no directory below the scanned tree is newer than the stamp, and while
include, rules.mk and .config are unchanged as well.

package/kernel/linux reads target/linux/*/modules.mk through SCAN_DEPS, so
the package scan watches those files although they sit in the target tree.

This takes the time of the prereq step from 0.45 s to 0.21 s, and the time
of a full no-op build from 3.40 s to 3.25 s for the OpenWrt One.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin e97e3f36bd kernel: configure the kernel only when an input changed
The configure stage rewrites the kernel configuration on every build. It
runs kconfig.pl twice and package-metadata.pl once, which takes an eighth of
a second. target/compile and target/install both pay it.

The rule carries FORCE because the kernel configuration also depends on the
package metadata, which is not a prerequisite. Add a check file that the
rule creates only when the configure stage must run, and make the stamp
depend on it. The check covers the kernel configuration files, .config, the
package metadata, the scripts that read them, the two build system files
that hold the recipe, the target directory and the user headers.

This takes the time of target/linux install on an unchanged tree from 1.33 s
to 1.15 s, and the time of a full no-op build from 3.68 s to 3.40 s for the
OpenWrt One.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 746c76168c build: create the images only when an input changed
target/linux install runs the whole image build on every build. It creates
the root file system image, the kernel images, the FIT containers and the
factory images again, although the inputs are the same. This takes 0.7 s for
the OpenWrt One and much more for a target with many devices.

Record a state after a successful image build:

 - a hash of the target directory, because the build normalises the
   timestamps of the root file system and they cannot report a change,
 - the name and the size of every image in the output directory, so that a
   deleted image brings the build back,
 - the name and the size of every file below json_info_files, because
   image_prepare drops that directory and profiles.json comes from it.

Skip the image build while the state is the same and no file of the kernel
build directory, the kernel tree, the image directory of the staging
directory, the host tools, the target directory of the tree, include,
scripts or .config is newer than the state file.

This takes the time of target/linux install on an unchanged tree from 1.77 s
to 1.33 s, and the time of a full no-op build from 4.11 s to 3.72 s for the
OpenWrt One.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 8d25643025 build: merge the packages only when the set of packages changed
package/merge deletes the symlink farm of every built package and creates
it again on every build. package/merge-index then reads all of those
packages to create the index. With two thousand packages both steps take a
third of a second, and the cost grows with the size of the configuration.

Record the name, the size and the timestamp of every package file. Skip
both steps while that list is unchanged and the index is present. A rebuild
of the symlinks drops the index, so merge-index creates it again.

This takes the time of package/install on an unchanged tree from 1.05 s to
0.60 s for the OpenWrt One.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin af60df881d build: run diffconfig only when the configuration changed
diffconfig.sh runs the configuration parser over the whole tree twice,
which takes 2 s of every build, and buildinfo runs it on each build.
The result depends only on .config and the Kconfig files.

Skip it while neither is newer than the file it wrote last time.
include/toplevel.mk guards the sync check of .config the same way.

This alone changes nothing on a parallel build, because buildinfo runs
beside the compile stamps. It pays once the other phases become shorter.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin d8442d2876 build: write the checksum file only when a file changed
sha256sums hashes every file of a directory on each build. package/index
calls it over the package output of this tree, which holds 1258 files,
and spends 0.7 s of a no-op build on a file that does not change.

Skip the pass while the file exists and nothing in the directory is
newer than it.

The checksum target of a build calls sha256sums as well, but it keeps
writing the file on every build. buildversion and feedsversion rewrite
version.buildinfo and feeds.buildinfo beside it without a guard, so the
directory always holds something newer. Only package/index gains here.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 3054effc90 build: rebuild the package index only when a package changed
package/index runs apk mkndx and make-index-json.py over every package
on each build, which takes 1.2 s of a no-op build of this tree with 1258
packages. The result depends only on the package files.

Skip the pass while packages.adb and index.json exist and no package
file, and no directory, is newer than index.json. The directory covers a
package that was added or removed, because neither changes a file that
is left. The comparison uses index.json rather than packages.adb,
because apk mkndx writes packages.adb through a rename, which sets the
mtime of the directory to that of the file it wrote. The directory would
then tie with packages.adb and the skip would rest on the two comparing
equal. index.json is written after it, so the directory is older.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 3cf47c5c17 build: compare timestamps with a fraction of a second
timestamp.pl decides whether a subdir has work to do. It scans the stamp
file and the source directories, keeps the newest, and reports that the
work is done when the newest one is the stamp. The scan reads the mtime
through the core stat of perl, which truncates it to whole seconds, and
the comparison is strict with the stamp scanned first. Two files that
share a second therefore count as a tie, and a tie says that the work is
done.

include/subdir.mk touches tmp/.build at the start of a build and the
stamp of a subdir at the end, so the two land in the same second once a
build takes less than a second between them. The target install stamp is
the last one of a build, roughly 0.4 s before the build ends, so it is
the one that ties first: the next build then skips target/install and
ships the images of the previous build. Around one build in five was
affected once the build became fast enough.

Read the mtime through Time::HiRes, which reports the fraction as well.
Two touches around a fork no longer share a timestamp. Keep the core
stat where the module is missing, which restores the old behaviour
rather than failing.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin bc2e85eb8d build: clean the package root dir only if the packages can have changed
The stamp of package/cleanup depends on tmp/.build, which is touched on
every make call, so every build deletes $(STAGING_DIR_ROOT) and every
package copies its files into it again, one after the other under a
lock. With all packages selected, this takes 5 s of a build that has
nothing to do, most of it in the 1102 kmods of package/kernel/linux.

Only clean it if .config, the package metadata or a file below package/
changed since the last cleanup. An edit of the source in a build
directory alone does not clean it, so a file that such an edit removes
from a package stays until then. make package/cleanup cleans it at any
time.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin e7d105af30 build: skip the .config sync check if nothing changed
Every build runs conf --defconfig on a copy of .config to warn if the
configuration is out of sync. This takes about a second before the
build starts.

Skip the check if the last one passed, and neither .config nor a
Kconfig file, the generated Kconfig files or conf changed since then.
If the check fails, it runs and warns again on every build, as before.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin cb18fbc331 build: regenerate package metadata only after it changed
prepare-tmpinfo runs on every build and calls package-metadata.pl
three times, although tmp/.packageinfo usually did not change. It also
writes tmp/.config-feeds.in on every build: the check against
tmp/.packageauxvars never passes, as that file is written again later
in the same recipe.

Generate the package metadata files only if tmp/.packageinfo or the
scripts are newer, and replace tmp/.config-feeds.in only if its content
changed.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 9d84fbff36 kernel: skip kbuild if the kernel tree did not change
kbuild runs twice per build, for the modules and for the image, and
takes about half a second each time even if there is nothing to do.

Store a hash of the kbuild command line after a successful run, and
skip the next run if the hash is the same and no file in the kernel
tree or the toolchain is newer than the stamp. The OpenWrt files at the
top of the kernel tree are ignored, apart from .config. Directories are
ignored, as kbuild changes them with temporary files, so a deleted file
alone does not trigger a run.

With CONFIG_STRIP_KERNEL_EXPORTS the image link reads symtab.h, which
sits above the kernel tree and follows the modules of the packages. The
scan of the image run names that file as well.

The workaround that deletes vmlinux now only runs together with kbuild.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 714886f3e4 kernel: build the separate initramfs kernel only once
With TARGET_ROOTFS_INITRAMFS_SEPARATE, the initramfs kernel differs
from the regular kernel only in CONFIG_RD_<ALGO>. Both are built in the
same tree, so every build switches the kernel config twice and relinks
vmlinux twice, even if nothing changed.

Give the regular kernel the same ramdisk support and use its image for
the initramfs as is. The initramfs step now only creates the initrd.
Remove the separate initramfs branches of Kernel/SetInitramfs, as it is
no longer called in this mode.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin b95b0928ac build: add per stage build time profiling
Set BUILD_TIME_LOG=<file> to record a begin and an end event for every
prepare, configure, compile and install stage of every tool, toolchain
component, kernel and package. scripts/build-time-report.pl turns the log
into a report.

The report divides each second of the build between the stages that run
in it, so the numbers show which stages hold up the wall clock rather
than which ones burn the most CPU. Nothing is recorded and no extra
process runs when BUILD_TIME_LOG is unset.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin bc96efb443 build: find the alias of a build directory with notdir
subdir calls diralias twice for every target of every build directory,
and diralias splits the path into words twice to find its last
component. This takes a quarter of the parse time of the main Makefile.

Use notdir, which returns the same last component. The rules do not
change.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 407e833140 build: expand the subdir recipes only when they run
subdir expands the recipe of every target of every build directory when
it creates the rules, although make runs only a few of them. The main
Makefile creates these rules on every make call, and a build calls it
several times in a row.

Put only a call of the recipe into the rules, so that make expands it
when the recipe runs. The commands do not change. The parse of the main
Makefile takes 0.10 s instead of 0.15 s, with 318 package directories.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 6258a11bac image: only define the rules of selected devices
A target defines more than 100 devices, and the image Makefile
expands the kernel, image and artifact rules of every one of them. The
rules of a device that is not selected are only reachable through
install-disabled and compile-disabled, so nothing uses them. The image
Makefile is parsed twice per build, 0.3 s each for mediatek.

Only expand Device/Build for selected devices, and for all devices if
CONFIG_IB is set, as the ImageBuilder compiles them all.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 9772acb4f7 build: only define the clean rule for packages not selected
BuildPackage expands the full BuildTarget/ipkg for every package in a
Makefile, whether it is selected or not. package/kernel/linux defines
more than 1000 kmods, and this takes about 0.9 s of its 1.2 s parse,
which is on the critical path of every build.

Move the clean rule into BuildTarget/ipkg/disabled, and only expand it
for packages that are not selected, unless DEVELOPER is set. The other
rules of such a package can not be reached, as its compile target only
depends on them if the package is selected. Target types without a
disabled variant are expanded as before.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 64acf83435 build: test for 64-bit time_t only when a host build needs it
host-build.mk runs the YEAR_2038 compiler test while it parses, so every
package with a host build runs it on every make call, also for a target
build or a build that has nothing to do.

Add the result to HOST_CONFIGURE_ARGS without expanding it, so the test
runs only when the host build is configured.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin a44d3e3256 build: find the source date of a package only once per directory
Make expands PKG_SOURCE_DATE_EPOCH for every recipe of a package, as
package-pack.mk exports it to the recipes. Every expansion runs
get_source_date_epoch.sh, which runs git.

Keep the result for each directory. The directory still depends on
whether the build directory has a version.date file.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 43a9ff1c94 build: compute the hash of a config symbol list only once
confvar runs a shell and mkhash every time make expands a stamp name that
contains it, 21 times in a make call for a package with a host build,
also if nothing has to be done.

The config symbols do not change during a make call, so keep the hash
of each list of symbols after the first use.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 842611776d build: hash the package files only once per Makefile
Build/CoreTargets fixes the value of STAMP_PREPARED, but its expansion
references STAMP_PREPARED several times before that happens, and every
reference hashes all files of the package again with find and mkhash.
HOST_STAMP_PREPARED carries the same hash and the same cost.

Compute the hash on first use in PKG_FILES_MD5 and keep it. Both stamps
read it.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 6b009ee3ea build: create the index, image info and checksums in one make call
world runs package/index, json_overview_image_info and checksum as
three separate calls of the top-level Makefile, and each call parses it
again. The calls run without -j, so one call with all three goals runs
them in the same order.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin f5fb15d869 build: list the available feeds only when needed
feeds.mk runs "scripts/feeds list -n" when it is included, which
happens in every package Makefile. The list is only needed to find the
package directories of the feeds, which a package Makefile only does
when it creates or cleans a package.

Evaluate FEEDS_AVAILABLE on first use.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin e3a89c4e4b build: detect the host triplet once per build
The top-level Makefile calls itself for prereq and for the build, and
every call runs config.guess again. config.guess runs the host C
compiler three times, which takes about 40 ms.

Take GNU_HOST_NAME from the environment in recursive calls, as for
REVISION and SOURCE_DATE_EPOCH.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 177325ba1a build: compute REVISION and SOURCE_DATE_EPOCH once per build
The top-level Makefile calls itself for prereq and for the build, and
every call runs getver.sh and get_source_date_epoch.sh again, although
the values are already exported by the first call.

Take them from the environment in recursive calls. The first call still
computes them, so a value in the environment of the user is ignored as
before.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin d29ffc6f39 build: evaluate NPROC only once
NPROC is a recursive variable, so every use starts a shell. The image
Makefile expands the initramfs rule once per device, which runs nproc
about 200 times and makes the parse of the mediatek image Makefile take
about 1 s. It is parsed twice per build.

Evaluate it on first use and keep the result.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin a15df19727 kernel: filter versioned kmod items in make
version_filter calls package-metadata.pl for every list that contains
a versioned item such as "foo@ge6.18". Parsing package/kernel/linux
runs it about 40 times, which takes 1.3 s of 2.2 s, and this parse is
on the critical path of every build.

Filter items of the form <name>@<op><version> in make. The version
comparison stays in package-metadata.pl, but runs only once per
version. Other forms are passed to package-metadata.pl as before.

The only difference in the output: items without a version condition
are no longer passed through the shell, so quotes in a KCONFIG value
are kept. That list is only used for variable names, so the result is
the same. All kmod packages and images are identical.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin bb332115ba package: build mtd-utils, libsepol and libselinux in parallel
A package builds with -j1 unless it sets PKG_BUILD_PARALLEL, and 171 of
the 220 packages do not set it. These three gain the most from the job
server on a cold build of this tree:

  mtd-utils   24.77 s -> 6.43 s
  libsepol    17.99 s -> 2.18 s
  libselinux   7.82 s -> 1.89 s

mtd-utils runs while nothing else does, so its 18 s count against the
wall clock in full.

Each one builds three times out of three with the job server, and each
build writes a tree that is identical to the one a serial build writes.
nftables gains as well, 17.9 s to 13.0 s, but it is left alone: its
build directory differs between two serial builds, so the check that
covers the other three says nothing about it.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 3d597977b6 tools: build erofs-utils, mtd-utils and cpio in parallel
A tool builds with -j1 unless it sets HOST_BUILD_PARALLEL, and 45 of the
73 tools do not set it. These three gain the most from the job server on
a cold build of this tree:

  erofs-utils  23.11 s -> 15.73 s
  mtd-utils    11.70 s ->  6.42 s
  cpio          8.46 s ->  3.56 s

Each one builds three times out of three with the job server, and each
build writes a tree that is identical to the one a serial build writes.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin b8d8bb9ba3 gettext-full: enable parallel builds again
Commit 30fb675847 ("gettext-full: disable parallel compilation")
disabled parallel builds of gettext 0.21, as they failed with make -j12.

With gettext 0.24.2, the host and the target build passed four times
each with make -j24, and libintl-full does not change. The host build
takes 172 s instead of 231 s, and the target build 142 s instead of
214 s. With all packages selected, both are on the longest chain of a
clean build.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin b029d56e0b hostapd: build the variants in parallel
The variants only write to their own build directories. The packages
without a variant are only built by the first variant.

With all packages selected, a clean build of the 31 variants takes 46 s
instead of 114 s, and the packages do not change. Together with the
commit that lists every build variant only once, a build that has
nothing to do takes 17 s instead of 33 s, as the variants were the
longest chain of it.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin b8d89de6e0 mediatek: build the boot loader variants in parallel
The variants of arm-trusted-firmware-mediatek and uboot-mediatek only
write to their own build directories and to files in the staging and
bin directories that have the variant in their name.

For the OpenWrt One, a clean build of the 9 TF-A variants takes 2 s
instead of 11 s, and of the 2 U-Boot variants 12 s instead of 21 s.
The images do not change.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 2d40469602 build: allow packages to build their variants in parallel
The variants of a package are built one after the other in one recipe.
For packages with many variants, such as the boot loaders, this makes
the variants the longest chain of the package build, also if nothing
has to be done.

Add PKG_PARALLEL_VARIANTS. If a package sets it, every variant gets its
own make target with the dependencies of the package, and the package
target depends on these targets. A package must only set it if its
variants share no files outside of their build directories.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin df6a1c978b uboot-mediatek: compress BL31 in the build directory
With FIP_COMPRESS set, the fip step compresses the BL31 image next to
the original in the staging directory. All U-Boot variants for the same
SoC, boot device and DRAM type write the same file.

Write the compressed BL31 to the build directory of the variant instead.
The fip does not change.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 728fb15aff build: use a separate staging directory per build variant
All variants of a package collect their InstallDev files in the same
directory below $(TMP_DIR) and remove it before and after use. Add the
build variant to its name, as for the staging file list, so that the
variants of a package do not share it.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin fbd3048f21 lantiq: pass the kernel make flags to three module packages
ltq-adsl-mei and ltq-deu call make directly after a cd into the kernel
directory, and ltq-ifxos builds its module through autotools. None of them
pass KERNEL_MAKE_FLAGS, so the module compile runs without the
-ffile-prefix-map that iremap adds. WARN_ON_ONCE() in check_copy_size()
expands to warn_slowpath_fmt(__FILE__, ...), so the absolute build path
lands in the .ko.

Measured on the published snapshot packages of lantiq/xway and
xrx200_legacy:
- drv_mei_cpe.ko holds
  /builder/shared-workdir/build/build_dir/target-mips_24kc_musl/linux-lantiq_xway/linux-6.18.52/include/linux/ucopysize.h
- drv_ifxos.ko holds the same path for linux-lantiq_xrx200_legacy
- ltq_atm_danube.ko, built with the flags, holds no such string

The module size follows the length of the build path, so the apk record
and every image that carries the module differ between two builds in
different directories.

ltq_deu_vr9.ko holds no such string today, but the package has the same
defect.

Pass the flags in the same way as ltq-atm and ltq-vdsl-vr9-mei already do.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin bda729e82f uboot-envtools: remove stray output from the boot log
/etc/uci-defaults/30_uboot-envtools runs in preinit, before the board
file is generated. Two of its helpers write to the console:

* uci batch prints the name of every anonymous section that "add"
  creates, and nothing redirects stdout.
* grep -q hides a match, but it still reports a file that does not
  exist. /etc/fw_env.config is absent until the first append creates it.

On the first boot after a flash this puts three lines on the console:

    cfg016cb7
    cfg026cb7
    grep: /etc/fw_env.config: No such file or directory

Redirect the batch output and add -s to the grep.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00
John Crispin 111e92cce0 xdp-tools: find the arch headers of the toolchain for the BPF programs
The configure script of xdp-tools sets ARCH_INCLUDES only from the
multiarch directory of a native compiler. For a cross compiler it stays
empty, and clang takes <linux/types.h> for the BPF programs from
/usr/include of the host. On hosts that have <asm/types.h> only in the
multiarch directory, such as Debian without gcc-multilib, the build
fails:

  /usr/include/linux/types.h:5:10: fatal error: 'asm/types.h' file not found

Add the include directories of the toolchain with -idirafter. They are
searched after the headers of xdp-tools and of the system, as the
multiarch directory of a native build, so they do not replace the Linux
headers that xdp-tools ships. The target compiler already has them as
system directories.

The configure script also tests for bpftool with the bash redirection &>.
/bin/sh may be dash, which runs the command in the background instead, so
configure drops a working bpftool and takes the output of whereis. Add a
second patch that uses >/dev/null 2>&1, which every shell supports.

Signed-off-by: John Crispin <john@phrozen.org>
2026-09-22 18:18:58 +02:00