#!/usr/bin/env python3 # # Copyright (C) 2019, VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # File: build-config # Purpose: # This script serves the same purpose as ./configure in traditional # autoconf setups. # It takes build configuration options from command line, checks them, # builds a config dictionary, augments it with some default and/or # computed values and saves it to build/build-config.json # for other build scripts to read. import argparse import re import sys import os import getpass import platform import json import defaults # argparse converts hyphens to underscores, # so for lookups in the original options hash we have to # convert them back def field_to_option(s): return re.sub(r'_', '-', s) def get_default_build_by(): return "{user}@{host}".format(user= getpass.getuser(), host=platform.node()) def get_validator(optdict, name): try: return optdict[name][2] except KeyError: return None def load_config(filename): with open(filename, 'r') as f: print(f'Loading {filename}') this_config = json.load(f) if not 'inherit_from' in this_config: print(f'No inheritance detected') return this_config inherited_config = load_config(this_config['inherit_from']) del this_config['inherit_from'] inherited_config.update(this_config) return inherited_config # Load the build flavor file build_flavor = os.getenv('VYOS_BUILD_FLAVOR') if build_flavor is None: build_flavor = defaults.DEFAULT_BUILD_FLAVOR try: build_defaults = load_config(build_flavor) except Exception as e: print("Failed to open the build flavor file {0}: {1}".format(build_flavor, e)) sys.exit(1) # Options dict format: # '$option_name_without_leading_dashes': { ('$help_string', $default_value_generator_thunk, $value_checker_thunk) } options = { 'architecture': ('Image target architecture (amd64 or i386 or armhf or arm64)', lambda: build_defaults['architecture'], lambda x: x in ['amd64', 'i386', 'armhf', 'arm64']), 'build-by': ('Builder identifier (e.g. jrandomhacker@example.net)', get_default_build_by, None), 'debian-mirror': ('Debian repository mirror for ISO build', lambda: build_defaults['debian_mirror'], None), 'debian-security-mirror': ('Debian security updates mirror', lambda: build_defaults['debian_security_mirror'], None), 'pbuilder-debian-mirror': ('Debian repository mirror for pbuilder env bootstrap', lambda: build_defaults['debian_mirror'], None), 'vyos-mirror': ('VyOS package mirror', lambda: build_defaults["vyos_mirror"], None), 'build-type': ('Build type, release or development', lambda: 'development', lambda x: x in ['release', 'development']), 'version': ('Version number (release builds only)', None, None), 'build-comment': ('Optional build comment', lambda: '', None) } # Create the option parser parser = argparse.ArgumentParser() for k, v in options.items(): help_string, default_value_thunk = v[0], v[1] if default_value_thunk is None: parser.add_argument('--' + k, type=str, help=help_string) else: parser.add_argument('--' + k, type=str, help=help_string, default=default_value_thunk()) # The debug option is a bit special since it's different type parser.add_argument('--debug', help="Enable debug output", action='store_true') # Custom APT entry and APT key options can be used multiple times parser.add_argument('--custom-apt-entry', help="Custom APT entry", action='append') parser.add_argument('--custom-apt-key', help="Custom APT key file", action='append') parser.add_argument('--custom-package', help="Custom package to install from repositories", action='append') args = vars(parser.parse_args()) # Validate options for k, v in args.items(): key = field_to_option(k) func = get_validator(options, k) if func is not None: if not func(v): print("{v} is not a valid value for --{o} option".format(o=key, v=v)) sys.exit(1) # Some fixup for mirror settings. # The idea is: if --debian-mirror is specified but --pbuilder-debian-mirror is not, # use the --debian-mirror value for both lb and pbuilder bootstrap if (args['debian_mirror'] != build_defaults["debian_mirror"]) and \ (args['pbuilder_debian_mirror'] == build_defaults["debian_mirror"]): args['pbuilder_debian_mirror'] = args['debian_mirror'] # Version can only be set for release builds, # for dev builds it hardly makes any sense if args['build_type'] == 'development': if args['version'] is not None: print("Version can only be set for release builds") print("Use --build-type=release option if you want to set version number") sys.exit(1) # Populate some defaults that are not configurable, # but that are handy to have in the options hash args['distribution'] = build_defaults["debian_distribution"] args['build_dir'] = defaults.BUILD_DIR args['pbuilder_config'] = defaults.PBUILDER_CONFIG args['vyos_branch'] = build_defaults["vyos_branch"] args['release_train'] = build_defaults["release_train"] # Add custom packages from build defaults if not args['custom_package']: args['custom_package'] = [] args['custom_package'] = args['custom_package'] + build_defaults['custom_packages'] if not args['custom_apt_entry']: args['custom_apt_entry'] = [] args['custom_apt_entry'] = args['custom_apt_entry'] + build_defaults['additional_repositories'] # Check the build environment and dependencies env_check_retval = os.system("scripts/check-build-env") if env_check_retval > 0: print("Build environment check failed, fix the issues and retry") args['kernel_version'] = build_defaults['kernel_version'] args['kernel_flavor'] = build_defaults['kernel_flavor'] args['bootloaders'] = build_defaults['bootloaders'] # Save to file os.makedirs(defaults.BUILD_DIR, exist_ok=True) print("Saving the build config to {0}".format(defaults.BUILD_CONFIG)) with open(defaults.BUILD_CONFIG, 'w') as f: json.dump(args, f, indent=4, sort_keys=True) print("\n", file=f)