Ejemplo n.º 1
0
Archivo: bsp.py Proyecto: lsandov1/poky
    def test_machine_world(self):
        '''
        "bitbake world" is expected to work regardless which machine is selected.
        BSP layers sometimes break that by enabling a recipe for a certain machine
        without checking whether that recipe actually can be built in the current
        distro configuration (for example, OpenGL might not enabled).

        This test iterates over all machines. It would be nicer to instantiate
        it once per machine. It merely checks for errors during parse
        time. It does not actually attempt to build anything.
        '''

        if not self.td['machines']:
            self.skipTest('No machines set with --machines.')
        msg = []
        for machine in self.td['machines']:
            # In contrast to test_machine_signatures() below, errors are fatal here.
            try:
                get_signatures(self.td['builddir'],
                               failsafe=False,
                               machine=machine)
            except RuntimeError as ex:
                msg.append(str(ex))
        if msg:
            msg.insert(0, 'The following machines broke a world build:')
            self.fail('\n'.join(msg))
Ejemplo n.º 2
0
 def test_world(self):
     '''
     "bitbake world" is expected to work. test_signatures does not cover that
     because it is more lenient and ignores recipes in a world build that
     are not actually buildable, so here we fail when "bitbake -S none world"
     fails.
     '''
     get_signatures(self.td['builddir'], failsafe=False)
Ejemplo n.º 3
0
 def test_world(self):
     '''
     "bitbake world" is expected to work. test_signatures does not cover that
     because it is more lenient and ignores recipes in a world build that
     are not actually buildable, so here we fail when "bitbake -S none world"
     fails.
     '''
     get_signatures(self.td['builddir'], failsafe=False)
Ejemplo n.º 4
0
    def test_signatures(self):
        if self.tc.layer['type'] == LayerType.SOFTWARE and \
           not self.tc.test_software_layer_signatures:
            raise unittest.SkipTest("Not testing for signature changes in a software layer %s." \
                     % self.tc.layer['name'])

        curr_sigs, _ = get_signatures(self.td['builddir'], failsafe=True)
        msg = compare_signatures(self.td['sigs'], curr_sigs)
        if msg is not None:
            self.fail('Adding layer %s changed signatures.\n%s' % (self.tc.layer['name'], msg))
Ejemplo n.º 5
0
    def test_refkit_conf_signature(self):
        """Ensure that including the refkit config does not change the signature of other layers."""
        old_path = sys.path
        try:
            sys.path = [self.yocto_compat_lib_path] + sys.path
            import compatlayer

            self.add_refkit_layers()

            # Ignore world build errors, some of the non-refkit layers might be broken.
            old_sigs, _ = compatlayer.get_signatures(self.poky_dir, failsafe=True)
            # Now add refkit-conf.inc, without changing the DISTRO_FEATURES.
            self.append_config('require conf/distro/include/refkit-config.inc')
            curr_sigs, _ = compatlayer.get_signatures(self.poky_dir, failsafe=True)
            msg = compatlayer.compare_signatures(old_sigs, curr_sigs)
            if msg is not None:
                self.fail('Including refkit-config.inc changed signatures.\n%s' % msg)
        finally:
            sys.path = old_path
Ejemplo n.º 6
0
    def test_signatures(self):
        if self.tc.layer['type'] == LayerType.SOFTWARE and \
           not self.tc.test_software_layer_signatures:
            raise unittest.SkipTest("Not testing for signature changes in a software layer %s." \
                     % self.tc.layer['name'])

        curr_sigs, _ = get_signatures(self.td['builddir'], failsafe=True)
        msg = compare_signatures(self.td['sigs'], curr_sigs)
        if msg is not None:
            self.fail('Adding layer %s changed signatures.\n%s' %
                      (self.tc.layer['name'], msg))
Ejemplo n.º 7
0
    def test_signatures(self):
        if self.tc.layer['type'] == LayerType.SOFTWARE:
            raise unittest.SkipTest("Layer %s isn't BSP or DISTRO one." \
                     % self.tc.layer['name'])

        sig_diff = {}

        curr_sigs = get_signatures(self.td['builddir'], failsafe=True)
        for task in self.td['sigs']:
            if task not in curr_sigs:
                continue

            if self.td['sigs'][task] != curr_sigs[task]:
                sig_diff[task] = '%s -> %s' % \
                        (self.td['sigs'][task], curr_sigs[task])

        detail = ''
        if sig_diff:
            for task in sig_diff:
                detail += "%s changed %s\n" % (task, sig_diff[task])
        self.assertFalse(bool(sig_diff), "Layer %s changed signatures.\n%s" % \
                (self.tc.layer['name'], detail))
Ejemplo n.º 8
0
Archivo: bsp.py Proyecto: lsandov1/poky
    def test_machine_signatures(self):
        '''
        Selecting a machine may only affect the signature of tasks that are specific
        to that machine. In other words, when MACHINE=A and MACHINE=B share a recipe
        foo and the output of foo, then both machine configurations must build foo
        in exactly the same way. Otherwise it is not possible to use both machines
        in the same distribution.

        This criteria can only be tested by testing different machines in combination,
        i.e. one main layer, potentially several additional BSP layers and an explicit
        choice of machines:
        yocto-compat-layer --additional-layers .../meta-intel --machines intel-corei7-64 imx6slevk -- .../meta-freescale
        '''

        if not self.td['machines']:
            self.skipTest('No machines set with --machines.')

        # Collect signatures for all machines that we are testing
        # and merge that into a hash:
        # tune -> task -> signature -> list of machines with that combination
        #
        # It is an error if any tune/task pair has more than one signature,
        # because that implies that the machines that caused those different
        # signatures do not agree on how to execute the task.
        tunes = {}
        # Preserve ordering of machines as chosen by the user.
        for machine in self.td['machines']:
            curr_sigs, tune2tasks = get_signatures(self.td['builddir'],
                                                   failsafe=True,
                                                   machine=machine)
            # Invert the tune -> [tasks] mapping.
            tasks2tune = {}
            for tune, tasks in tune2tasks.items():
                for task in tasks:
                    tasks2tune[task] = tune
            for task, sighash in curr_sigs.items():
                tunes.setdefault(tasks2tune[task],
                                 {}).setdefault(task, {}).setdefault(
                                     sighash, []).append(machine)

        msg = []
        pruned = 0
        last_line_key = None
        # do_fetch, do_unpack, ..., do_build
        taskname_list = []
        if tunes:
            # The output below is most useful when we start with tasks that are at
            # the bottom of the dependency chain, i.e. those that run first. If
            # those tasks differ, the rest also does.
            #
            # To get an ordering of tasks, we do a topological sort of the entire
            # depgraph for the base configuration, then on-the-fly flatten that list by stripping
            # out the recipe names and removing duplicates. The base configuration
            # is not necessarily representative, but should be close enough. Tasks
            # that were not encountered get a default priority.
            depgraph = get_depgraph()
            depends = depgraph['tdepends']
            WHITE = 1
            GRAY = 2
            BLACK = 3
            color = {}
            found = set()

            def visit(task):
                color[task] = GRAY
                for dep in depends.get(task, ()):
                    if color.setdefault(dep, WHITE) == WHITE:
                        visit(dep)
                color[task] = BLACK
                pn, taskname = task.rsplit('.', 1)
                if taskname not in found:
                    taskname_list.append(taskname)
                    found.add(taskname)

            for task in depends.keys():
                if color.setdefault(task, WHITE) == WHITE:
                    visit(task)

        taskname_order = dict([(task, index)
                               for index, task in enumerate(taskname_list)])

        def task_key(task):
            pn, taskname = task.rsplit(':', 1)
            return (pn, taskname_order.get(taskname,
                                           len(taskname_list)), taskname)

        for tune in sorted(tunes.keys()):
            tasks = tunes[tune]
            # As for test_signatures it would be nicer to sort tasks
            # by dependencies here, but that is harder because we have
            # to report on tasks from different machines, which might
            # have different dependencies. We resort to pruning the
            # output by reporting only one task per recipe if the set
            # of machines matches.
            #
            # "bitbake-diffsigs -t -s" is intelligent enough to print
            # diffs recursively, so often it does not matter that much
            # if we don't pick the underlying difference
            # here. However, sometimes recursion fails
            # (https://bugzilla.yoctoproject.org/show_bug.cgi?id=6428).
            #
            # To mitigate that a bit, we use a hard-coded ordering of
            # tasks that represents how they normally run and prefer
            # to print the ones that run first.
            for task in sorted(tasks.keys(), key=task_key):
                signatures = tasks[task]
                # do_build can be ignored: it is know to have
                # different signatures in some cases, for example in
                # the allarch ca-certificates due to RDEPENDS=openssl.
                # That particular dependency is whitelisted via
                # SIGGEN_EXCLUDE_SAFE_RECIPE_DEPS, but still shows up
                # in the sstate signature hash because filtering it
                # out would be hard and running do_build multiple
                # times doesn't really matter.
                if len(signatures.keys()) > 1 and \
                   not task.endswith(':do_build'):
                    # Error!
                    #
                    # Sort signatures by machines, because the hex values don't mean anything.
                    # => all-arch adwaita-icon-theme:do_build: 1234... (beaglebone, qemux86) != abcdf... (qemux86-64)
                    #
                    # Skip the line if it is covered already by the predecessor (same pn, same sets of machines).
                    pn, taskname = task.rsplit(':', 1)
                    next_line_key = (pn, sorted(signatures.values()))
                    if next_line_key != last_line_key:
                        line = '   %s %s: ' % (tune, task)
                        line += ' != '.join([
                            '%s (%s)' % (signature, ', '.join(
                                [m for m in signatures[signature]]))
                            for signature in sorted(
                                signatures.keys(), key=lambda s: signatures[s])
                        ])
                        last_line_key = next_line_key
                        msg.append(line)
                        # Randomly pick two mismatched signatures and remember how to invoke
                        # bitbake-diffsigs for them.
                        iterator = iter(signatures.items())
                        a = next(iterator)
                        b = next(iterator)
                        diffsig_machines = '(%s) != (%s)' % (', '.join(
                            a[1]), ', '.join(b[1]))
                        diffsig_params = '-t %s %s -s %s %s' % (pn, taskname,
                                                                a[0], b[0])
                    else:
                        pruned += 1

        if msg:
            msg.insert(
                0,
                'The machines have conflicting signatures for some shared tasks:'
            )
            if pruned > 0:
                msg.append('')
                msg.append(
                    '%d tasks where not listed because some other task of the recipe already differed.'
                    % pruned)
                msg.append(
                    'It is likely that differences from different recipes also have the same root cause.'
                )
            msg.append('')
            # Explain how to investigate...
            msg.append(
                'To investigate, run bitbake-diffsigs -t recipename taskname -s fromsig tosig.'
            )
            cmd = 'bitbake-diffsigs %s' % diffsig_params
            msg.append('Example: %s in the last line' % diffsig_machines)
            msg.append('Command: %s' % cmd)
            # ... and actually do it automatically for that example, but without aborting
            # when that fails.
            try:
                output = check_command('Comparing signatures failed.',
                                       cmd).decode('utf-8')
            except RuntimeError as ex:
                output = str(ex)
            msg.extend(['   ' + line for line in output.splitlines()])
            self.fail('\n'.join(msg))
Ejemplo n.º 9
0
def main():
    parser = argparse.ArgumentParser(
        description="Yocto Project compatibility layer tool", add_help=False)
    parser.add_argument('layers',
                        metavar='LAYER_DIR',
                        nargs='+',
                        help='Layer to test compatibility with Yocto Project')
    parser.add_argument('-o',
                        '--output-log',
                        help='File to output log (optional)',
                        action='store')
    parser.add_argument('-n',
                        '--no-auto',
                        help='Disable auto layer discovery',
                        action='store_true')
    parser.add_argument('-d',
                        '--debug',
                        help='Enable debug output',
                        action='store_true')
    parser.add_argument('-q',
                        '--quiet',
                        help='Print only errors',
                        action='store_true')

    parser.add_argument('-h',
                        '--help',
                        action='help',
                        default=argparse.SUPPRESS,
                        help='show this help message and exit')

    args = parser.parse_args()

    if args.output_log:
        fh = logging.FileHandler(args.output_log)
        fh.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
        logger.addHandler(fh)
    if args.debug:
        logger.setLevel(logging.DEBUG)
    elif args.quiet:
        logger.setLevel(logging.ERROR)

    if not 'BUILDDIR' in os.environ:
        logger.error("You must source the environment before run this script.")
        logger.error("$ source oe-init-build-env")
        return 1
    builddir = os.environ['BUILDDIR']
    bblayersconf = os.path.join(builddir, 'conf', 'bblayers.conf')

    layers = detect_layers(args.layers, args.no_auto)
    if not layers:
        logger.error("Fail to detect layers")
        return 1

    logger.info("Detected layers:")
    for layer in layers:
        if layer['type'] == LayerType.ERROR_BSP_DISTRO:
            logger.error("%s: Can't be DISTRO and BSP type at the same time."\
                     " The conf/distro and conf/machine folders was found."\
                     % layer['name'])
            layers.remove(layer)
        elif layer['type'] == LayerType.ERROR_NO_LAYER_CONF:
            logger.error("%s: Don't have conf/layer.conf file."\
                     % layer['name'])
            layers.remove(layer)
        else:
            logger.info("%s: %s, %s" %
                        (layer['name'], layer['type'], layer['path']))
    if not layers:
        return 1

    shutil.copyfile(bblayersconf, bblayersconf + '.backup')

    def cleanup_bblayers(signum, frame):
        shutil.copyfile(bblayersconf + '.backup', bblayersconf)
        os.unlink(bblayersconf + '.backup')

    signal.signal(signal.SIGTERM, cleanup_bblayers)
    signal.signal(signal.SIGINT, cleanup_bblayers)

    td = {}
    results = collections.OrderedDict()

    logger.info('')
    logger.info('Getting initial bitbake variables ...')
    td['bbvars'] = get_bb_vars()
    logger.info('Getting initial signatures ...')
    td['builddir'] = builddir
    td['sigs'] = get_signatures(td['builddir'])
    logger.info('')

    layers_tested = 0
    for layer in layers:
        if layer['type'] == LayerType.ERROR_NO_LAYER_CONF or \
                layer['type'] == LayerType.ERROR_BSP_DISTRO:
            continue

        shutil.copyfile(bblayersconf + '.backup', bblayersconf)

        if not add_layer(bblayersconf, layer, layers, logger):
            continue

        result = test_layer_compatibility(td, layer)
        results[layer['name']] = result
        layers_tested = layers_tested + 1

    if layers_tested:
        logger.info('')
        logger.info('Summary of results:')
        logger.info('')
        for layer_name in results:
            logger.info('%s ... %s' % (layer_name, 'PASS' if \
                    results[layer_name].wasSuccessful() else 'FAIL'))

    cleanup_bblayers(None, None)

    return 0
Ejemplo n.º 10
0
def main():
    parser = argparse.ArgumentParser(
        description="Yocto Project compatibility layer tool", add_help=False)
    parser.add_argument('layers',
                        metavar='LAYER_DIR',
                        nargs='+',
                        help='Layer to test compatibility with Yocto Project')
    parser.add_argument('-o',
                        '--output-log',
                        help='File to output log (optional)',
                        action='store')
    parser.add_argument('--dependency',
                        nargs="+",
                        help='Layers to process for dependencies',
                        action='store')
    parser.add_argument('--machines',
                        nargs="+",
                        help='List of MACHINEs to be used during testing',
                        action='store')
    parser.add_argument('--additional-layers',
                        nargs="+",
                        help='List of additional layers to add during testing',
                        action='store')
    parser.add_argument('-n',
                        '--no-auto',
                        help='Disable auto layer discovery',
                        action='store_true')
    parser.add_argument('-d',
                        '--debug',
                        help='Enable debug output',
                        action='store_true')
    parser.add_argument('-q',
                        '--quiet',
                        help='Print only errors',
                        action='store_true')

    parser.add_argument('-h',
                        '--help',
                        action='help',
                        default=argparse.SUPPRESS,
                        help='show this help message and exit')

    args = parser.parse_args()

    if args.output_log:
        fh = logging.FileHandler(args.output_log)
        fh.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
        logger.addHandler(fh)
    if args.debug:
        logger.setLevel(logging.DEBUG)
    elif args.quiet:
        logger.setLevel(logging.ERROR)

    if not 'BUILDDIR' in os.environ:
        logger.error("You must source the environment before run this script.")
        logger.error("$ source oe-init-build-env")
        return 1
    builddir = os.environ['BUILDDIR']
    bblayersconf = os.path.join(builddir, 'conf', 'bblayers.conf')

    layers = detect_layers(args.layers, args.no_auto)
    if not layers:
        logger.error("Fail to detect layers")
        return 1
    if args.additional_layers:
        additional_layers = detect_layers(args.additional_layers, args.no_auto)
    else:
        additional_layers = []
    if args.dependency:
        dep_layers = detect_layers(args.dependency, args.no_auto)
        dep_layers = dep_layers + layers
    else:
        dep_layers = layers

    logger.info("Detected layers:")
    for layer in layers:
        if layer['type'] == LayerType.ERROR_BSP_DISTRO:
            logger.error("%s: Can't be DISTRO and BSP type at the same time."\
                     " The conf/distro and conf/machine folders was found."\
                     % layer['name'])
            layers.remove(layer)
        elif layer['type'] == LayerType.ERROR_NO_LAYER_CONF:
            logger.error("%s: Don't have conf/layer.conf file."\
                     % layer['name'])
            layers.remove(layer)
        else:
            logger.info("%s: %s, %s" %
                        (layer['name'], layer['type'], layer['path']))
    if not layers:
        return 1

    shutil.copyfile(bblayersconf, bblayersconf + '.backup')

    def cleanup_bblayers(signum, frame):
        shutil.copyfile(bblayersconf + '.backup', bblayersconf)
        os.unlink(bblayersconf + '.backup')

    signal.signal(signal.SIGTERM, cleanup_bblayers)
    signal.signal(signal.SIGINT, cleanup_bblayers)

    td = {}
    results = collections.OrderedDict()
    results_status = collections.OrderedDict()

    layers_tested = 0
    for layer in layers:
        if layer['type'] == LayerType.ERROR_NO_LAYER_CONF or \
                layer['type'] == LayerType.ERROR_BSP_DISTRO:
            continue

        logger.info('')
        logger.info("Setting up for %s(%s), %s" %
                    (layer['name'], layer['type'], layer['path']))

        shutil.copyfile(bblayersconf + '.backup', bblayersconf)

        missing_dependencies = not add_layer_dependencies(
            bblayersconf, layer, dep_layers, logger)
        if not missing_dependencies:
            for additional_layer in additional_layers:
                if not add_layer_dependencies(bblayersconf, additional_layer,
                                              dep_layers, logger):
                    missing_dependencies = True
                    break
        if not add_layer_dependencies(bblayersconf, layer, dep_layers, logger) or \
           any(map(lambda additional_layer: not add_layer_dependencies(bblayersconf, additional_layer, dep_layers, logger),
                   additional_layers)):
            logger.info('Skipping %s due to missing dependencies.' %
                        layer['name'])
            results[layer['name']] = None
            results_status[layer['name']] = 'SKIPPED (Missing dependencies)'
            layers_tested = layers_tested + 1
            continue

        if any(
                map(
                    lambda additional_layer: not add_layer(
                        bblayersconf, additional_layer, dep_layers, logger),
                    additional_layers)):
            logger.info('Skipping %s due to missing additional layers.' %
                        layer['name'])
            results[layer['name']] = None
            results_status[
                layer['name']] = 'SKIPPED (Missing additional layers)'
            layers_tested = layers_tested + 1
            continue

        logger.info('Getting initial bitbake variables ...')
        td['bbvars'] = get_bb_vars()
        logger.info('Getting initial signatures ...')
        td['builddir'] = builddir
        td['sigs'], td['tunetasks'] = get_signatures(td['builddir'])
        td['machines'] = args.machines

        if not add_layer(bblayersconf, layer, dep_layers, logger):
            logger.info('Skipping %s ???.' % layer['name'])
            results[layer['name']] = None
            results_status[layer['name']] = 'SKIPPED (Unknown)'
            layers_tested = layers_tested + 1
            continue

        result = test_layer_compatibility(td, layer)
        results[layer['name']] = result
        results_status[layer['name']] = 'PASS' if results[
            layer['name']].wasSuccessful() else 'FAIL'
        layers_tested = layers_tested + 1

    if layers_tested:
        logger.info('')
        logger.info('Summary of results:')
        logger.info('')
        for layer_name in results_status:
            logger.info('%s ... %s' % (layer_name, results_status[layer_name]))

    cleanup_bblayers(None, None)

    return 0
Ejemplo n.º 11
0
 def test_signatures(self):
     curr_sigs, _ = get_signatures(self.td['builddir'], failsafe=True)
     msg = compare_signatures(self.td['sigs'], curr_sigs)
     if msg is not None:
         self.fail('Adding layer %s changed signatures.\n%s' %
                   (self.tc.layer['name'], msg))
Ejemplo n.º 12
0
    def test_signatures(self):
        if self.tc.layer['type'] == LayerType.SOFTWARE:
            raise unittest.SkipTest("Layer %s isn't BSP or DISTRO one." \
                     % self.tc.layer['name'])

        # task -> (old signature, new signature)
        sig_diff = {}
        curr_sigs, _ = get_signatures(self.td['builddir'], failsafe=True)
        for task in self.td['sigs']:
            if task in curr_sigs and \
               self.td['sigs'][task] != curr_sigs[task]:
                sig_diff[task] = (self.td['sigs'][task], curr_sigs[task])

        if sig_diff:
            # Beware, depgraph uses task=<pn>.<taskname> whereas get_signatures()
            # uses <pn>:<taskname>. Need to convert sometimes. The output follows
            # the convention from get_signatures() because that seems closer to
            # normal bitbake output.
            def sig2graph(task):
                pn, taskname = task.rsplit(':', 1)
                return pn + '.' + taskname

            def graph2sig(task):
                pn, taskname = task.rsplit('.', 1)
                return pn + ':' + taskname

            depgraph = get_depgraph()
            depends = depgraph['tdepends']

            # If a task A has a changed signature, but none of its
            # dependencies, then we need to report it because it is
            # the one which introduces a change. Any task depending on
            # A (directly or indirectly) will also have a changed
            # signature, but we don't need to report it. It might have
            # its own changes, which will become apparent once the
            # issues that we do report are fixed and the test gets run
            # again.
            sig_diff_filtered = []
            for task, (old_sig, new_sig) in sig_diff.items():
                deps_tainted = False
                for dep in depends.get(sig2graph(task), ()):
                    if graph2sig(dep) in sig_diff:
                        deps_tainted = True
                        break
                if not deps_tainted:
                    sig_diff_filtered.append((task, old_sig, new_sig))

            msg = []
            msg.append(
                'Layer %s changed %d signatures, initial differences (first hash without, second with layer):'
                % (self.tc.layer['name'], len(sig_diff)))
            for diff in sorted(sig_diff_filtered):
                recipe, taskname = diff[0].rsplit(':', 1)
                cmd = 'bitbake-diffsigs --task %s %s --signature %s %s' % \
                      (recipe, taskname, diff[1], diff[2])
                msg.append('   %s: %s -> %s' % diff)
                msg.append('      %s' % cmd)
                try:
                    output = check_command(
                        'Determining signature difference failed.',
                        cmd).decode('utf-8')
                except RuntimeError as error:
                    output = str(error)
                if output:
                    msg.extend(
                        ['      ' + line for line in output.splitlines()])
                    msg.append('')
            self.fail('\n'.join(msg))
Ejemplo n.º 13
0
def main():
    parser = argparse.ArgumentParser(
            description="Yocto Project compatibility layer tool",
            add_help=False)
    parser.add_argument('layers', metavar='LAYER_DIR', nargs='+',
            help='Layer to test compatibility with Yocto Project')
    parser.add_argument('-o', '--output-log',
            help='File to output log (optional)', action='store')
    parser.add_argument('--dependency', nargs="+",
            help='Layers to process for dependencies', action='store')
    parser.add_argument('--machines', nargs="+",
            help='List of MACHINEs to be used during testing', action='store')
    parser.add_argument('--additional-layers', nargs="+",
            help='List of additional layers to add during testing', action='store')
    parser.add_argument('-n', '--no-auto', help='Disable auto layer discovery',
            action='store_true')
    parser.add_argument('-d', '--debug', help='Enable debug output',
            action='store_true')
    parser.add_argument('-q', '--quiet', help='Print only errors',
            action='store_true')

    parser.add_argument('-h', '--help', action='help',
            default=argparse.SUPPRESS,
            help='show this help message and exit')

    args = parser.parse_args()

    if args.output_log:
        fh = logging.FileHandler(args.output_log)
        fh.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
        logger.addHandler(fh)
    if args.debug:
        logger.setLevel(logging.DEBUG)
    elif args.quiet:
        logger.setLevel(logging.ERROR)

    if not 'BUILDDIR' in os.environ:
        logger.error("You must source the environment before run this script.")
        logger.error("$ source oe-init-build-env")
        return 1
    builddir = os.environ['BUILDDIR']
    bblayersconf = os.path.join(builddir, 'conf', 'bblayers.conf')

    layers = detect_layers(args.layers, args.no_auto)
    if not layers:
        logger.error("Fail to detect layers")
        return 1
    if args.additional_layers:
        additional_layers = detect_layers(args.additional_layers, args.no_auto)
    else:
        additional_layers = []
    if args.dependency:
        dep_layers = detect_layers(args.dependency, args.no_auto)
        dep_layers = dep_layers + layers
    else:
        dep_layers = layers

    logger.info("Detected layers:")
    for layer in layers:
        if layer['type'] == LayerType.ERROR_BSP_DISTRO:
            logger.error("%s: Can't be DISTRO and BSP type at the same time."\
                     " The conf/distro and conf/machine folders was found."\
                     % layer['name'])
            layers.remove(layer)
        elif layer['type'] == LayerType.ERROR_NO_LAYER_CONF:
            logger.error("%s: Don't have conf/layer.conf file."\
                     % layer['name'])
            layers.remove(layer)
        else:
            logger.info("%s: %s, %s" % (layer['name'], layer['type'],
                layer['path']))
    if not layers:
        return 1

    shutil.copyfile(bblayersconf, bblayersconf + '.backup')
    def cleanup_bblayers(signum, frame):
        shutil.copyfile(bblayersconf + '.backup', bblayersconf)
        os.unlink(bblayersconf + '.backup')
    signal.signal(signal.SIGTERM, cleanup_bblayers)
    signal.signal(signal.SIGINT, cleanup_bblayers)

    td = {}
    results = collections.OrderedDict()
    results_status = collections.OrderedDict()

    layers_tested = 0
    for layer in layers:
        if layer['type'] == LayerType.ERROR_NO_LAYER_CONF or \
                layer['type'] == LayerType.ERROR_BSP_DISTRO:
            continue

        logger.info('')
        logger.info("Setting up for %s(%s), %s" % (layer['name'], layer['type'],
            layer['path']))

        shutil.copyfile(bblayersconf + '.backup', bblayersconf)

        missing_dependencies = not add_layer_dependencies(bblayersconf, layer, dep_layers, logger)
        if not missing_dependencies:
            for additional_layer in additional_layers:
                if not add_layer_dependencies(bblayersconf, additional_layer, dep_layers, logger):
                    missing_dependencies = True
                    break
        if not add_layer_dependencies(bblayersconf, layer, dep_layers, logger) or \
           any(map(lambda additional_layer: not add_layer_dependencies(bblayersconf, additional_layer, dep_layers, logger),
                   additional_layers)):
            logger.info('Skipping %s due to missing dependencies.' % layer['name'])
            results[layer['name']] = None
            results_status[layer['name']] = 'SKIPPED (Missing dependencies)'
            layers_tested = layers_tested + 1
            continue

        if any(map(lambda additional_layer: not add_layer(bblayersconf, additional_layer, dep_layers, logger),
                   additional_layers)):
            logger.info('Skipping %s due to missing additional layers.' % layer['name'])
            results[layer['name']] = None
            results_status[layer['name']] = 'SKIPPED (Missing additional layers)'
            layers_tested = layers_tested + 1
            continue

        logger.info('Getting initial bitbake variables ...')
        td['bbvars'] = get_bb_vars()
        logger.info('Getting initial signatures ...')
        td['builddir'] = builddir
        td['sigs'], td['tunetasks'] = get_signatures(td['builddir'])
        td['machines'] = args.machines

        if not add_layer(bblayersconf, layer, dep_layers, logger):
            logger.info('Skipping %s ???.' % layer['name'])
            results[layer['name']] = None
            results_status[layer['name']] = 'SKIPPED (Unknown)'
            layers_tested = layers_tested + 1
            continue

        result = test_layer_compatibility(td, layer)
        results[layer['name']] = result
        results_status[layer['name']] = 'PASS' if results[layer['name']].wasSuccessful() else 'FAIL'
        layers_tested = layers_tested + 1

    if layers_tested:
        logger.info('')
        logger.info('Summary of results:')
        logger.info('')
        for layer_name in results_status:
            logger.info('%s ... %s' % (layer_name, results_status[layer_name]))

    cleanup_bblayers(None, None)

    return 0
Ejemplo n.º 14
0
    def test_signatures(self):
        if self.tc.layer['type'] == LayerType.SOFTWARE:
            raise unittest.SkipTest("Layer %s isn't BSP or DISTRO one." \
                     % self.tc.layer['name'])

        # task -> (old signature, new signature)
        sig_diff = {}
        curr_sigs, _ = get_signatures(self.td['builddir'], failsafe=True)
        for task in self.td['sigs']:
            if task in curr_sigs and \
               self.td['sigs'][task] != curr_sigs[task]:
                sig_diff[task] = (self.td['sigs'][task], curr_sigs[task])

        if sig_diff:
            # Beware, depgraph uses task=<pn>.<taskname> whereas get_signatures()
            # uses <pn>:<taskname>. Need to convert sometimes. The output follows
            # the convention from get_signatures() because that seems closer to
            # normal bitbake output.
            def sig2graph(task):
                pn, taskname = task.rsplit(':', 1)
                return pn + '.' + taskname
            def graph2sig(task):
                pn, taskname = task.rsplit('.', 1)
                return pn + ':' + taskname
            depgraph = get_depgraph()
            depends = depgraph['tdepends']

            # If a task A has a changed signature, but none of its
            # dependencies, then we need to report it because it is
            # the one which introduces a change. Any task depending on
            # A (directly or indirectly) will also have a changed
            # signature, but we don't need to report it. It might have
            # its own changes, which will become apparent once the
            # issues that we do report are fixed and the test gets run
            # again.
            sig_diff_filtered = []
            for task, (old_sig, new_sig) in sig_diff.items():
                deps_tainted = False
                for dep in depends.get(sig2graph(task), ()):
                    if graph2sig(dep) in sig_diff:
                        deps_tainted = True
                        break
                if not deps_tainted:
                    sig_diff_filtered.append((task, old_sig, new_sig))

            msg = []
            msg.append('Layer %s changed %d signatures, initial differences (first hash without, second with layer):' %
                       (self.tc.layer['name'], len(sig_diff)))
            for diff in sorted(sig_diff_filtered):
                recipe, taskname = diff[0].rsplit(':', 1)
                cmd = 'bitbake-diffsigs --task %s %s --signature %s %s' % \
                      (recipe, taskname, diff[1], diff[2])
                msg.append('   %s: %s -> %s' % diff)
                msg.append('      %s' % cmd)
                try:
                    output = check_command('Determining signature difference failed.',
                                           cmd).decode('utf-8')
                except RuntimeError as error:
                    output = str(error)
                if output:
                    msg.extend(['      ' + line for line in output.splitlines()])
                    msg.append('')
            self.fail('\n'.join(msg))