def upgrade(args, config, basepath, workspace):
    """Entry point for the devtool 'upgrade' subcommand"""

    if args.recipename in workspace:
        raise DevtoolError("recipe %s is already in your workspace" %
                           args.recipename)
    if not args.version and not args.srcrev:
        raise DevtoolError(
            "You must provide a version using the --version/-V option, or for recipes that fetch from an SCM such as git, the --srcrev/-S option"
        )
    if args.srcbranch and not args.srcrev:
        raise DevtoolError(
            "If you specify --srcbranch/-B then you must use --srcrev/-S to specify the revision"
            % args.recipename)

    reason = oe.recipeutils.validate_pn(args.recipename)
    if reason:
        raise DevtoolError(reason)

    tinfoil = setup_tinfoil(basepath=basepath, tracking=True)

    rd = parse_recipe(config, tinfoil, args.recipename, True)
    if not rd:
        return 1

    pn = rd.getVar('PN', True)
    if pn != args.recipename:
        logger.info('Mapping %s to %s' % (args.recipename, pn))
    if pn in workspace:
        raise DevtoolError("recipe %s is already in your workspace" % pn)

    standard._check_compatible_recipe(pn, rd)
    if rd.getVar('PV', True) == args.version and rd.getVar(
            'SRCREV', True) == args.srcrev:
        raise DevtoolError(
            "Current and upgrade versions are the same version" % version)

    rf = None
    try:
        rev1 = standard._extract_source(args.srctree, False, 'devtool-orig',
                                        False, rd)
        rev2, md5, sha256 = _extract_new_source(args.version, args.srctree,
                                                args.no_patch, args.srcrev,
                                                args.branch, args.keep_temp,
                                                tinfoil, rd)
        rf = _create_new_recipe(args.version, md5, sha256, args.srcrev,
                                args.srcbranch, config.workspace_path, tinfoil,
                                rd)
    except bb.process.CmdError as e:
        _upgrade_error(e, rf, args.srctree)
    except DevtoolError as e:
        _upgrade_error(e, rf, args.srctree)
    standard._add_md5(config, pn, os.path.dirname(rf))

    af = _write_append(rf, args.srctree, args.same_dir, args.no_same_dir, rev2,
                       config.workspace_path, rd)
    standard._add_md5(config, pn, af)
    logger.info('Upgraded source extracted to %s' % args.srctree)
    return 0
Beispiel #2
0
def upgrade(args, config, basepath, workspace):
    """Entry point for the devtool 'upgrade' subcommand"""

    if args.recipename in workspace:
        raise DevtoolError("recipe %s is already in your workspace" % args.recipename)
    if not args.version and not args.srcrev:
        raise DevtoolError("You must provide a version using the --version/-V option, or for recipes that fetch from an SCM such as git, the --srcrev/-S option")
    if args.srcbranch and not args.srcrev:
        raise DevtoolError("If you specify --srcbranch/-B then you must use --srcrev/-S to specify the revision" % args.recipename)

    tinfoil = setup_tinfoil(basepath=basepath, tracking=True)
    try:
        rd = parse_recipe(config, tinfoil, args.recipename, True)
        if not rd:
            return 1

        pn = rd.getVar('PN')
        if pn != args.recipename:
            logger.info('Mapping %s to %s' % (args.recipename, pn))
        if pn in workspace:
            raise DevtoolError("recipe %s is already in your workspace" % pn)

        if args.srctree:
            srctree = os.path.abspath(args.srctree)
        else:
            srctree = standard.get_default_srctree(config, pn)

        standard._check_compatible_recipe(pn, rd)
        old_srcrev = rd.getVar('SRCREV')
        if old_srcrev == 'INVALID':
            old_srcrev = None
        if old_srcrev and not args.srcrev:
            raise DevtoolError("Recipe specifies a SRCREV value; you must specify a new one when upgrading")
        if rd.getVar('PV') == args.version and old_srcrev == args.srcrev:
            raise DevtoolError("Current and upgrade versions are the same version")

        rf = None
        try:
            rev1 = standard._extract_source(srctree, False, 'devtool-orig', False, rd, tinfoil)
            rev2, md5, sha256 = _extract_new_source(args.version, srctree, args.no_patch,
                                                    args.srcrev, args.branch, args.keep_temp,
                                                    tinfoil, rd)
            rf, copied = _create_new_recipe(args.version, md5, sha256, args.srcrev, args.srcbranch, config.workspace_path, tinfoil, rd)
        except bb.process.CmdError as e:
            _upgrade_error(e, rf, srctree)
        except DevtoolError as e:
            _upgrade_error(e, rf, srctree)
        standard._add_md5(config, pn, os.path.dirname(rf))

        af = _write_append(rf, srctree, args.same_dir, args.no_same_dir, rev2,
                        copied, config.workspace_path, rd)
        standard._add_md5(config, pn, af)
        logger.info('Upgraded source extracted to %s' % srctree)
        logger.info('New recipe is %s' % rf)
    finally:
        tinfoil.shutdown()
    return 0
Beispiel #3
0
def upgrade(args, config, basepath, workspace):
    """Entry point for the devtool 'upgrade' subcommand"""

    if args.recipename in workspace:
        raise DevtoolError("recipe %s is already in your workspace" % args.recipename)
    if not args.version and not args.srcrev:
        raise DevtoolError("You must provide a version using the --version/-V option, or for recipes that fetch from an SCM such as git, the --srcrev/-S option")
    if args.srcbranch and not args.srcrev:
        raise DevtoolError("If you specify --srcbranch/-B then you must use --srcrev/-S to specify the revision" % args.recipename)

    tinfoil = setup_tinfoil(basepath=basepath, tracking=True)
    try:
        rd = parse_recipe(config, tinfoil, args.recipename, True)
        if not rd:
            return 1

        pn = rd.getVar('PN')
        if pn != args.recipename:
            logger.info('Mapping %s to %s' % (args.recipename, pn))
        if pn in workspace:
            raise DevtoolError("recipe %s is already in your workspace" % pn)

        if args.srctree:
            srctree = os.path.abspath(args.srctree)
        else:
            srctree = standard.get_default_srctree(config, pn)

        standard._check_compatible_recipe(pn, rd)
        old_srcrev = rd.getVar('SRCREV')
        if old_srcrev == 'INVALID':
            old_srcrev = None
        if old_srcrev and not args.srcrev:
            raise DevtoolError("Recipe specifies a SRCREV value; you must specify a new one when upgrading")
        if rd.getVar('PV') == args.version and old_srcrev == args.srcrev:
            raise DevtoolError("Current and upgrade versions are the same version")

        rf = None
        try:
            rev1 = standard._extract_source(srctree, False, 'devtool-orig', False, rd, tinfoil)
            rev2, md5, sha256 = _extract_new_source(args.version, srctree, args.no_patch,
                                                    args.srcrev, args.branch, args.keep_temp,
                                                    tinfoil, rd)
            rf, copied = _create_new_recipe(args.version, md5, sha256, args.srcrev, args.srcbranch, config.workspace_path, tinfoil, rd)
        except bb.process.CmdError as e:
            _upgrade_error(e, rf, srctree)
        except DevtoolError as e:
            _upgrade_error(e, rf, srctree)
        standard._add_md5(config, pn, os.path.dirname(rf))

        af = _write_append(rf, srctree, args.same_dir, args.no_same_dir, rev2,
                        copied, config.workspace_path, rd)
        standard._add_md5(config, pn, af)
        logger.info('Upgraded source extracted to %s' % srctree)
        logger.info('New recipe is %s' % rf)
    finally:
        tinfoil.shutdown()
    return 0
Beispiel #4
0
def upgrade(args, config, basepath, workspace):
    """Entry point for the devtool 'upgrade' subcommand"""

    if args.recipename in workspace:
        raise DevtoolError("recipe %s is already in your workspace" % args.recipename)
    if not args.version and not args.srcrev:
        raise DevtoolError("You must provide a version using the --version/-V option, or for recipes that fetch from an SCM such as git, the --srcrev/-S option")
    if args.srcbranch and not args.srcrev:
        raise DevtoolError("If you specify --srcbranch/-B then you must use --srcrev/-S to specify the revision" % args.recipename)

    reason = oe.recipeutils.validate_pn(args.recipename)
    if reason:
        raise DevtoolError(reason)

    tinfoil = setup_tinfoil(basepath=basepath, tracking=True)

    rd = parse_recipe(config, tinfoil, args.recipename, True)
    if not rd:
        return 1

    pn = rd.getVar('PN', True)
    if pn != args.recipename:
        logger.info('Mapping %s to %s' % (args.recipename, pn))
    if pn in workspace:
        raise DevtoolError("recipe %s is already in your workspace" % pn)

    standard._check_compatible_recipe(pn, rd)
    if rd.getVar('PV', True) == args.version and rd.getVar('SRCREV', True) == args.srcrev:
        raise DevtoolError("Current and upgrade versions are the same version" % version)

    rf = None
    try:
        rev1 = standard._extract_source(args.srctree, False, 'devtool-orig', False, rd)
        rev2, md5, sha256 = _extract_new_source(args.version, args.srctree, args.no_patch,
                                                args.srcrev, args.branch, args.keep_temp,
                                                tinfoil, rd)
        rf = _create_new_recipe(args.version, md5, sha256, args.srcrev, args.srcbranch, config.workspace_path, tinfoil, rd)
    except bb.process.CmdError as e:
        _upgrade_error(e, rf, args.srctree)
    except DevtoolError as e:
        _upgrade_error(e, rf, args.srctree)
    standard._add_md5(config, pn, os.path.dirname(rf))

    af = _write_append(rf, args.srctree, args.same_dir, args.no_same_dir, rev2,
                       config.workspace_path, rd)
    standard._add_md5(config, pn, af)
    logger.info('Upgraded source extracted to %s' % args.srctree)
    return 0
Beispiel #5
0
def upgrade(args, config, basepath, workspace):
    """Entry point for the devtool 'upgrade' subcommand"""

    if args.recipename in workspace:
        raise DevtoolError("recipe %s is already in your workspace" %
                           args.recipename)
    if args.srcbranch and not args.srcrev:
        raise DevtoolError(
            "If you specify --srcbranch/-B then you must use --srcrev/-S to specify the revision"
            % args.recipename)

    _check_git_config()

    tinfoil = setup_tinfoil(basepath=basepath, tracking=True)
    try:
        rd = parse_recipe(config, tinfoil, args.recipename, True)
        if not rd:
            return 1

        pn = rd.getVar('PN')
        if pn != args.recipename:
            logger.info('Mapping %s to %s' % (args.recipename, pn))
        if pn in workspace:
            raise DevtoolError("recipe %s is already in your workspace" % pn)

        if args.srctree:
            srctree = os.path.abspath(args.srctree)
        else:
            srctree = standard.get_default_srctree(config, pn)

        # try to automatically discover latest version and revision if not provided on command line
        if not args.version and not args.srcrev:
            version_info = oe.recipeutils.get_recipe_upstream_version(rd)
            if version_info['version'] and not version_info[
                    'version'].endswith("new-commits-available"):
                args.version = version_info['version']
            if version_info['revision']:
                args.srcrev = version_info['revision']
        if not args.version and not args.srcrev:
            raise DevtoolError(
                "Automatic discovery of latest version/revision failed - you must provide a version using the --version/-V option, or for recipes that fetch from an SCM such as git, the --srcrev/-S option."
            )

        standard._check_compatible_recipe(pn, rd)
        old_srcrev = rd.getVar('SRCREV')
        if old_srcrev == 'INVALID':
            old_srcrev = None
        if old_srcrev and not args.srcrev:
            raise DevtoolError(
                "Recipe specifies a SRCREV value; you must specify a new one when upgrading"
            )
        old_ver = rd.getVar('PV')
        if old_ver == args.version and old_srcrev == args.srcrev:
            raise DevtoolError(
                "Current and upgrade versions are the same version")
        if args.version:
            if bb.utils.vercmp_string(args.version, old_ver) < 0:
                logger.warning(
                    'Upgrade version %s compares as less than the current version %s. If you are using a package feed for on-target upgrades or providing this recipe for general consumption, then you should increment PE in the recipe (or if there is no current PE value set, set it to "1")'
                    % (args.version, old_ver))
            check_prerelease_version(args.version, 'devtool upgrade')

        rf = None
        license_diff = None
        try:
            logger.info('Extracting current version source...')
            rev1, srcsubdir1 = standard._extract_source(
                srctree,
                False,
                'devtool-orig',
                False,
                config,
                basepath,
                workspace,
                args.fixed_setup,
                rd,
                tinfoil,
                no_overrides=args.no_overrides)
            old_licenses = _extract_licenses(srctree,
                                             rd.getVar('LIC_FILES_CHKSUM'))
            logger.info('Extracting upgraded version source...')
            rev2, md5, sha256, srcbranch, srcsubdir2 = _extract_new_source(
                args.version, srctree, args.no_patch, args.srcrev,
                args.srcbranch, args.branch, args.keep_temp, tinfoil, rd)
            new_licenses = _extract_licenses(srctree,
                                             rd.getVar('LIC_FILES_CHKSUM'))
            license_diff = _generate_license_diff(old_licenses, new_licenses)
            rf, copied = _create_new_recipe(args.version, md5, sha256,
                                            args.srcrev, srcbranch, srcsubdir1,
                                            srcsubdir2, config.workspace_path,
                                            tinfoil, rd, license_diff,
                                            new_licenses)
        except bb.process.CmdError as e:
            _upgrade_error(e, rf, srctree)
        except DevtoolError as e:
            _upgrade_error(e, rf, srctree)
        standard._add_md5(config, pn, os.path.dirname(rf))

        af = _write_append(rf, srctree, args.same_dir, args.no_same_dir, rev2,
                           copied, config.workspace_path, rd)
        standard._add_md5(config, pn, af)

        update_unlockedsigs(basepath, workspace, args.fixed_setup, [pn])

        logger.info('Upgraded source extracted to %s' % srctree)
        logger.info('New recipe is %s' % rf)
        if license_diff:
            logger.info(
                'License checksums have been updated in the new recipe; please refer to it for the difference between the old and the new license texts.'
            )
    finally:
        tinfoil.shutdown()
    return 0
Beispiel #6
0
def upgrade(args, config, basepath, workspace):
    """Entry point for the devtool 'upgrade' subcommand"""

    if args.recipename in workspace:
        raise DevtoolError("recipe %s is already in your workspace" % args.recipename)
    if args.srcbranch and not args.srcrev:
        raise DevtoolError("If you specify --srcbranch/-B then you must use --srcrev/-S to specify the revision" % args.recipename)

    _check_git_config()

    tinfoil = setup_tinfoil(basepath=basepath, tracking=True)
    try:
        rd = parse_recipe(config, tinfoil, args.recipename, True)
        if not rd:
            return 1

        pn = rd.getVar('PN')
        if pn != args.recipename:
            logger.info('Mapping %s to %s' % (args.recipename, pn))
        if pn in workspace:
            raise DevtoolError("recipe %s is already in your workspace" % pn)

        if args.srctree:
            srctree = os.path.abspath(args.srctree)
        else:
            srctree = standard.get_default_srctree(config, pn)

        # try to automatically discover latest version and revision if not provided on command line
        if not args.version and not args.srcrev:
            version_info = oe.recipeutils.get_recipe_upstream_version(rd)
            if version_info['version'] and not version_info['version'].endswith("new-commits-available"):
                args.version = version_info['version']
            if version_info['revision']:
                args.srcrev = version_info['revision']
        if not args.version and not args.srcrev:
            raise DevtoolError("Automatic discovery of latest version/revision failed - you must provide a version using the --version/-V option, or for recipes that fetch from an SCM such as git, the --srcrev/-S option.")

        standard._check_compatible_recipe(pn, rd)
        old_srcrev = rd.getVar('SRCREV')
        if old_srcrev == 'INVALID':
            old_srcrev = None
        if old_srcrev and not args.srcrev:
            raise DevtoolError("Recipe specifies a SRCREV value; you must specify a new one when upgrading")
        old_ver = rd.getVar('PV')
        if old_ver == args.version and old_srcrev == args.srcrev:
            raise DevtoolError("Current and upgrade versions are the same version")
        if args.version:
            if bb.utils.vercmp_string(args.version, old_ver) < 0:
                logger.warning('Upgrade version %s compares as less than the current version %s. If you are using a package feed for on-target upgrades or providing this recipe for general consumption, then you should increment PE in the recipe (or if there is no current PE value set, set it to "1")' % (args.version, old_ver))
            check_prerelease_version(args.version, 'devtool upgrade')

        rf = None
        license_diff = None
        try:
            logger.info('Extracting current version source...')
            rev1, srcsubdir1 = standard._extract_source(srctree, False, 'devtool-orig', False, config, basepath, workspace, args.fixed_setup, rd, tinfoil, no_overrides=args.no_overrides)
            old_licenses = _extract_licenses(srctree, rd.getVar('LIC_FILES_CHKSUM'))
            logger.info('Extracting upgraded version source...')
            rev2, md5, sha256, srcbranch, srcsubdir2 = _extract_new_source(args.version, srctree, args.no_patch,
                                                    args.srcrev, args.srcbranch, args.branch, args.keep_temp,
                                                    tinfoil, rd)
            new_licenses = _extract_licenses(srctree, rd.getVar('LIC_FILES_CHKSUM'))
            license_diff = _generate_license_diff(old_licenses, new_licenses)
            rf, copied = _create_new_recipe(args.version, md5, sha256, args.srcrev, srcbranch, srcsubdir1, srcsubdir2, config.workspace_path, tinfoil, rd, license_diff, new_licenses)
        except bb.process.CmdError as e:
            _upgrade_error(e, rf, srctree)
        except DevtoolError as e:
            _upgrade_error(e, rf, srctree)
        standard._add_md5(config, pn, os.path.dirname(rf))

        af = _write_append(rf, srctree, args.same_dir, args.no_same_dir, rev2,
                        copied, config.workspace_path, rd)
        standard._add_md5(config, pn, af)

        update_unlockedsigs(basepath, workspace, args.fixed_setup, [pn])

        logger.info('Upgraded source extracted to %s' % srctree)
        logger.info('New recipe is %s' % rf)
        if license_diff:
            logger.info('License checksums have been updated in the new recipe; please refer to it for the difference between the old and the new license texts.')
    finally:
        tinfoil.shutdown()
    return 0
Beispiel #7
0
def devimport(args, config, basepath, workspace):
    """Entry point for the devtool 'import' subcommand"""

    def get_pn(name):
        """ Returns the filename of a workspace recipe/append"""
        metadata = name.split('/')[-1]
        fn, _ = os.path.splitext(metadata)
        return fn

    if not os.path.exists(args.file):
        raise DevtoolError('Tar archive %s does not exist. Export your workspace using "devtool export"' % args.file)

    with tarfile.open(args.file) as tar:
        # Get exported metadata
        export_workspace_path = export_workspace = None
        try:
            metadata = tar.getmember(export.metadata)
        except KeyError as ke:
            raise DevtoolError('The export metadata file created by "devtool export" was not found. "devtool import" can only be used to import tar archives created by "devtool export".')

        tar.extract(metadata)
        with open(metadata.name) as fdm:
            export_workspace_path, export_workspace = json.load(fdm)
        os.unlink(metadata.name)

        members = tar.getmembers()

        # Get appends and recipes from the exported archive, these
        # will be needed to find out those appends without corresponding
        # recipe pair
        append_fns, recipe_fns = set(), set()
        for member in members:
            if member.name.startswith('appends'):
                append_fns.add(get_pn(member.name))
            elif member.name.startswith('recipes'):
                recipe_fns.add(get_pn(member.name))

        # Setup tinfoil, get required data and shutdown
        tinfoil = setup_tinfoil(config_only=False, basepath=basepath)
        try:
            current_fns = [os.path.basename(recipe[0]) for recipe in tinfoil.cooker.recipecaches[''].pkg_fn.items()]
        finally:
            tinfoil.shutdown()

        # Find those appends that do not have recipes in current metadata
        non_importables = []
        for fn in append_fns - recipe_fns:
            # Check on current metadata (covering those layers indicated in bblayers.conf)
            for current_fn in current_fns:
                if fnmatch.fnmatch(current_fn, '*' + fn.replace('%', '') + '*'):
                    break
            else:
                non_importables.append(fn)
                logger.warning('No recipe to append %s.bbapppend, skipping' % fn)

        # Extract
        imported = []
        for member in members:
            if member.name == export.metadata:
                continue

            for nonimp in non_importables:
                pn = nonimp.split('_')[0]
                # do not extract data from non-importable recipes or metadata
                if member.name.startswith('appends/%s' % nonimp) or \
                        member.name.startswith('recipes/%s' % nonimp) or \
                        member.name.startswith('sources/%s' % pn):
                    break
            else:
                path = os.path.join(config.workspace_path, member.name)
                if os.path.exists(path):
                    # by default, no file overwrite is done unless -o is given by the user
                    if args.overwrite:
                        try:
                            tar.extract(member, path=config.workspace_path)
                        except PermissionError as pe:
                            logger.warning(pe)
                    else:
                        logger.warning('File already present. Use --overwrite/-o to overwrite it: %s' % member.name)
                        continue
                else:
                    tar.extract(member, path=config.workspace_path)

                # Update EXTERNALSRC and the devtool md5 file
                if member.name.startswith('appends'):
                    if export_workspace_path:
                        # appends created by 'devtool modify' just need to update the workspace
                        replace_from_file(path, export_workspace_path, config.workspace_path)

                        # appends created by 'devtool add' need replacement of exported source tree
                        pn = get_pn(member.name).split('_')[0]
                        exported_srctree = export_workspace[pn]['srctree']
                        if exported_srctree:
                            replace_from_file(path, exported_srctree, os.path.join(config.workspace_path, 'sources', pn))

                    standard._add_md5(config, pn, path)
                    imported.append(pn)

    if imported:
        logger.info('Imported recipes into workspace %s: %s' % (config.workspace_path, ', '.join(imported)))
    else:
        logger.warning('No recipes imported into the workspace')

    return 0
Beispiel #8
0
def devimport(args, config, basepath, workspace):
    """Entry point for the devtool 'import' subcommand"""
    def get_pn(name):
        """ Returns the filename of a workspace recipe/append"""
        metadata = name.split('/')[-1]
        fn, _ = os.path.splitext(metadata)
        return fn

    if not os.path.exists(args.file):
        raise DevtoolError(
            'Tar archive %s does not exist. Export your workspace using "devtool export"'
            % args.file)

    with tarfile.open(args.file) as tar:
        # Get exported metadata
        export_workspace_path = export_workspace = None
        try:
            metadata = tar.getmember(export.metadata)
        except KeyError as ke:
            raise DevtoolError(
                'The export metadata file created by "devtool export" was not found. "devtool import" can only be used to import tar archives created by "devtool export".'
            )

        tar.extract(metadata)
        with open(metadata.name) as fdm:
            export_workspace_path, export_workspace = json.load(fdm)
        os.unlink(metadata.name)

        members = tar.getmembers()

        # Get appends and recipes from the exported archive, these
        # will be needed to find out those appends without corresponding
        # recipe pair
        append_fns, recipe_fns = set(), set()
        for member in members:
            if member.name.startswith('appends'):
                append_fns.add(get_pn(member.name))
            elif member.name.startswith('recipes'):
                recipe_fns.add(get_pn(member.name))

        # Setup tinfoil, get required data and shutdown
        tinfoil = setup_tinfoil(config_only=False, basepath=basepath)
        try:
            current_fns = [
                os.path.basename(recipe[0])
                for recipe in tinfoil.cooker.recipecaches[''].pkg_fn.items()
            ]
        finally:
            tinfoil.shutdown()

        # Find those appends that do not have recipes in current metadata
        non_importables = []
        for fn in append_fns - recipe_fns:
            # Check on current metadata (covering those layers indicated in bblayers.conf)
            for current_fn in current_fns:
                if fnmatch.fnmatch(current_fn,
                                   '*' + fn.replace('%', '') + '*'):
                    break
            else:
                non_importables.append(fn)
                logger.warn('No recipe to append %s.bbapppend, skipping' % fn)

        # Extract
        imported = []
        for member in members:
            if member.name == export.metadata:
                continue

            for nonimp in non_importables:
                pn = nonimp.split('_')[0]
                # do not extract data from non-importable recipes or metadata
                if member.name.startswith('appends/%s' % nonimp) or \
                        member.name.startswith('recipes/%s' % nonimp) or \
                        member.name.startswith('sources/%s' % pn):
                    break
            else:
                path = os.path.join(config.workspace_path, member.name)
                if os.path.exists(path):
                    # by default, no file overwrite is done unless -o is given by the user
                    if args.overwrite:
                        try:
                            tar.extract(member, path=config.workspace_path)
                        except PermissionError as pe:
                            logger.warn(pe)
                    else:
                        logger.warn(
                            'File already present. Use --overwrite/-o to overwrite it: %s'
                            % member.name)
                        continue
                else:
                    tar.extract(member, path=config.workspace_path)

                # Update EXTERNALSRC and the devtool md5 file
                if member.name.startswith('appends'):
                    if export_workspace_path:
                        # appends created by 'devtool modify' just need to update the workspace
                        replace_from_file(path, export_workspace_path,
                                          config.workspace_path)

                        # appends created by 'devtool add' need replacement of exported source tree
                        pn = get_pn(member.name).split('_')[0]
                        exported_srctree = export_workspace[pn]['srctree']
                        if exported_srctree:
                            replace_from_file(
                                path, exported_srctree,
                                os.path.join(config.workspace_path, 'sources',
                                             pn))

                    standard._add_md5(config, pn, path)
                    imported.append(pn)

    if imported:
        logger.info('Imported recipes into workspace %s: %s' %
                    (config.workspace_path, ', '.join(imported)))
    else:
        logger.warn('No recipes imported into the workspace')

    return 0