From: Collin Funk Date: Sat, 18 Jul 2026 03:06:28 +0000 (-0700) Subject: build: reduce size of multi-call binary by reusing test logic X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=664b8663dd926cafb99dc0351bb787b156419311;p=thirdparty%2Fcoreutils.git build: reduce size of multi-call binary by reusing test logic Map the '[' command to which selects appropriate behavior at runtime, rather than separate binaries for each closely related utility. $ size src/coreutils-prev text data bss dec hex filename 1485188 7348 2191968 3684504 383898 src/coreutils-prev $ size src/coreutils text data bss dec hex filename 1476092 7348 2191936 3675376 3814f0 src/coreutils * src/local.mk (noinst_HEADERS): Add src/test.h. (src___SOURCES): Remove src/lbracket.c. Add src/test.c and src/test-lbracket.c. (src_test_SOURCES): New variable. (src/coreutils.h): Adjust the function name to be single_binary_main_lbracket instead of single_binary_main__. * build-aux/gen-single-binary.sh: Likewise. Map '[' to 'test'. (override_single): Adjust the file name to be coreutils-lbracket.c instead of coreutils-_.c. * src/lbracket.c: Remove file. * cfg.mk (_src): Don't match src/lbracket.c in the regular expression. Match src/test-lbracket.c and src/test-test.c. * src/coreutils-lbracket.c: New file. * src/test-lbracket.c: Likewise. * src/test-test.c: Likewise. * src/test.h: Likewise. * src/test.c: Include test.h. (LBRACKET): Remove macro. (PROGRAM_NAME): Use the variable TEST_MODE to determine the program name. (main): Use the variable TEST_MODE instead of the LBRACKET macro to implement behavior specific to '['. * NEWS: Mention the improvement. --- diff --git a/NEWS b/NEWS index 501f813b55..19fb04ce1e 100644 --- a/NEWS +++ b/NEWS @@ -79,6 +79,10 @@ GNU coreutils NEWS -*- outline -*- ** Build-related + The multi-call binary built with configure --enable-single-binary + in size by around 9KB through the more efficient reuse of the 'test' utility + by '['. + configure no longer accepts the --with-linux-crypto option, which allowed cksum, md5sum, and sha*sum to use the Linux AF_ALG API. This API will be deprecated in Linux 7.2 and is less performant than OpenSSL. diff --git a/build-aux/gen-single-binary.sh b/build-aux/gen-single-binary.sh index a89a6bc839..8d9fb0d529 100755 --- a/build-aux/gen-single-binary.sh +++ b/build-aux/gen-single-binary.sh @@ -62,12 +62,20 @@ echo "## Automatically generated by $me. DO NOT EDIT BY HAND!" override_single() { from="$1"; to="$2"; - eval "src_${from}_SOURCES='src/coreutils-${from}.c'" + # Use coreutils-lbracket.c instead of coreutils-_.c. + if test "$from" = _; then + src_name=lbracket + else + src_name="$from"; + fi + eval "src_${from}_SOURCES='src/coreutils-${src_name}.c'" eval "src_from_LDADD=\$src_${from}_LDADD" eval "src_${from}_LDADD='$src_from_LDADD src/libsinglebin_${to}.a'" eval "src_libsinglebin_${from}_a_DEPENDENCIES='src/libsinglebin_${to}.a'" echo "src_libsinglebin_${from}_a_DEPENDENCIES = src/libsinglebin_${to}.a" } +# This is for the '[' program. Automake transliterates '[' and '/' to '_'. +override_single _ test override_single dir ls override_single vdir ls override_single arch uname @@ -104,10 +112,16 @@ for cmd in $ALL_PROGRAMS; do # CFLAGS # Hack any other program defining a main() replacing its main by - # single_binary_main_$PROGRAM_NAME. - echo "${base}_CFLAGS = \"-Dmain=single_binary_main_${cmd} (int, char **);" \ - " int single_binary_main_${cmd}\" " \ - "-Dusage=_usage_${cmd} \$(src_coreutils_CFLAGS)" + # single_binary_main_$PROGRAM_NAME. Use single_binary_main_lbracket + # instead of single_binary_main__. + if test "$cmd" = _; then + fn=lbracket + else + fn="$cmd" + fi + echo "${base}_CFLAGS = \"-Dmain=single_binary_main_${fn} (int, char **);" \ + " int single_binary_main_${fn}\" " \ + "-Dusage=_usage_${fn} \$(src_coreutils_CFLAGS)" var=src_${cmd}_CFLAGS eval "value=\$$var" if [ "x$value" != "x" ]; then diff --git a/cfg.mk b/cfg.mk index d5e8f9025d..2848d6fdc0 100644 --- a/cfg.mk +++ b/cfg.mk @@ -929,8 +929,9 @@ _x_system_c := (libstdbuf|make-prime-list)\.c exclude_file_name_regexp--sc_system_h_headers = \ ^src/($(_x_system_h)|$(_x_system_c))$$ -_src := (false|lbracket|chown-(chgrp|chown) -_src := $(_src)|ls-(dir|ls|vdir)|make-prime-list|tac-pipe|uname-(arch|uname)) +_src := (false|chown-(chgrp|chown) +_src := $(_src)|ls-(dir|ls|vdir)|make-prime-list|tac-pipe|test-(lbracket|test) +_src := $(_src)|uname-(arch|uname)) _gl_src = (xdecto.max|cl-strtold) exclude_file_name_regexp--sc_require_config_h_first = \ (^lib/buffer-lcm\.c|gl/lib/$(_gl_src)\.c|src/$(_src)\.c)$$ diff --git a/src/coreutils-lbracket.c b/src/coreutils-lbracket.c new file mode 100644 index 0000000000..8b705b4241 --- /dev/null +++ b/src/coreutils-lbracket.c @@ -0,0 +1,31 @@ +/* lbracket -- wrapper to test with the right test_mode. + Copyright (C) 2026 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include +#include "system.h" + +#include "test.h" +/* Ensure that the main for test is declared even if the tool is not being + built in this single-binary. */ +int single_binary_main_test (int argc, char **argv); +int single_binary_main_lbracket (int argc, char **argv); + +int +single_binary_main_lbracket (int argc, char **argv) +{ + test_mode = TEST_LBRACKET; + return single_binary_main_test (argc, argv); +} diff --git a/src/lbracket.c b/src/lbracket.c deleted file mode 100644 index b57ca9bb0c..0000000000 --- a/src/lbracket.c +++ /dev/null @@ -1,2 +0,0 @@ -#define LBRACKET 1 -#include "test.c" diff --git a/src/local.mk b/src/local.mk index 9d9c9814bb..1a6079d69e 100644 --- a/src/local.mk +++ b/src/local.mk @@ -66,6 +66,7 @@ noinst_HEADERS = \ src/system.h \ src/temp-stream.h \ src/term-sig.h \ + src/test.h \ src/uname.h \ src/wc.h @@ -381,7 +382,8 @@ src_ginstall_SOURCES = src/install.c src/prog-fprintf.c $(copy_sources) \ $(selinux_sources) # This is for the '[' program. Automake transliterates '[' and '/' to '_'. -src___SOURCES = src/lbracket.c +src___SOURCES = src/test.c src/test-lbracket.c +src_test_SOURCES = src/test.c src/test-test.c nodist_src_coreutils_SOURCES = src/coreutils.h src_coreutils_SOURCES = src/coreutils.c @@ -768,7 +770,7 @@ src/speedlist.h: src/termios.c lib/config.h src/speedgen # SINGLE_BINARY_PROGRAM(program_name_str, main_name) # once for each program list on $(single_binary_progs). Note that # for [ the macro invocation is: -# SINGLE_BINARY_PROGRAM("[", _) +# SINGLE_BINARY_PROGRAM("[", lbracket) DISTCLEANFILES += src/coreutils.h src/coreutils.h: Makefile $(AM_V_GEN)rm -f $@ @@ -776,7 +778,11 @@ src/coreutils.h: Makefile $(AM_V_at)for prog in x $(single_binary_progs); do \ test $$prog = x && continue; \ prog=`basename $$prog`; \ - main=`echo $$prog | tr '[' '_'`; \ + main=`if test $$prog = '['; then \ + echo lbracket; \ + else \ + echo $$prog; \ + fi`; \ echo "SINGLE_BINARY_PROGRAM(\"$$prog\", $$main)"; \ done | sort > $@t $(AM_V_at)chmod a-w $@t diff --git a/src/test-lbracket.c b/src/test-lbracket.c new file mode 100644 index 0000000000..74dadbc459 --- /dev/null +++ b/src/test-lbracket.c @@ -0,0 +1,2 @@ +#include "test.h" +enum test_modes test_mode = TEST_LBRACKET; diff --git a/src/test-test.c b/src/test-test.c new file mode 100644 index 0000000000..2364628f9c --- /dev/null +++ b/src/test-test.c @@ -0,0 +1,2 @@ +#include "test.h" +enum test_modes test_mode = TEST_TEST; diff --git a/src/test.c b/src/test.c index ab7f249929..692c37263b 100644 --- a/src/test.c +++ b/src/test.c @@ -26,16 +26,8 @@ #define TEST_STANDALONE 1 -#ifndef LBRACKET -# define LBRACKET 0 -#endif - /* The official name of this program (e.g., no 'g' prefix). */ -#if LBRACKET -# define PROGRAM_NAME "[" -#else -# define PROGRAM_NAME "test" -#endif +#define PROGRAM_NAME (test_mode == TEST_TEST ? "test" : "[") #include "system.h" #include "assure.h" @@ -44,6 +36,7 @@ #include "quote.h" #include "stat-time.h" #include "strnumcmp.h" +#include "test.h" #include @@ -864,7 +857,7 @@ main (int margc, char **margv) argv = margv; - if (LBRACKET) + if (test_mode == TEST_LBRACKET) { /* Recognize --help or --version, but only when invoked in the "[" form, when the last argument is not "]". Use direct diff --git a/src/test.h b/src/test.h new file mode 100644 index 0000000000..f432487ac8 --- /dev/null +++ b/src/test.h @@ -0,0 +1,10 @@ +enum test_modes +{ + /* This is for the 'test' program. */ + TEST_TEST, + + /* This is for the '[' program. */ + TEST_LBRACKET +}; + +extern enum test_modes test_mode;