xref: /src/tools/build/make.py (revision 81fc74fd26da2f75c3010595db7a706c3e28d95b)
1af6a4c17SAlex Richardson#!/usr/bin/env python3
2af6a4c17SAlex Richardson# PYTHON_ARGCOMPLETE_OKAY
3af6a4c17SAlex Richardson# -
44d846d26SWarner Losh# SPDX-License-Identifier: BSD-2-Clause
5af6a4c17SAlex Richardson#
6af6a4c17SAlex Richardson# Copyright (c) 2018 Alex Richardson <arichardson@FreeBSD.org>
7af6a4c17SAlex Richardson#
8af6a4c17SAlex Richardson# Redistribution and use in source and binary forms, with or without
9af6a4c17SAlex Richardson# modification, are permitted provided that the following conditions
10af6a4c17SAlex Richardson# are met:
11af6a4c17SAlex Richardson# 1. Redistributions of source code must retain the above copyright
12af6a4c17SAlex Richardson#    notice, this list of conditions and the following disclaimer.
13af6a4c17SAlex Richardson# 2. Redistributions in binary form must reproduce the above copyright
14af6a4c17SAlex Richardson#    notice, this list of conditions and the following disclaimer in the
15af6a4c17SAlex Richardson#    documentation and/or other materials provided with the distribution.
16af6a4c17SAlex Richardson#
17af6a4c17SAlex Richardson# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18af6a4c17SAlex Richardson# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19af6a4c17SAlex Richardson# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20af6a4c17SAlex Richardson# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21af6a4c17SAlex Richardson# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22af6a4c17SAlex Richardson# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23af6a4c17SAlex Richardson# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24af6a4c17SAlex Richardson# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25af6a4c17SAlex Richardson# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26af6a4c17SAlex Richardson# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27af6a4c17SAlex Richardson# SUCH DAMAGE.
28af6a4c17SAlex Richardson#
29af6a4c17SAlex Richardson#
30af6a4c17SAlex Richardson
31af6a4c17SAlex Richardson# This script makes it easier to build on non-FreeBSD systems by bootstrapping
32af6a4c17SAlex Richardson# bmake and inferring required compiler variables.
33af6a4c17SAlex Richardson#
34af6a4c17SAlex Richardson# On FreeBSD you can use it the same way as just calling make:
35af6a4c17SAlex Richardson# `MAKEOBJDIRPREFIX=~/obj ./tools/build/make.py buildworld -DWITH_FOO`
36af6a4c17SAlex Richardson#
3781fc74fdSJohn Baldwin# On Linux and MacOS you may need to explicitly indicate the cross toolchain
3881fc74fdSJohn Baldwin# to use.  You can do this by:
3981fc74fdSJohn Baldwin# - setting XCC/XCXX/XLD/XCPP to the paths of each tool
4081fc74fdSJohn Baldwin# - using --cross-bindir to specify the path to the cross-compiler bindir:
41af6a4c17SAlex Richardson#   `MAKEOBJDIRPREFIX=~/obj ./tools/build/make.py
42af6a4c17SAlex Richardson#    --cross-bindir=/path/to/cross/compiler buildworld -DWITH_FOO TARGET=foo
43af6a4c17SAlex Richardson#    TARGET_ARCH=bar`
4481fc74fdSJohn Baldwin# - using --cross-toolchain to specify the package containing the cross-compiler
4581fc74fdSJohn Baldwin#   (MacOS only currently):
4681fc74fdSJohn Baldwin#   `MAKEOBJDIRPREFIX=~/obj ./tools/build/make.py
4781fc74fdSJohn Baldwin#    --cross-toolchain=llvm@NN buildworld -DWITH_FOO TARGET=foo
4881fc74fdSJohn Baldwin#    TARGET_ARCH=bar`
4981fc74fdSJohn Baldwin#
5081fc74fdSJohn Baldwin# On MacOS, this tool will search for an llvm toolchain installed via brew and
5181fc74fdSJohn Baldwin# use it as the cross toolchain if an explicit toolchain is not specified.
5281fc74fdSJohn Baldwin
53af6a4c17SAlex Richardsonimport argparse
54e68d702bSJohn Baldwinimport functools
55af6a4c17SAlex Richardsonimport os
56af6a4c17SAlex Richardsonimport shlex
57af6a4c17SAlex Richardsonimport shutil
58af6a4c17SAlex Richardsonimport subprocess
59af6a4c17SAlex Richardsonimport sys
60af6a4c17SAlex Richardsonfrom pathlib import Path
61af6a4c17SAlex Richardson
62af6a4c17SAlex Richardson
635157b451SJessica Clarke# List of targets that are independent of TARGET/TARGET_ARCH and thus do not
645157b451SJessica Clarke# need them to be set. Keep in the same order as Makefile documents them (if
655157b451SJessica Clarke# they are documented).
665157b451SJessica Clarkemach_indep_targets = [
675157b451SJessica Clarke    "cleanuniverse",
685157b451SJessica Clarke    "universe",
695157b451SJessica Clarke    "universe-toolchain",
70edec803cSJessica Clarke    "tinderbox",
715157b451SJessica Clarke    "worlds",
725157b451SJessica Clarke    "kernels",
735157b451SJessica Clarke    "kernel-toolchains",
745157b451SJessica Clarke    "targets",
755157b451SJessica Clarke    "toolchains",
765157b451SJessica Clarke    "makeman",
775157b451SJessica Clarke    "sysent",
785157b451SJessica Clarke]
795157b451SJessica Clarke
805157b451SJessica Clarke
81af6a4c17SAlex Richardsondef run(cmd, **kwargs):
82af6a4c17SAlex Richardson    cmd = list(map(str, cmd))  # convert all Path objects to str
83af6a4c17SAlex Richardson    debug("Running", cmd)
84af6a4c17SAlex Richardson    subprocess.check_call(cmd, **kwargs)
85af6a4c17SAlex Richardson
86af6a4c17SAlex Richardson
8769cfdc81SJessica Clarke# Always bootstraps in order to control bmake's config to ensure compatibility
88af6a4c17SAlex Richardsondef bootstrap_bmake(source_root, objdir_prefix):
89af6a4c17SAlex Richardson    bmake_source_dir = source_root / "contrib/bmake"
90af6a4c17SAlex Richardson    bmake_build_dir = objdir_prefix / "bmake-build"
91af6a4c17SAlex Richardson    bmake_install_dir = objdir_prefix / "bmake-install"
92af6a4c17SAlex Richardson    bmake_binary = bmake_install_dir / "bin/bmake"
9369cfdc81SJessica Clarke    bmake_config = bmake_install_dir / ".make-py-config"
94af6a4c17SAlex Richardson
9569cfdc81SJessica Clarke    bmake_source_version = subprocess.run([
96b771d570SJessica Clarke        "sh", "-c", ". \"$0\"/VERSION; echo $_MAKE_VERSION", bmake_source_dir],
97b771d570SJessica Clarke        stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.strip()
9869cfdc81SJessica Clarke    try:
9969cfdc81SJessica Clarke        bmake_source_version = int(bmake_source_version)
10069cfdc81SJessica Clarke    except ValueError:
10169cfdc81SJessica Clarke        sys.exit("Invalid source bmake version '" + bmake_source_version + "'")
10269cfdc81SJessica Clarke
10369cfdc81SJessica Clarke    bmake_installed_version = 0
10469cfdc81SJessica Clarke    if bmake_binary.exists():
10569cfdc81SJessica Clarke        bmake_installed_version = subprocess.run([
10669cfdc81SJessica Clarke            bmake_binary, "-r", "-f", "/dev/null", "-V", "MAKE_VERSION"],
107b771d570SJessica Clarke            stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.strip()
10869cfdc81SJessica Clarke        try:
10969cfdc81SJessica Clarke            bmake_installed_version = int(bmake_installed_version.strip())
11069cfdc81SJessica Clarke        except ValueError:
11169cfdc81SJessica Clarke            print("Invalid installed bmake version '" +
11269cfdc81SJessica Clarke                  bmake_installed_version + "', treating as not present")
113af6a4c17SAlex Richardson
114af6a4c17SAlex Richardson    configure_args = [
11562f243acSJessica Clarke        "--with-default-sys-path=.../share/mk:" +
11662f243acSJessica Clarke        str(bmake_install_dir / "share/mk"),
117af6a4c17SAlex Richardson        "--with-machine=amd64",  # TODO? "--with-machine-arch=amd64",
118af6a4c17SAlex Richardson        "--without-filemon", "--prefix=" + str(bmake_install_dir)]
11969cfdc81SJessica Clarke
12069cfdc81SJessica Clarke    configure_args_str = ' '.join([shlex.quote(x) for x in configure_args])
12169cfdc81SJessica Clarke    if bmake_config.exists():
12269cfdc81SJessica Clarke        last_configure_args_str = bmake_config.read_text()
12369cfdc81SJessica Clarke    else:
12469cfdc81SJessica Clarke        last_configure_args_str = ""
12569cfdc81SJessica Clarke
12669cfdc81SJessica Clarke    debug("Source bmake version: " + str(bmake_source_version))
12769cfdc81SJessica Clarke    debug("Installed bmake version: " + str(bmake_installed_version))
12869cfdc81SJessica Clarke    debug("Configure args: " + configure_args_str)
12969cfdc81SJessica Clarke    debug("Last configure args: " + last_configure_args_str)
13069cfdc81SJessica Clarke
13169cfdc81SJessica Clarke    if bmake_installed_version == bmake_source_version and \
13269cfdc81SJessica Clarke       configure_args_str == last_configure_args_str:
13369cfdc81SJessica Clarke        return bmake_binary
13469cfdc81SJessica Clarke
13569cfdc81SJessica Clarke    print("Bootstrapping bmake...")
13669cfdc81SJessica Clarke    if bmake_build_dir.exists():
13769cfdc81SJessica Clarke        shutil.rmtree(str(bmake_build_dir))
13869cfdc81SJessica Clarke    if bmake_install_dir.exists():
13969cfdc81SJessica Clarke        shutil.rmtree(str(bmake_install_dir))
14069cfdc81SJessica Clarke
14169cfdc81SJessica Clarke    os.makedirs(str(bmake_build_dir))
14269cfdc81SJessica Clarke
14369cfdc81SJessica Clarke    env = os.environ.copy()
14469cfdc81SJessica Clarke    global new_env_vars
14569cfdc81SJessica Clarke    env.update(new_env_vars)
14669cfdc81SJessica Clarke
147af6a4c17SAlex Richardson    run(["sh", bmake_source_dir / "boot-strap"] + configure_args,
148af6a4c17SAlex Richardson        cwd=str(bmake_build_dir), env=env)
149af6a4c17SAlex Richardson    run(["sh", bmake_source_dir / "boot-strap", "op=install"] + configure_args,
150af6a4c17SAlex Richardson        cwd=str(bmake_build_dir))
15169cfdc81SJessica Clarke    bmake_config.write_text(configure_args_str)
15269cfdc81SJessica Clarke
153af6a4c17SAlex Richardson    print("Finished bootstrapping bmake...")
154af6a4c17SAlex Richardson    return bmake_binary
155af6a4c17SAlex Richardson
156af6a4c17SAlex Richardson
157af6a4c17SAlex Richardsondef debug(*args, **kwargs):
158af6a4c17SAlex Richardson    global parsed_args
159af6a4c17SAlex Richardson    if parsed_args.debug:
160af6a4c17SAlex Richardson        print(*args, **kwargs)
161af6a4c17SAlex Richardson
162af6a4c17SAlex Richardson
163af6a4c17SAlex Richardsondef is_make_var_set(var):
164af6a4c17SAlex Richardson    return any(
165af6a4c17SAlex Richardson        x.startswith(var + "=") or x == ("-D" + var) for x in sys.argv[1:])
166af6a4c17SAlex Richardson
167af6a4c17SAlex Richardson
168af6a4c17SAlex Richardsondef check_required_make_env_var(varname, binary_name, bindir):
169af6a4c17SAlex Richardson    global new_env_vars
170af6a4c17SAlex Richardson    if os.getenv(varname):
171af6a4c17SAlex Richardson        return
172af6a4c17SAlex Richardson    if not bindir:
173af6a4c17SAlex Richardson        sys.exit("Could not infer value for $" + varname + ". Either set $" +
17481fc74fdSJohn Baldwin                 varname + " or pass --cross-bindir=/cross/compiler/dir/bin" +
17581fc74fdSJohn Baldwin                 " or --cross-toolchain=<package>")
176af6a4c17SAlex Richardson    # try to infer the path to the tool
177af6a4c17SAlex Richardson    guess = os.path.join(bindir, binary_name)
178af6a4c17SAlex Richardson    if not os.path.isfile(guess):
179af6a4c17SAlex Richardson        sys.exit("Could not infer value for $" + varname + ": " + guess +
180af6a4c17SAlex Richardson                 " does not exist")
181af6a4c17SAlex Richardson    new_env_vars[varname] = guess
182af6a4c17SAlex Richardson    debug("Inferred", varname, "as", guess)
183accf9611SUlrich Spörlein    global parsed_args
184accf9611SUlrich Spörlein    if parsed_args.debug:
185accf9611SUlrich Spörlein        run([guess, "--version"])
186af6a4c17SAlex Richardson
1873b4da25eSJessica Clarke
1882b181156SAlex Richardsondef check_xtool_make_env_var(varname, binary_name):
1892b181156SAlex Richardson    # Avoid calling brew --prefix on macOS if all variables are already set:
1902b181156SAlex Richardson    if os.getenv(varname):
1912b181156SAlex Richardson        return
1922b181156SAlex Richardson    global parsed_args
1932b181156SAlex Richardson    if parsed_args.cross_bindir is None:
19481fc74fdSJohn Baldwin        cross_bindir = cross_toolchain_bindir(binary_name,
19581fc74fdSJohn Baldwin                                              parsed_args.cross_toolchain)
196e68d702bSJohn Baldwin    else:
197e68d702bSJohn Baldwin        cross_bindir = parsed_args.cross_bindir
1982b181156SAlex Richardson    return check_required_make_env_var(varname, binary_name,
199e68d702bSJohn Baldwin                                       cross_bindir)
200af6a4c17SAlex Richardson
2013b4da25eSJessica Clarke
202e68d702bSJohn Baldwin@functools.cache
203e68d702bSJohn Baldwindef brew_prefix(package: str) -> str:
204e68d702bSJohn Baldwin    path = subprocess.run(["brew", "--prefix", package], stdout=subprocess.PIPE,
205e68d702bSJohn Baldwin                          stderr=subprocess.PIPE).stdout.strip()
206e68d702bSJohn Baldwin    debug("Inferred", package, "dir as", path)
207e68d702bSJohn Baldwin    return path.decode("utf-8")
208e68d702bSJohn Baldwin
209e68d702bSJohn Baldwindef binary_path(bindir: str, binary_name: str) -> "Optional[str]":
210e68d702bSJohn Baldwin    try:
211e68d702bSJohn Baldwin        if bindir and Path(bindir, "bin", binary_name).exists():
212e68d702bSJohn Baldwin            return str(Path(bindir, "bin"))
213e68d702bSJohn Baldwin    except OSError:
214e68d702bSJohn Baldwin        pass
215e68d702bSJohn Baldwin    return None
216e68d702bSJohn Baldwin
21781fc74fdSJohn Baldwindef cross_toolchain_bindir(binary_name: str, package: "Optional[str]") -> str:
218af6a4c17SAlex Richardson    # default to homebrew-installed clang on MacOS if available
219af6a4c17SAlex Richardson    if sys.platform.startswith("darwin"):
220af6a4c17SAlex Richardson        if shutil.which("brew"):
22181fc74fdSJohn Baldwin            if not package:
22281fc74fdSJohn Baldwin                package = "llvm"
22381fc74fdSJohn Baldwin            bindir = binary_path(brew_prefix(package), binary_name)
224e68d702bSJohn Baldwin            if bindir:
225e68d702bSJohn Baldwin                return bindir
226e68d702bSJohn Baldwin
227e68d702bSJohn Baldwin            # brew installs lld as a separate package for LLVM 19 and later
228e68d702bSJohn Baldwin            if binary_name == "ld.lld":
22981fc74fdSJohn Baldwin                lld_package = package.replace("llvm", "lld")
23081fc74fdSJohn Baldwin                bindir = binary_path(brew_prefix(lld_package), binary_name)
231e68d702bSJohn Baldwin                if bindir:
232e68d702bSJohn Baldwin                    return bindir
233af6a4c17SAlex Richardson    return None
234af6a4c17SAlex Richardson
235af6a4c17SAlex Richardson
236af6a4c17SAlex Richardsonif __name__ == "__main__":
237af6a4c17SAlex Richardson    parser = argparse.ArgumentParser(
238af6a4c17SAlex Richardson        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
239af6a4c17SAlex Richardson    parser.add_argument("--host-bindir",
240af6a4c17SAlex Richardson                        help="Directory to look for cc/c++/cpp/ld to build "
241af6a4c17SAlex Richardson                             "host (" + sys.platform + ") binaries",
242af6a4c17SAlex Richardson                        default="/usr/bin")
243a26ace4dSAlex Richardson    parser.add_argument("--cross-bindir", default=None,
244af6a4c17SAlex Richardson                        help="Directory to look for cc/c++/cpp/ld to build "
245af6a4c17SAlex Richardson                             "target binaries (only needed if XCC/XCPP/XLD "
246af6a4c17SAlex Richardson                             "are not set)")
247af6a4c17SAlex Richardson    parser.add_argument("--cross-compiler-type", choices=("clang", "gcc"),
248af6a4c17SAlex Richardson                        default="clang",
249af6a4c17SAlex Richardson                        help="Compiler type to find in --cross-bindir (only "
250af6a4c17SAlex Richardson                             "needed if XCC/XCPP/XLD are not set)"
251af6a4c17SAlex Richardson                             "Note: using CC is currently highly experimental")
25281fc74fdSJohn Baldwin    parser.add_argument("--cross-toolchain", default=None,
25381fc74fdSJohn Baldwin                        help="Name of package containing cc/c++/cpp/ld to build "
25481fc74fdSJohn Baldwin                             "target binaries (only needed if XCC/XCPP/XLD "
25581fc74fdSJohn Baldwin                             "are not set)")
256af6a4c17SAlex Richardson    parser.add_argument("--host-compiler-type", choices=("cc", "clang", "gcc"),
257af6a4c17SAlex Richardson                        default="cc",
258af6a4c17SAlex Richardson                        help="Compiler type to find in --host-bindir (only "
259af6a4c17SAlex Richardson                             "needed if CC/CPP/CXX are not set). ")
260af6a4c17SAlex Richardson    parser.add_argument("--debug", action="store_true",
261af6a4c17SAlex Richardson                        help="Print information on inferred env vars")
26231ba4ce8SAlex Richardson    parser.add_argument("--bootstrap-toolchain", action="store_true",
26331ba4ce8SAlex Richardson                        help="Bootstrap the toolchain instead of using an "
26431ba4ce8SAlex Richardson                             "external one (experimental and not recommended)")
265af6a4c17SAlex Richardson    parser.add_argument("--clean", action="store_true",
266af6a4c17SAlex Richardson                        help="Do a clean rebuild instead of building with "
26743e083beSAlex Richardson                             "-DWITHOUT_CLEAN")
268af6a4c17SAlex Richardson    parser.add_argument("--no-clean", action="store_false", dest="clean",
269af6a4c17SAlex Richardson                        help="Do a clean rebuild instead of building with "
27043e083beSAlex Richardson                             "-DWITHOUT_CLEAN")
271af6a4c17SAlex Richardson    try:
272af6a4c17SAlex Richardson        import argcomplete  # bash completion:
273af6a4c17SAlex Richardson
274af6a4c17SAlex Richardson        argcomplete.autocomplete(parser)
275af6a4c17SAlex Richardson    except ImportError:
276af6a4c17SAlex Richardson        pass
277af6a4c17SAlex Richardson    parsed_args, bmake_args = parser.parse_known_args()
278af6a4c17SAlex Richardson
279af6a4c17SAlex Richardson    MAKEOBJDIRPREFIX = os.getenv("MAKEOBJDIRPREFIX")
280af6a4c17SAlex Richardson    if not MAKEOBJDIRPREFIX:
281af6a4c17SAlex Richardson        sys.exit("MAKEOBJDIRPREFIX is not set, cannot continue!")
282af6a4c17SAlex Richardson    if not Path(MAKEOBJDIRPREFIX).is_dir():
283af6a4c17SAlex Richardson        sys.exit(
284d55de30eSJose Luis Duran            "Chosen MAKEOBJDIRPREFIX=" + MAKEOBJDIRPREFIX + " doesn't exist!")
285af6a4c17SAlex Richardson    objdir_prefix = Path(MAKEOBJDIRPREFIX).absolute()
286af6a4c17SAlex Richardson    source_root = Path(__file__).absolute().parent.parent.parent
287af6a4c17SAlex Richardson
288af6a4c17SAlex Richardson    new_env_vars = {}
289af6a4c17SAlex Richardson    if not sys.platform.startswith("freebsd"):
290af6a4c17SAlex Richardson        if not is_make_var_set("TARGET") or not is_make_var_set("TARGET_ARCH"):
2915157b451SJessica Clarke            if not set(sys.argv).intersection(set(mach_indep_targets)):
292af6a4c17SAlex Richardson                sys.exit("TARGET= and TARGET_ARCH= must be set explicitly "
293af6a4c17SAlex Richardson                         "when building on non-FreeBSD")
29431ba4ce8SAlex Richardson    if not parsed_args.bootstrap_toolchain:
295af6a4c17SAlex Richardson        # infer values for CC/CXX/CPP
296af6a4c17SAlex Richardson        if parsed_args.host_compiler_type == "gcc":
297af6a4c17SAlex Richardson            default_cc, default_cxx, default_cpp = ("gcc", "g++", "cpp")
298accf9611SUlrich Spörlein        # FIXME: this should take values like `clang-9` and then look for
299accf9611SUlrich Spörlein        # clang-cpp-9, etc. Would alleviate the need to set the bindir on
300accf9611SUlrich Spörlein        # ubuntu/debian at least.
301af6a4c17SAlex Richardson        elif parsed_args.host_compiler_type == "clang":
302af6a4c17SAlex Richardson            default_cc, default_cxx, default_cpp = (
303af6a4c17SAlex Richardson                "clang", "clang++", "clang-cpp")
304af6a4c17SAlex Richardson        else:
305af6a4c17SAlex Richardson            default_cc, default_cxx, default_cpp = ("cc", "c++", "cpp")
306af6a4c17SAlex Richardson
307af6a4c17SAlex Richardson        check_required_make_env_var("CC", default_cc, parsed_args.host_bindir)
308af6a4c17SAlex Richardson        check_required_make_env_var("CXX", default_cxx,
309af6a4c17SAlex Richardson                                    parsed_args.host_bindir)
310af6a4c17SAlex Richardson        check_required_make_env_var("CPP", default_cpp,
311af6a4c17SAlex Richardson                                    parsed_args.host_bindir)
312af6a4c17SAlex Richardson        # Using the default value for LD is fine (but not for XLD!)
313af6a4c17SAlex Richardson
314af6a4c17SAlex Richardson        # On non-FreeBSD we need to explicitly pass XCC/XLD/X_COMPILER_TYPE
315d037edf8SAlex Richardson        use_cross_gcc = parsed_args.cross_compiler_type == "gcc"
3162b181156SAlex Richardson        check_xtool_make_env_var("XCC", "gcc" if use_cross_gcc else "clang")
3172b181156SAlex Richardson        check_xtool_make_env_var("XCXX", "g++" if use_cross_gcc else "clang++")
3182b181156SAlex Richardson        check_xtool_make_env_var("XCPP",
3192b181156SAlex Richardson                                 "cpp" if use_cross_gcc else "clang-cpp")
3202b181156SAlex Richardson        check_xtool_make_env_var("XLD", "ld" if use_cross_gcc else "ld.lld")
321d037edf8SAlex Richardson
322d037edf8SAlex Richardson        # We also need to set STRIPBIN if there is no working strip binary
323d037edf8SAlex Richardson        # in $PATH.
324d037edf8SAlex Richardson        if not shutil.which("strip"):
325d037edf8SAlex Richardson            if sys.platform.startswith("darwin"):
326d037edf8SAlex Richardson                # On macOS systems we have to use /usr/bin/strip.
32788db1cc9SAlex Richardson                sys.exit("Cannot find required tool 'strip'. Please install "
32888db1cc9SAlex Richardson                         "the host compiler and command line tools.")
329d037edf8SAlex Richardson            if parsed_args.host_compiler_type == "clang":
330d037edf8SAlex Richardson                strip_binary = "llvm-strip"
331d037edf8SAlex Richardson            else:
332d037edf8SAlex Richardson                strip_binary = "strip"
333d037edf8SAlex Richardson            check_required_make_env_var("STRIPBIN", strip_binary,
3342b181156SAlex Richardson                                        parsed_args.host_bindir)
335d037edf8SAlex Richardson        if os.getenv("STRIPBIN") or "STRIPBIN" in new_env_vars:
336d037edf8SAlex Richardson            # If we are setting STRIPBIN, we have to set XSTRIPBIN to the
337d037edf8SAlex Richardson            # default if it is not set otherwise already.
338d037edf8SAlex Richardson            if not os.getenv("XSTRIPBIN") and not is_make_var_set("XSTRIPBIN"):
339d037edf8SAlex Richardson                # Use the bootstrapped elftoolchain strip:
340d037edf8SAlex Richardson                new_env_vars["XSTRIPBIN"] = "strip"
341af6a4c17SAlex Richardson
342af6a4c17SAlex Richardson    bmake_binary = bootstrap_bmake(source_root, objdir_prefix)
343af6a4c17SAlex Richardson    # at -j1 cleandir+obj is unbearably slow. AUTO_OBJ helps a lot
344af6a4c17SAlex Richardson    debug("Adding -DWITH_AUTO_OBJ")
345af6a4c17SAlex Richardson    bmake_args.append("-DWITH_AUTO_OBJ")
346af6a4c17SAlex Richardson    if parsed_args.clean is False:
347af6a4c17SAlex Richardson        bmake_args.append("-DWITHOUT_CLEAN")
348af6a4c17SAlex Richardson    if (parsed_args.clean is None and not is_make_var_set("NO_CLEAN")
349af6a4c17SAlex Richardson            and not is_make_var_set("WITHOUT_CLEAN")):
350af6a4c17SAlex Richardson        # Avoid accidentally deleting all of the build tree and wasting lots of
351af6a4c17SAlex Richardson        # time cleaning directories instead of just doing a rm -rf ${.OBJDIR}
3523b4da25eSJessica Clarke        want_clean = input("You did not set -DWITHOUT_CLEAN/--(no-)clean."
353af6a4c17SAlex Richardson                           " Did you really mean to do a clean build? y/[N] ")
354af6a4c17SAlex Richardson        if not want_clean.lower().startswith("y"):
35543e083beSAlex Richardson            bmake_args.append("-DWITHOUT_CLEAN")
356af6a4c17SAlex Richardson
357af6a4c17SAlex Richardson    env_cmd_str = " ".join(
358af6a4c17SAlex Richardson        shlex.quote(k + "=" + v) for k, v in new_env_vars.items())
359af6a4c17SAlex Richardson    make_cmd_str = " ".join(
360af6a4c17SAlex Richardson        shlex.quote(s) for s in [str(bmake_binary)] + bmake_args)
361af6a4c17SAlex Richardson    debug("Running `env ", env_cmd_str, " ", make_cmd_str, "`", sep="")
362af6a4c17SAlex Richardson    os.environ.update(new_env_vars)
363b7ac17b4SAlfredo Dal'Ava Junior
364b7ac17b4SAlfredo Dal'Ava Junior    # Fedora defines bash function wrapper for some shell commands and this
365b7ac17b4SAlfredo Dal'Ava Junior    # makes 'which <command>' return the function's source code instead of
366b7ac17b4SAlfredo Dal'Ava Junior    # the binary path. Undefine it to restore the original behavior.
367b7ac17b4SAlfredo Dal'Ava Junior    os.unsetenv("BASH_FUNC_which%%")
368b7ac17b4SAlfredo Dal'Ava Junior    os.unsetenv("BASH_FUNC_ml%%")
369b7ac17b4SAlfredo Dal'Ava Junior    os.unsetenv("BASH_FUNC_module%%")
370b7ac17b4SAlfredo Dal'Ava Junior
371af6a4c17SAlex Richardson    os.chdir(str(source_root))
372af6a4c17SAlex Richardson    os.execv(str(bmake_binary), [str(bmake_binary)] + bmake_args)
373