def main(): parser = argparse.ArgumentParser(description="ESP-IDF app builder") parser.add_argument( "-v", "--verbose", action="count", help= "Increase the logging level of the script. Can be specified multiple times.", ) parser.add_argument( "--log-file", type=argparse.FileType("w"), help="Write the script log to the specified file, instead of stderr", ) parser.add_argument( "build_list", type=argparse.FileType("r"), nargs="?", default=sys.stdin, help= "Name of the file to read the list of builds from. If not specified, read from stdin.", ) args = parser.parse_args() setup_logging(args) build_items = [BuildItem.from_json(line) for line in args.build_list] if not build_items: logging.error("Empty build list!") raise SystemExit(1) found_warnings = 0 for build_item in build_items: if not build_item.build_log_path: logging.debug("No log file for {}".format(build_item.work_dir)) continue with open(build_item.build_log_path, "r") as log_file: for line_no, line in enumerate(log_file): if line_has_warnings(line): logging.error("Issue in app {}, config {}:".format( build_item.app_dir, build_item.config_name)) logging.error(line.rstrip("\n")) logging.error("See {}:{} for details".format( os.path.basename(build_item.build_log_path), line_no + 1)) found_warnings += 1 break if found_warnings: logging.error("Checked {} builds, found {} warnings".format( len(build_items), found_warnings)) raise SystemExit(1) logging.info("No warnings found")
def main(): # type: () -> None parser = argparse.ArgumentParser(description='ESP-IDF app builder') parser.add_argument( '-v', '--verbose', action='count', help= 'Increase the logging level of the script. Can be specified multiple times.', ) parser.add_argument( '--log-file', type=argparse.FileType('w'), help='Write the script log to the specified file, instead of stderr', ) parser.add_argument( 'build_list', type=argparse.FileType('r'), nargs='?', default=sys.stdin, help= 'Name of the file to read the list of builds from. If not specified, read from stdin.', ) args = parser.parse_args() setup_logging(args) build_items = [BuildItem.from_json(line) for line in args.build_list] if not build_items: logging.warning('Empty build list') SystemExit(0) found_warnings = 0 for build_item in build_items: if not build_item.build_log_path: logging.debug('No log file for {}'.format(build_item.work_dir)) continue with open(build_item.build_log_path, 'r') as log_file: for line_no, line in enumerate(log_file): if line_has_warnings(line): logging.error('Issue in app {}, config {}:'.format( build_item.app_dir, build_item.config_name)) logging.error(line.rstrip('\n')) logging.error('See {}:{} for details'.format( os.path.basename(build_item.build_log_path), line_no + 1)) found_warnings += 1 break if found_warnings: logging.error('Checked {} builds, found {} warnings'.format( len(build_items), found_warnings)) raise SystemExit(1) logging.info('No warnings found')
def find_builds_for_app(app_path, work_dir, build_dir, build_log, target_arg, build_system, config_rules, preserve_artifacts=True): # type: (str, str, str, str, str, str, typing.List[ConfigRule], bool) -> typing.List[BuildItem] """ Find configurations (sdkconfig file fragments) for the given app, return them as BuildItem objects :param app_path: app directory (can be / usually will be a relative path) :param work_dir: directory where the app should be copied before building. May contain env. variables and placeholders. :param build_dir: directory where the build will be done, relative to the work_dir. May contain placeholders. :param build_log: path of the build log. May contain placeholders. May be None, in which case the log should go into stdout/stderr. :param target_arg: the value of IDF_TARGET passed to the script. Used to filter out configurations with a different CONFIG_IDF_TARGET value. :param build_system: name of the build system, index into BUILD_SYSTEMS dictionary :param config_rules: mapping of sdkconfig file name patterns to configuration names :param preserve_artifacts: determine if the built binary will be uploaded as artifacts. :return: list of BuildItems representing build configuration of the app """ build_items = [] # type: typing.List[BuildItem] default_config_name = "" for rule in config_rules: if not rule.file_name: default_config_name = rule.config_name continue sdkconfig_paths = glob.glob(os.path.join(app_path, rule.file_name)) sdkconfig_paths = sorted(sdkconfig_paths) for sdkconfig_path in sdkconfig_paths: # Check if the sdkconfig file specifies IDF_TARGET, and if it is matches the --target argument. sdkconfig_dict = dict_from_sdkconfig(sdkconfig_path) target_from_config = sdkconfig_dict.get("CONFIG_IDF_TARGET") if target_from_config is not None and target_from_config != target_arg: logging.debug( "Skipping sdkconfig {} which requires target {}".format( sdkconfig_path, target_from_config)) continue # Figure out the config name config_name = rule.config_name or "" if "*" in rule.file_name: # convert glob pattern into a regex regex_str = r".*" + rule.file_name.replace(".", r"\.").replace( "*", r"(.*)") groups = re.match(regex_str, sdkconfig_path) assert groups config_name = groups.group(1) sdkconfig_path = os.path.relpath(sdkconfig_path, app_path) logging.debug( 'Adding build: app {}, sdkconfig {}, config name "{}"'.format( app_path, sdkconfig_path, config_name)) build_items.append( BuildItem( app_path, work_dir, build_dir, build_log, target_arg, sdkconfig_path, config_name, build_system, preserve_artifacts, )) if not build_items: logging.debug( 'Adding build: app {}, default sdkconfig, config name "{}"'.format( app_path, default_config_name)) return [ BuildItem( app_path, work_dir, build_dir, build_log, target_arg, None, default_config_name, build_system, preserve_artifacts, ) ] return build_items
action='store_true', help="Don't actually build, only print the build commands", ) parser.add_argument( '--keep-going', action='store_true', help="Don't exit immediately when a build fails.", ) parser.add_argument( '--output-build-list', type=argparse.FileType('w'), help='If specified, the list of builds (with all the placeholders expanded) will be written to this file.', ) parser.add_argument( '--size-info', type=argparse.FileType('a'), help='If specified, the test case name and size info json will be written to this file' ) parser.add_argument( 'build_list', type=argparse.FileType('r'), nargs='?', default=sys.stdin, help='Name of the file to read the list of builds from. If not specified, read from stdin.', ) args = parser.parse_args() setup_logging(args) items = [BuildItem.from_json(line) for line in args.build_list] build_apps(items, args.parallel_count, args.parallel_index, args.dry_run, args.build_verbose, args.keep_going, args.output_build_list, args.size_info)
def main(): parser = argparse.ArgumentParser(description="ESP-IDF app builder") parser.add_argument( "-v", "--verbose", action="count", help= "Increase the logging level of the script. Can be specified multiple times.", ) parser.add_argument( "--build-verbose", action="store_true", help="Enable verbose output from build system.", ) parser.add_argument( "--log-file", type=argparse.FileType("w"), help="Write the script log to the specified file, instead of stderr", ) parser.add_argument( "--parallel-count", default=1, type=int, help= "Number of parallel build jobs. Note that this script doesn't start the jobs, " + "it needs to be executed multiple times with same value of --parallel-count and " + "different values of --parallel-index.", ) parser.add_argument( "--parallel-index", default=1, type=int, help= "Index (1-based) of the job, out of the number specified by --parallel-count.", ) parser.add_argument( "--format", default="json", choices=["json"], help="Format to read the list of builds", ) parser.add_argument( "--dry-run", action="store_true", help="Don't actually build, only print the build commands", ) parser.add_argument( "--keep-going", action="store_true", help="Don't exit immediately when a build fails.", ) parser.add_argument( "--output-build-list", type=argparse.FileType("w"), help= "If specified, the list of builds (with all the placeholders expanded) will be written to this file.", ) parser.add_argument( "build_list", type=argparse.FileType("r"), nargs="?", default=sys.stdin, help= "Name of the file to read the list of builds from. If not specified, read from stdin.", ) args = parser.parse_args() setup_logging(args) build_items = [BuildItem.from_json(line) for line in args.build_list] if not build_items: logging.error("Empty build list!") raise SystemExit(1) num_builds = len(build_items) num_jobs = args.parallel_count job_index = args.parallel_index - 1 # convert to 0-based index num_builds_per_job = (num_builds + num_jobs - 1) // num_jobs min_job_index = num_builds_per_job * job_index if min_job_index >= num_builds: logging.warn( "Nothing to do for job {} (build total: {}, per job: {})".format( job_index + 1, num_builds, num_builds_per_job)) raise SystemExit(0) max_job_index = min(num_builds_per_job * (job_index + 1) - 1, num_builds - 1) logging.info( "Total {} builds, max. {} builds per job, running builds {}-{}".format( num_builds, num_builds_per_job, min_job_index + 1, max_job_index + 1)) builds_for_current_job = build_items[min_job_index:max_job_index + 1] for i, build_info in enumerate(builds_for_current_job): index = i + min_job_index + 1 build_info.index = index build_info.dry_run = args.dry_run build_info.verbose = args.build_verbose build_info.keep_going = args.keep_going logging.debug(" Build {}: {}".format(index, repr(build_info))) if args.output_build_list: args.output_build_list.write(build_info.to_json_expanded() + "\n") failed_builds = [] for build_info in builds_for_current_job: logging.info("Running build {}: {}".format(build_info.index, repr(build_info))) build_system_class = BUILD_SYSTEMS[build_info.build_system] try: build_system_class.build(build_info) except BuildError as e: logging.error(e.message) if args.keep_going: failed_builds.append(build_info) else: raise SystemExit(1) if failed_builds: logging.error("The following build have failed:") for build in failed_builds: logging.error(" {}".format(build)) raise SystemExit(1)
def main(): parser = argparse.ArgumentParser(description='ESP-IDF app builder') parser.add_argument( '-v', '--verbose', action='count', help= 'Increase the logging level of the script. Can be specified multiple times.', ) parser.add_argument( '--build-verbose', action='store_true', help='Enable verbose output from build system.', ) parser.add_argument( '--log-file', type=argparse.FileType('w'), help='Write the script log to the specified file, instead of stderr', ) parser.add_argument( '--parallel-count', default=1, type=int, help= "Number of parallel build jobs. Note that this script doesn't start the jobs, " + 'it needs to be executed multiple times with same value of --parallel-count and ' + 'different values of --parallel-index.', ) parser.add_argument( '--parallel-index', default=1, type=int, help= 'Index (1-based) of the job, out of the number specified by --parallel-count.', ) parser.add_argument( '--format', default='json', choices=['json'], help='Format to read the list of builds', ) parser.add_argument( '--dry-run', action='store_true', help="Don't actually build, only print the build commands", ) parser.add_argument( '--keep-going', action='store_true', help="Don't exit immediately when a build fails.", ) parser.add_argument( '--output-build-list', type=argparse.FileType('w'), help= 'If specified, the list of builds (with all the placeholders expanded) will be written to this file.', ) parser.add_argument( '--size-info', type=argparse.FileType('a'), help= 'If specified, the test case name and size info json will be written to this file' ) parser.add_argument( 'build_list', type=argparse.FileType('r'), nargs='?', default=sys.stdin, help= 'Name of the file to read the list of builds from. If not specified, read from stdin.', ) args = parser.parse_args() setup_logging(args) build_items = [BuildItem.from_json(line) for line in args.build_list] if not build_items: logging.warning('Empty build list') SystemExit(0) num_builds = len(build_items) num_jobs = args.parallel_count job_index = args.parallel_index - 1 # convert to 0-based index num_builds_per_job = (num_builds + num_jobs - 1) // num_jobs min_job_index = num_builds_per_job * job_index if min_job_index >= num_builds: logging.warn( 'Nothing to do for job {} (build total: {}, per job: {})'.format( job_index + 1, num_builds, num_builds_per_job)) raise SystemExit(0) max_job_index = min(num_builds_per_job * (job_index + 1) - 1, num_builds - 1) logging.info( 'Total {} builds, max. {} builds per job, running builds {}-{}'.format( num_builds, num_builds_per_job, min_job_index + 1, max_job_index + 1)) builds_for_current_job = build_items[min_job_index:max_job_index + 1] for i, build_info in enumerate(builds_for_current_job): index = i + min_job_index + 1 build_info.index = index build_info.dry_run = args.dry_run build_info.verbose = args.build_verbose build_info.keep_going = args.keep_going logging.debug(' Build {}: {}'.format(index, repr(build_info))) if args.output_build_list: args.output_build_list.write(build_info.to_json_expanded() + '\n') failed_builds = [] for build_info in builds_for_current_job: logging.info('Running build {}: {}'.format(build_info.index, repr(build_info))) build_system_class = BUILD_SYSTEMS[build_info.build_system] try: build_system_class.build(build_info) except BuildError as e: logging.error(str(e)) if args.keep_going: failed_builds.append(build_info) else: raise SystemExit(1) else: if args.size_info: build_info.write_size_info(args.size_info) if not build_info.preserve: logging.info('Removing build directory {}'.format( build_info.build_path)) # we only remove binaries here, log files are still needed by check_build_warnings.py rmdir(build_info.build_path, exclude_file_pattern=SIZE_JSON_FN) if failed_builds: logging.error('The following build have failed:') for build in failed_builds: logging.error(' {}'.format(build)) raise SystemExit(1)