def main(argv, out, err): # We need a UTF-8 locale, so bail out if we don't have one. More # specifically, things like the version() computation traverse the file # system and, if they hit a UTF-8 filename, they try to decode it into your # preferred encoding and trigger an exception. encoding = locale.getpreferredencoding().lower() if encoding not in ('utf-8', 'utf8'): err.write('CAmkES uses UTF-8 encoding, but your locale\'s preferred ' 'encoding is %s. You can override your locale with the LANG ' 'environment variable.\n' % encoding) return -1 options = parse_args(argv, out, err) # register object sizes with loader if options.object_sizes: register_object_sizes( yaml.load(options.object_sizes, Loader=yaml.FullLoader)) # Ensure we were supplied equal items, outfiles and templates if len(options.outfile) != len(options.item) != len(options.template): err.write( 'Different number of items and outfiles. Required one outfile location ' 'per item requested.\n') return -1 # No duplicates in outfiles if len(set(options.outfile)) != len(options.outfile): err.write('Duplicate outfiles requrested through --outfile.\n') return -1 # Save us having to pass debugging everywhere. die = functools.partial(_die, options) log.set_verbosity(options.verbosity) ast = pickle.load(options.load_ast) # Locate the assembly. assembly = ast.assembly if assembly is None: die('No assembly found') # Do some extra checks if the user asked for verbose output. if options.verbosity >= 2: # Try to catch type mismatches in attribute settings. Note that it is # not possible to conclusively evaluate type correctness because the # attributes' type system is (deliberately) too loose. That is, the # type of an attribute can be an uninterpreted C type the user will # provide post hoc. for i in assembly.composition.instances: for a in i.type.attributes: value = assembly.configuration[i.name].get(a.name) if value is not None: if a.type == 'string' and not \ isinstance(value, six.string_types): log.warning('attribute %s.%s has type string but is ' 'set to a value that is not a string' % (i.name, a.name)) elif a.type == 'int' and not \ isinstance(value, numbers.Number): log.warning('attribute %s.%s has type int but is set ' 'to a value that is not an integer' % (i.name, a.name)) try: r = Renderer(options.templates) except jinja2.exceptions.TemplateSyntaxError as e: die('template syntax error: %s' % e) if options.load_object_state is not None: render_state = pickle.load(options.load_object_state) elif options.save_object_state is None: render_state = None else: obj_space = ObjectAllocator() obj_space.spec.arch = options.architecture render_state = RenderState(obj_space=obj_space) for i in assembly.composition.instances: # Don't generate any code for hardware components. if i.type.hardware: continue key = i.address_space if key not in render_state.cspaces: cnode = render_state.obj_space.alloc( ObjectType.seL4_CapTableObject, name="%s_cnode" % key, label=key) render_state.cspaces[key] = CSpaceAllocator(cnode) pd = obj_space.alloc(lookup_architecture( options.architecture).vspace().object, name="%s_group_bin_pd" % key, label=key) addr_space = AddressSpaceAllocator( re.sub(r'[^A-Za-z0-9]', '_', "%s_group_bin" % key), pd) render_state.pds[key] = pd render_state.addr_spaces[key] = addr_space for (item, outfile, template) in zip(options.item, options.outfile, options.template): key = item.split("/") if key[0] == "component": i = [ x for x in assembly.composition.instances if x.name == key[1] ][0] obj_key = i.address_space elif key[0] == "connector": c = [ c for c in assembly.composition.connections if c.name == key[1] ][0] if key[2] == "to": i = c.to_ends[int(key[3])] elif key[2] == "from": i = c.from_ends[int(key[3])] else: die("Invalid connector end") obj_key = i.instance.address_space elif key[0] == "assembly": i = assembly obj_key = None else: die("item: \"%s\" does not have the correct formatting to render." % item) try: g = r.render(i, assembly, template, render_state, obj_key, outfile_name=outfile.name, options=options, my_pd=render_state.pds[obj_key] if obj_key else None) outfile.write(g) outfile.close() except TemplateError as inst: if hasattr(i, 'name'): die(rendering_error(i.name, inst)) else: die(rendering_error(i.parent.name, inst)) read = r.get_files_used() # Write a Makefile dependency rule if requested. if options.makefile_dependencies is not None: options.makefile_dependencies.write( '%s: \\\n %s\n' % (options.outfile[0].name, ' \\\n '.join(sorted(read)))) if options.save_object_state is not None: # Write the render_state to the supplied outfile pickle.dump(render_state, options.save_object_state) sys.exit(0)
def main(): parser = argparse.ArgumentParser( description="") parser.add_argument('--architecture', '--arch', default='aarch32', type=lambda x: type('')(x).lower(), choices=valid_architectures(), help='Target architecture.') parser.add_argument('--object-sizes', required=True, type=argparse.FileType('r')) subparsers = parser.add_subparsers() parser_a = subparsers.add_parser('build_cnode') parser_a.add_argument('--ccspace', nargs='+', type=argparse.FileType('w'), action='append') parser_a.set_defaults(which="build_cnode") parser_a.add_argument('--manifest-in', type=argparse.FileType('rb')) parser_a.add_argument('--elffile', nargs='+', action='append') parser_b = subparsers.add_parser('gen_cdl') parser_b.add_argument('--outfile', type=argparse.FileType('w')) parser_b.set_defaults(which="gen_cdl") parser_b.add_argument('--manifest-in', type=argparse.FileType('rb')) parser_b.add_argument('--elffile', nargs='+', action='append') parser_b.add_argument('--keys', nargs='+', action='append') parser_b.add_argument('--fprovide-tcb-caps', action='store_true', default=True, help='Hand out TCB caps to components, allowing them to ' 'exit cleanly.') parser_b.add_argument('--fno-provide-tcb-caps', action='store_false', dest='fprovide_tcb_caps', help='Do not hand out TCB caps, causing ' 'components to fault on exiting.') parser_b.add_argument('--save-object-state', type=argparse.FileType('wb')) parser_b.add_argument('--static-alloc', action='store_true', help='Perform static object allocation (requires --untyped)') parser_b.add_argument('--dynamic-alloc', action='store_false', dest='static_alloc', help='Cancel --static-alloc') parser_b.add_argument('--untyped', type=argparse.FileType('r'), help="YAML file with available seL4 bootinfo untypeds") args = parser.parse_args() register_object_sizes(yaml.load(args.object_sizes, Loader=yaml.FullLoader)) if args.which == "build_cnode": data = yaml.load(args.manifest_in, Loader=yaml.FullLoader) assert 'cap_symbols' in data and 'region_symbols' in data, "Invalid file format" elfs = [item for sublist in args.elffile for item in sublist] cspaces = [item for sublist in args.ccspace for item in sublist] targets = zip(elfs, cspaces) manifest(data['cap_symbols'], data['region_symbols'], args.architecture, targets) return 0 if args.which == "gen_cdl": if args.static_alloc and not args.untyped: parser.error('--static-alloc requires --untyped') allocator_state = pickle.load(args.manifest_in) elfs = [item for sublist in args.elffile for item in sublist] keys = [item for sublist in args.keys for item in sublist] targets = zip(elfs, keys) obj_space = final_spec(args, allocator_state.obj_space, allocator_state.cspaces, allocator_state.addr_spaces, targets, args.architecture) # Calculate final layout for objects and ASID slots... ASIDTableAllocator().allocate(obj_space.spec) if args.static_alloc: alloc = BestFitAllocator() for ut in yaml.load(args.untyped, Loader=yaml.FullLoader): if len(ut): is_device, paddr, size_bits = ut['device'], ut['paddr'], ut['size_bits'] alloc.add_untyped(Untyped("root_untyped_0x%x" % paddr, size_bits=size_bits, paddr=paddr), is_device) alloc.allocate(obj_space.spec) args.outfile.write(repr(obj_space.spec)) if args.save_object_state: pickle.dump(allocator_state, args.save_object_state) return 0
def main(argv, out, err): # We need a UTF-8 locale, so bail out if we don't have one. More # specifically, things like the version() computation traverse the file # system and, if they hit a UTF-8 filename, they try to decode it into your # preferred encoding and trigger an exception. encoding = locale.getpreferredencoding().lower() if encoding not in ('utf-8', 'utf8'): err.write('CAmkES uses UTF-8 encoding, but your locale\'s preferred ' 'encoding is %s. You can override your locale with the LANG ' 'environment variable.\n' % encoding) return -1 options = parse_args(argv, out, err) # register object sizes with loader if options.object_sizes: register_object_sizes( yaml.load(options.object_sizes, Loader=yaml.FullLoader)) # Ensure we were supplied equal items and outfiles if len(options.outfile) != len(options.item): err.write( 'Different number of items and outfiles. Required one outfile location ' 'per item requested.\n') return -1 # No duplicates in items or outfiles if len(set(options.item)) != len(options.item): err.write('Duplicate items requested through --item.\n') return -1 if len(set(options.outfile)) != len(options.outfile): err.write('Duplicate outfiles requrested through --outfile.\n') return -1 # Save us having to pass debugging everywhere. die = functools.partial(_die, options) log.set_verbosity(options.verbosity) ast = pickle.load(options.load_ast) # Locate the assembly. assembly = ast.assembly if assembly is None: die('No assembly found') # Do some extra checks if the user asked for verbose output. if options.verbosity >= 2: # Try to catch type mismatches in attribute settings. Note that it is # not possible to conclusively evaluate type correctness because the # attributes' type system is (deliberately) too loose. That is, the # type of an attribute can be an uninterpreted C type the user will # provide post hoc. for i in assembly.composition.instances: for a in i.type.attributes: value = assembly.configuration[i.name].get(a.name) if value is not None: if a.type == 'string' and not \ isinstance(value, six.string_types): log.warning('attribute %s.%s has type string but is ' 'set to a value that is not a string' % (i.name, a.name)) elif a.type == 'int' and not \ isinstance(value, numbers.Number): log.warning('attribute %s.%s has type int but is set ' 'to a value that is not an integer' % (i.name, a.name)) templates = Templates(options.platform) [templates.add_root(t) for t in options.templates] try: r = Renderer(templates) except jinja2.exceptions.TemplateSyntaxError as e: die('template syntax error: %s' % e) # The user may have provided their own connector definitions (with # associated) templates, in which case they won't be in the built-in lookup # dictionary. Let's add them now. Note, definitions here that conflict with # existing lookup entries will overwrite the existing entries. Note that # the extra check that the connector has some templates is just an # optimisation; the templates module handles connectors without templates # just fine. for c in (x for x in ast.items if isinstance(x, Connector) and ( x.from_template is not None or x.to_template is not None)): try: # Find a connection that uses this type. connection = next(x for x in ast if isinstance(x, Connection) and x.type == c) # Add the custom templates and update our collection of read # inputs. templates.add(c, connection) except TemplateError as e: die('while adding connector %s: %s' % (c.name, e)) except StopIteration: # No connections use this type. There's no point adding it to the # template lookup dictionary. pass if options.load_object_state is not None: render_state = pickle.load(options.load_object_state) else: obj_space = ObjectAllocator() obj_space.spec.arch = options.architecture render_state = AllocatorState(obj_space=obj_space) for i in assembly.composition.instances: # Don't generate any code for hardware components. if i.type.hardware: continue key = i.address_space if key not in render_state.cspaces: cnode = render_state.obj_space.alloc( ObjectType.seL4_CapTableObject, name="%s_cnode" % key, label=key) render_state.cspaces[key] = CSpaceAllocator(cnode) pd = obj_space.alloc(lookup_architecture( options.architecture).vspace().object, name="%s_group_bin_pd" % key, label=key) addr_space = AddressSpaceAllocator( re.sub(r'[^A-Za-z0-9]', '_', "%s_group_bin" % key), pd) render_state.pds[key] = pd render_state.addr_spaces[key] = addr_space for (item, outfile) in zip(options.item, options.outfile): key = item.split("/") if len(key) is 1: # We are rendering something that isn't a component or connection. i = assembly obj_key = None template = templates.lookup(item) elif key[1] in [ "source", "header", "c_environment_source", "cakeml_start_source", "cakeml_end_source", "camkesConstants", "linker", "debug", "simple", "rump_config" ]: # We are rendering a component template i = [ x for x in assembly.composition.instances if x.name == key[0] ][0] obj_key = i.address_space template = templates.lookup(item, i) elif key[1] in ["from", "to"]: # We are rendering a connection template c = [ c for c in assembly.composition.connections if c.name == key[0] ][0] if key[1] == "to": i = c.to_ends[int(key[-1])] elif key[1] == "from": i = c.from_ends[int(key[-1])] else: die("Invalid connector end") obj_key = i.instance.address_space template = templates.lookup("/".join(key[:-1]), c) else: die("item: \"%s\" does not have the correct formatting to lookup a template." % item) try: g = r.render(i, assembly, template, render_state, obj_key, outfile_name=outfile.name, options=options, my_pd=render_state.pds[obj_key] if obj_key else None) outfile.write(g) outfile.close() except TemplateError as inst: die(rendering_error(i.name, inst)) read = r.get_files_used() # Write a Makefile dependency rule if requested. if options.makefile_dependencies is not None: options.makefile_dependencies.write( '%s: \\\n %s\n' % (options.outfile[0].name, ' \\\n '.join(sorted(read)))) if options.save_object_state is not None: # Write the render_state to the supplied outfile pickle.dump(render_state, options.save_object_state) sys.exit(0)
def main(argv, out, err): # We need a UTF-8 locale, so bail out if we don't have one. More # specifically, things like the version() computation traverse the file # system and, if they hit a UTF-8 filename, they try to decode it into your # preferred encoding and trigger an exception. encoding = locale.getpreferredencoding().lower() if encoding not in ('utf-8', 'utf8'): err.write('CAmkES uses UTF-8 encoding, but your locale\'s preferred ' 'encoding is %s. You can override your locale with the LANG ' 'environment variable.\n' % encoding) return -1 options = parse_args(argv, out, err) # register object sizes with loader if options.object_sizes: register_object_sizes(yaml.load(options.object_sizes)) # Ensure we were supplied equal items and outfiles if len(options.outfile) != len(options.item): err.write( 'Different number of items and outfiles. Required one outfile location ' 'per item requested.\n') return -1 # No duplicates in items or outfiles if len(set(options.item)) != len(options.item): err.write('Duplicate items requested through --item.\n') return -1 if len(set(options.outfile)) != len(options.outfile): err.write('Duplicate outfiles requrested through --outfile.\n') return -1 # Save us having to pass debugging everywhere. die = functools.partial(_die, options) log.set_verbosity(options.verbosity) # Build a list of item/outfile pairs that we have yet to match and process all_items = set(zip(options.item, options.outfile)) done_items = set([]) def done(s, file, item, r, read): ret = 0 if s: file.write(s) file.close() done_items.add((item, file)) if len(all_items - done_items) == 0: read |= r.get_files_used() # Write a Makefile dependency rule if requested. if options.makefile_dependencies is not None: options.makefile_dependencies.write( '%s: \\\n %s\n' % (options.outfile[0].name, ' \\\n '.join(sorted(read)))) if options.save_object_state is not None: # Write the render_state to the supplied outfile pickle.dump(renderoptions.render_state, options.save_object_state) sys.exit(ret) ast = pickle.load(options.load_ast) read = set() # Locate the assembly. assembly = ast.assembly if assembly is None: die('No assembly found') # Do some extra checks if the user asked for verbose output. if options.verbosity >= 2: # Try to catch type mismatches in attribute settings. Note that it is # not possible to conclusively evaluate type correctness because the # attributes' type system is (deliberately) too loose. That is, the # type of an attribute can be an uninterpreted C type the user will # provide post hoc. for i in assembly.composition.instances: for a in i.type.attributes: value = assembly.configuration[i.name].get(a.name) if value is not None: if a.type == 'string' and not \ isinstance(value, six.string_types): log.warning('attribute %s.%s has type string but is ' 'set to a value that is not a string' % (i.name, a.name)) elif a.type == 'int' and not \ isinstance(value, numbers.Number): log.warning('attribute %s.%s has type int but is set ' 'to a value that is not an integer' % (i.name, a.name)) obj_space = ObjectAllocator() obj_space.spec.arch = options.architecture render_state = RenderState(obj_space=obj_space) templates = Templates(options.platform) [templates.add_root(t) for t in options.templates] try: r = Renderer(templates) except jinja2.exceptions.TemplateSyntaxError as e: die('template syntax error: %s' % e) # The user may have provided their own connector definitions (with # associated) templates, in which case they won't be in the built-in lookup # dictionary. Let's add them now. Note, definitions here that conflict with # existing lookup entries will overwrite the existing entries. Note that # the extra check that the connector has some templates is just an # optimisation; the templates module handles connectors without templates # just fine. for c in (x for x in ast.items if isinstance(x, Connector) and ( x.from_template is not None or x.to_template is not None)): try: # Find a connection that uses this type. connection = next(x for x in ast if isinstance(x, Connection) and x.type == c) # Add the custom templates and update our collection of read # inputs. templates.add(c, connection) except TemplateError as e: die('while adding connector %s: %s' % (c.name, e)) except StopIteration: # No connections use this type. There's no point adding it to the # template lookup dictionary. pass def apply_capdl_filters(renderoptions): # Derive a set of usable ELF objects from the filenames we were passed. render_state = renderoptions.render_state elfs = {} for e in options.elf: try: name = os.path.basename(e) if name in elfs: raise Exception( 'duplicate ELF files of name \'%s\' encountered' % name) elf = ELF(e, name, options.architecture) group = name.replace("_group_bin", "") # Avoid inferring a TCB as we've already created our own. elf_spec = elf.get_spec( infer_tcb=False, infer_asid=False, pd=render_state.pds[group], use_large_frames=options.largeframe, addr_space=render_state.addr_spaces[group]) render_state.obj_space.merge(elf_spec, label=group) elfs[name] = (e, elf) except Exception as inst: die('While opening \'%s\': %s' % (e, inst)) for space in render_state.cspaces.values(): for (slot, tcb) in [ (v, v.referent) for (k, v) in space.cnode.slots.items() if v is not None and isinstance(v.referent, TCB) ]: elf = elfs.get(tcb.elf) funcs = {"get_vaddr": lambda x: elf[1].get_symbol_vaddr(x)} tcb.ip = simple_eval(str(tcb.ip), functions=funcs) tcb.sp = simple_eval(str(tcb.sp), functions=funcs) tcb.addr = simple_eval(str(tcb.addr), functions=funcs) if not options.fprovide_tcb_caps: del space.cnode[slot] space.cnode.finalise_size( arch=lookup_architecture(options.architecture)) renderoptions = RenderOptions( options.verbosity, options.frpc_lock_elision, options.fspecialise_syscall_stubs, options.fprovide_tcb_caps, options.largeframe, options.largeframe_dma, options.architecture, options.debug_fault_handlers, options.default_priority, options.default_max_priority, options.default_affinity, options.default_period, options.default_budget, options.default_data, options.default_size_bits, options.default_stack_size, options.realtime, options.verification_base_name, render_state) def instantiate_misc_templates(renderoptions): for (item, outfile) in (all_items - done_items): try: template = templates.lookup(item) if template: g = r.render(assembly, assembly, template, renderoptions.render_state, None, outfile_name=outfile.name, options=renderoptions) done(g, outfile, item, r, read) except TemplateError as inst: die(rendering_error(item, inst)) if "camkes-gen.cmake" in options.item: instantiate_misc_templates(renderoptions) if options.load_object_state is not None: # There is an assumption that if load_object_state is set, we # skip all of the component and connector logic below. # FIXME: refactor to clarify control flow renderoptions.render_state = pickle.load(options.load_object_state) apply_capdl_filters(renderoptions) instantiate_misc_templates(renderoptions) # If a template wasn't instantiated, something went wrong, and we can't recover raise CAmkESError("No template instantiated on capdl generation path") # We're now ready to instantiate the template the user requested, but there # are a few wrinkles in the process. Namely, # 1. Template instantiation needs to be done in a deterministic order. The # runner is invoked multiple times and template code needs to be # allocated identical cap slots in each run. # 2. Components and connections need to be instantiated before any other # templates, regardless of whether they are the ones we are after. Some # other templates, such as the Makefile depend on the obj_space and # cspaces. # 3. All actual code templates, up to the template that was requested, # need to be instantiated. This is related to (1) in that the cap slots # allocated are dependent on what allocations have been done prior to a # given allocation call. # Instantiate the per-component source and header files. for i in assembly.composition.instances: # Don't generate any code for hardware components. if i.type.hardware: continue if i.address_space not in renderoptions.render_state.cspaces: cnode = renderoptions.render_state.obj_space.alloc( ObjectType.seL4_CapTableObject, name="%s_cnode" % i.address_space, label=i.address_space) renderoptions.render_state.cspaces[ i.address_space] = CSpaceAllocator(cnode) pd = obj_space.alloc(lookup_architecture( options.architecture).vspace().object, name="%s_group_bin_pd" % i.address_space, label=i.address_space) addr_space = AddressSpaceAllocator( re.sub(r'[^A-Za-z0-9]', '_', "%s_group_bin" % i.address_space), pd) renderoptions.render_state.pds[i.address_space] = pd renderoptions.render_state.addr_spaces[ i.address_space] = addr_space for t in ('%s/source' % i.name, '%s/header' % i.name, '%s/c_environment_source' % i.name, '%s/cakeml_start_source' % i.name, '%s/cakeml_end_source' % i.name, '%s/linker' % i.name): try: template = templates.lookup(t, i) g = '' if template: g = r.render( i, assembly, template, renderoptions.render_state, i.address_space, outfile_name=None, options=renderoptions, my_pd=renderoptions.render_state.pds[i.address_space]) for (item, outfile) in (all_items - done_items): if item == t: if not template: log.warning('Warning: no template for %s' % item) done(g, outfile, item, r, read) break except TemplateError as inst: die(rendering_error(i.name, inst)) # Instantiate the per-connection files. for c in assembly.composition.connections: for t in (('%s/from/source' % c.name, c.from_ends), ('%s/from/header' % c.name, c.from_ends), ('%s/to/source' % c.name, c.to_ends), ('%s/to/header' % c.name, c.to_ends), ('%s/to/cakeml' % c.name, c.to_ends)): template = templates.lookup(t[0], c) if template is not None: for id, e in enumerate(t[1]): item = '%s/%d' % (t[0], id) g = '' try: g = r.render(e, assembly, template, renderoptions.render_state, e.instance.address_space, outfile_name=None, options=renderoptions, my_pd=renderoptions.render_state.pds[ e.instance.address_space]) except TemplateError as inst: die(rendering_error(item, inst)) except jinja2.exceptions.TemplateNotFound: die('While rendering %s: missing template for %s' % (item, c.type.name)) for (target, outfile) in (all_items - done_items): if target == item: if not template: log.warning('Warning: no template for %s' % item) done(g, outfile, item, r, read) break # The following block handles instantiations of per-connection # templates that are neither a 'source' or a 'header', as handled # above. We assume that none of these need instantiation unless we are # actually currently looking for them (== options.item). That is, we # assume that following templates, like the CapDL spec, do not require # these templates to be rendered prior to themselves. # FIXME: This is a pretty ugly way of handling this. It would be nicer # for the runner to have a more general notion of per-'thing' templates # where the per-component templates, the per-connection template loop # above, and this loop could all be done in a single unified control # flow. for (item, outfile) in (all_items - done_items): for t in (('%s/from/' % c.name, c.from_ends), ('%s/to/' % c.name, c.to_ends)): if not item.startswith(t[0]): # This is not the item we're looking for. continue # If we've reached here then this is the exact item we're after. template = templates.lookup(item, c) if template is None: die('no registered template for %s' % item) for e in t[1]: try: g = r.render(e, assembly, template, renderoptions.render_state, e.instance.address_space, outfile_name=None, options=renderoptions, my_pd=renderoptions.render_state.pds[ e.instance.address_space]) done(g, outfile, item, r, read) except TemplateError as inst: die(rendering_error(item, inst)) # Perform any per component special generation. This needs to happen last # as these template needs to run after all other capabilities have been # allocated for i in assembly.composition.instances: # Don't generate any code for hardware components. if i.type.hardware: continue assert i.address_space in renderoptions.render_state.cspaces SPECIAL_TEMPLATES = [('debug', 'debug'), ('simple', 'simple'), ('rump_config', 'rumprun')] for special in [ bl for bl in SPECIAL_TEMPLATES if assembly.configuration[i.name].get(bl[0]) ]: for t in ('%s/%s' % (i.name, special[1]), ): try: template = templates.lookup(t, i) g = '' if template: g = r.render(i, assembly, template, renderoptions.render_state, i.address_space, outfile_name=None, options=renderoptions, my_pd=renderoptions.render_state.pds[ i.address_space]) for (item, outfile) in (all_items - done_items): if item == t: if not template: log.warning('Warning: no template for %s' % item) done(g, outfile, item, r, read) except TemplateError as inst: die(rendering_error(i.name, inst)) # Check if there are any remaining items not_done = all_items - done_items if len(not_done) > 0: for (item, outfile) in not_done: err.write('No valid element matching --item %s.\n' % item) return -1 return 0