/*++ Copyright (c) 2016 Minoca Corp. All Rights Reserved Module Name: env.ck Abstract: This build module contains the environment and functions used throughout the Minoca OS build. Author: Evan Green 14-Apr-2016 Environment: Build --*/ TRUE = 1; FALSE = 0; arch ?= getenv("ARCH"); //debug ?= getenv("DEBUG"); //variant ?= getenv("VARIANT"); arch ?= "x86"; debug ?= "dbg"; variant ?= ""; outroot = "^/../.."; binroot = outroot + "/bin"; stripped_dir = binroot + "/stripped"; cflags ?= getenv("CFLAGS") ? [getenv("CFLAGS")] : ["-Wall", "-Werror"]; cppflags ?= getenv("CPPFLAGS") ? [getenv("CPPFLAGS")] : []; ldflags ?= getenv("LDFLAGS") ? [getenv("LDFLAGS")] : []; asflags ?= getenv("ASFLAGS") ? [getenv("ASLAGS")] : []; global_config = { "ARCH": arch, "DEBUG": debug, "VARIANT": variant, "BUILD_CC": getenv("BUILD_CC"), "BUILD_AR": getenv("BUILD_AR"), "BUILD_OBJCOPY": getenv("BUILD_OBJCOPY"), "BUILD_STRIP": getenv("BUILD_STRIP"), "CC": getenv("CC"), "AR": getenv("AR"), "OBJCOPY": getenv("OBJCOPY"), "STRIP": getenv("STRIP"), "RCC": getenv("RCC"), "IASL", getenv("IASL"), "SHELL": getenv("SHELL"), }; build_os ?= uname_s(); build_arch ?= uname_m(); if (build_arch == "i686") { build_arch = "x86"; } else if (build_arch == "x86-64") { build_arch = "x64"; } assert(((arch == "x86") || (arch == "armv7") || (arch == "armv6") || (arch == "x64")), "Invalid architecture"); assert(((build_arch == "x86") || (build_arch == "armv7") || (build_arch == "armv6") || (build_arch == "x64")), "Unknown build architecture"); assert(((debug == "dbg") || (debug == "rel")), "Invalid debug setting"); // // Set up build architecture-specific globals. // build_bfd_arch = ""; build_obj_format = ""; if (build_arch == "x86") { build_bfd_arch = "i386"; build_obj_format = "elf32-i386"; if (build_os == "Windows") { build_obj_format = "pe-i386"; } } else if ((build_arch == "armv7") || (build_arch == "armv6")) { build_bfd_arch = "arm"; build_obj_format = "elf32-littlearm"; } else if (build_arch == "x64") { build_bfd_arch = "x86-64"; build_obj_format = "elf64-x86-64"; } else { print("Set build_bfd_arch and build_obj_format for new architecture.\n"); } // // Set up target architecture-specific globals. // bfd_arch = ""; obj_format = ""; if (arch == "x86") { bfd_arch = "i386"; obj_format = "elf32-i386"; if (variant == "q") { tool_prefix = "i586-pc-minoca-"; } else { tool_prefix = "i686-pc-minoca-"; } } else if ((arch == "armv7") || (arch == "armv6")) { bfd_arch = "arm"; obj_format = "elf32-littlearm"; tool_prefix = "arm-none-minoca-"; } else if (arch == "x64") { bfd_arch = "x86-64"; obj_format = "elf64-x86-64"; tool_prefix = "x86_64-pc-minoca-"; } // // Set a default build compiler if none was set. // global_config["BUILD_CC"] ?= "gcc"; global_config["BUILD_AR"] ?= "ar"; global_config["BUILD_OBJCOPY"] ?= "objcopy"; global_config["BUILD_STRIP"] ?= "strip"; global_config["RCC"] ?= "windres"; global_config["IASL"] ?= "iasl"; global_config["SHELL"] ?= "sh"; echo = "echo"; // // Pin down CFLAGS and the like. // if (debug == "dbg") { cflags += ["-O1", "-DDEBUG=1"]; } else { cflags += ["-O2", "-Wno-unused-but-set-variable", "-DNDEBUG"]; } cflags += ["-fno-builtin", "-fno-omit-frame-pointer", "-g", "-save-temps=obj", "-ffunction-sections", "-fdata-sections", "-fvisibility=hidden"]; cppflags += ["-I$//include"]; // // TODO: Figure out REVISION, BUILD_TIME, BUILD_TIME_STRING, BUILD_STRING, // and BUILD_USER. // cppflags += ["-DREVISION=0 -DBUILD_TIME_STRING=\\\"\\\" -DBUILD_TIME=0 -DBUILD_STRING=\\\"\\\" -DBUILD_USER=\\\"None\\\""]; build_cflags = cflags + []; build_cppflags = cppflags + []; cflags += ["-fpic"]; if (build_os == "Windows") { build_cflags += ["-mno-ms-bitfields"]; } else { build_cflags += ["-fpic"]; } if (arch == "armv6") { cppflags += ["-march=armv6zk", "-marm", "-mfpu=vfp"]; } if (arch == "x86") { cflags += ["-mno-ms-bitfields"]; if (variant == "q") { cppflags += ["-Wa,-momit-lock-prefix=yes", "-march=i586"]; } } asflags += ["-Wa,-g"]; ldflags += ["-Wl,--gc-sections"]; objcopy_flags ?= [ "--rename-section .data=.rodata,alloc,load,data,contents,readonly", "-I binary", "-O " + obj_format, "-B " + bfd_arch ]; build_objcopy_flags ?= [ "--rename-section .data=.rodata,alloc,load,data,contents,readonly", "-I binary", "-O " + build_obj_format, "-B " + build_bfd_arch ]; if (build_os == "Windows") { build_objcopy_flags += ["--prefix-symbols=_"]; } // // Set a default target compiler if one was not set. On Minoca building its own // architecture, use the native compiler. // if ((build_os == "Minoca") && (build_arch == arch)) { global_config["CC"] ?= global_config["BUILD_CC"]; global_config["AR"] ?= global_config["BUILD_AR"]; global_config["OBJCOPY"] ?= global_config["BUILD_OBJCOPY"]; global_config["STRIP"] ?= global_config["BUILD_STRIP"]; } else { global_config["CC"] ?= tool_prefix + "gcc"; global_config["AR"] ?= tool_prefix + "ar"; global_config["OBJCOPY"] ?= tool_prefix + "objcopy"; global_config["STRIP"] ?= tool_prefix + "strip"; } global_config["BASE_CFLAGS"] = cflags; global_config["BASE_CPPFLAGS"] = cppflags; global_config["BASE_LDFLAGS"] = ldflags; global_config["BASE_ASFLAGS"] = asflags; global_config["OBJCOPY_FLAGS"] = objcopy_flags; global_config["BUILD_BASE_CFLAGS"] = build_cflags; global_config["BUILD_BASE_CPPFLAGS"] = build_cppflags; global_config["BUILD_BASE_LDFLAGS"] = ldflags; global_config["BUILD_BASE_ASFLAGS"] = asflags; global_config["BUILD_OBJCOPY_FLAGS"] = build_objcopy_flags; global_config["IASL_FLAGS"] = ["-we"]; // // Add a config value, creating it if it does not exist. // function add_config(entry, name, value) { entry["config"] ?= {}; entry["config"][name] ?= []; entry["config"][name] += [value]; return; } // // Create a phony group target that depends on all the given input entries. // function group(name, entries) { entry = { "label": name, "type": "target", "tool": "phony", "inputs": entries, "config": {} }; return [entry]; } // // Create a copy target. // function copy(source, destination, destination_label, flags, mode) { config = {}; if (flags) { config["CPFLAGS"] = flags; } if (mode) { config["config"] = mode; } entry = { "type": "target", "tool": "copy", "label": destination_label, "inputs": [source], "output": destination, "config": config }; if (!destination_label) { entry["label"] = destination; } return [entry]; } // // Add a stripped version of the target. // function strip(params) { tool_name = "strip"; if (get(params, "build")) { tool_name = "build_strip"; } params["type"] = "target"; params["tool"] = tool_name; return [params]; } // // Replace the current target with a copied version in the bin directory. Also // strip unless told not to. // function binplace(params) { label = get(params, "label"); label ?= get(params, "output"); source = get(params, "output"); source ?= label; assert(label && source, "Label or source must be defined"); // // Set the output since the label is going to be renamed and create the // copy target. // params["output"] = source; file_name = basename(source); destination = binroot + "/" + file_name; cpflags = get(params, "cpflags"); mode = get(params, "chmod"); new_original_label = label + "_orig"; original_target = ":" + new_original_label; copied_entry = copy(original_target, destination, label, cpflags, mode)[0]; // // The original label was given to the copied destination, so tack a _orig // on the source label. // params["label"] = new_original_label; entries = [copied_entry, params]; // // Unless asked not to, create a stripped entry as well. // if (!get(params, "nostrip")) { stripped_entry = { "label": label + "_stripped", "inputs": [original_target], "output": stripped_dir + "/" + file_name, "build": get(params, "build"), }; // // Make the binplaced copy depend on the stripped version. // copied_entry["implicit"] = [":" + stripped_entry["label"]]; entries += strip(stripped_entry); } return entries; } // // Create a group of object file targets from source file targets. Returns a // list of the object names in the first element and the object file target // entries in the second element. // function compiled_sources(params) { inputs = params["inputs"]; objs = []; entries = []; sources_config = get(params, "sources_config"); prefix = get(params, "prefix"); build = get(params, "build"); includes = get(params, "includes"); if (includes) { sources_config ?= {}; sources_config["CPPFLAGS"] ?= []; for (include in includes) { sources_config["CPPFLAGS"] += ["-I" + include]; } } assert(len(inputs) != 0, "Compilation must have inputs"); for (input in inputs) { input_parts = split_extension(input); ext = input_parts[1]; suffix = ".o"; if (ext == "c") { tool = "cc"; } else if (ext == "cc") { tool = "cxx"; } else if (ext == "S") { tool = "as"; } else if (ext == "rc") { tool = "rcc"; suffix = ".rsc"; } else { objs += [input]; continue; } if (build) { tool = "build_" + tool; } obj_name = input_parts[0] + suffix; if (prefix) { obj_name = prefix + "/" + obj_name; } obj = { "type": "target", "label": obj_name, "output": obj_name, "inputs": [input], "tool": tool, "config": sources_config, }; entries += [obj]; objs += [":" + obj_name]; } if (prefix) { params["output"] ?= params["label"]; params["output"] = prefix + "/" + params["output"]; } return [objs, entries]; } // // Vanilla link of a set of sources into some sort of executable or shared // object. // function executable(params) { build = get(params, "build"); compilation = compiled_sources(params); objs = compilation[0]; entries = compilation[1]; params["type"] = "target"; params["inputs"] = objs; params["tool"] = "ld"; if (build) { params["tool"] = "build_ld"; } // // Convert options for text_address, linker_script, and entry to actual // LDFLAGS. // text_address = get(params, "text_address"); if (text_address) { text_option = "-Wl,-Ttext-segment=" + text_address + " -Wl,-Ttext=" + text_address; add_config(params, "LDFLAGS", text_option); } linker_script = get(params, "linker_script"); if (linker_script) { script_option = "-Wl,-T" + linker_script; add_config(params, "LDFLAGS", script_option); } entry = get(params, "entry"); if (entry) { entry_option = "-Wl,-e" + entry + " -Wl,-u" + entry; add_config(params, "LDFLAGS", entry_option); } if (get(params, "binplace")) { entries += binplace(params); } else { entries += [params]; } return entries; } // // Creates a regular position independent application. // function application(params) { build = get(params, "build"); exename = get(params, "output"); exename ?= params["label"]; if (build && (build_os == "Windows")) { params["output"] = exename + ".exe"; } add_config(params, "LDFLAGS", "-pie"); return executable(params); } // // Creates a shared library or DLL. // function shared_library(params) { build = get(params, "build"); soname = get(params, "output"); soname ?= params["label"]; major_version = get(params, "major_version"); add_config(params, "LDFLAGS", "-shared"); if ((!build) || (build_os != "Windows")) { soname += ".so"; if (major_version != null) { soname += "." + major_version; } add_config(params, "LDFLAGS", "-Wl,-soname=" + soname); } else { soname += ".dll"; } params["output"] = soname; return executable(params); } // // Creates a static archive. // function static_library(params) { compilation = compiled_sources(params); objs = compilation[0]; entries = compilation[1]; params["type"] = "target"; output = get(params, "output"); output ?= params["label"]; params["output"] = output + ".a"; params["inputs"] = objs; params["tool"] = "ar"; if (build) { params["tool"] = "build_ar"; } if (get(params, "binplace")) { entries += binplace(params); } else { entries += [params]; } return entries; } // // Create a list of compiled .aml files from a list of asl files. Returns a // list where the first element is a list of all the resulting target names, // and the second element is a list of the target entries. // function compiled_asl(inputs) { entries = []; objs = []; assert(len(inputs) != 0, "Compilation must have inputs"); for (input in inputs) { input_parts = split_extension(input); ext = input_parts[1]; suffix = ".aml"; tool = "iasl"; obj_name = input_parts[0] + suffix; obj = { "type": "target", "label": obj_name, "output": obj_name, "inputs": [input], "tool": tool }; entries += [obj]; objs += [":" + obj_name]; } return [objs, entries]; } // // Create a group of object file targets from binary files. Returns a // list of the object names in the first element and the object file target // entries in the second element. // function objectified_binaries(params) { inputs = params["inputs"]; objs = []; entries = []; objcopy_config = get(params, "config"); prefix = get(params, "prefix"); build = get(params, "build"); assert(len(inputs) != 0, "Compilation must have inputs"); for (input in inputs) { input_parts = split_extension(input); ext = input_parts[1]; suffix = ".o"; tool = "objcopy"; if (build) { tool = "build_" + tool; } obj_name = input_parts[0] + suffix; if (prefix) { obj_name = prefix + "/" + obj_name; } obj = { "type": "target", "label": obj_name, "output": obj_name, "inputs": [input], "tool": tool, "config": objcopy_config, }; entries += [obj]; objs += [":" + obj_name]; } if (prefix) { params["output"] ?= params["label"]; params["output"] = prefix + "/" + params["output"]; } return [objs, entries]; } // // Create a single object file from a binary file. // function objectified_binary(params) { return objectified_binaries(params)[1]; } // // Create a library from a set of objectified files. // function objectified_library(params) { build = get(params, "build"); compilation = objectified_binaries(params); objs = compilation[0]; entries = compilation[1]; params["type"] = "target"; output = get(params, "output"); output ?= params["label"]; params["output"] = output + ".a"; params["inputs"] = objs; params["tool"] = "ar"; if (build) { params["tool"] = "build_ar"; } entries += [params]; return entries; } // // Create a flat binary from an executable image. // function flattened_binary(params) { params["type"] = "target"; params["tool"] = "objcopy"; flags = "OBJCOPY_FLAGS"; if (get(params, "build")) { params["tool"] = "build_objcopy"; flags = "BUILD_OBJCOPY_FLAGS"; } add_config(params, flags, "-O binary"); if (get(params, "binplace")) { entries = binplace(params); } else { entries = [params]; } return entries; } // // Create a Minoca kernel driver. // function driver(params) { params["entry"] ?= "DriverEntry"; soname = get(params, "output"); soname ?= params["label"]; if (soname != "kernel") { soname += ".drv"; params["output"] = soname; params["inputs"] += ["//kernel:kernel"]; } add_config(params, "LDFLAGS", "-shared"); add_config(params, "LDFLAGS", "-Wl,-soname=" + soname); add_config(params, "LDFLAGS", "-nostdlib"); return executable(params); } // // Define a function for creating a runtime driver .FFS file from an ELF. // function uefi_runtime_ffs(name) { elfconv_config = { "ELFCONV_FLAGS": "-t efiruntimedriver" }; pe = { "type": "target", "label": name, "inputs": [":" + name + ".elf"], "implicit": ["//uefi/tools/elfconv:elfconv"], "tool": "elfconv", "config": elfconv_config }; ffs = { "type": "target", "label": name + ".ffs", "inputs": [":" + name], "implicit": ["//uefi/tools/genffs:genffs"], "tool": "genffs_runtime" }; return [pe, ffs]; } // // Define a function that creates a UEFI firmware volume object file based on // a platform name and a list of FFS inputs. // function uefi_fwvol_o(name, ffs) { fwv_name = name + "fwv"; fwv = { "type": "target", "label": fwv_name, "inputs": ffs, "implicit": ["//uefi/tools/genfv:genfv"], "tool": "genfv" }; fwv_o = { "label": fwv_name + ".o", "inputs": [":" + fwv_name], "tool": "objcopy", "config": {"OBJCOPY_FLAGS": global_config["OBJCOPY_FLAGS"]} }; return [fwv, fwv_o]; }