Index: Makefile.inc1 =================================================================== --- Makefile.inc1 +++ Makefile.inc1 @@ -1979,13 +1979,28 @@ # bootstrap-tool. _config_deps= lib/libnv usr.bin/lex/lib +.if !empty(BUILD_WITH_OPIPEFAIL) && empty(.MAKEFLAGS:M-n) +# The bash -o pipefail option will cause the whole pipeline to fail if one +# of the commands returned a non-zero exit code. This is useful to find +# missing/incompatible commands in the bootstrap $PATH since the default shell +# will just ignore the exit code. +_pipefail_shell=tools/build/bash-with-pipefail +# Note: we can't set __MAKE_SHELL to bash-with-pipefail since otherwise bmake +# will complain about an unknown shell in sys.mk (.SHELL: path=${__MAKE_SHELL}) +# To work around this bash-with-pipefail will be installed as +# ${WORLDTMP}/legacy/bin/sh. This also has the advantage that make rules using +# sh will now use bash-with-pipefail instead and get the additional checks. +CROSSENV+= __MAKE_SHELL=${WORLDTMP}/legacy/bin/sh +BMAKEENV+= __MAKE_SHELL=${WORLDTMP}/legacy/bin/sh +.endif + legacy: .PHONY .if ${BOOTSTRAPPING} < ${MINIMUM_SUPPORTED_OSREL} && ${BOOTSTRAPPING} != 0 @echo "ERROR: Source upgrades from versions prior to ${MINIMUM_SUPPORTED_REL} are not supported."; \ false .endif -.for _tool in tools/build ${_elftoolchain_libs} ${_config_deps} +.for _tool in tools/build ${_elftoolchain_libs} ${_config_deps} ${_pipefail_shell} ${_+_}@${ECHODIR} "===> ${_tool} (obj,includes,all,install)"; \ cd ${.CURDIR}/${_tool}; \ if [ -z "${NO_OBJWALK}" ]; then ${MAKE} DIRPRFX=${_tool}/ obj; fi; \ Index: tools/build/bash-with-pipefail/Makefile =================================================================== --- /dev/null +++ tools/build/bash-with-pipefail/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD$ + +PROG= bash-with-pipefail +SRCS= bash-with-pipefail.c +BINDIR= /bin + +LINKS= ${BINDIR}/bash-with-pipefail ${BINDIR}/sh + +.include Index: tools/build/bash-with-pipefail/bash-with-pipefail.c =================================================================== --- /dev/null +++ tools/build/bash-with-pipefail/bash-with-pipefail.c @@ -0,0 +1,67 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright 2018 Alex Richardson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +/* + * Start bash with -o pipefail to catch errors. + * This is used when building world on a non-FreeBSD host to ensure that we + * don't depend on binaries that don't exist and are used in pipeline. + * Without -o pipefail missing commands in a pipeline are ignored and can + * cause empty input files to be generated. + */ +int main(int argc, char** argv) { + char** new_argv; + char* bash_path; + + new_argv = calloc(argc + 3, sizeof(char*)); + new_argv[0] = "bash"; /* bash won't support -o pipefail in sh mode */ + new_argv[1] = "-eo"; + new_argv[2] = "pipefail"; + + for (int i = 1; i < argc; i++) + new_argv[i + 2] = argv[i]; + new_argv[argc + 2] = NULL; + + /* + * Since we are running with a restricted $PATH, we will not be able + * to find bash in $PATH. Try /usr/local/bin and /bin first + */ + if (access("/usr/local/bin/bash", F_OK) == 0) + bash_path = "/usr/local/bin/bash"; + else if (access("/bin/bash", F_OK) == 0) + bash_path = "/bin/bash"; + else + errx(EX_OSFILE, "Could not infer path to bash"); + + if (execvp(bash_path, new_argv) != 0) + err(EX_UNAVAILABLE, "Could not execute %s", bash_path); +}