Example #1
0
    def __init__(self, project_options=None):
        if project_options is None:
            project_options = snapcraft.ProjectOptions()

        self.build_tools = []
        self._project_options = project_options

        self._snapcraft_yaml = get_snapcraft_yaml()
        snapcraft_yaml = _snapcraft_yaml_load(self._snapcraft_yaml)

        self._validator = Validator(snapcraft_yaml)
        self._validator.validate()

        snapcraft_yaml = self._process_remote_parts(snapcraft_yaml)
        snapcraft_yaml = self._expand_filesets(snapcraft_yaml)
        self.data = self._expand_env(snapcraft_yaml)

        self._ensure_no_duplicate_app_aliases()

        # both confinement type and build quality are optionals
        _ensure_confinement_default(self.data, self._validator.schema)
        _ensure_grade_default(self.data, self._validator.schema)

        self.build_tools = self.data.get('build-packages', [])
        self.build_tools.extend(project_options.additional_build_packages)

        self.parts = parts.PartsConfig(self.data, self._project_options,
                                       self._validator, self.build_tools,
                                       self._snapcraft_yaml)

        if 'architectures' not in self.data:
            self.data['architectures'] = [self._project_options.deb_arch]
Example #2
0
    def __init__(self, project_options=None):
        if project_options is None:
            project_options = snapcraft.ProjectOptions()

        self.build_tools = []
        self._project_options = project_options

        self._snapcraft_yaml = _get_snapcraft_yaml()
        self.data = _snapcraft_yaml_load(self._snapcraft_yaml)

        self._validator = Validator(self.data)
        self._validator.validate()
        _ensure_confinement_default(self.data, self._validator.schema)

        self.build_tools = self.data.get('build-packages', [])
        self.build_tools.extend(project_options.additional_build_packages)

        self.parts = parts.PartsConfig(self.data.get('parts', {}),
                                       self._project_options,
                                       self._validator,
                                       self.build_tools,
                                       self._snapcraft_yaml)

        if 'architectures' not in self.data:
            self.data['architectures'] = [self._project_options.deb_arch]
Example #3
0
    def __init__(self, project_options=None):
        if project_options is None:
            project_options = snapcraft.ProjectOptions()

        self.build_tools = []
        self._project_options = project_options

        self._snapcraft_yaml = get_snapcraft_yaml()
        snapcraft_yaml = _snapcraft_yaml_load(self._snapcraft_yaml)

        self._validator = Validator(snapcraft_yaml)
        self._validator.validate()

        snapcraft_yaml = self._process_remote_parts(snapcraft_yaml)
        snapcraft_yaml = self._expand_filesets(snapcraft_yaml)
        self.data = self._expand_env(snapcraft_yaml)

        self._ensure_no_duplicate_app_aliases()

        # both confinement type and build quality are optionals
        _ensure_confinement_default(self.data, self._validator.schema)
        _ensure_grade_default(self.data, self._validator.schema)

        self.build_tools = self.data.get('build-packages', [])
        self.build_tools.extend(project_options.additional_build_packages)

        self.parts = parts.PartsConfig(self.data,
                                       self._project_options,
                                       self._validator,
                                       self.build_tools,
                                       self._snapcraft_yaml)

        if 'architectures' not in self.data:
            self.data['architectures'] = [self._project_options.deb_arch]
Example #4
0
    def __init__(self, project_options=None):
        self.build_tools = []
        self.all_parts = []
        self._part_names = []
        self._project_options = project_options
        self.after_requests = {}

        self.data = _snapcraft_yaml_load()

        # To make the transition less painful
        self._remap_skills_to_interfaces()

        self._validator = Validator(self.data)
        self._validator.validate()

        self.build_tools = self.data.get('build-packages', [])

        self._wiki = wiki.Wiki()

        for part_name in self.data.get('parts', []):
            self._part_names.append(part_name)
            properties = self.data['parts'][part_name] or {}

            plugin_name = properties.pop('plugin', None)
            if not plugin_name:
                logger.info(
                    'Searching the wiki to compose part "{}"'.format(
                        part_name))
                with contextlib.suppress(KeyError):
                    properties = self._wiki.compose(part_name, properties)
                    plugin_name = properties.pop('plugin', None)
                    # The wiki still supports using 'type' for snapcraft 1.x
                    if 'type' in properties:
                        del properties['type']

            if not plugin_name:
                raise PluginNotDefinedError(part_name)

            if 'after' in properties:
                self.after_requests[part_name] = properties.pop('after')

            properties['stage'] = _expand_filesets_for('stage', properties)
            properties['snap'] = _expand_filesets_for('snap', properties)

            if 'filesets' in properties:
                del properties['filesets']

            self.load_plugin(part_name, plugin_name, properties)

        self._compute_part_dependencies()
        self.all_parts = self._sort_parts()

        if 'architectures' not in self.data:
            self.data['architectures'] = [common.get_arch(), ]
Example #5
0
    def __init__(self, project_options=None):
        if project_options is None:
            project_options = snapcraft.ProjectOptions()

        self.build_tools = []
        self.all_parts = []
        self._part_names = []
        self._project_options = project_options
        self.after_requests = {}

        self.data = _snapcraft_yaml_load()

        self._validator = Validator(self.data)
        self._validator.validate()

        self.build_tools = self.data.get('build-packages', [])
        self.build_tools.extend(project_options.additional_build_packages)

        self._wiki = wiki.Wiki()
        self._process_parts()
Example #6
0
    def __init__(self, project_options=None):
        if project_options is None:
            project_options = snapcraft.ProjectOptions()

        self.build_tools = []
        self.all_parts = []
        self._part_names = []
        self._project_options = project_options
        self.after_requests = {}

        self._snapcraft_yaml = _get_snapcraft_yaml()
        self.data = _snapcraft_yaml_load(self._snapcraft_yaml)

        self._validator = Validator(self.data)
        self._validator.validate()
        _ensure_confinement_default(self.data, self._validator.schema)

        self.build_tools = self.data.get('build-packages', [])
        self.build_tools.extend(project_options.additional_build_packages)

        self._remote_parts = parts.get_remote_parts()
        self._process_parts()
Example #7
0
    def __init__(self, project_options=None):
        if project_options is None:
            project_options = snapcraft.ProjectOptions()

        self.build_tools = []
        self.all_parts = []
        self._part_names = []
        self._project_options = project_options
        self.after_requests = {}

        self.data = _snapcraft_yaml_load()

        self._validator = Validator(self.data)
        self._validator.validate()

        self.build_tools = self.data.get('build-packages', [])
        self.build_tools.extend(project_options.additional_build_packages)

        self._wiki = wiki.Wiki()
        self._process_parts()
Example #8
0
    def __init__(self, project_options=None):
        if project_options is None:
            project_options = snapcraft.ProjectOptions()

        self.build_tools = []
        self.all_parts = []
        self._part_names = []
        self._project_options = project_options
        self.after_requests = {}

        self._snapcraft_yaml = _get_snapcraft_yaml()
        self.data = _snapcraft_yaml_load(self._snapcraft_yaml)

        self._validator = Validator(self.data)
        self._validator.validate()
        _ensure_confinement_default(self.data, self._validator.schema)

        self.build_tools = self.data.get('build-packages', [])
        self.build_tools.extend(project_options.additional_build_packages)
        self._process_parts()
Example #9
0
class Config:

    @property
    def part_names(self):
        return self.parts.part_names

    @property
    def all_parts(self):
        return self.parts.all_parts

    @property
    def _remote_parts(self):
        if getattr(self, '_remote_parts_attr', None) is None:
            self._remote_parts_attr = get_remote_parts()
        return self._remote_parts_attr

    def __init__(self, project_options=None):
        if project_options is None:
            project_options = snapcraft.ProjectOptions()

        self.build_tools = []
        self._project_options = project_options

        self._snapcraft_yaml = get_snapcraft_yaml()
        snapcraft_yaml = _snapcraft_yaml_load(self._snapcraft_yaml)

        self._validator = Validator(snapcraft_yaml)
        self._validator.validate()

        snapcraft_yaml = self._process_remote_parts(snapcraft_yaml)
        snapcraft_yaml = self._expand_filesets(snapcraft_yaml)
        self.data = self._expand_env(snapcraft_yaml)

        self._ensure_no_duplicate_app_aliases()

        # both confinement type and build quality are optionals
        _ensure_confinement_default(self.data, self._validator.schema)
        _ensure_grade_default(self.data, self._validator.schema)

        self.build_tools = self.data.get('build-packages', [])
        self.build_tools.extend(project_options.additional_build_packages)

        self.parts = parts.PartsConfig(self.data,
                                       self._project_options,
                                       self._validator,
                                       self.build_tools,
                                       self._snapcraft_yaml)

        if 'architectures' not in self.data:
            self.data['architectures'] = [self._project_options.deb_arch]

    def _ensure_no_duplicate_app_aliases(self):
        # Prevent multiple apps within a snap from having duplicate alias names
        aliases = []
        for app_name, app in self.data.get('apps', {}).items():
            aliases.extend(app.get('aliases', []))
        seen = set()
        duplicates = set()
        for alias in aliases:
            if alias in seen:
                duplicates.add(alias)
            else:
                seen.add(alias)
        if duplicates:
            raise errors.DuplicateAliasError(aliases=duplicates)

    def get_project_state(self, step):
        """Returns a dict of states for the given step of each part."""

        state = {}
        for part in self.parts.all_parts:
            state[part.name] = part.get_state(step)

        return state

    def stage_env(self):
        stage_dir = self._project_options.stage_dir
        core_dynamic_linker = self._project_options.get_core_dynamic_linker()
        env = []

        env += _runtime_env(stage_dir, self._project_options.arch_triplet)
        env += _build_env_for_stage(
            stage_dir,
            self.data['name'],
            self.data['confinement'],
            self._project_options.arch_triplet,
            core_dynamic_linker=core_dynamic_linker)
        for part in self.parts.all_parts:
            env += part.env(stage_dir)

        return env

    def snap_env(self):
        snap_dir = self._project_options.snap_dir
        env = []

        env += _runtime_env(snap_dir, self._project_options.arch_triplet)
        dependency_paths = set()
        for part in self.parts.all_parts:
            env += part.env(snap_dir)
            dependency_paths |= part.get_primed_dependency_paths()

        # Dependency paths are only valid if they actually exist. Sorting them
        # here as well so the LD_LIBRARY_PATH is consistent between runs.
        dependency_paths = sorted({
            path for path in dependency_paths if os.path.isdir(path)})

        if dependency_paths:
            # Add more specific LD_LIBRARY_PATH from the dependencies.
            env.append('LD_LIBRARY_PATH="' + ':'.join(dependency_paths) +
                       ':$LD_LIBRARY_PATH"')

        return env

    def project_env(self):
        return [
            'SNAPCRAFT_STAGE={}'.format(self._project_options.stage_dir),
            'SNAPCRAFT_PROJECT_NAME={}'.format(self.data['name']),
            'SNAPCRAFT_PROJECT_VERSION={}'.format(self.data['version']),
        ]

    def _expand_env(self, snapcraft_yaml):
        environment_keys = ['name', 'version']
        for key in snapcraft_yaml:
            if any((key == env_key for env_key in environment_keys)):
                continue
            snapcraft_yaml[key] = replace_attr(
                snapcraft_yaml[key],
                [
                    ('$SNAPCRAFT_PROJECT_NAME', snapcraft_yaml['name']),
                    ('$SNAPCRAFT_PROJECT_VERSION', snapcraft_yaml['version']),
                    ('$SNAPCRAFT_STAGE', self._project_options.stage_dir),
                ])
        return snapcraft_yaml

    def _expand_filesets(self, snapcraft_yaml):
        parts = snapcraft_yaml.get('parts', {})

        for part_name in parts:
            # FIXME: Remove `snap` from here; it's deprecated
            for step in ('stage', 'snap', 'prime'):
                step_fileset = _expand_filesets_for(step, parts[part_name])
                parts[part_name][step] = step_fileset

        return snapcraft_yaml

    def _process_remote_parts(self, snapcraft_yaml):
        parts = snapcraft_yaml.get('parts', {})
        new_parts = {}

        for part_name in parts:
            if 'plugin' not in parts[part_name]:
                properties = self._remote_parts.compose(part_name,
                                                        parts[part_name])
                new_parts[part_name] = properties
            else:
                new_parts[part_name] = parts[part_name].copy()

            after_parts = parts[part_name].get('after', [])
            after_remote_parts = [p for p in after_parts if p not in parts]

            for after_part in after_remote_parts:
                properties = self._remote_parts.get_part(after_part)
                new_parts[after_part] = properties

        snapcraft_yaml['parts'] = new_parts
        return snapcraft_yaml
Example #10
0
class Config:

    @property
    def part_names(self):
        return self._part_names

    def __init__(self, project_options=None):
        if project_options is None:
            project_options = snapcraft.ProjectOptions()

        self.build_tools = []
        self.all_parts = []
        self._part_names = []
        self._project_options = project_options
        self.after_requests = {}

        self.data = _snapcraft_yaml_load()

        self._validator = Validator(self.data)
        self._validator.validate()
        _ensure_confinement_default(self.data, self._validator.schema)

        self.build_tools = self.data.get('build-packages', [])
        self.build_tools.extend(project_options.additional_build_packages)

        self._wiki = wiki.Wiki()
        self._process_parts()

    def _process_parts(self):
        for part_name in self.data.get('parts', []):
            self._part_names.append(part_name)
            properties = self.data['parts'][part_name] or {}

            plugin_name = properties.pop('plugin', None)
            if not plugin_name:
                logger.info(
                    'Searching the wiki to compose part "{}"'.format(
                        part_name))
                with contextlib.suppress(KeyError):
                    properties = self._wiki.compose(part_name, properties)
                    plugin_name = properties.pop('plugin', None)
                    # The wiki still supports using 'type' for snapcraft 1.x
                    if 'type' in properties:
                        del properties['type']

            if not plugin_name:
                raise PluginNotDefinedError(part_name)

            if 'after' in properties:
                self.after_requests[part_name] = properties.pop('after')

            properties['stage'] = _expand_filesets_for('stage', properties)
            properties['snap'] = _expand_filesets_for('snap', properties)

            if 'filesets' in properties:
                del properties['filesets']

            self.load_plugin(part_name, plugin_name, properties)

        self._compute_part_dependencies()
        self.all_parts = self._sort_parts()

        if 'architectures' not in self.data:
            self.data['architectures'] = [self._project_options.deb_arch]

    def _compute_part_dependencies(self):
        '''Gather the lists of dependencies and adds to all_parts.'''

        for part in self.all_parts:
            dep_names = self.after_requests.get(part.name, [])
            for dep in dep_names:
                found = False
                for i in range(len(self.all_parts)):
                    if dep == self.all_parts[i].name:
                        part.deps.append(self.all_parts[i])
                        found = True
                        break
                if not found:
                    wiki_part = self._wiki.get_part(dep)
                    found = True if wiki_part else False
                    if found:
                        plugin_name = wiki_part.pop('plugin')
                        part.deps.append(self.load_plugin(
                            dep, plugin_name, wiki_part))
                        self._part_names.append(dep)
                if not found:
                    raise SnapcraftLogicError(
                        'part name missing {}'.format(dep))

    def _sort_parts(self):
        '''Performs an inneficient but easy to follow sorting of parts.'''
        sorted_parts = []

        while self.all_parts:
            top_part = None
            for part in self.all_parts:
                mentioned = False
                for other in self.all_parts:
                    if part in other.deps:
                        mentioned = True
                        break
                if not mentioned:
                    top_part = part
                    break
            if not top_part:
                raise SnapcraftLogicError(
                    'circular dependency chain found in parts definition')
            sorted_parts = [top_part] + sorted_parts
            self.all_parts.remove(top_part)

        return sorted_parts

    def part_prereqs(self, part_name):
        """Returns a set with all of part_names' prerequisites."""
        return set(self.after_requests.get(part_name, []))

    def part_dependents(self, part_name):
        """Returns a set of all the parts that depend upon part_name."""

        dependents = set()
        for part, prerequisites in self.after_requests.items():
            if part_name in prerequisites:
                dependents.add(part)

        return dependents

    def get_part(self, part_name):
        for part in self.all_parts:
            if part.name == part_name:
                return part

        return None

    def get_project_state(self, step):
        """Returns a dict of states for the given step of each part."""

        state = {}
        for part in self.all_parts:
            state[part.name] = part.get_state(step)

        return state

    def validate_parts(self, part_names):
        for part_name in part_names:
            if part_name not in self._part_names:
                raise EnvironmentError(
                    'The part named {!r} is not defined in '
                    '\'snapcraft.yaml\''.format(part_name))

    def load_plugin(self, part_name, plugin_name, properties):
        part = pluginhandler.load_plugin(
            part_name, plugin_name, properties,
            self._project_options, self._validator.part_schema)

        self.build_tools += part.code.build_packages
        self.build_tools += sources.get_required_packages(part.code.options)
        self.all_parts.append(part)
        return part

    def build_env_for_part(self, part, root_part=True):
        """Return a build env of all the part's dependencies."""

        env = []
        stagedir = self._project_options.stage_dir

        if root_part:
            # this has to come before any {}/usr/bin
            env += _create_pkg_config_override(
                part.bindir, part.installdir, stagedir,
                self._project_options.arch_triplet)
            env += part.env(part.installdir)
            env += _runtime_env(part.installdir,
                                self._project_options.arch_triplet)
            env += _runtime_env(stagedir,
                                self._project_options.arch_triplet)
            env += _build_env(part.installdir,
                              self._project_options.arch_triplet)
            env += _build_env_for_stage(stagedir,
                                        self._project_options.arch_triplet)
        else:
            env += part.env(stagedir)
            env += _runtime_env(stagedir,
                                self._project_options.arch_triplet)

        for dep_part in part.deps:
            env += dep_part.env(stagedir)
            env += self.build_env_for_part(dep_part, root_part=False)

        return env

    def stage_env(self):
        stage_dir = self._project_options.stage_dir
        env = []

        env += _runtime_env(stage_dir, self._project_options.arch_triplet)
        env += _build_env_for_stage(stage_dir,
                                    self._project_options.arch_triplet)
        for part in self.all_parts:
            env += part.env(stage_dir)

        return env

    def snap_env(self):
        snap_dir = self._project_options.snap_dir
        env = []

        env += _runtime_env(snap_dir, self._project_options.arch_triplet)
        dependency_paths = set()
        for part in self.all_parts:
            env += part.env(snap_dir)
            dependency_paths |= part.get_primed_dependency_paths()

        # Dependency paths are only valid if they actually exist. Sorting them
        # here as well so the LD_LIBRARY_PATH is consistent between runs.
        dependency_paths = sorted({
            path for path in dependency_paths if os.path.isdir(path)})

        if dependency_paths:
            # Add more specific LD_LIBRARY_PATH from the dependencies.
            env.append('LD_LIBRARY_PATH="' + ':'.join(dependency_paths) +
                       ':$LD_LIBRARY_PATH"')

        return env
Example #11
0
class Config:
    @property
    def part_names(self):
        return self.parts.part_names

    @property
    def all_parts(self):
        return self.parts.all_parts

    @property
    def _remote_parts(self):
        if getattr(self, '_remote_parts_attr', None) is None:
            self._remote_parts_attr = get_remote_parts()
        return self._remote_parts_attr

    def __init__(self, project_options=None):
        if project_options is None:
            project_options = snapcraft.ProjectOptions()

        self.build_tools = []
        self._project_options = project_options

        self._snapcraft_yaml = get_snapcraft_yaml()
        snapcraft_yaml = _snapcraft_yaml_load(self._snapcraft_yaml)

        self._validator = Validator(snapcraft_yaml)
        self._validator.validate()

        snapcraft_yaml = self._process_remote_parts(snapcraft_yaml)
        snapcraft_yaml = self._expand_filesets(snapcraft_yaml)
        self.data = self._expand_env(snapcraft_yaml)

        self._ensure_no_duplicate_app_aliases()

        # both confinement type and build quality are optionals
        _ensure_confinement_default(self.data, self._validator.schema)
        _ensure_grade_default(self.data, self._validator.schema)

        self.build_tools = self.data.get('build-packages', [])
        self.build_tools.extend(project_options.additional_build_packages)

        self.parts = parts.PartsConfig(self.data, self._project_options,
                                       self._validator, self.build_tools,
                                       self._snapcraft_yaml)

        if 'architectures' not in self.data:
            self.data['architectures'] = [self._project_options.deb_arch]

    def get_metadata(self):
        return {
            'name': self.data['name'],
            'version': self.data['version'],
            'arch': self.data['architectures']
        }

    def _ensure_no_duplicate_app_aliases(self):
        # Prevent multiple apps within a snap from having duplicate alias names
        aliases = []
        for app_name, app in self.data.get('apps', {}).items():
            aliases.extend(app.get('aliases', []))
        seen = set()
        duplicates = set()
        for alias in aliases:
            if alias in seen:
                duplicates.add(alias)
            else:
                seen.add(alias)
        if duplicates:
            raise errors.DuplicateAliasError(aliases=duplicates)

    def get_project_state(self, step):
        """Returns a dict of states for the given step of each part."""

        state = {}
        for part in self.parts.all_parts:
            state[part.name] = states.get_state(part.statedir, step)

        return state

    def stage_env(self):
        stage_dir = self._project_options.stage_dir
        core_dynamic_linker = self._project_options.get_core_dynamic_linker()
        env = []

        env += _runtime_env(stage_dir, self._project_options.arch_triplet)
        env += _build_env_for_stage(stage_dir,
                                    self.data['name'],
                                    self.data['confinement'],
                                    self._project_options.arch_triplet,
                                    core_dynamic_linker=core_dynamic_linker)
        for part in self.parts.all_parts:
            env += part.env(stage_dir)

        return env

    def snap_env(self):
        snap_dir = self._project_options.snap_dir
        env = []

        env += _runtime_env(snap_dir, self._project_options.arch_triplet)
        dependency_paths = set()
        for part in self.parts.all_parts:
            env += part.env(snap_dir)
            dependency_paths |= part.get_primed_dependency_paths()

        # Dependency paths are only valid if they actually exist. Sorting them
        # here as well so the LD_LIBRARY_PATH is consistent between runs.
        dependency_paths = sorted(
            {path
             for path in dependency_paths if os.path.isdir(path)})

        if dependency_paths:
            # Add more specific LD_LIBRARY_PATH from the dependencies.
            env.append('LD_LIBRARY_PATH="' + ':'.join(dependency_paths) +
                       ':$LD_LIBRARY_PATH"')

        return env

    def project_env(self):
        return [
            'SNAPCRAFT_STAGE={}'.format(self._project_options.stage_dir),
            'SNAPCRAFT_PROJECT_NAME={}'.format(self.data['name']),
            'SNAPCRAFT_PROJECT_VERSION={}'.format(self.data['version']),
        ]

    def _expand_env(self, snapcraft_yaml):
        environment_keys = ['name', 'version']
        for key in snapcraft_yaml:
            if any((key == env_key for env_key in environment_keys)):
                continue
            snapcraft_yaml[key] = replace_attr(snapcraft_yaml[key], [
                ('$SNAPCRAFT_PROJECT_NAME', snapcraft_yaml['name']),
                ('$SNAPCRAFT_PROJECT_VERSION', snapcraft_yaml['version']),
                ('$SNAPCRAFT_STAGE', self._project_options.stage_dir),
            ])
        return snapcraft_yaml

    def _expand_filesets(self, snapcraft_yaml):
        parts = snapcraft_yaml.get('parts', {})

        for part_name in parts:
            # FIXME: Remove `snap` from here; it's deprecated
            for step in ('stage', 'snap', 'prime'):
                step_fileset = _expand_filesets_for(step, parts[part_name])
                parts[part_name][step] = step_fileset

        return snapcraft_yaml

    def _process_remote_parts(self, snapcraft_yaml):
        parts = snapcraft_yaml.get('parts', {})
        new_parts = {}

        for part_name in parts:
            if 'plugin' not in parts[part_name]:
                properties = self._remote_parts.compose(
                    part_name, parts[part_name])
                new_parts[part_name] = properties
            else:
                new_parts[part_name] = parts[part_name].copy()

            after_parts = parts[part_name].get('after', [])
            after_remote_parts = [p for p in after_parts if p not in parts]

            for after_part in after_remote_parts:
                properties = self._remote_parts.get_part(after_part)
                new_parts[after_part] = properties

        snapcraft_yaml['parts'] = new_parts
        return snapcraft_yaml
Example #12
0
class Config:

    @property
    def part_names(self):
        return self.parts.part_names

    @property
    def all_parts(self):
        return self.parts.all_parts

    def __init__(self, project_options=None):
        if project_options is None:
            project_options = snapcraft.ProjectOptions()

        self.build_tools = []
        self._project_options = project_options

        self._snapcraft_yaml = _get_snapcraft_yaml()
        snapcraft_yaml = _snapcraft_yaml_load(self._snapcraft_yaml)
        self._validator = Validator(snapcraft_yaml)
        self._validator.validate()
        self.data = self._expand_env(snapcraft_yaml)

        # both confinement type and build quality are optionals
        _ensure_confinement_default(self.data, self._validator.schema)
        _ensure_grade_default(self.data, self._validator.schema)

        self.build_tools = self.data.get('build-packages', [])
        self.build_tools.extend(project_options.additional_build_packages)

        self.parts = parts.PartsConfig(self.data.get('parts', {}),
                                       self._project_options,
                                       self._validator,
                                       self.build_tools,
                                       self._snapcraft_yaml)

        if 'architectures' not in self.data:
            self.data['architectures'] = [self._project_options.deb_arch]

    def get_project_state(self, step):
        """Returns a dict of states for the given step of each part."""

        state = {}
        for part in self.parts.all_parts:
            state[part.name] = part.get_state(step)

        return state

    def stage_env(self):
        stage_dir = self._project_options.stage_dir
        env = []

        env += _runtime_env(stage_dir, self._project_options.arch_triplet)
        env += _build_env_for_stage(stage_dir,
                                    self._project_options.arch_triplet)
        for part in self.parts.all_parts:
            env += part.env(stage_dir)

        return env

    def snap_env(self):
        snap_dir = self._project_options.snap_dir
        env = []

        env += _runtime_env(snap_dir, self._project_options.arch_triplet)
        dependency_paths = set()
        for part in self.parts.all_parts:
            env += part.env(snap_dir)
            dependency_paths |= part.get_primed_dependency_paths()

        # Dependency paths are only valid if they actually exist. Sorting them
        # here as well so the LD_LIBRARY_PATH is consistent between runs.
        dependency_paths = sorted({
            path for path in dependency_paths if os.path.isdir(path)})

        if dependency_paths:
            # Add more specific LD_LIBRARY_PATH from the dependencies.
            env.append('LD_LIBRARY_PATH="' + ':'.join(dependency_paths) +
                       ':$LD_LIBRARY_PATH"')

        return env

    def project_env(self):
        return [
            'SNAPCRAFT_STAGE={}'.format(self._project_options.stage_dir),
            'SNAPCRAFT_PROJECT_NAME={}'.format(self.data['name']),
            'SNAPCRAFT_PROJECT_VERSION={}'.format(self.data['version']),
        ]

    def _expand_env(self, snapcraft_yaml):
        environment_keys = ['name', 'version']
        for key in snapcraft_yaml:
            if any((key == env_key for env_key in environment_keys)):
                continue
            snapcraft_yaml[key] = replace_attr(
                snapcraft_yaml[key],
                [
                    ('$SNAPCRAFT_PROJECT_NAME', snapcraft_yaml['name']),
                    ('$SNAPCRAFT_PROJECT_VERSION', snapcraft_yaml['version']),
                    ('$SNAPCRAFT_STAGE', self._project_options.stage_dir),
                ])
        return snapcraft_yaml
Example #13
0
class Config:
    @property
    def part_names(self):
        return self._part_names

    def __init__(self, project_options=None):
        if project_options is None:
            project_options = snapcraft.ProjectOptions()

        self.build_tools = []
        self.all_parts = []
        self._part_names = []
        self._project_options = project_options
        self.after_requests = {}

        self.data = _snapcraft_yaml_load()

        self._validator = Validator(self.data)
        self._validator.validate()
        _ensure_confinement_default(self.data, self._validator.schema)

        self.build_tools = self.data.get('build-packages', [])
        self.build_tools.extend(project_options.additional_build_packages)

        self._wiki = wiki.Wiki()
        self._process_parts()

    def _process_parts(self):
        for part_name in self.data.get('parts', []):
            self._part_names.append(part_name)
            properties = self.data['parts'][part_name] or {}

            plugin_name = properties.pop('plugin', None)
            if not plugin_name:
                logger.info('Searching the wiki to compose part "{}"'.format(
                    part_name))
                with contextlib.suppress(KeyError):
                    properties = self._wiki.compose(part_name, properties)
                    plugin_name = properties.pop('plugin', None)
                    # The wiki still supports using 'type' for snapcraft 1.x
                    if 'type' in properties:
                        del properties['type']

            if not plugin_name:
                raise PluginNotDefinedError(part_name)

            if 'after' in properties:
                self.after_requests[part_name] = properties.pop('after')

            properties['stage'] = _expand_filesets_for('stage', properties)
            properties['snap'] = _expand_filesets_for('snap', properties)

            if 'filesets' in properties:
                del properties['filesets']

            self.load_plugin(part_name, plugin_name, properties)

        self._compute_part_dependencies()
        self.all_parts = self._sort_parts()

        if 'architectures' not in self.data:
            self.data['architectures'] = [self._project_options.deb_arch]

    def _compute_part_dependencies(self):
        '''Gather the lists of dependencies and adds to all_parts.'''

        for part in self.all_parts:
            dep_names = self.after_requests.get(part.name, [])
            for dep in dep_names:
                found = False
                for i in range(len(self.all_parts)):
                    if dep == self.all_parts[i].name:
                        part.deps.append(self.all_parts[i])
                        found = True
                        break
                if not found:
                    wiki_part = self._wiki.get_part(dep)
                    found = True if wiki_part else False
                    if found:
                        plugin_name = wiki_part.pop('plugin')
                        part.deps.append(
                            self.load_plugin(dep, plugin_name, wiki_part))
                        self._part_names.append(dep)
                if not found:
                    raise SnapcraftLogicError(
                        'part name missing {}'.format(dep))

    def _sort_parts(self):
        '''Performs an inneficient but easy to follow sorting of parts.'''
        sorted_parts = []

        while self.all_parts:
            top_part = None
            for part in self.all_parts:
                mentioned = False
                for other in self.all_parts:
                    if part in other.deps:
                        mentioned = True
                        break
                if not mentioned:
                    top_part = part
                    break
            if not top_part:
                raise SnapcraftLogicError(
                    'circular dependency chain found in parts definition')
            sorted_parts = [top_part] + sorted_parts
            self.all_parts.remove(top_part)

        return sorted_parts

    def part_prereqs(self, part_name):
        """Returns a set with all of part_names' prerequisites."""
        return set(self.after_requests.get(part_name, []))

    def part_dependents(self, part_name):
        """Returns a set of all the parts that depend upon part_name."""

        dependents = set()
        for part, prerequisites in self.after_requests.items():
            if part_name in prerequisites:
                dependents.add(part)

        return dependents

    def get_part(self, part_name):
        for part in self.all_parts:
            if part.name == part_name:
                return part

        return None

    def get_project_state(self, step):
        """Returns a dict of states for the given step of each part."""

        state = {}
        for part in self.all_parts:
            state[part.name] = part.get_state(step)

        return state

    def validate_parts(self, part_names):
        for part_name in part_names:
            if part_name not in self._part_names:
                raise EnvironmentError('The part named {!r} is not defined in '
                                       '\'snapcraft.yaml\''.format(part_name))

    def load_plugin(self, part_name, plugin_name, properties):
        part = pluginhandler.load_plugin(part_name, plugin_name, properties,
                                         self._project_options,
                                         self._validator.part_schema)

        self.build_tools += part.code.build_packages
        self.build_tools += sources.get_required_packages(part.code.options)
        self.all_parts.append(part)
        return part

    def build_env_for_part(self, part, root_part=True):
        """Return a build env of all the part's dependencies."""

        env = []
        stagedir = self._project_options.stage_dir

        if root_part:
            # this has to come before any {}/usr/bin
            env += _create_pkg_config_override(
                part.bindir, part.installdir, stagedir,
                self._project_options.arch_triplet)
            env += part.env(part.installdir)
            env += _runtime_env(part.installdir,
                                self._project_options.arch_triplet)
            env += _runtime_env(stagedir, self._project_options.arch_triplet)
            env += _build_env(part.installdir,
                              self._project_options.arch_triplet)
            env += _build_env_for_stage(stagedir,
                                        self._project_options.arch_triplet)
        else:
            env += part.env(stagedir)
            env += _runtime_env(stagedir, self._project_options.arch_triplet)

        for dep_part in part.deps:
            env += dep_part.env(stagedir)
            env += self.build_env_for_part(dep_part, root_part=False)

        return env

    def stage_env(self):
        stage_dir = self._project_options.stage_dir
        env = []

        env += _runtime_env(stage_dir, self._project_options.arch_triplet)
        env += _build_env_for_stage(stage_dir,
                                    self._project_options.arch_triplet)
        for part in self.all_parts:
            env += part.env(stage_dir)

        return env

    def snap_env(self):
        snap_dir = self._project_options.snap_dir
        env = []

        env += _runtime_env(snap_dir, self._project_options.arch_triplet)
        dependency_paths = set()
        for part in self.all_parts:
            env += part.env(snap_dir)
            dependency_paths |= part.get_primed_dependency_paths()

        # Dependency paths are only valid if they actually exist. Sorting them
        # here as well so the LD_LIBRARY_PATH is consistent between runs.
        dependency_paths = sorted(
            {path
             for path in dependency_paths if os.path.isdir(path)})

        if dependency_paths:
            # Add more specific LD_LIBRARY_PATH from the dependencies.
            env.append('LD_LIBRARY_PATH="' + ':'.join(dependency_paths) +
                       ':$LD_LIBRARY_PATH"')

        return env
Example #14
0
class Config:

    @property
    def part_names(self):
        return self.parts.part_names

    @property
    def all_parts(self):
        return self.parts.all_parts

    def __init__(self, project_options=None):
        if project_options is None:
            project_options = snapcraft.ProjectOptions()

        self.build_tools = []
        self._project_options = project_options

        self._snapcraft_yaml = _get_snapcraft_yaml()
        self.data = _snapcraft_yaml_load(self._snapcraft_yaml)

        self._validator = Validator(self.data)
        self._validator.validate()
        _ensure_confinement_default(self.data, self._validator.schema)

        self.build_tools = self.data.get('build-packages', [])
        self.build_tools.extend(project_options.additional_build_packages)

        self.parts = parts.PartsConfig(self.data.get('parts', {}),
                                       self._project_options,
                                       self._validator,
                                       self.build_tools,
                                       self._snapcraft_yaml)

        if 'architectures' not in self.data:
            self.data['architectures'] = [self._project_options.deb_arch]

    def get_project_state(self, step):
        """Returns a dict of states for the given step of each part."""

        state = {}
        for part in self.parts.all_parts:
            state[part.name] = part.get_state(step)

        return state

    def stage_env(self):
        stage_dir = self._project_options.stage_dir
        env = []

        env += _runtime_env(stage_dir, self._project_options.arch_triplet)
        env += _build_env_for_stage(stage_dir,
                                    self._project_options.arch_triplet)
        for part in self.parts.all_parts:
            env += part.env(stage_dir)

        return env

    def snap_env(self):
        snap_dir = self._project_options.snap_dir
        env = []

        env += _runtime_env(snap_dir, self._project_options.arch_triplet)
        dependency_paths = set()
        for part in self.parts.all_parts:
            env += part.env(snap_dir)
            dependency_paths |= part.get_primed_dependency_paths()

        # Dependency paths are only valid if they actually exist. Sorting them
        # here as well so the LD_LIBRARY_PATH is consistent between runs.
        dependency_paths = sorted({
            path for path in dependency_paths if os.path.isdir(path)})

        if dependency_paths:
            # Add more specific LD_LIBRARY_PATH from the dependencies.
            env.append('LD_LIBRARY_PATH="' + ':'.join(dependency_paths) +
                       ':$LD_LIBRARY_PATH"')

        return env