Beispiel #1
0
def parse_layout_conf(repo_location, repo_name=None):
	eapi = read_corresponding_eapi_file(os.path.join(repo_location, REPO_NAME_LOC))

	layout_filename = os.path.join(repo_location, "metadata", "layout.conf")
	layout_file = KeyValuePairFileLoader(layout_filename, None, None)
	layout_data, layout_errors = layout_file.load()

	data = {}

	# None indicates abscence of a masters setting, which later code uses
	# to trigger a backward compatibility fallback that sets an implicit
	# master. In order to avoid this fallback behavior, layout.conf can
	# explicitly set masters to an empty value, which will result in an
	# empty tuple here instead of None.
	masters = layout_data.get('masters')
	if masters is not None:
		masters = tuple(masters.split())
	data['masters'] = masters
	data['aliases'] = tuple(layout_data.get('aliases', '').split())

	data['eapis-banned'] = tuple(layout_data.get('eapis-banned', '').split())
	data['eapis-deprecated'] = tuple(layout_data.get('eapis-deprecated', '').split())

	data['sign-commit'] = layout_data.get('sign-commits', 'false').lower() \
		== 'true'

	data['sign-manifest'] = layout_data.get('sign-manifests', 'true').lower() \
		== 'true'

	data['thin-manifest'] = layout_data.get('thin-manifests', 'false').lower() \
		== 'true'

	data['repo-name'] = _gen_valid_repo(layout_data.get('repo-name', ''))

	manifest_policy = layout_data.get('use-manifests', 'strict').lower()
	data['allow-missing-manifest'] = manifest_policy != 'strict'
	data['create-manifest'] = manifest_policy != 'false'
	data['disable-manifest'] = manifest_policy == 'false'

	# for compatibility w/ PMS, fallback to pms; but also check if the
	# cache exists or not.
	cache_formats = layout_data.get('cache-formats', '').lower().split()
	if not cache_formats:
		# Auto-detect cache formats, and prefer md5-cache if available.
		# This behavior was deployed in portage-2.1.11.14, so that the
		# default egencache format could eventually be changed to md5-dict
		# in portage-2.1.11.32. WARNING: Versions prior to portage-2.1.11.14
		# will NOT recognize md5-dict format unless it is explicitly
		# listed in layout.conf.
		cache_formats = []
		if os.path.isdir(os.path.join(repo_location, 'metadata', 'md5-cache')):
			cache_formats.append('md5-dict')
		if os.path.isdir(os.path.join(repo_location, 'metadata', 'cache')):
			cache_formats.append('pms')
	data['cache-formats'] = tuple(cache_formats)

	manifest_hashes = layout_data.get('manifest-hashes')
	manifest_required_hashes = layout_data.get('manifest-required-hashes')

	if manifest_required_hashes is not None and manifest_hashes is None:
		repo_name = _get_repo_name(repo_location, cached=repo_name)
		warnings.warn((_("Repository named '%(repo_name)s' specifies "
			"'manifest-required-hashes' setting without corresponding "
			"'manifest-hashes'. Portage will default it to match "
			"the required set but please add the missing entry "
			"to: %(layout_filename)s") %
			{"repo_name": repo_name or 'unspecified',
			"layout_filename":layout_filename}),
			SyntaxWarning)
		manifest_hashes = manifest_required_hashes

	if manifest_hashes is not None:
		# require all the hashes unless specified otherwise
		if manifest_required_hashes is None:
			manifest_required_hashes = manifest_hashes

		manifest_required_hashes = frozenset(manifest_required_hashes.upper().split())
		manifest_hashes = frozenset(manifest_hashes.upper().split())
		missing_required_hashes = manifest_required_hashes.difference(
			manifest_hashes)
		if missing_required_hashes:
			repo_name = _get_repo_name(repo_location, cached=repo_name)
			warnings.warn((_("Repository named '%(repo_name)s' has a "
				"'manifest-hashes' setting that does not contain "
				"the '%(hash)s' hashes which are listed in "
				"'manifest-required-hashes'. Please fix that file "
				"if you want to generate valid manifests for this "
				"repository: %(layout_filename)s") %
				{"repo_name": repo_name or 'unspecified',
				"hash": ' '.join(missing_required_hashes),
				"layout_filename":layout_filename}),
				SyntaxWarning)
		unsupported_hashes = manifest_hashes.difference(
			get_valid_checksum_keys())
		if unsupported_hashes:
			repo_name = _get_repo_name(repo_location, cached=repo_name)
			warnings.warn((_("Repository named '%(repo_name)s' has a "
				"'manifest-hashes' setting that contains one "
				"or more hash types '%(hashes)s' which are not supported by "
				"this portage version. You will have to upgrade "
				"portage if you want to generate valid manifests for "
				"this repository: %(layout_filename)s") %
				{"repo_name": repo_name or 'unspecified',
				"hashes":" ".join(sorted(unsupported_hashes)),
				"layout_filename":layout_filename}),
				DeprecationWarning)

	data['manifest-hashes'] = manifest_hashes
	data['manifest-required-hashes'] = manifest_required_hashes

	data['update-changelog'] = layout_data.get('update-changelog', 'false').lower() \
		== 'true'

	raw_formats = layout_data.get('profile-formats')
	if raw_formats is None:
		if eapi_allows_directories_on_profile_level_and_repository_level(eapi):
			raw_formats = ('portage-1',)
		else:
			raw_formats = ('portage-1-compat',)
	else:
		raw_formats = set(raw_formats.split())
		unknown = raw_formats.difference(_valid_profile_formats)
		if unknown:
			repo_name = _get_repo_name(repo_location, cached=repo_name)
			warnings.warn((_("Repository named '%(repo_name)s' has unsupported "
				"profiles in use ('profile-formats = %(unknown_fmts)s' setting in "
				"'%(layout_filename)s; please upgrade portage.") %
				dict(repo_name=repo_name or 'unspecified',
				layout_filename=layout_filename,
				unknown_fmts=" ".join(unknown))),
				DeprecationWarning)
		raw_formats = tuple(raw_formats.intersection(_valid_profile_formats))
	data['profile-formats'] = raw_formats

	try:
		eapi = layout_data['profile_eapi_when_unspecified']
	except KeyError:
		pass
	else:
		if 'profile-default-eapi' not in raw_formats:
			warnings.warn((_("Repository named '%(repo_name)s' has "
				"profile_eapi_when_unspecified setting in "
				"'%(layout_filename)s', but 'profile-default-eapi' is "
				"not listed in the profile-formats field. Please "
				"report this issue to the repository maintainer.") %
				dict(repo_name=repo_name or 'unspecified',
				layout_filename=layout_filename)),
				SyntaxWarning)
		elif not portage.eapi_is_supported(eapi):
			warnings.warn((_("Repository named '%(repo_name)s' has "
				"unsupported EAPI '%(eapi)s' setting in "
				"'%(layout_filename)s'; please upgrade portage.") %
				dict(repo_name=repo_name or 'unspecified',
				eapi=eapi, layout_filename=layout_filename)),
				SyntaxWarning)
		else:
			data['profile_eapi_when_unspecified'] = eapi

	return data, layout_errors
Beispiel #2
0
	def __init__(self, name, repo_opts, local_config=True):
		"""Build a RepoConfig with options in repo_opts
		   Try to read repo_name in repository location, but if
		   it is not found use variable name as repository name"""

		force = repo_opts.get('force')
		if force is not None:
			force = tuple(force.split())
		self.force = force
		if force is None:
			force = ()

		self.local_config = local_config

		if local_config or 'aliases' in force:
			aliases = repo_opts.get('aliases')
			if aliases is not None:
				aliases = tuple(aliases.split())
		else:
			aliases = None

		self.aliases = aliases

		if local_config or 'eclass-overrides' in force:
			eclass_overrides = repo_opts.get('eclass-overrides')
			if eclass_overrides is not None:
				eclass_overrides = tuple(eclass_overrides.split())
		else:
			eclass_overrides = None

		self.eclass_overrides = eclass_overrides
		# Eclass databases and locations are computed later.
		self.eclass_db = None
		self.eclass_locations = None

		if local_config or 'masters' in force:
			# Masters from repos.conf override layout.conf.
			masters = repo_opts.get('masters')
			if masters is not None:
				masters = tuple(masters.split())
		else:
			masters = None

		self.masters = masters

		#The main-repo key makes only sense for the 'DEFAULT' section.
		self.main_repo = repo_opts.get('main-repo')

		priority = repo_opts.get('priority')
		if priority is not None:
			try:
				priority = int(priority)
			except ValueError:
				priority = None
		self.priority = priority

		sync_type = repo_opts.get('sync-type')
		if sync_type is not None:
			sync_type = sync_type.strip()
		self.sync_type = sync_type or None

		sync_umask = repo_opts.get('sync-umask')
		if sync_umask is not None:
			sync_umask = sync_umask.strip()
		self.sync_umask = sync_umask or None

		sync_uri = repo_opts.get('sync-uri')
		if sync_uri is not None:
			sync_uri = sync_uri.strip()
		self.sync_uri = sync_uri or None

		sync_user = repo_opts.get('sync-user')
		if sync_user is not None:
			sync_user = sync_user.strip()
		self.sync_user = sync_user or None

		auto_sync = repo_opts.get('auto-sync', 'yes')
		if auto_sync is not None:
			auto_sync = auto_sync.strip().lower()
		self.auto_sync = auto_sync

		self.clone_depth = repo_opts.get('clone-depth')
		self.sync_depth = repo_opts.get('sync-depth')

		if self.sync_depth is not None:
			warnings.warn(_("repos.conf: sync-depth is deprecated,"
				" use clone-depth instead"))

		self.sync_hooks_only_on_change = repo_opts.get(
			'sync-hooks-only-on-change', 'false').lower() == 'true'

		self.strict_misc_digests = repo_opts.get(
			'strict-misc-digests', 'true').lower() == 'true'

		self.sync_openpgp_key_path = repo_opts.get(
			'sync-openpgp-key-path', None)

		for k in ('sync_openpgp_key_refresh_retry_count',
			'sync_openpgp_key_refresh_retry_delay_max',
			'sync_openpgp_key_refresh_retry_delay_exp_base',
			'sync_openpgp_key_refresh_retry_delay_mult',
			'sync_openpgp_key_refresh_retry_overall_timeout'):
			setattr(self, k, repo_opts.get(k.replace('_', '-'), None))

		self.module_specific_options = {}

		# Not implemented.
		format = repo_opts.get('format')
		if format is not None:
			format = format.strip()
		self.format = format

		location = repo_opts.get('location')
		if location is not None and location.strip():
			if os.path.isdir(location) or portage._sync_mode:
				location = os.path.realpath(location)
		else:
			location = None
		self.location = location

		missing = True
		self.name = name
		if self.location is not None:
			self.name, missing = self._read_valid_repo_name(self.location)
			if missing:
				# The name from repos.conf has to be used here for
				# things like emerge-webrsync to work when the repo
				# is empty (bug #484950).
				if name is not None:
					self.name = name
				if portage._sync_mode:
					missing = False

		elif name == "DEFAULT":
			missing = False

		self.eapi = None
		self.missing_repo_name = missing
		# sign_commit is disabled by default, since it requires Git >=1.7.9,
		# and key_id configured by `git config user.signingkey key_id`
		self.sign_commit = False
		self.sign_manifest = True
		self.thin_manifest = False
		self.allow_missing_manifest = False
		self.allow_provide_virtual = False
		self.create_manifest = True
		self.disable_manifest = False
		self.manifest_hashes = None
		self.manifest_required_hashes = None
		self.update_changelog = False
		self.cache_formats = None
		self.portage1_profiles = True
		self.portage1_profiles_compat = False
		self.find_invalid_path_char = _find_invalid_path_char
		self._masters_orig = None

		# Parse layout.conf.
		if self.location:
			layout_data = parse_layout_conf(self.location, self.name)[0]
			self._masters_orig = layout_data['masters']

			# layout.conf masters may be overridden here if we have a masters
			# setting from the user's repos.conf
			if self.masters is None:
				self.masters = layout_data['masters']

			if (local_config or 'aliases' in force) and layout_data['aliases']:
				aliases = self.aliases
				if aliases is None:
					aliases = ()
				# repos.conf aliases come after layout.conf aliases, giving
				# them the ability to do incremental overrides
				self.aliases = layout_data['aliases'] + tuple(aliases)

			if layout_data['repo-name']:
				# allow layout.conf to override repository name
				# useful when having two copies of the same repo enabled
				# to avoid modifying profiles/repo_name in one of them
				self.name = layout_data['repo-name']
				self.missing_repo_name = False

			for value in ('allow-missing-manifest',
				'cache-formats',
				'create-manifest', 'disable-manifest', 'manifest-hashes',
				'manifest-required-hashes', 'profile-formats',
				'sign-commit', 'sign-manifest', 'thin-manifest', 'update-changelog'):
				setattr(self, value.lower().replace("-", "_"), layout_data[value])

			# If profile-formats specifies a default EAPI, then set
			# self.eapi to that, otherwise set it to "0" as specified
			# by PMS.
			self.eapi = layout_data.get(
				'profile_eapi_when_unspecified', '0')

			eapi = read_corresponding_eapi_file(
				os.path.join(self.location, REPO_NAME_LOC),
				default=self.eapi)

			self.portage1_profiles = eapi_allows_directories_on_profile_level_and_repository_level(eapi) or \
				any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats'])
			self.portage1_profiles_compat = not eapi_allows_directories_on_profile_level_and_repository_level(eapi) and \
				layout_data['profile-formats'] == ('portage-1-compat',)

			self._eapis_banned = frozenset(layout_data['eapis-banned'])
			self._eapis_deprecated = frozenset(layout_data['eapis-deprecated'])
Beispiel #3
0
	def __init__(self, name, repo_opts, local_config=True):
		"""Build a RepoConfig with options in repo_opts
		   Try to read repo_name in repository location, but if
		   it is not found use variable name as repository name"""

		force = repo_opts.get('force')
		if force is not None:
			force = tuple(force.split())
		self.force = force
		if force is None:
			force = ()

		self.local_config = local_config

		if local_config or 'aliases' in force:
			aliases = repo_opts.get('aliases')
			if aliases is not None:
				aliases = tuple(aliases.split())
		else:
			aliases = None

		self.aliases = aliases

		if local_config or 'eclass-overrides' in force:
			eclass_overrides = repo_opts.get('eclass-overrides')
			if eclass_overrides is not None:
				eclass_overrides = tuple(eclass_overrides.split())
		else:
			eclass_overrides = None

		self.eclass_overrides = eclass_overrides
		# Eclass databases and locations are computed later.
		self.eclass_db = None
		self.eclass_locations = None

		if local_config or 'masters' in force:
			# Masters from repos.conf override layout.conf.
			masters = repo_opts.get('masters')
			if masters is not None:
				masters = tuple(masters.split())
		else:
			masters = None

		self.masters = masters

		#The main-repo key makes only sense for the 'DEFAULT' section.
		self.main_repo = repo_opts.get('main-repo')

		priority = repo_opts.get('priority')
		if priority is not None:
			try:
				priority = int(priority)
			except ValueError:
				priority = None
		self.priority = priority

		sync_cvs_repo = repo_opts.get('sync-cvs-repo')
		if sync_cvs_repo is not None:
			sync_cvs_repo = sync_cvs_repo.strip()
		self.sync_cvs_repo = sync_cvs_repo or None

		sync_type = repo_opts.get('sync-type')
		if sync_type is not None:
			sync_type = sync_type.strip()
		self.sync_type = sync_type or None

		sync_uri = repo_opts.get('sync-uri')
		if sync_uri is not None:
			sync_uri = sync_uri.strip()
		self.sync_uri = sync_uri or None

		# Not implemented.
		format = repo_opts.get('format')
		if format is not None:
			format = format.strip()
		self.format = format

		location = repo_opts.get('location')
		self.user_location = location
		if location is not None and location.strip():
			if os.path.isdir(location) or portage._sync_mode:
				location = os.path.realpath(location)
		else:
			location = None
		self.location = location

		eapi = None
		missing = True
		self.name = name
		if self.location is not None:
			eapi = read_corresponding_eapi_file(os.path.join(self.location, REPO_NAME_LOC))
			self.name, missing = self._read_valid_repo_name(self.location)
			if missing:
				# The name from repos.conf has to be used here for
				# things like emerge-webrsync to work when the repo
				# is empty (bug #484950).
				if name is not None:
					self.name = name
				if portage._sync_mode:
					missing = False

		elif name == "DEFAULT":
			missing = False

		self.eapi = eapi
		self.missing_repo_name = missing
		# sign_commit is disabled by default, since it requires Git >=1.7.9,
		# and key_id configured by `git config user.signingkey key_id`
		self.sign_commit = False
		self.sign_manifest = True
		self.thin_manifest = False
		self.allow_missing_manifest = False
		self.allow_provide_virtual = False
		self.create_manifest = True
		self.disable_manifest = False
		self.manifest_hashes = None
		self.update_changelog = False
		self.cache_formats = None
		self.portage1_profiles = True
		self.portage1_profiles_compat = False
		self.find_invalid_path_char = _find_invalid_path_char
		self._masters_orig = None

		# Parse layout.conf.
		if self.location:
			layout_data = parse_layout_conf(self.location, self.name)[0]
			self._masters_orig = layout_data['masters']

			# layout.conf masters may be overridden here if we have a masters
			# setting from the user's repos.conf
			if self.masters is None:
				self.masters = layout_data['masters']

			if (local_config or 'aliases' in force) and layout_data['aliases']:
				aliases = self.aliases
				if aliases is None:
					aliases = ()
				# repos.conf aliases come after layout.conf aliases, giving
				# them the ability to do incremental overrides
				self.aliases = layout_data['aliases'] + tuple(aliases)

			if layout_data['repo-name']:
				# allow layout.conf to override repository name
				# useful when having two copies of the same repo enabled
				# to avoid modifying profiles/repo_name in one of them
				self.name = layout_data['repo-name']

			for value in ('allow-missing-manifest',
				'allow-provide-virtual', 'cache-formats',
				'create-manifest', 'disable-manifest', 'manifest-hashes',
				'profile-formats',
				'sign-commit', 'sign-manifest', 'thin-manifest', 'update-changelog'):
				setattr(self, value.lower().replace("-", "_"), layout_data[value])

			self.portage1_profiles = eapi_allows_directories_on_profile_level_and_repository_level(eapi) or \
				any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats'])
			self.portage1_profiles_compat = not eapi_allows_directories_on_profile_level_and_repository_level(eapi) and \
				layout_data['profile-formats'] == ('portage-1-compat',)

			self._eapis_banned = frozenset(layout_data['eapis-banned'])
			self._eapis_deprecated = frozenset(layout_data['eapis-deprecated'])
Beispiel #4
0
	def _addProfile(self, currentPath, repositories, known_repos):
		current_abs_path = os.path.abspath(currentPath)
		allow_directories = True
		allow_parent_colon = True
		repo_loc = None
		compat_mode = False

		eapi_file = os.path.join(currentPath, "eapi")
		eapi = "0"
		f = None
		try:
			f = io.open(_unicode_encode(eapi_file,
				encoding=_encodings['fs'], errors='strict'),
				mode='r', encoding=_encodings['content'], errors='replace')
			eapi = f.readline().strip()
		except IOError:
			pass
		else:
			if not eapi_is_supported(eapi):
				raise ParseError(_(
					"Profile contains unsupported "
					"EAPI '%s': '%s'") % \
					(eapi, os.path.realpath(eapi_file),))
		finally:
			if f is not None:
				f.close()

		intersecting_repos = [x for x in known_repos if current_abs_path.startswith(x[0])]
		if intersecting_repos:
			# protect against nested repositories.  Insane configuration, but the longest
			# path will be the correct one.
			repo_loc, layout_data = max(intersecting_repos, key=lambda x:len(x[0]))
			allow_directories = eapi_allows_directories_on_profile_level_and_repository_level(eapi) or \
				any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats'])
			compat_mode = not eapi_allows_directories_on_profile_level_and_repository_level(eapi) and \
				layout_data['profile-formats'] == ('portage-1-compat',)
			allow_parent_colon = any(x in _allow_parent_colon
				for x in layout_data['profile-formats'])

		if compat_mode:
			offenders = _PORTAGE1_DIRECTORIES.intersection(os.listdir(currentPath))
			offenders = sorted(x for x in offenders
				if os.path.isdir(os.path.join(currentPath, x)))
			if offenders:
				warnings.warn(_("Profile '%(profile_path)s' in repository "
					"'%(repo_name)s' is implicitly using 'portage-1' profile format, but "
					"the repository profiles are not marked as that format.  This will break "
					"in the future.  Please either convert the following paths "
					"to files, or add\nprofile-formats = portage-1\nto the "
					"repositories layout.conf.  Files: '%(files)s'\n")
					% dict(profile_path=currentPath, repo_name=repo_loc,
						files=', '.join(offenders)))

		parentsFile = os.path.join(currentPath, "parent")
		if os.path.exists(parentsFile):
			parents = grabfile(parentsFile)
			if not parents:
				raise ParseError(
					_("Empty parent file: '%s'") % parentsFile)
			for parentPath in parents:
				abs_parent = parentPath[:1] == os.sep
				if not abs_parent and allow_parent_colon:
					parentPath = self._expand_parent_colon(parentsFile,
						parentPath, repo_loc, repositories)

				# NOTE: This os.path.join() call is intended to ignore
				# currentPath if parentPath is already absolute.
				parentPath = normalize_path(os.path.join(
					currentPath, parentPath))

				if abs_parent or repo_loc is None or \
					not parentPath.startswith(repo_loc):
					# It seems that this parent may point outside
					# of the current repo, so realpath it.
					parentPath = os.path.realpath(parentPath)

				if os.path.exists(parentPath):
					self._addProfile(parentPath, repositories, known_repos)
				else:
					raise ParseError(
						_("Parent '%s' not found: '%s'") %  \
						(parentPath, parentsFile))

		self.profiles.append(currentPath)
		self.profiles_complex.append(
			_profile_node(currentPath, allow_directories))
Beispiel #5
0
    def __init__(self, name, repo_opts, local_config=True):
        """Build a RepoConfig with options in repo_opts
		   Try to read repo_name in repository location, but if
		   it is not found use variable name as repository name"""

        force = repo_opts.get('force')
        if force is not None:
            force = tuple(force.split())
        self.force = force
        if force is None:
            force = ()

        self.local_config = local_config

        if local_config or 'aliases' in force:
            aliases = repo_opts.get('aliases')
            if aliases is not None:
                aliases = tuple(aliases.split())
        else:
            aliases = None

        self.aliases = aliases

        if local_config or 'eclass-overrides' in force:
            eclass_overrides = repo_opts.get('eclass-overrides')
            if eclass_overrides is not None:
                eclass_overrides = tuple(eclass_overrides.split())
        else:
            eclass_overrides = None

        self.eclass_overrides = eclass_overrides
        # Eclass databases and locations are computed later.
        self.eclass_db = None
        self.eclass_locations = None

        if local_config or 'masters' in force:
            # Masters from repos.conf override layout.conf.
            masters = repo_opts.get('masters')
            if masters is not None:
                masters = tuple(masters.split())
        else:
            masters = None

        self.masters = masters

        #The main-repo key makes only sense for the 'DEFAULT' section.
        self.main_repo = repo_opts.get('main-repo')

        priority = repo_opts.get('priority')
        if priority is not None:
            try:
                priority = int(priority)
            except ValueError:
                priority = None
        self.priority = priority

        sync_type = repo_opts.get('sync-type')
        if sync_type is not None:
            sync_type = sync_type.strip()
        self.sync_type = sync_type or None

        sync_umask = repo_opts.get('sync-umask')
        if sync_umask is not None:
            sync_umask = sync_umask.strip()
        self.sync_umask = sync_umask or None

        sync_uri = repo_opts.get('sync-uri')
        if sync_uri is not None:
            sync_uri = sync_uri.strip()
        self.sync_uri = sync_uri or None

        sync_user = repo_opts.get('sync-user')
        if sync_user is not None:
            sync_user = sync_user.strip()
        self.sync_user = sync_user or None

        auto_sync = repo_opts.get('auto-sync', 'yes')
        if auto_sync is not None:
            auto_sync = auto_sync.strip().lower()
        self.auto_sync = auto_sync

        self.sync_depth = repo_opts.get('sync-depth')
        self.sync_hooks_only_on_change = repo_opts.get(
            'sync-hooks-only-on-change', 'false').lower() == 'true'

        self.module_specific_options = {}

        # Not implemented.
        format = repo_opts.get('format')
        if format is not None:
            format = format.strip()
        self.format = format

        location = repo_opts.get('location')
        if location is not None and location.strip():
            if os.path.isdir(location) or portage._sync_mode:
                location = os.path.realpath(location)
        else:
            location = None
        self.location = location

        missing = True
        self.name = name
        if self.location is not None:
            self.name, missing = self._read_valid_repo_name(self.location)
            if missing:
                # The name from repos.conf has to be used here for
                # things like emerge-webrsync to work when the repo
                # is empty (bug #484950).
                if name is not None:
                    self.name = name
                if portage._sync_mode:
                    missing = False

        elif name == "DEFAULT":
            missing = False

        self.eapi = None
        self.missing_repo_name = missing
        # sign_commit is disabled by default, since it requires Git >=1.7.9,
        # and key_id configured by `git config user.signingkey key_id`
        self.sign_commit = False
        self.sign_manifest = True
        self.thin_manifest = False
        self.allow_missing_manifest = False
        self.allow_provide_virtual = False
        self.create_manifest = True
        self.disable_manifest = False
        self.manifest_hashes = None
        self.update_changelog = False
        self.cache_formats = None
        self.portage1_profiles = True
        self.portage1_profiles_compat = False
        self.find_invalid_path_char = _find_invalid_path_char
        self._masters_orig = None

        # Parse layout.conf.
        if self.location:
            layout_data = parse_layout_conf(self.location, self.name)[0]
            self._masters_orig = layout_data['masters']

            # layout.conf masters may be overridden here if we have a masters
            # setting from the user's repos.conf
            if self.masters is None:
                self.masters = layout_data['masters']

            if (local_config or 'aliases' in force) and layout_data['aliases']:
                aliases = self.aliases
                if aliases is None:
                    aliases = ()
                # repos.conf aliases come after layout.conf aliases, giving
                # them the ability to do incremental overrides
                self.aliases = layout_data['aliases'] + tuple(aliases)

            if layout_data['repo-name']:
                # allow layout.conf to override repository name
                # useful when having two copies of the same repo enabled
                # to avoid modifying profiles/repo_name in one of them
                self.name = layout_data['repo-name']
                self.missing_repo_name = False

            for value in ('allow-missing-manifest', 'allow-provide-virtual',
                          'cache-formats', 'create-manifest',
                          'disable-manifest', 'manifest-hashes',
                          'profile-formats', 'sign-commit', 'sign-manifest',
                          'thin-manifest', 'update-changelog'):
                setattr(self,
                        value.lower().replace("-", "_"), layout_data[value])

            # If profile-formats specifies a default EAPI, then set
            # self.eapi to that, otherwise set it to "0" as specified
            # by PMS.
            self.eapi = layout_data.get('profile_eapi_when_unspecified', '0')

            eapi = read_corresponding_eapi_file(os.path.join(
                self.location, REPO_NAME_LOC),
                                                default=self.eapi)

            self.portage1_profiles = eapi_allows_directories_on_profile_level_and_repository_level(eapi) or \
             any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats'])
            self.portage1_profiles_compat = not eapi_allows_directories_on_profile_level_and_repository_level(eapi) and \
             layout_data['profile-formats'] == ('portage-1-compat',)

            self._eapis_banned = frozenset(layout_data['eapis-banned'])
            self._eapis_deprecated = frozenset(layout_data['eapis-deprecated'])
Beispiel #6
0
    def _addProfile(self, currentPath, repositories, known_repos):
        current_abs_path = os.path.abspath(currentPath)
        allow_directories = True
        allow_parent_colon = True
        repo_loc = None
        compat_mode = False
        current_formats = ()
        eapi = None

        intersecting_repos = [
            x for x in known_repos if current_abs_path.startswith(x[0])
        ]
        if intersecting_repos:
            # Handle nested repositories. The longest path
            # will be the correct one.
            repo_loc, layout_data = max(intersecting_repos,
                                        key=lambda x: len(x[0]))
            eapi = layout_data.get("profile_eapi_when_unspecified")

        eapi_file = os.path.join(currentPath, "eapi")
        eapi = eapi or "0"
        f = None
        try:
            f = io.open(_unicode_encode(eapi_file,
                                        encoding=_encodings['fs'],
                                        errors='strict'),
                        mode='r',
                        encoding=_encodings['content'],
                        errors='replace')
            eapi = f.readline().strip()
        except IOError:
            pass
        else:
            if not eapi_is_supported(eapi):
                raise ParseError(_(
                 "Profile contains unsupported "
                 "EAPI '%s': '%s'") % \
                 (eapi, os.path.realpath(eapi_file),))
        finally:
            if f is not None:
                f.close()

        if intersecting_repos:
            allow_directories = eapi_allows_directories_on_profile_level_and_repository_level(eapi) or \
             any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats'])
            compat_mode = not eapi_allows_directories_on_profile_level_and_repository_level(eapi) and \
             layout_data['profile-formats'] == ('portage-1-compat',)
            allow_parent_colon = any(x in _allow_parent_colon
                                     for x in layout_data['profile-formats'])
            current_formats = tuple(layout_data['profile-formats'])

        if compat_mode:
            offenders = _PORTAGE1_DIRECTORIES.intersection(
                os.listdir(currentPath))
            offenders = sorted(x for x in offenders
                               if os.path.isdir(os.path.join(currentPath, x)))
            if offenders:
                warnings.warn(
                    _("\nThe selected profile is implicitly using the 'portage-1' format:\n"
                      "\tprofile = %(profile_path)s\n"
                      "But this repository is not using that format:\n"
                      "\trepo = %(repo_name)s\n"
                      "This will break in the future.  Please convert these dirs to files:\n"
                      "\t%(files)s\n"
                      "Or, add this line to the repository's layout.conf:\n"
                      "\tprofile-formats = portage-1") %
                    dict(profile_path=currentPath,
                         repo_name=repo_loc,
                         files='\n\t'.join(offenders)))

        parentsFile = os.path.join(currentPath, "parent")
        if exists_raise_eaccess(parentsFile):
            parents = grabfile(parentsFile)
            if not parents:
                raise ParseError(_("Empty parent file: '%s'") % parentsFile)
            for parentPath in parents:
                abs_parent = parentPath[:1] == os.sep
                if not abs_parent and allow_parent_colon:
                    parentPath = self._expand_parent_colon(
                        parentsFile, parentPath, repo_loc, repositories)

                # NOTE: This os.path.join() call is intended to ignore
                # currentPath if parentPath is already absolute.
                parentPath = normalize_path(
                    os.path.join(currentPath, parentPath))

                if abs_parent or repo_loc is None or \
                 not parentPath.startswith(repo_loc):
                    # It seems that this parent may point outside
                    # of the current repo, so realpath it.
                    parentPath = os.path.realpath(parentPath)

                if exists_raise_eaccess(parentPath):
                    self._addProfile(parentPath, repositories, known_repos)
                else:
                    raise ParseError(
                     _("Parent '%s' not found: '%s'") %  \
                     (parentPath, parentsFile))

        self.profiles.append(currentPath)
        self.profiles_complex.append(
            _profile_node(currentPath, allow_directories, False,
                          current_formats, eapi, 'build-id'
                          in current_formats))
Beispiel #7
0
	def _addProfile(self, currentPath, repositories, known_repos):
		current_abs_path = os.path.abspath(currentPath)
		allow_directories = True
		allow_parent_colon = True
		repo_loc = None
		compat_mode = False
		current_formats = ()
		eapi = None

		intersecting_repos = [x for x in known_repos
			if current_abs_path.startswith(x[0])]
		if intersecting_repos:
			# Handle nested repositories. The longest path
			# will be the correct one.
			repo_loc, layout_data = max(intersecting_repos,
				key=lambda x:len(x[0]))
			eapi = layout_data.get("profile_eapi_when_unspecified")

		eapi_file = os.path.join(currentPath, "eapi")
		eapi = eapi or "0"
		f = None
		try:
			f = io.open(_unicode_encode(eapi_file,
				encoding=_encodings['fs'], errors='strict'),
				mode='r', encoding=_encodings['content'], errors='replace')
			eapi = f.readline().strip()
		except IOError:
			pass
		else:
			if not eapi_is_supported(eapi):
				raise ParseError(_(
					"Profile contains unsupported "
					"EAPI '%s': '%s'") % \
					(eapi, os.path.realpath(eapi_file),))
		finally:
			if f is not None:
				f.close()

		if intersecting_repos:
			allow_directories = eapi_allows_directories_on_profile_level_and_repository_level(eapi) or \
				any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats'])
			compat_mode = not eapi_allows_directories_on_profile_level_and_repository_level(eapi) and \
				layout_data['profile-formats'] == ('portage-1-compat',)
			allow_parent_colon = any(x in _allow_parent_colon
				for x in layout_data['profile-formats'])
			current_formats = tuple(layout_data['profile-formats'])


		if compat_mode:
			offenders = _PORTAGE1_DIRECTORIES.intersection(os.listdir(currentPath))
			offenders = sorted(x for x in offenders
				if os.path.isdir(os.path.join(currentPath, x)))
			if offenders:
				warnings.warn(_(
					"\nThe selected profile is implicitly using the 'portage-1' format:\n"
					"\tprofile = %(profile_path)s\n"
					"But this repository is not using that format:\n"
					"\trepo = %(repo_name)s\n"
					"This will break in the future.  Please convert these dirs to files:\n"
					"\t%(files)s\n"
					"Or, add this line to the repository's layout.conf:\n"
					"\tprofile-formats = portage-1")
					% dict(profile_path=currentPath, repo_name=repo_loc,
						files='\n\t'.join(offenders)))

		parentsFile = os.path.join(currentPath, "parent")
		if exists_raise_eaccess(parentsFile):
			parents = grabfile(parentsFile)
			if not parents:
				raise ParseError(
					_("Empty parent file: '%s'") % parentsFile)
			for parentPath in parents:
				abs_parent = parentPath[:1] == os.sep
				if not abs_parent and allow_parent_colon:
					parentPath = self._expand_parent_colon(parentsFile,
						parentPath, repo_loc, repositories)

				# NOTE: This os.path.join() call is intended to ignore
				# currentPath if parentPath is already absolute.
				parentPath = normalize_path(os.path.join(
					currentPath, parentPath))

				if abs_parent or repo_loc is None or \
					not parentPath.startswith(repo_loc):
					# It seems that this parent may point outside
					# of the current repo, so realpath it.
					parentPath = os.path.realpath(parentPath)

				if exists_raise_eaccess(parentPath):
					self._addProfile(parentPath, repositories, known_repos)
				else:
					raise ParseError(
						_("Parent '%s' not found: '%s'") %  \
						(parentPath, parentsFile))

		self.profiles.append(currentPath)
		self.profiles_complex.append(
			_profile_node(currentPath, allow_directories, False,
				current_formats, eapi))
Beispiel #8
0
def parse_layout_conf(repo_location, repo_name=None):
    eapi = read_corresponding_eapi_file(
        os.path.join(repo_location, REPO_NAME_LOC))

    layout_filename = os.path.join(repo_location, "metadata", "layout.conf")
    layout_file = KeyValuePairFileLoader(layout_filename, None, None)
    layout_data, layout_errors = layout_file.load()

    data = {}

    # None indicates abscence of a masters setting, which later code uses
    # to trigger a backward compatibility fallback that sets an implicit
    # master. In order to avoid this fallback behavior, layout.conf can
    # explicitly set masters to an empty value, which will result in an
    # empty tuple here instead of None.
    masters = layout_data.get('masters')
    if masters is not None:
        masters = tuple(masters.split())
    data['masters'] = masters
    data['aliases'] = tuple(layout_data.get('aliases', '').split())

    data['allow-provide-virtual'] = \
     layout_data.get('allow-provide-virtuals', 'false').lower() == 'true'

    data['eapis-banned'] = tuple(layout_data.get('eapis-banned', '').split())
    data['eapis-deprecated'] = tuple(
        layout_data.get('eapis-deprecated', '').split())

    data['sign-commit'] = layout_data.get('sign-commits', 'false').lower() \
     == 'true'

    data['sign-manifest'] = layout_data.get('sign-manifests', 'true').lower() \
     == 'true'

    data['thin-manifest'] = layout_data.get('thin-manifests', 'false').lower() \
     == 'true'

    data['repo-name'] = _gen_valid_repo(layout_data.get('repo-name', ''))

    manifest_policy = layout_data.get('use-manifests', 'strict').lower()
    data['allow-missing-manifest'] = manifest_policy != 'strict'
    data['create-manifest'] = manifest_policy != 'false'
    data['disable-manifest'] = manifest_policy == 'false'

    # for compatibility w/ PMS, fallback to pms; but also check if the
    # cache exists or not.
    cache_formats = layout_data.get('cache-formats', '').lower().split()
    if not cache_formats:
        # Auto-detect cache formats, and prefer md5-cache if available.
        # This behavior was deployed in portage-2.1.11.14, so that the
        # default egencache format could eventually be changed to md5-dict
        # in portage-2.1.11.32. WARNING: Versions prior to portage-2.1.11.14
        # will NOT recognize md5-dict format unless it is explicitly
        # listed in layout.conf.
        cache_formats = []
        if os.path.isdir(os.path.join(repo_location, 'metadata', 'md5-cache')):
            cache_formats.append('md5-dict')
        if os.path.isdir(os.path.join(repo_location, 'metadata', 'cache')):
            cache_formats.append('pms')
    data['cache-formats'] = tuple(cache_formats)

    manifest_hashes = layout_data.get('manifest-hashes')
    if manifest_hashes is not None:
        manifest_hashes = frozenset(manifest_hashes.upper().split())
        if MANIFEST2_REQUIRED_HASH not in manifest_hashes:
            repo_name = _get_repo_name(repo_location, cached=repo_name)
            warnings.warn(
                (_("Repository named '%(repo_name)s' has a "
                   "'manifest-hashes' setting that does not contain "
                   "the '%(hash)s' hash which is required by this "
                   "portage version. You will have to upgrade portage "
                   "if you want to generate valid manifests for this "
                   "repository: %(layout_filename)s") % {
                       "repo_name": repo_name or 'unspecified',
                       "hash": MANIFEST2_REQUIRED_HASH,
                       "layout_filename": layout_filename
                   }), DeprecationWarning)
        unsupported_hashes = manifest_hashes.difference(
            MANIFEST2_HASH_FUNCTIONS)
        if unsupported_hashes:
            repo_name = _get_repo_name(repo_location, cached=repo_name)
            warnings.warn((
                _("Repository named '%(repo_name)s' has a "
                  "'manifest-hashes' setting that contains one "
                  "or more hash types '%(hashes)s' which are not supported by "
                  "this portage version. You will have to upgrade "
                  "portage if you want to generate valid manifests for "
                  "this repository: %(layout_filename)s") % {
                      "repo_name": repo_name or 'unspecified',
                      "hashes": " ".join(sorted(unsupported_hashes)),
                      "layout_filename": layout_filename
                  }), DeprecationWarning)
    data['manifest-hashes'] = manifest_hashes

    data['update-changelog'] = layout_data.get('update-changelog', 'false').lower() \
     == 'true'

    raw_formats = layout_data.get('profile-formats')
    if raw_formats is None:
        if eapi_allows_directories_on_profile_level_and_repository_level(eapi):
            raw_formats = ('portage-1', )
        else:
            raw_formats = ('portage-1-compat', )
    else:
        raw_formats = set(raw_formats.split())
        unknown = raw_formats.difference(_valid_profile_formats)
        if unknown:
            repo_name = _get_repo_name(repo_location, cached=repo_name)
            warnings.warn((_(
                "Repository named '%(repo_name)s' has unsupported "
                "profiles in use ('profile-formats = %(unknown_fmts)s' setting in "
                "'%(layout_filename)s; please upgrade portage.") %
                           dict(repo_name=repo_name or 'unspecified',
                                layout_filename=layout_filename,
                                unknown_fmts=" ".join(unknown))),
                          DeprecationWarning)
        raw_formats = tuple(raw_formats.intersection(_valid_profile_formats))
    data['profile-formats'] = raw_formats

    try:
        eapi = layout_data['profile_eapi_when_unspecified']
    except KeyError:
        pass
    else:
        if 'profile-default-eapi' not in raw_formats:
            warnings.warn(
                (_("Repository named '%(repo_name)s' has "
                   "profile_eapi_when_unspecified setting in "
                   "'%(layout_filename)s', but 'profile-default-eapi' is "
                   "not listed in the profile-formats field. Please "
                   "report this issue to the repository maintainer.") %
                 dict(repo_name=repo_name or 'unspecified',
                      layout_filename=layout_filename)), SyntaxWarning)
        elif not portage.eapi_is_supported(eapi):
            warnings.warn(
                (_("Repository named '%(repo_name)s' has "
                   "unsupported EAPI '%(eapi)s' setting in "
                   "'%(layout_filename)s'; please upgrade portage.") %
                 dict(repo_name=repo_name or 'unspecified',
                      eapi=eapi,
                      layout_filename=layout_filename)), SyntaxWarning)
        else:
            data['profile_eapi_when_unspecified'] = eapi

    return data, layout_errors
Beispiel #9
0
	def __init__(self, name, repo_opts):
		"""Build a RepoConfig with options in repo_opts
		   Try to read repo_name in repository location, but if
		   it is not found use variable name as repository name"""
		aliases = repo_opts.get('aliases')
		if aliases is not None:
			aliases = tuple(aliases.split())
		self.aliases = aliases

		eclass_overrides = repo_opts.get('eclass-overrides')
		if eclass_overrides is not None:
			eclass_overrides = tuple(eclass_overrides.split())
		self.eclass_overrides = eclass_overrides
		# Eclass databases and locations are computed later.
		self.eclass_db = None
		self.eclass_locations = None

		# Masters from repos.conf override layout.conf.
		masters = repo_opts.get('masters')
		if masters is not None:
			masters = tuple(masters.split())
		self.masters = masters

		#The main-repo key makes only sense for the 'DEFAULT' section.
		self.main_repo = repo_opts.get('main-repo')

		priority = repo_opts.get('priority')
		if priority is not None:
			try:
				priority = int(priority)
			except ValueError:
				priority = None
		self.priority = priority

		sync = repo_opts.get('sync')
		if sync is not None:
			sync = sync.strip()
		self.sync = sync

		format = repo_opts.get('format')
		if format is not None:
			format = format.strip()
		self.format = format

		location = repo_opts.get('location')
		self.user_location = location
		if location is not None and location.strip():
			if os.path.isdir(location):
				location = os.path.realpath(location)
		else:
			location = None
		self.location = location

		eapi = None
		missing = True
		if self.location is not None:
			eapi = read_corresponding_eapi_file(os.path.join(self.location, REPO_NAME_LOC))
			name, missing = self._read_valid_repo_name(self.location)
		elif name == "DEFAULT": 
			missing = False

		self.eapi = eapi
		self.name = name
		self.missing_repo_name = missing
		# sign_commit is disabled by default, since it requires Git >=1.7.9,
		# and key_id configured by `git config user.signingkey key_id`
		self.sign_commit = False
		self.sign_manifest = True
		self.thin_manifest = False
		self.allow_missing_manifest = False
		self.allow_provide_virtual = False
		self.create_manifest = True
		self.disable_manifest = False
		self.manifest_hashes = None
		self.update_changelog = False
		self.cache_formats = None
		self.portage1_profiles = True
		self.portage1_profiles_compat = False
		self.find_invalid_path_char = _find_invalid_path_char

		# Parse layout.conf.
		if self.location:
			layout_filename = os.path.join(self.location, "metadata", "layout.conf")
			layout_data = parse_layout_conf(self.location, self.name)[0]

			# layout.conf masters may be overridden here if we have a masters
			# setting from the user's repos.conf
			if self.masters is None:
				self.masters = layout_data['masters']

			if layout_data['aliases']:
				aliases = self.aliases
				if aliases is None:
					aliases = ()
				# repos.conf aliases come after layout.conf aliases, giving
				# them the ability to do incremental overrides
				self.aliases = layout_data['aliases'] + tuple(aliases)

			for value in ('allow-missing-manifest',
				'allow-provide-virtual', 'cache-formats',
				'create-manifest', 'disable-manifest', 'manifest-hashes',
				'profile-formats',
				'sign-commit', 'sign-manifest', 'thin-manifest', 'update-changelog'):
				setattr(self, value.lower().replace("-", "_"), layout_data[value])

			self.portage1_profiles = eapi_allows_directories_on_profile_level_and_repository_level(eapi) or \
				any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats'])
			self.portage1_profiles_compat = not eapi_allows_directories_on_profile_level_and_repository_level(eapi) and \
				layout_data['profile-formats'] == ('portage-1-compat',)
Beispiel #10
0
def parse_layout_conf(repo_location, repo_name=None):
	eapi = read_corresponding_eapi_file(os.path.join(repo_location, REPO_NAME_LOC))

	layout_filename = os.path.join(repo_location, "metadata", "layout.conf")
	layout_file = KeyValuePairFileLoader(layout_filename, None, None)
	layout_data, layout_errors = layout_file.load()

	data = {}

	# None indicates abscence of a masters setting, which later code uses
	# to trigger a backward compatibility fallback that sets an implicit
	# master. In order to avoid this fallback behavior, layout.conf can
	# explicitly set masters to an empty value, which will result in an
	# empty tuple here instead of None.
	masters = layout_data.get('masters')
	if masters is not None:
		masters = tuple(masters.split())
	data['masters'] = masters
	data['aliases'] = tuple(layout_data.get('aliases', '').split())

	data['allow-provide-virtual'] = \
		layout_data.get('allow-provide-virtuals', 'false').lower() == 'true'

	data['sign-commit'] = layout_data.get('sign-commits', 'false').lower() \
		== 'true'

	data['sign-manifest'] = layout_data.get('sign-manifests', 'true').lower() \
		== 'true'

	data['thin-manifest'] = layout_data.get('thin-manifests', 'false').lower() \
		== 'true'

	manifest_policy = layout_data.get('use-manifests', 'strict').lower()
	data['allow-missing-manifest'] = manifest_policy != 'strict'
	data['create-manifest'] = manifest_policy != 'false'
	data['disable-manifest'] = manifest_policy == 'false'

	# for compatibility w/ PMS, fallback to pms; but also check if the
	# cache exists or not.
	cache_formats = layout_data.get('cache-formats', '').lower().split()
	if not cache_formats:
		# Auto-detect cache formats, and prefer md5-cache if available.
		# After this behavior is deployed in stable portage, the default
		# egencache format can be changed to md5-dict.
		cache_formats = []
		if os.path.isdir(os.path.join(repo_location, 'metadata', 'md5-cache')):
			cache_formats.append('md5-dict')
		if os.path.isdir(os.path.join(repo_location, 'metadata', 'cache')):
			cache_formats.append('pms')
	data['cache-formats'] = tuple(cache_formats)

	manifest_hashes = layout_data.get('manifest-hashes')
	if manifest_hashes is not None:
		manifest_hashes = frozenset(manifest_hashes.upper().split())
		if MANIFEST2_REQUIRED_HASH not in manifest_hashes:
			repo_name = _get_repo_name(repo_location, cached=repo_name)
			warnings.warn((_("Repository named '%(repo_name)s' has a "
				"'manifest-hashes' setting that does not contain "
				"the '%(hash)s' hash which is required by this "
				"portage version. You will have to upgrade portage "
				"if you want to generate valid manifests for this "
				"repository: %(layout_filename)s") %
				{"repo_name": repo_name or 'unspecified',
				"hash":MANIFEST2_REQUIRED_HASH,
				"layout_filename":layout_filename}),
				DeprecationWarning)
		unsupported_hashes = manifest_hashes.difference(
			MANIFEST2_HASH_FUNCTIONS)
		if unsupported_hashes:
			repo_name = _get_repo_name(repo_location, cached=repo_name)
			warnings.warn((_("Repository named '%(repo_name)s' has a "
				"'manifest-hashes' setting that contains one "
				"or more hash types '%(hashes)s' which are not supported by "
				"this portage version. You will have to upgrade "
				"portage if you want to generate valid manifests for "
				"this repository: %(layout_filename)s") %
				{"repo_name": repo_name or 'unspecified',
				"hashes":" ".join(sorted(unsupported_hashes)),
				"layout_filename":layout_filename}),
				DeprecationWarning)
	data['manifest-hashes'] = manifest_hashes

	data['update-changelog'] = layout_data.get('update-changelog', 'false').lower() \
		== 'true'

	raw_formats = layout_data.get('profile-formats')
	if raw_formats is None:
		if eapi_allows_directories_on_profile_level_and_repository_level(eapi):
			raw_formats = ('portage-1',)
		else:
			raw_formats = ('portage-1-compat',)
	else:
		raw_formats = set(raw_formats.split())
		unknown = raw_formats.difference(_valid_profile_formats)
		if unknown:
			repo_name = _get_repo_name(repo_location, cached=repo_name)
			warnings.warn((_("Repository named '%(repo_name)s' has unsupported "
				"profiles in use ('profile-formats = %(unknown_fmts)s' setting in "
				"'%(layout_filename)s; please upgrade portage.") %
				dict(repo_name=repo_name or 'unspecified',
				layout_filename=layout_filename,
				unknown_fmts=" ".join(unknown))),
				DeprecationWarning)
		raw_formats = tuple(raw_formats.intersection(_valid_profile_formats))
	data['profile-formats'] = raw_formats

	return data, layout_errors
Beispiel #11
0
	def __init__(self, name, repo_opts):
		"""Build a RepoConfig with options in repo_opts
		   Try to read repo_name in repository location, but if
		   it is not found use variable name as repository name"""
		aliases = repo_opts.get('aliases')
		if aliases is not None:
			aliases = tuple(aliases.split())
		self.aliases = aliases

		eclass_overrides = repo_opts.get('eclass-overrides')
		if eclass_overrides is not None:
			eclass_overrides = tuple(eclass_overrides.split())
		self.eclass_overrides = eclass_overrides
		# Eclass databases and locations are computed later.
		self.eclass_db = None
		self.eclass_locations = None

		# Masters from repos.conf override layout.conf.
		masters = repo_opts.get('masters')
		if masters is not None:
			masters = tuple(masters.split())
		self.masters = masters

		#The main-repo key makes only sense for the 'DEFAULT' section.
		self.main_repo = repo_opts.get('main-repo')

		priority = repo_opts.get('priority')
		if priority is not None:
			try:
				priority = int(priority)
			except ValueError:
				priority = None
		self.priority = priority

		sync = repo_opts.get('sync')
		if sync is not None:
			sync = sync.strip()
		self.sync = sync

		format = repo_opts.get('format')
		if format is not None:
			format = format.strip()
		self.format = format

		location = repo_opts.get('location')
		self.user_location = location
		if location is not None and location.strip():
			if os.path.isdir(location):
				location = os.path.realpath(location)
		else:
			location = None
		self.location = location

		eapi = None
		missing = True
		if self.location is not None:
			eapi = read_corresponding_eapi_file(os.path.join(self.location, REPO_NAME_LOC))
			name, missing = self._read_valid_repo_name(self.location)
		elif name == "DEFAULT": 
			missing = False

		self.eapi = eapi
		self.name = name
		self.missing_repo_name = missing
		# sign_commit is disabled by default, since it requires Git >=1.7.9,
		# and key_id configured by `git config user.signingkey key_id`
		self.sign_commit = False
		self.sign_manifest = True
		self.thin_manifest = False
		self.allow_missing_manifest = False
		self.allow_provide_virtual = False
		self.create_manifest = True
		self.disable_manifest = False
		self.manifest_hashes = None
		self.update_changelog = False
		self.cache_formats = None
		self.portage1_profiles = True
		self.portage1_profiles_compat = False
		self.find_invalid_path_char = _find_invalid_path_char

		# Parse layout.conf.
		if self.location:
			layout_filename = os.path.join(self.location, "metadata", "layout.conf")
			layout_data = parse_layout_conf(self.location, self.name)[0]

			# layout.conf masters may be overridden here if we have a masters
			# setting from the user's repos.conf
			if self.masters is None:
				self.masters = layout_data['masters']

			if layout_data['aliases']:
				aliases = self.aliases
				if aliases is None:
					aliases = ()
				# repos.conf aliases come after layout.conf aliases, giving
				# them the ability to do incremental overrides
				self.aliases = layout_data['aliases'] + tuple(aliases)

			for value in ('allow-missing-manifest',
				'allow-provide-virtual', 'cache-formats',
				'create-manifest', 'disable-manifest', 'manifest-hashes',
				'profile-formats',
				'sign-commit', 'sign-manifest', 'thin-manifest', 'update-changelog'):
				setattr(self, value.lower().replace("-", "_"), layout_data[value])

			self.portage1_profiles = eapi_allows_directories_on_profile_level_and_repository_level(eapi) or \
				any(x in _portage1_profiles_allow_directories for x in layout_data['profile-formats'])
			self.portage1_profiles_compat = not eapi_allows_directories_on_profile_level_and_repository_level(eapi) and \
				layout_data['profile-formats'] == ('portage-1-compat',)
Beispiel #12
0
def parse_layout_conf(repo_location, repo_name=None):
    eapi = read_corresponding_eapi_file(os.path.join(repo_location, REPO_NAME_LOC))

    layout_filename = os.path.join(repo_location, "metadata", "layout.conf")
    layout_file = KeyValuePairFileLoader(layout_filename, None, None)
    layout_data, layout_errors = layout_file.load()

    data = {}

    # None indicates abscence of a masters setting, which later code uses
    # to trigger a backward compatibility fallback that sets an implicit
    # master. In order to avoid this fallback behavior, layout.conf can
    # explicitly set masters to an empty value, which will result in an
    # empty tuple here instead of None.
    masters = layout_data.get("masters")
    if masters is not None:
        masters = tuple(masters.split())
    data["masters"] = masters
    data["aliases"] = tuple(layout_data.get("aliases", "").split())

    data["allow-provide-virtual"] = layout_data.get("allow-provide-virtuals", "false").lower() == "true"

    data["eapis-banned"] = tuple(layout_data.get("eapis-banned", "").split())
    data["eapis-deprecated"] = tuple(layout_data.get("eapis-deprecated", "").split())

    data["sign-commit"] = layout_data.get("sign-commits", "false").lower() == "true"

    data["sign-manifest"] = layout_data.get("sign-manifests", "true").lower() == "true"

    data["thin-manifest"] = layout_data.get("thin-manifests", "false").lower() == "true"

    data["repo-name"] = _gen_valid_repo(layout_data.get("repo-name", ""))

    manifest_policy = layout_data.get("use-manifests", "strict").lower()
    data["allow-missing-manifest"] = manifest_policy != "strict"
    data["create-manifest"] = manifest_policy != "false"
    data["disable-manifest"] = manifest_policy == "false"

    # for compatibility w/ PMS, fallback to pms; but also check if the
    # cache exists or not.
    cache_formats = layout_data.get("cache-formats", "").lower().split()
    if not cache_formats:
        # Auto-detect cache formats, and prefer md5-cache if available.
        # This behavior was deployed in portage-2.1.11.14, so that the
        # default egencache format could eventually be changed to md5-dict
        # in portage-2.1.11.32. WARNING: Versions prior to portage-2.1.11.14
        # will NOT recognize md5-dict format unless it is explicitly
        # listed in layout.conf.
        cache_formats = []
        if os.path.isdir(os.path.join(repo_location, "metadata", "md5-cache")):
            cache_formats.append("md5-dict")
        if os.path.isdir(os.path.join(repo_location, "metadata", "cache")):
            cache_formats.append("pms")
    data["cache-formats"] = tuple(cache_formats)

    manifest_hashes = layout_data.get("manifest-hashes")
    if manifest_hashes is not None:
        manifest_hashes = frozenset(manifest_hashes.upper().split())
        if MANIFEST2_REQUIRED_HASH not in manifest_hashes:
            repo_name = _get_repo_name(repo_location, cached=repo_name)
            warnings.warn(
                (
                    _(
                        "Repository named '%(repo_name)s' has a "
                        "'manifest-hashes' setting that does not contain "
                        "the '%(hash)s' hash which is required by this "
                        "portage version. You will have to upgrade portage "
                        "if you want to generate valid manifests for this "
                        "repository: %(layout_filename)s"
                    )
                    % {
                        "repo_name": repo_name or "unspecified",
                        "hash": MANIFEST2_REQUIRED_HASH,
                        "layout_filename": layout_filename,
                    }
                ),
                DeprecationWarning,
            )
        unsupported_hashes = manifest_hashes.difference(MANIFEST2_HASH_FUNCTIONS)
        if unsupported_hashes:
            repo_name = _get_repo_name(repo_location, cached=repo_name)
            warnings.warn(
                (
                    _(
                        "Repository named '%(repo_name)s' has a "
                        "'manifest-hashes' setting that contains one "
                        "or more hash types '%(hashes)s' which are not supported by "
                        "this portage version. You will have to upgrade "
                        "portage if you want to generate valid manifests for "
                        "this repository: %(layout_filename)s"
                    )
                    % {
                        "repo_name": repo_name or "unspecified",
                        "hashes": " ".join(sorted(unsupported_hashes)),
                        "layout_filename": layout_filename,
                    }
                ),
                DeprecationWarning,
            )
    data["manifest-hashes"] = manifest_hashes

    data["update-changelog"] = layout_data.get("update-changelog", "false").lower() == "true"

    raw_formats = layout_data.get("profile-formats")
    if raw_formats is None:
        if eapi_allows_directories_on_profile_level_and_repository_level(eapi):
            raw_formats = ("portage-1",)
        else:
            raw_formats = ("portage-1-compat",)
    else:
        raw_formats = set(raw_formats.split())
        unknown = raw_formats.difference(_valid_profile_formats)
        if unknown:
            repo_name = _get_repo_name(repo_location, cached=repo_name)
            warnings.warn(
                (
                    _(
                        "Repository named '%(repo_name)s' has unsupported "
                        "profiles in use ('profile-formats = %(unknown_fmts)s' setting in "
                        "'%(layout_filename)s; please upgrade portage."
                    )
                    % dict(
                        repo_name=repo_name or "unspecified",
                        layout_filename=layout_filename,
                        unknown_fmts=" ".join(unknown),
                    )
                ),
                DeprecationWarning,
            )
        raw_formats = tuple(raw_formats.intersection(_valid_profile_formats))
    data["profile-formats"] = raw_formats

    try:
        eapi = layout_data["profile_eapi_when_unspecified"]
    except KeyError:
        pass
    else:
        if "profile-default-eapi" not in raw_formats:
            warnings.warn(
                (
                    _(
                        "Repository named '%(repo_name)s' has "
                        "profile_eapi_when_unspecified setting in "
                        "'%(layout_filename)s', but 'profile-default-eapi' is "
                        "not listed in the profile-formats field. Please "
                        "report this issue to the repository maintainer."
                    )
                    % dict(repo_name=repo_name or "unspecified", layout_filename=layout_filename)
                ),
                SyntaxWarning,
            )
        elif not portage.eapi_is_supported(eapi):
            warnings.warn(
                (
                    _(
                        "Repository named '%(repo_name)s' has "
                        "unsupported EAPI '%(eapi)s' setting in "
                        "'%(layout_filename)s'; please upgrade portage."
                    )
                    % dict(repo_name=repo_name or "unspecified", eapi=eapi, layout_filename=layout_filename)
                ),
                SyntaxWarning,
            )
        else:
            data["profile_eapi_when_unspecified"] = eapi

    return data, layout_errors
Beispiel #13
0
    def _addProfile(self, currentPath, repositories, known_repos,
                    previous_repos):
        current_abs_path = os.path.abspath(currentPath)
        allow_directories = True
        allow_parent_colon = True
        repo_loc = None
        compat_mode = False
        current_formats = ()
        eapi = None

        intersecting_repos = tuple(x for x in known_repos
                                   if current_abs_path.startswith(x[0]))
        if intersecting_repos:
            # Handle nested repositories. The longest path
            # will be the correct one.
            repo_loc, layout_data = max(intersecting_repos,
                                        key=lambda x: len(x[0]))
            eapi = layout_data.get("profile_eapi_when_unspecified")

        eapi_file = os.path.join(currentPath, "eapi")
        eapi = eapi or "0"
        f = None
        try:
            f = io.open(
                _unicode_encode(eapi_file,
                                encoding=_encodings["fs"],
                                errors="strict"),
                mode="r",
                encoding=_encodings["content"],
                errors="replace",
            )
            eapi = f.readline().strip()
        except IOError:
            pass
        else:
            if not eapi_is_supported(eapi):
                raise ParseError(
                    _("Profile contains unsupported "
                      "EAPI '%s': '%s'") % (
                          eapi,
                          os.path.realpath(eapi_file),
                      ))
        finally:
            if f is not None:
                f.close()

        if intersecting_repos:
            allow_directories = (
                eapi_allows_directories_on_profile_level_and_repository_level(
                    eapi) or any(x in _portage1_profiles_allow_directories
                                 for x in layout_data["profile-formats"]))
            compat_mode = (
                not eapi_allows_directories_on_profile_level_and_repository_level(
                    eapi)
                and layout_data["profile-formats"] == ("portage-1-compat", ))
            allow_parent_colon = any(x in _allow_parent_colon
                                     for x in layout_data["profile-formats"])
            current_formats = tuple(layout_data["profile-formats"])

        # According to PMS, a deprecated profile warning is not inherited. Since
        # the current profile node may have been inherited by a user profile
        # node, the deprecation warning may be relevant even if it is not a
        # top-level profile node. Therefore, consider the deprecated warning
        # to be irrelevant when the current profile node belongs to the same
        # repo as the previous profile node.
        show_deprecated_warning = tuple(x[0] for x in previous_repos) != tuple(
            x[0] for x in intersecting_repos)

        if compat_mode:
            offenders = _PORTAGE1_DIRECTORIES.intersection(
                os.listdir(currentPath))
            offenders = sorted(x for x in offenders
                               if os.path.isdir(os.path.join(currentPath, x)))
            if offenders:
                warnings.warn(
                    _("\nThe selected profile is implicitly using the 'portage-1' format:\n"
                      "\tprofile = %(profile_path)s\n"
                      "But this repository is not using that format:\n"
                      "\trepo = %(repo_name)s\n"
                      "This will break in the future.  Please convert these dirs to files:\n"
                      "\t%(files)s\n"
                      "Or, add this line to the repository's layout.conf:\n"
                      "\tprofile-formats = portage-1") % dict(
                          profile_path=currentPath,
                          repo_name=repo_loc,
                          files="\n\t".join(offenders),
                      ))

        parentsFile = os.path.join(currentPath, "parent")
        if exists_raise_eaccess(parentsFile):
            parents = grabfile(parentsFile)
            if not parents:
                raise ParseError(_("Empty parent file: '%s'") % parentsFile)
            for parentPath in parents:
                abs_parent = parentPath[:1] == os.sep
                if not abs_parent and allow_parent_colon:
                    parentPath = self._expand_parent_colon(
                        parentsFile, parentPath, repo_loc, repositories)

                # NOTE: This os.path.join() call is intended to ignore
                # currentPath if parentPath is already absolute.
                parentPath = normalize_path(
                    os.path.join(currentPath, parentPath))

                if (abs_parent or repo_loc is None
                        or not parentPath.startswith(repo_loc)):
                    # It seems that this parent may point outside
                    # of the current repo, so realpath it.
                    parentPath = os.path.realpath(parentPath)

                if exists_raise_eaccess(parentPath):
                    self._addProfile(parentPath, repositories, known_repos,
                                     intersecting_repos)
                else:
                    raise ParseError(
                        _("Parent '%s' not found: '%s'") %
                        (parentPath, parentsFile))

        self.profiles.append(currentPath)
        self.profiles_complex.append(
            _profile_node(
                currentPath,
                allow_directories,
                False,
                current_formats,
                eapi,
                "build-id" in current_formats,
                show_deprecated_warning=show_deprecated_warning,
            ))