From 71e2ea773414b2316bbe6b803b9a52c38d3752e8 Mon Sep 17 00:00:00 2001 From: Alex Deymo Date: Thu, 5 Jun 2014 19:50:32 -0700 Subject: build: support building all tools in a single binary Add the --enable-single-binary option to the configure file. When enabled, this option builds a single binary file containing the selected tools. Which tool gets executed depends on the value of argv[0] which can be set implicitly through symlinks to the single program. This setup reduces significantly the size of a complete coreutils install, since code from lib/libcoreutils.a is not duplicated in every one of the more than 100 binaries. Runtime overhead is increased due to more dynamic libraries being loaded, and extra initialization being performed for all utils. Also initially a larger binary is loaded from storage, though this is usually alleviated due to caching and lazy mmaping of unused blocks, and in fact the single binary should have better caching characteristics. Comparing the size of the individual versus single binary on x86_64: $ cd src $ size coreutils $ size -t $(../build-aux/gen-lists-of-programs.sh --list-progs | grep -Ev '(coreutils|libstdbuf)') | tail -n1 text data bss dec hex filename 1097416 5388 88432 1191236 122d44 src/coreutils 4901010 124964 163768 5189742 4f306e (TOTALS) Storage requirements are reduced similarly: $ cd src $ du -h coreutils $ du -ch $(../build-aux/gen-lists-of-programs.sh --list-progs | grep -Ev '(coreutils|libstdbuf)') | tail -n1 1.2M coreutils 5.3M total When installing, the makefile will create either symlinks or shebangs based on the --enable-single-binary setting, for each configured tool. In this way, all the tools are still callable individually, but they are all implemented by the same "coreutils" binary installed on the same directory. * .gitignore: Add new generated files. * Makefile.am: New rules to generate build-aux/gen-single-binary.sh and install symlinks. * NEWS: Mention the new feature. * README: Add "coreutils" to the list of utils. * bootstrap.conf: Regenerate src/single-binary.mk * build-aux/gen-lists-of-programs.sh: New --list-progs option. * build-aux/gen-single-binary.sh: Regenerate * configure.ac: New --enable-single-binary option and other variables. Disallow --enable-single-binary=symlinks with --program-prefix et. al. * man/coreutils.x: Manpage hook. * man/local.mk: Add manpage hook and fix dependencies. * src/coreutils.c: Multicall implementation. * src/local.mk: New rules for the single binary option. * tests/local.mk: Add $single_binary_progs to support require_built_() from init.cfg * tests/misc/env.sh: Avoid the use of symlink to echo. * tests/misc/help-version.sh: Add exception for coreutils. * tests/install/basic-1.sh: Really avoid using ginstall strip functionality if there is an issue with the independent strip command. * src/kill.c: Changes to call exit() in main. * src/readlink.c: Likewise. * src/shuf.c: Likewise. * src/timeout.c: Likewise. * src/truncate.c: Likewise. --- tests/install/basic-1.sh | 10 +++++++--- tests/local.mk | 3 ++- tests/misc/coreutils.sh | 36 ++++++++++++++++++++++++++++++++++++ tests/misc/env.sh | 15 ++++++++++----- tests/misc/help-version.sh | 2 +- 5 files changed, 56 insertions(+), 10 deletions(-) create mode 100755 tests/misc/coreutils.sh (limited to 'tests') diff --git a/tests/install/basic-1.sh b/tests/install/basic-1.sh index 9835d463e..0c0d1de86 100755 --- a/tests/install/basic-1.sh +++ b/tests/install/basic-1.sh @@ -46,13 +46,17 @@ is not readable, so skipping the remaining tests in this file." cp "$just_built_dd" . || fail=1 cp $dd $dd2 || fail=1 -strip $dd2 \ - || warn_ "WARNING!!! Your strip command doesn't seem to work, +strip=-s +if ! strip $dd2; then + ! test -e $abs_top_builddir/src/coreutils \ + && warn_ "WARNING!!! Your strip command doesn't seem to work, so skipping the test of install's --strip option." + strip= +fi # This test would fail with 3.16s when using versions of strip that # don't work on read-only files (the one from binutils works fine). -ginstall -s -c -m 555 $dd $dir || fail=1 +ginstall $strip -c -m 555 $dd $dir || fail=1 # Make sure the source file is still around. test -f $dd || fail=1 diff --git a/tests/local.mk b/tests/local.mk index 86050dc79..e0f1f84ee 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -65,7 +65,7 @@ TESTS_ENVIRONMENT = \ abs_top_builddir='$(abs_top_builddir)' \ abs_top_srcdir='$(abs_top_srcdir)' \ abs_srcdir='$(abs_srcdir)' \ - built_programs='$(built_programs)' \ + built_programs='$(built_programs) $(single_binary_progs)' \ host_os=$(host_os) \ host_triplet='$(host_triplet)' \ srcdir='$(srcdir)' \ @@ -249,6 +249,7 @@ all_tests = \ tests/pr/pr-tests.pl \ tests/misc/pwd-option.sh \ tests/misc/chcon-fail.sh \ + tests/misc/coreutils.sh \ tests/misc/cut.pl \ tests/misc/cut-huge-range.sh \ tests/misc/wc.pl \ diff --git a/tests/misc/coreutils.sh b/tests/misc/coreutils.sh new file mode 100755 index 000000000..a22bc9f64 --- /dev/null +++ b/tests/misc/coreutils.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# Verify behavior of separate coreutils multicall binary + +# Copyright (C) 2014 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 . + + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ coreutils + +test -s "$abs_top_builddir/src/coreutils.h" \ + || skip_ "multicall binary is disabled" + +# Yes outputs all its params so is good to verify argv manipulations +echo 'y' > exp +coreutils --coreutils-prog=yes | head -n10 | uniq > out +compare exp out || fail=1 + +# Ensure if incorrect program passed, we diagnose +echo "coreutils: unknown program 'blah'" > exp +coreutils --coreutils-prog='blah' --help 2>err && fail=1 +compare exp err || fail=1 + +Exit $fail diff --git a/tests/misc/env.sh b/tests/misc/env.sh index c4b97377a..883c58f54 100755 --- a/tests/misc/env.sh +++ b/tests/misc/env.sh @@ -20,6 +20,10 @@ . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src print_ver_ env +# A simple shebang program to call "echo" from symlinks like "./-u" or "./--". +echo "#!$abs_top_builddir/src/echo simple_echo" > simple_echo \ + || framework_failure_ +chmod a+x simple_echo || framework_failure_ # Verify clearing the environment a=1 @@ -105,9 +109,10 @@ export PATH # Use -- to end options (but not variable assignments). # On some systems, execve("-i") invokes a shebang script ./-i on PATH as # '/bin/sh -i', rather than '/bin/sh -- -i', which doesn't do what we want. -# Avoid the issue by using an executable rather than a script. +# Avoid the issue by using a shebang to 'echo' passing a second parameter +# before the '-i'. See the definition of simple_echo before. # Test -u, rather than -i, to minimize PATH problems. -ln -s "$abs_top_builddir/src/echo" ./-u || framework_failure_ +ln -s "simple_echo" ./-u || framework_failure_ case $(env -u echo echo good) in good) ;; *) fail=1 ;; @@ -117,16 +122,16 @@ case $(env -u echo -- echo good) in *) fail=1 ;; esac case $(env -- -u pass) in - pass) ;; + *pass) ;; *) fail=1 ;; esac # After options have ended, the first argument not containing = is a program. env a=b -- true test $? = 127 || fail=1 -ln -s "$abs_top_builddir/src/echo" ./-- || framework_failure_ +ln -s "simple_echo" ./-- || framework_failure_ case $(env a=b -- true || echo fail) in - true) ;; + *true) ;; *) fail=1 ;; esac diff --git a/tests/misc/help-version.sh b/tests/misc/help-version.sh index b4939f7af..0598557f2 100755 --- a/tests/misc/help-version.sh +++ b/tests/misc/help-version.sh @@ -239,7 +239,7 @@ parted_setup () { args="-s $tmp_in mklabel gpt" # something more than --help or --version. for i in $built_programs; do # Skip these. - case $i in chroot|stty|tty|false|chcon|runcon) continue;; esac + case $i in chroot|stty|tty|false|chcon|runcon|coreutils) continue;; esac rm -rf $tmp_in $tmp_in2 $tmp_dir $tmp_out $bigZ_in $zin $zin2 echo z |gzip > $zin -- cgit v1.2.3-70-g09d2