1#!/bin/env python3 2# 3# Haiku build configuration tool 4# Copyright 2002-2018, Haiku, Inc. All rights reserved. 5# 6 7import argparse 8import os 9import subprocess 10import sys 11import errno 12from pprint import pprint 13 14parser = argparse.ArgumentParser(description='Configure a build of Haiku') 15parser.add_argument('--target-arch', nargs=1, 16 help='Target architectures. First provided is primary.', type=str, action='append', 17 choices=('x86_gcc2', 'x86', 'x86_64', 'ppc', 'm68k', 'arm', 'arm64', 'riscv64')) 18parser.add_argument('--bootstrap', nargs=3, 19 help='Prepare for a bootstrap build. No pre-built packages will be used, instead they will be built from the sources (in several phases).', 20 metavar=('<haikuporter>','<haikuports.cross>', '<haikuports>')) 21parser.add_argument('--build-gcc-toolchain', nargs=1, 22 help='Assume cross compilation. Build a gcc-based toolchain.', 23 metavar=('<buildtools dir>')) 24parser.add_argument('--use-gcc-toolchain', nargs=1, 25 help='Assume cross compilation. Build using an existing gcc-based toolchain.', 26 metavar=('<prefix>')) 27parser.add_argument('--use-clang', default=False, action='store_true', help='Assume native clang build') 28parser.add_argument('--distro-compatibility', nargs=1, 29 help='The distribution\'s level of compatibility with the official Haiku distribution. The generated files will contain the respective trademarks accordingly.', 30 choices=('official', 'compatible', 'default'), default='default') 31args = vars(parser.parse_args()) 32 33### Global functions 34 35def mkdir_p(path): 36 try: 37 os.makedirs(path) 38 except OSError as exc: 39 if exc.errno == errno.EEXIST and os.path.isdir(path): 40 pass 41 else: 42 raise 43 44def bok(message): 45 start_color = "" 46 end_color = "" 47 if sys.stdout.isatty(): 48 start_color = "\033[92m" 49 end_color = "\033[0m" 50 print(start_color + message + end_color) 51 52def berror(message): 53 start_color = "" 54 end_color = "" 55 if sys.stdout.isatty(): 56 start_color = "\033[91m" 57 end_color = "\033[0m" 58 print(start_color + message + end_color) 59 60def binfo(message): 61 start_color = "" 62 end_color = "" 63 if sys.stdout.isatty(): 64 start_color = "\033[94m" 65 end_color = "\033[0m" 66 print(start_color + message + end_color) 67 68### Global Varables 69 70(host_sysname, host_nodename, host_release, host_version, host_machine) = os.uname() 71buildConfig = [] 72 73# TODO: Remove "../.." if this ever moves to the source root 74sourceDir = os.path.realpath(os.path.dirname(os.path.realpath(__file__)) + "/../..") 75outputDir = os.getcwd() 76 77# If run in our source dir, assume generated 78if outputDir == sourceDir: 79 outputDir = sourceDir + "/generated" 80 mkdir_p(outputDir) 81 82### Helper Functions 83 84# Run a command, collect stdout into a string 85def cmdrun(cmd): 86 return subprocess.check_output(cmd).decode(sys.stdout.encoding) 87 88# Get a config key 89def get_build_config(key): 90 global buildConfig 91 for i in buildConfig: 92 if i["key"] == key: 93 return i["value"] 94 return None 95 96# Delete a config key 97def drop_build_config(key): 98 global buildConfig 99 value = get_build_config(key) 100 if value != None: 101 buildConfig.remove({"key": key, "value": value}) 102 103# Set a config key 104def set_build_config(key, value): 105 global buildConfig 106 if get_build_config(key) != None: 107 drop_build_config(key) 108 buildConfig.append({"key": key, "value": value}) 109 110def write_build_config(filename): 111 global buildConfig 112 with open(filename, "w") as fh: 113 fh.write("# -- WARNING --\n") 114 fh.write("# This file was AUTOMATICALLY GENERATED by configure, and will be completely\n") 115 fh.write("# overwritten the next time configure is run.\n\n") 116 for i in buildConfig: 117 fh.write(i["key"] + " ?= " + str(i["value"]) + " ;\n") 118 119def triplet_lookup(arch): 120 if arch == "x86_gcc2": 121 return "i586-pc-haiku" 122 elif arch == "x86": 123 return "i586-pc-haiku" 124 elif arch == "x86_64": 125 return "x86_64-unknown-haiku" 126 elif arch == "ppc": 127 return "powerpc-apple-haiku" 128 elif arch == "m68k": 129 return "m68k-unknown-haiku" 130 elif arch == "arm": 131 return "arm-unknown-haiku" 132 elif arch == "riscv64": 133 return "riscv64-unknown-haiku" 134 else: 135 berror("Unsupported target architecture: " + arch) 136 exit(1) 137 138def platform_lookup(sysname): 139 if sysname == "Darwin": 140 return "darwin" 141 elif sysname == "FreeBSD": 142 return "freebsd" 143 elif sysname == "Haiku": 144 return "haiku_host" 145 elif sysname == "Linux": 146 return "linux" 147 elif sysname == "OpenBSD": 148 return "openbsd" 149 elif sysname == "SunOS": 150 return "sunos" 151 else: 152 berror("Unknown platform: " + sysname) 153 exit(1) 154 155def setup_bootstrap(): 156 if args["bootstrap"] == None: 157 return 158 set_build_config("HOST_HAIKU_PORTER", os.path.abspath(args["bootstrap"][0])) 159 set_build_config("HAIKU_PORTS", os.path.abspath(args["bootstrap"][1])) 160 set_build_config("HAIKU_PORTS_CROSS", os.path.abspath(args["bootstrap"][2])) 161 162def setup_host_tools(): 163 set_build_config("HOST_SHA256", "sha256sum") 164 set_build_config("HOST_EXTENDED_REGEX_SED", "sed -r") 165 166def setup_host_compiler(): 167 cc = os.environ.get("CC") 168 if cc == None: 169 # We might want to step through each potential compiler here 170 cc = "gcc" 171 set_build_config("HOST_PLATFORM", platform_lookup(host_sysname)) 172 set_build_config("HOST_CC", cc) 173 set_build_config("HOST_CC_LD", cmdrun([cc, "-print-prog-name=ld"]).strip()) 174 set_build_config("HOST_CC_OBJCOPY", cmdrun([cc, "-print-prog-name=objcopy"]).strip()) 175 set_build_config("HOST_GCC_MACHINE", cmdrun([cc, "-dumpmachine"]).strip()) 176 set_build_config("HOST_GCC_RAW_VERSION", cmdrun([cc, "-dumpversion"]).strip()) 177 178def setup_target_compiler(arch): 179 cc = get_build_config("HOST_CC") 180 triplet = triplet_lookup(arch) 181 set_build_config("HAIKU_GCC_RAW_VERSION_" + arch, cmdrun([cc, "-dumpversion"]).strip()) 182 set_build_config("HAIKU_GCC_MACHINE_" + arch, triplet) 183 set_build_config("HAIKU_CPU_" + arch, arch) 184 if args["use_clang"]: 185 set_build_config("HAIKU_CC_" + arch, "clang -target " + triplet + " -B llvm-") 186 187def build_gcc_toolchain(buildtools_dir, arch): 188 bok(arch + " toolchain build complete!") 189 190### Workflow 191 192umask = os.umask(0) 193os.umask(umask) 194if umask > 22: 195 berror("Your umask is too restrictive (should be <= 0022; is actually " + str(umask) + ")") 196 print() 197 berror("Additionally, if the source tree was cloned with a too-restrictive umask,") 198 berror("you will need to run \"git checkout\" again to fix this.") 199 exit(1) 200 201if args["target_arch"] == None: 202 berror("You need to specify at least one target architecture via --target-arch") 203 exit(1) 204 205if args["use_clang"] == False and args["build_gcc_toolchain"] == None and args["use_gcc_toolchain"] == None: 206 berror("You need to pick a toolchain via --build-gcc-toolchain, --use-gcc-toolchain, or --use-clang") 207 exit(1) 208elif args["use_clang"] == True: 209 bok("Using the host's clang toolchain with a haiku target.") 210elif args["build_gcc_toolchain"] != None: 211 bok("Building a gcc cross-compiler.") 212elif args["use_gcc_toolchain"] != None: 213 bok("Using the existing gcc toolchain at " + args["use_gcc_toolchain"][0]) 214 215mkdir_p(outputDir + "/build") 216 217# Some Defaults 218set_build_config("TARGET_PLATFORM", "haiku") 219set_build_config("HAIKU_INCLUDE_SOURCES", 0) 220set_build_config("HAIKU_USE_GCC_PIPE", 0) 221set_build_config("HAIKU_HOST_USE_32BIT", 0) 222set_build_config("HAIKU_HOST_USE_XATTR", "") 223set_build_config("HAIKU_HOST_USE_XATTR_REF", "") 224set_build_config("HAIKU_DISTRO_COMPATIBILITY", args["distro_compatibility"]) 225 226setup_bootstrap() 227setup_host_tools() 228setup_host_compiler() 229 230binfo("Configuring a Haiku build at " + outputDir) 231 232for arch in args["target_arch"]: 233 binfo("Configuring " + arch[0] + " architecture...") 234 setup_target_compiler(arch[0]) 235 236 if args["build_gcc_toolchain"] != None: 237 build_gcc_toolchain(args["build_gcc_toolchain"][0], arch[0]) 238 239write_build_config(outputDir + "/build/BuildConfig") 240 241# Write out an entry Jamfile in our build directory 242with open(outputDir + "/Jamfile", "w") as fh: 243 fh.write("# -- WARNING --\n") 244 fh.write("# This file was AUTOMATICALLY GENERATED by configure, and will be completely\n") 245 fh.write("# overwritten the next time configure is run.\n\n") 246 fh.write("HAIKU_TOP = " + os.path.relpath(sourceDir, outputDir) + " ;\n") 247 fh.write("HAIKU_OUTPUT_DIR = " + os.path.relpath(outputDir, os.getcwd()) + " ;\n\n") 248 fh.write("include [ FDirName $(HAIKU_TOP) Jamfile ] ;\n") 249 250bok("Configuration complete!") 251