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', 'riscv32', '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 == "riscv32": 133 return "riscv32-unknown-haiku" 134 elif arch == "riscv64": 135 return "riscv64-unknown-haiku" 136 else: 137 berror("Unsupported target architecture: " + arch) 138 exit(1) 139 140def platform_lookup(sysname): 141 if sysname == "Darwin": 142 return "darwin" 143 elif sysname == "FreeBSD": 144 return "freebsd" 145 elif sysname == "Haiku": 146 return "haiku_host" 147 elif sysname == "Linux": 148 return "linux" 149 elif sysname == "OpenBSD": 150 return "openbsd" 151 elif sysname == "SunOS": 152 return "sunos" 153 else: 154 berror("Unknown platform: " + sysname) 155 exit(1) 156 157def setup_bootstrap(): 158 if args["bootstrap"] == None: 159 return 160 set_build_config("HOST_HAIKU_PORTER", os.path.abspath(args["bootstrap"][0])) 161 set_build_config("HAIKU_PORTS", os.path.abspath(args["bootstrap"][1])) 162 set_build_config("HAIKU_PORTS_CROSS", os.path.abspath(args["bootstrap"][2])) 163 164def setup_host_tools(): 165 set_build_config("HOST_SHA256", "sha256sum") 166 set_build_config("HOST_EXTENDED_REGEX_SED", "sed -r") 167 168def setup_host_compiler(): 169 cc = os.environ.get("CC") 170 if cc == None: 171 # We might want to step through each potential compiler here 172 cc = "gcc" 173 set_build_config("HOST_PLATFORM", platform_lookup(host_sysname)) 174 set_build_config("HOST_CC", cc) 175 set_build_config("HOST_CC_LD", cmdrun([cc, "-print-prog-name=ld"]).strip()) 176 set_build_config("HOST_CC_OBJCOPY", cmdrun([cc, "-print-prog-name=objcopy"]).strip()) 177 set_build_config("HOST_GCC_MACHINE", cmdrun([cc, "-dumpmachine"]).strip()) 178 set_build_config("HOST_GCC_RAW_VERSION", cmdrun([cc, "-dumpversion"]).strip()) 179 180def setup_target_compiler(arch): 181 cc = get_build_config("HOST_CC") 182 triplet = triplet_lookup(arch) 183 set_build_config("HAIKU_GCC_RAW_VERSION_" + arch, cmdrun([cc, "-dumpversion"]).strip()) 184 set_build_config("HAIKU_GCC_MACHINE_" + arch, triplet) 185 set_build_config("HAIKU_CPU_" + arch, arch) 186 if args["use_clang"]: 187 set_build_config("HAIKU_CC_" + arch, "clang -target " + triplet + " -B llvm-") 188 189def build_gcc_toolchain(buildtools_dir, arch): 190 bok(arch + " toolchain build complete!") 191 192### Workflow 193 194umask = os.umask(0) 195os.umask(umask) 196if umask > 22: 197 berror("Your umask is too restrictive (should be <= 0022; is actually " + str(umask) + ")") 198 print() 199 berror("Additionally, if the source tree was cloned with a too-restrictive umask,") 200 berror("you will need to run \"git checkout\" again to fix this.") 201 exit(1) 202 203if args["target_arch"] == None: 204 berror("You need to specify at least one target architecture via --target-arch") 205 exit(1) 206 207if args["use_clang"] == False and args["build_gcc_toolchain"] == None and args["use_gcc_toolchain"] == None: 208 berror("You need to pick a toolchain via --build-gcc-toolchain, --use-gcc-toolchain, or --use-clang") 209 exit(1) 210elif args["use_clang"] == True: 211 bok("Using the host's clang toolchain with a haiku target.") 212elif args["build_gcc_toolchain"] != None: 213 bok("Building a gcc cross-compiler.") 214elif args["use_gcc_toolchain"] != None: 215 bok("Using the existing gcc toolchain at " + args["use_gcc_toolchain"][0]) 216 217mkdir_p(outputDir + "/build") 218 219# Some Defaults 220set_build_config("TARGET_PLATFORM", "haiku") 221set_build_config("HAIKU_INCLUDE_SOURCES", 0) 222set_build_config("HAIKU_USE_GCC_PIPE", 0) 223set_build_config("HAIKU_HOST_USE_32BIT", 0) 224set_build_config("HAIKU_HOST_USE_XATTR", "") 225set_build_config("HAIKU_HOST_USE_XATTR_REF", "") 226set_build_config("HAIKU_DISTRO_COMPATIBILITY", args["distro_compatibility"]) 227 228setup_bootstrap() 229setup_host_tools() 230setup_host_compiler() 231 232binfo("Configuring a Haiku build at " + outputDir) 233 234for arch in args["target_arch"]: 235 binfo("Configuring " + arch[0] + " architecture...") 236 setup_target_compiler(arch[0]) 237 238 if args["build_gcc_toolchain"] != None: 239 build_gcc_toolchain(args["build_gcc_toolchain"][0], arch[0]) 240 241write_build_config(outputDir + "/build/BuildConfig") 242 243# Write out an entry Jamfile in our build directory 244with open(outputDir + "/Jamfile", "w") as fh: 245 fh.write("# -- WARNING --\n") 246 fh.write("# This file was AUTOMATICALLY GENERATED by configure, and will be completely\n") 247 fh.write("# overwritten the next time configure is run.\n\n") 248 fh.write("HAIKU_TOP = " + os.path.relpath(sourceDir, outputDir) + " ;\n") 249 fh.write("HAIKU_OUTPUT_DIR = " + os.path.relpath(outputDir, os.getcwd()) + " ;\n\n") 250 fh.write("include [ FDirName $(HAIKU_TOP) Jamfile ] ;\n") 251 252bok("Configuration complete!") 253