diff --git a/edk2-build.py b/edk2-build.py index 1e67bac..e14e608 100755 --- a/edk2-build.py +++ b/edk2-build.py @@ -1,9 +1,13 @@ #!/usr/bin/python3 +""" +build helper script for edk2, see +https://gitlab.com/kraxel/edk2-build-config + +""" import os import sys -import glob import shutil -import optparse +import argparse import subprocess import configparser @@ -11,6 +15,7 @@ rebase_prefix = "" version_override = None release_date = None +# pylint: disable=unused-variable def check_rebase(): """ detect 'git rebase -x edk2-build.py master' testbuilds """ global rebase_prefix @@ -18,16 +23,16 @@ def check_rebase(): gitdir = '.git' if os.path.isfile(gitdir): - with open(gitdir) as f: + with open(gitdir, 'r', encoding = 'utf-8') as f: (unused, gitdir) = f.read().split() if not os.path.exists(f'{gitdir}/rebase-merge/msgnum'): - return "" - with open(f'{gitdir}/rebase-merge/msgnum', 'r') as f: + return + with open(f'{gitdir}/rebase-merge/msgnum', 'r', encoding = 'utf-8') as f: msgnum = int(f.read()) - with open(f'{gitdir}/rebase-merge/end', 'r') as f: + with open(f'{gitdir}/rebase-merge/end', 'r', encoding = 'utf-8') as f: end = int(f.read()) - with open(f'{gitdir}/rebase-merge/head-name', 'r') as f: + with open(f'{gitdir}/rebase-merge/head-name', 'r', encoding = 'utf-8') as f: head = f.read().strip().split('/') rebase_prefix = f'[ {int(msgnum/2)} / {int(end/2)} - {head[-1]} ] ' @@ -38,8 +43,14 @@ def check_rebase(): def get_coredir(cfg): if cfg.has_option('global', 'core'): return os.path.abspath(cfg['global']['core']) - else: - return os.getcwd() + return os.getcwd() + +def get_toolchain(cfg, build): + if cfg.has_option(build, 'tool'): + return cfg[build]['tool'] + if cfg.has_option('global', 'tool'): + return cfg['global']['tool'] + return 'GCC5' def get_version(cfg): coredir = get_coredir(cfg) @@ -49,15 +60,18 @@ def get_version(cfg): print(f'### version [override]: {version}') return version if os.environ.get('RPM_PACKAGE_NAME'): - version = os.environ.get('RPM_PACKAGE_NAME'); - version += '-' + os.environ.get('RPM_PACKAGE_VERSION'); - version += '-' + os.environ.get('RPM_PACKAGE_RELEASE'); + version = os.environ.get('RPM_PACKAGE_NAME') + version += '-' + os.environ.get('RPM_PACKAGE_VERSION') + version += '-' + os.environ.get('RPM_PACKAGE_RELEASE') print('') print(f'### version [rpmbuild]: {version}') return version if os.path.exists(coredir + '/.git'): - cmdline = [ 'git', 'describe', '--tags', '--abbrev=8', '--match=edk2-stable*' ] - result = subprocess.run(cmdline, stdout = subprocess.PIPE, cwd = coredir) + cmdline = [ 'git', 'describe', '--tags', '--abbrev=8', + '--match=edk2-stable*' ] + result = subprocess.run(cmdline, cwd = coredir, + stdout = subprocess.PIPE, + check = True) version = result.stdout.decode().strip() print('') print(f'### version [git]: {version}') @@ -73,7 +87,7 @@ def pcd_version(cfg): return [] return [ '--pcd', pcd_string('PcdFirmwareVersionString', version) ] -def pcd_release_date(cfg): +def pcd_release_date(): if release_date is None: return [] return [ '--pcd', pcd_string('PcdFirmwareReleaseDateString', release_date) ] @@ -90,13 +104,13 @@ def build_message(line, line2 = None): print(f'### {rebase_prefix}{line}') if line2: print(f'### {line2}') - print('###') + print('###', flush = True) def build_run(cmdline, name, section, silent = False): - print(cmdline) + print(cmdline, flush = True) if silent: print('### building in silent mode ...', flush = True) - result = subprocess.run(cmdline, + result = subprocess.run(cmdline, check = False, stdout = subprocess.PIPE, stderr = subprocess.STDOUT) @@ -113,13 +127,14 @@ def build_run(cmdline, name, section, silent = False): else: print('### OK') else: - result = subprocess.run(cmdline) + result = subprocess.run(cmdline, check = False) if result.returncode: - print(f'ERROR: {cmdline[0]} exited with {result.returncode} while building {name}') + print(f'ERROR: {cmdline[0]} exited with {result.returncode}' + f' while building {name}') sys.exit(result.returncode) -def build_copy(plat, tgt, dstdir, copy): - srcdir = f'Build/{plat}/{tgt}_GCC5' +def build_copy(plat, tgt, toolchain, dstdir, copy): + srcdir = f'Build/{plat}/{tgt}_{toolchain}' names = copy.split() srcfile = names[0] if len(names) > 1: @@ -145,72 +160,73 @@ def pad_file(dstdir, pad): dstdir + '/' + name, ] print(f'# padding: {dstdir} / {name} => {size}') - subprocess.run(cmdline) + subprocess.run(cmdline, check = True) +# pylint: disable=too-many-branches def build_one(cfg, build, jobs = None, silent = False): - cmdline = [ 'build' ] - cmdline += [ '-t', 'GCC5' ] - cmdline += [ '-p', cfg[build]['conf'] ] + b = cfg[build] - if (cfg[build]['conf'].startswith('OvmfPkg/') or - cfg[build]['conf'].startswith('ArmVirtPkg/')): + cmdline = [ 'build' ] + cmdline += [ '-t', get_toolchain(cfg, build) ] + cmdline += [ '-p', b['conf'] ] + + if (b['conf'].startswith('OvmfPkg/') or + b['conf'].startswith('ArmVirtPkg/')): cmdline += pcd_version(cfg) - cmdline += pcd_release_date(cfg) + cmdline += pcd_release_date() if jobs: cmdline += [ '-n', jobs ] - for arch in cfg[build]['arch'].split(): + for arch in b['arch'].split(): cmdline += [ '-a', arch ] - if 'opts' in cfg[build]: - for name in cfg[build]['opts'].split(): + if 'opts' in b: + for name in b['opts'].split(): section = 'opts.' + name for opt in cfg[section]: cmdline += [ '-D', opt + '=' + cfg[section][opt] ] - if 'pcds' in cfg[build]: - for name in cfg[build]['pcds'].split(): + if 'pcds' in b: + for name in b['pcds'].split(): section = 'pcds.' + name for pcd in cfg[section]: cmdline += [ '--pcd', pcd + '=' + cfg[section][pcd] ] - if 'tgts' in cfg[build]: - tgts = cfg[build]['tgts'].split() + if 'tgts' in b: + tgts = b['tgts'].split() else: tgts = [ 'DEBUG' ] for tgt in tgts: desc = None - if 'desc' in cfg[build]: - desc = cfg[build]['desc'] - build_message(f'building: {cfg[build]["conf"]} ({cfg[build]["arch"]}, {tgt})', + if 'desc' in b: + desc = b['desc'] + build_message(f'building: {b["conf"]} ({b["arch"]}, {tgt})', f'description: {desc}') build_run(cmdline + [ '-b', tgt ], - cfg[build]['conf'], + b['conf'], build + '.' + tgt, silent) - if 'plat' in cfg[build]: + if 'plat' in b: # copy files - for cpy in cfg[build]: + for cpy in b: if not cpy.startswith('cpy'): continue - build_copy(cfg[build]['plat'], - tgt, - cfg[build]['dest'], - cfg[build][cpy]) + build_copy(b['plat'], tgt, + get_toolchain(cfg, build), + b['dest'], b[cpy]) # pad builds - for pad in cfg[build]: + for pad in b: if not pad.startswith('pad'): continue - pad_file(cfg[build]['dest'], - cfg[build][pad]) + pad_file(b['dest'], b[pad]) def build_basetools(silent = False): - build_message(f'building: BaseTools') + build_message('building: BaseTools') basedir = os.environ['EDK_TOOLS_PATH'] cmdline = [ 'make', '-C', basedir ] build_run(cmdline, 'BaseTools', 'build.basetools', silent) def binary_exists(name): - for dir in os.environ['PATH'].split(':'): - if os.path.exists(dir + '/' + name): + for pdir in os.environ['PATH'].split(':'): + if os.path.exists(pdir + '/' + name): return True return False @@ -232,8 +248,8 @@ def prepare_env(cfg): packages.append(coredir) # add basetools to path - for dir in dirs: - p = coredir + '/' + dir + for pdir in dirs: + p = coredir + '/' + pdir if not os.path.exists(p): continue if p in path: @@ -241,12 +257,12 @@ def prepare_env(cfg): path.insert(0, p) # run edksetup if needed - toolsdef = coredir + '/Conf/tools_def.txt'; + toolsdef = coredir + '/Conf/tools_def.txt' if not os.path.exists(toolsdef): os.makedirs(os.path.dirname(toolsdef), exist_ok = True) build_message('running BaseTools/BuildEnv') - cmdline = [ 'sh', 'BaseTools/BuildEnv' ] - subprocess.run(cmdline, cwd = coredir) + cmdline = [ 'bash', 'BaseTools/BuildEnv' ] + subprocess.run(cmdline, cwd = coredir, check = True) # set variables os.environ['PATH'] = ':'.join(path) @@ -282,44 +298,62 @@ def build_list(cfg): if 'desc' in cfg[build]: desc = cfg[build]['desc'] print(f'# {name:20s} - {desc}') - + def main(): - parser = optparse.OptionParser() - parser.add_option('-c', '--config', dest = 'configfile', - type = 'string', default = '.edk2.builds', metavar = 'FILE', - help = 'read configuration from FILE (default: .edk2.builds)') - parser.add_option('-C', '--directory', dest = 'directory', type = 'string', metavar = 'DIR', - help = 'change to DIR before building') - parser.add_option('-j', '--jobs', dest = 'jobs', type = 'string', metavar = 'JOBS' - 'allow up to JOBS parallel build jobs') - parser.add_option('-m', '--match', dest = 'match', type = 'string', metavar = 'INCLUDE', - help = 'only run builds matching INCLUDE (substring)') - parser.add_option('-x', '--exclude', dest = 'exclude', type = 'string', metavar = 'EXCLUDE', - help = 'skip builds matching EXCLUDE (substring)') - parser.add_option('-l', '--list', dest = 'list', action = 'store_true', default = False, - help = 'list build configs available') - parser.add_option('--silent', dest = 'silent', action = 'store_true', default = False, - help = 'write build output to logfiles, write to console only on errors') - parser.add_option('--core', dest = 'core', type = 'string', metavar = 'DIR', - help = 'location of the core edk2 repository (i.e. where BuildTools are located)') - parser.add_option('--pkg', '--package', dest = 'pkgs', type = 'string', action = 'append', metavar = 'DIR', - help = 'location(s) of additional packages (can be specified multiple times)') - parser.add_option('--version-override', dest = 'version_override', type = 'string', metavar = 'VERSION', - help = 'set firmware build version') - parser.add_option('--release-date', dest = 'release_date', type = 'string', metavar = 'DATE', - help = 'set firmware build release date (in MM/DD/YYYY format)') - (options, args) = parser.parse_args() + parser = argparse.ArgumentParser(prog = 'edk2-build', + description = 'edk2 build helper script') + parser.add_argument('-c', '--config', dest = 'configfile', + type = str, default = '.edk2.builds', metavar = 'FILE', + help = 'read configuration from FILE (default: .edk2.builds)') + parser.add_argument('-C', '--directory', dest = 'directory', type = str, + help = 'change to DIR before building', metavar = 'DIR') + parser.add_argument('-j', '--jobs', dest = 'jobs', type = str, + help = 'allow up to JOBS parallel build jobs', + metavar = 'JOBS') + parser.add_argument('-m', '--match', dest = 'match', type = str, + help = 'only run builds matching INCLUDE (substring)', + metavar = 'INCLUDE') + parser.add_argument('-x', '--exclude', dest = 'exclude', type = str, + help = 'skip builds matching EXCLUDE (substring)', + metavar = 'EXCLUDE') + parser.add_argument('-l', '--list', dest = 'list', + action = 'store_true', default = False, + help = 'list build configs available') + parser.add_argument('--silent', dest = 'silent', + action = 'store_true', default = False, + help = 'write build output to logfiles, ' + 'write to console only on errors') + parser.add_argument('--core', dest = 'core', type = str, metavar = 'DIR', + help = 'location of the core edk2 repository ' + '(i.e. where BuildTools are located)') + parser.add_argument('--pkg', '--package', dest = 'pkgs', + type = str, action = 'append', metavar = 'DIR', + help = 'location(s) of additional packages ' + '(can be specified multiple times)') + parser.add_argument('-t', '--toolchain', dest = 'toolchain', type = str, metavar = 'NAME', + help = 'tool chain to be used to build edk2') + parser.add_argument('--version-override', dest = 'version_override', + type = str, metavar = 'VERSION', + help = 'set firmware build version') + parser.add_argument('--release-date', dest = 'release_date', + type = str, metavar = 'DATE', + help = 'set firmware build release date (in MM/DD/YYYY format)') + options = parser.parse_args() if options.directory: os.chdir(options.directory) + if not os.path.exists(options.configfile): + print('config file "{options.configfile}" not found') + return 1 + cfg = configparser.ConfigParser() cfg.optionxform = str cfg.read(options.configfile) if options.list: build_list(cfg) - return + return 0 if not cfg.has_section('global'): cfg.add_section('global') @@ -327,6 +361,8 @@ def main(): cfg.set('global', 'core', options.core) if options.pkgs: cfg.set('global', 'pkgs', ' '.join(options.pkgs)) + if options.toolchain: + cfg.set('global', 'tool', options.toolchain) global version_override global release_date @@ -349,5 +385,7 @@ def main(): continue build_one(cfg, build, options.jobs, options.silent) + return 0 + if __name__ == '__main__': sys.exit(main())