manifest.py revision 2854
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# CDDL HEADER START
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# The contents of this file are subject to the terms of the
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# Common Development and Distribution License (the "License").
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# You may not use this file except in compliance with the License.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# See the License for the specific language governing permissions
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen# and limitations under the License.
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# When distributing Covered Code, include this CDDL HEADER in each
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# If applicable, add the following below this CDDL HEADER, with the
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# fields enclosed by brackets "[]" replaced with your own identifying
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# information: Portions Copyright [yyyy] [name of copyright owner]
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# CDDL HEADER END
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen# Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainenfrom collections import namedtuple, defaultdict
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenfrom itertools import groupby, chain, repeat, izip
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainenfrom pkg.misc import EmptyDict, EmptyI, expanddirs, PKG_FILE_MODE, PKG_DIR_MODE
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainenfrom pkg.actions.attribute import AttributeAction
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen namedtuple("ManifestDifference", "added changed removed")):
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen [ ( actions.generic.NSG, actions.generic.NSG ) ],
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen [ ( actions.generic.NSG, actions.generic.NSG ) ],
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen [ ( actions.generic.NSG, actions.generic.NSG ) ],
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen """Returns the serialized state of this object in a format
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen that that can be easily stored using JSON, pickle, etc."""
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen return misc.json_encode(ManifestDifference.__name__,
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen commonize=ManifestDifference.__state__commonize,
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen """Allocate a new object using previously serialized state
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen obtained via getstate()."""
445f9e31c6c3aa6c0a72be8565da8f6e594d24fbTimo Sirainen # decode serialized state into python objects
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen state = misc.json_decode(ManifestDifference.__name__,
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen commonize=ManifestDifference.__state__commonize,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen """A Manifest is the representation of the actions composing a specific
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen package version on both the client and the repository. Both purposes
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen utilize the same storage format.
9e59a1f3f095b3099478562cf3f3970a24736970Timo Sirainen The serialized structure of a manifest is an unordered list of actions.
178511b57faa7c3f8203dd8b7e4059d00cbfc23aTimo Sirainen The special action, "set", represents a package attribute.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen The reserved attribute, "fmri", represents the package and version
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen described by this manifest. It is available as a string via the
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen attributes dictionary, and as an FMRI object from the fmri member.
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen The list of manifest-wide reserved attributes is
33d63688ed8b26dc333e3c2edbfb2fe6e412604dTimo Sirainen base_directory Default base directory, for non-user images.
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen fmri Package FMRI.
4d25408732be27e91f0430f71e87242760c2517cTimo Sirainen isa Package is intended for a list of ISAs.
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen platform Package is intended for a list of platforms.
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen relocatable Suitable for User Image.
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen All non-prefixed attributes are reserved to the framework. Third
9e59a1f3f095b3099478562cf3f3970a24736970Timo Sirainen parties may prefix their attributes with a reversed domain name, domain
9e59a1f3f095b3099478562cf3f3970a24736970Timo Sirainen name, or stock symbol. An example might be
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen com.example,supported
c3412ddeb9abc13f99d3caf50faf76cd99f7e9d2Timo Sirainen as an indicator that a specific package version is supported by the
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen vendor, example.com.
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen manifest.null is provided as the null manifest. Differences against the
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen null manifest result in the complete set of attributes and actions of
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen the non-null manifest, meaning that all operations can be viewed as
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen tranitions between the manifest being installed and the manifest already
b9ac6179d3aee0d1641a4ee1d78da28628929c61Timo Sirainen present in the image (which may be the null manifest).
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen self._variants = None # variants seen in package
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen self.attributes = {} # package-wide attributes
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen if "pkg.fmri" not in self.attributes and self.fmri != None:
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen r += "set name=pkg.fmri value=%s\n" % self.fmri
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen """A generator function that returns the unsorted manifest
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen contents as lines of text."""
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if "pkg.fmri" not in self.attributes and self.fmri != None:
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen yield "set name=pkg.fmri value=%s\n" % self.fmri
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen def difference(self, origin, origin_exclude=EmptyI,
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen """Return three lists of action pairs representing origin and
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen destination actions. The first list contains the pairs
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen representing additions, the second list contains the pairs
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen representing updates, and the third list contains the pairs
755fe6da51ab7f54aa1d86913cb344bffef60e79Timo Sirainen representing removals. All three lists are in the order in
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen which they should be executed."""
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen # XXX Do we need to find some way to assert that the keys are
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen # all unique?
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # No origin was provided, so nothing has been changed or
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # removed; only added. In addition, this doesn't need
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # to be sorted since the caller likely already does
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen [(None, a) for a in self.gen_actions(self_exclude)],
36e2fa21c22452470c1509cc63de20f7415c7b5eTimo Sirainen """handle key values that may be lists"""
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen # Transform list of actions into a dictionary keyed by
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen # action key attribute, key attribute and mediator, or
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen # id if there is no key attribute.
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen added = [(None, sdict[i]) for i in sset - oset]
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen removed = [(odict[i], None) for i in oset - sset]
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen # XXX Do changed actions need to be sorted at all? This is
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # likely to be the largest list, so we might save significant
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # time by not sorting. Should we sort above? Insert into a
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # sorted list?
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # singlesort = lambda x: x[0] or x[1]
a393d9d6dabdc46cf724f8cb004a652b4036d53dTimo Sirainen return ManifestDifference(added, changed, removed)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen """Like the unix utility comm, except that this function
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen takes an arbitrary number of manifests and compares them,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen returning a tuple consisting of each manifest's actions
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen that are not the same for all manifests, followed by a
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen list of actions that are the same in each manifest."""
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # Must specify at least one manifest.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # construct list of dictionaries of actions in each
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen # manifest, indexed by unique key and variant combination
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen # The unique key for each action is based on its
2ae575a66f2a302f047f6de062a70b75f8bebc7bTimo Sirainen # type, key attribute, and unique variants set
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen # on the action.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # If there is no key attribute for the
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # action, then fallback to the object
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen # id for the action as its identifier.
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen # catch duplicate actions here...
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen # construct list of key sets in each dict
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen common_keys = reduce(lambda a, b: a & b, m_sets)
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen # determine which common_keys have common actions
f0f9c8e94abac18f8acd91b9e724c4c32863723aTimo Sirainen [m_dicts[i][k] for k in m_sets[i] - common_keys]
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen def combined_difference(self, origin, ov=EmptyI, sv=EmptyI):
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen """Where difference() returns three lists, combined_difference()
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen returns a single list of the concatenation of the three."""
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen return list(chain(*self.difference(origin, ov, sv)))
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen def humanized_differences(self, other, ov=EmptyI, sv=EmptyI):
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen """Output expects that self is newer than other. Use of sets
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen requires that we convert the action objects into some marshalled
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen form, otherwise set member identities are derived from the
4d25408732be27e91f0430f71e87242760c2517cTimo Sirainen object pointers, rather than the contents."""
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen """Generate contents of dircache file containing all dirctories
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen referenced explicitly or implicitly from self.actions. Include
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen variants as values; collapse variants where possible."""
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen for d in expanddirs(a.directory_references()):
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen yield "dir path=%s %s\n" % \
33d63688ed8b26dc333e3c2edbfb2fe6e412604dTimo Sirainen for t in v.iteritems()))
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen """Generate contents of mediatorcache file containing all
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen mediators referenced explicitly or implicitly from self.actions.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen Include variants as values; collapse variants where possible."""
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen if (a.name == "link" or a.name == "hardlink") and \
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen mediators = self._actions_to_dict(gen_references)
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen for mediation, mvariants in mediators.iteritems():
33d63688ed8b26dc333e3c2edbfb2fe6e412604dTimo Sirainen a = "set name=pkg.mediator " \
77af8c68c416179e717fc2d551f72ec50b499c13Timo Sirainen """create dictionary of all actions referenced explicitly or
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen implicitly from self.actions... include variants as values;
77af8c68c416179e717fc2d551f72ec50b499c13Timo Sirainen collapse variants where possible"""
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen # build a dictionary containing all directories tagged w/
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen variants = dict((name, a.attrs[name]) for name in v + f)
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen # remove any tags if any entries are always delivered (NULL)
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen # could collapse refs where all variants are present
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen # (the current logic only collapses them if at least
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen # one reference is delivered without a facet or
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen """ return a list of directories implicitly or
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen explicitly referenced by this object"""
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen assert excludes == EmptyI or self.excludes == EmptyI
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # generate actions that contain directories
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen """A generator function that yields tuples of the form (mediator,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen mediations) expressing the set of possible mediations for this
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen package, where 'mediations' is a set() of possible mediations for
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen the mediator. Each mediation is a tuple of the form (priority,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen version, implementation).
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen assert excludes == EmptyI or self.excludes == EmptyI
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen # generate actions that contain mediators
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen alist = self._cache["manifest.mediatorcache"] = [
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if not excludes or act.include_this(excludes)):
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen # Consider this mediation unavailable
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen # if it can't be parsed for whatever
064bfeee2f9156683b191cc0f3f7b242720942f7Timo Sirainen """Generate actions in manifest through ordered callable list"""
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen assert excludes == EmptyI or self.excludes == EmptyI
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen def gen_actions_by_type(self, atype, excludes=EmptyI):
ab3c1eab9ca13916358a9e8b12df8212fefb7dbfTimo Sirainen """Generate actions in the manifest of type "type"
99be58a447b69d62cbd9e764000a06226b9c9c89Timo Sirainen through ordered callable list"""
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen assert excludes == EmptyI or self.excludes == EmptyI
ff640b54224881abbc21141f217c881d6ba5cd28Timo Sirainen def gen_actions_by_types(self, atypes, excludes=EmptyI):
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen """Generate actions in the manifest of types "atypes"
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen through ordered callable list."""
4d25408732be27e91f0430f71e87242760c2517cTimo Sirainen def gen_key_attribute_value_by_type(self, atype, excludes=EmptyI):
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen """Generate the value of the key attribute for each action
4d25408732be27e91f0430f71e87242760c2517cTimo Sirainen of type "type" in the manifest."""
1276e0340fe29495b6694dc7508f070cf6fca1cfTimo Sirainen for a in self.gen_actions_by_type(atype, excludes)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen """Find actions in the manifest which are duplicates (i.e.,
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen represent the same object) but which are not identical (i.e.,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen have all the same attributes)."""
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen """Return a key on which actions can be sorted."""
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen acts = [a for a in self.gen_actions(excludes)]
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen for k, g in groupby(sorted(acts, key=fun), fun):
60d3fa9883237e896a8704275b6116fa46f7ffdaTimo Sirainen # Get an iterable for the string.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if l.endswith("\\"): # allow continuation chars
7fd72a47d7ddfbd38c8697e228b6951f495dfb61Timo Sirainen if not l or l[0] == "#": # ignore blank lines & comments
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen # Accumulate errors and continue so that as
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen # much of the action data as possible can be
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen def set_content(self, content=None, excludes=EmptyI, pathname=None,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen """Populate the manifest with actions.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen 'content' is an optional value containing either the text
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen representation of the manifest or an iterable of
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen action objects.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen 'excludes' is optional. If provided it must be a length two
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen list with the variants to be excluded as the first element and
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen the facets to be excluded as the second element.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen 'pathname' is an optional filename containing the location of
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen the manifest content.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen 'signatures' is an optional boolean value that indicates whether
907723f35f4d3dfc774ca42d00a8a7b8ef90dd5dTimo Sirainen a manifest signature should be generated. This is only possible
36816b5af1472ae76a1909ae3cf29fd614b2ebfcTimo Sirainen when 'content' is a string or 'pathname' is provided.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen assert content is not None or pathname is not None
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # So we could build up here the type/key_attr dictionaries like
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # sdict and odict in difference() above, and have that be our
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # main datastore, rather than the simple list we have now. If
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # we do that here, we can even assert that the "same" action
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # can't be in a manifest twice. (The problem of having the same
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # action more than once in packages that can be installed
4214b59ac7f3899f8d887d055ef519f5a622d249Timo Sirainen # together has to be solved somewhere else, though.)
dc8552739fa29f011ab71ec383ec6d580a5a9661Timo Sirainen # Generate manifest signature based upon
dc8552739fa29f011ab71ec383ec6d580a5a9661Timo Sirainen # input content, but only if signatures
dc8552739fa29f011ab71ec383ec6d580a5a9661Timo Sirainen # were requested.
4214b59ac7f3899f8d887d055ef519f5a622d249Timo Sirainen # Make sure that either no excludes were provided or that both
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen # variants and facet excludes were.
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen """Remove any actions from the manifest which should be
930dcf1576f99057ad572420d9c75f3212e46a2eTimo Sirainen self.set_content(content=self.actions, excludes=excludes)
930dcf1576f99057ad572420d9c75f3212e46a2eTimo Sirainen """Performs any needed transformations on the action then adds
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen it to the manifest.
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen The "action" parameter is the action object that should be
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen added to the manifest.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen The "excludes" parameter is the variants to exclude from the
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen # XXX handle legacy transition issues; not needed once support
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen # for upgrading images from older releases (< build 151) has
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen # been removed.
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen if aname == "set" and attrs["name"] == "authority":
8907d617ce7c4f390c0f42f6f694db2fecdd5775Timo Sirainen # Translate old action to new.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if excludes and not action.include_this(excludes):
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen # Reset facet/variant cache if needed (if one is set,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # then both are set, so only need to check for one).
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen self.actions_bytype.setdefault(aname, []).append(action)
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen # add any set actions to attributes
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen """Fill attribute array w/ set action contents."""
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen def search_dict(file_path, excludes, return_line=False,
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen """Produces the search dictionary for a specific manifest.
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen A dictionary is constructed which maps a tuple of token,
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen action type, key, and the value that matched the token to
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen the byte offset into the manifest file. file_path is the
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen path to the manifest file. excludes is the variants which
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen should be allowed in this image. return_line is a debugging
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen flag which makes the function map the information to the
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen string of the line, rather than the byte offset to allow
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen easier debugging."""
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen log = lambda x: None
cece2b9cd692c06025cc0a7a0ff54d996a8c90efTimo Sirainen """Translates what actions.generate_indices produces
cece2b9cd692c06025cc0a7a0ff54d996a8c90efTimo Sirainen into a dictionary mapping token, action_name, key, and
cece2b9cd692c06025cc0a7a0ff54d996a8c90efTimo Sirainen the value that should be displayed for matching that
cece2b9cd692c06025cc0a7a0ff54d996a8c90efTimo Sirainen token to byte offsets into the manifest file.
f0569d9fbb25c8437760be69f194595a841ad711Timo Sirainen The "lst" parameter is the data to be converted.
755fe6da51ab7f54aa1d86913cb344bffef60e79Timo Sirainen The "cp" parameter is the byte offset into the file
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen for the action which produced lst."""
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen for action_name, subtype, tok, full_value in lst:
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen "an action which is"
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen " missing the "
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen "expected attribute"
f1b7a02a05fbca580934c7312aae63ea9542aa79Timo Sirainen ": %(at)s.\nThe "
755fe6da51ab7f54aa1d86913cb344bffef60e79Timo Sirainen """This method takes a string representing the on-disk
755fe6da51ab7f54aa1d86913cb344bffef60e79Timo Sirainen manifest content, and returns a hash value."""
71a74e26cf070a205d31cf6c6fae003f90027b63Timo Sirainen # Byte stream expected, so pass encoded.
319944c0f35b311c998854e96d6463a084fd90aeTimo Sirainen """Verifies whether the signatures for the contents of
319944c0f35b311c998854e96d6463a084fd90aeTimo Sirainen the manifest match the specified signature data. Raises
319944c0f35b311c998854e96d6463a084fd90aeTimo Sirainen the 'BadManifestSignatures' exception on failure."""
319944c0f35b311c998854e96d6463a084fd90aeTimo Sirainen """Store the manifest contents to disk."""
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen fd, fn = tempfile.mkstemp(dir=t_dir, prefix=t_prefix)
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen # We specifically avoid sorting manifests before writing
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen # them to disk-- there's really no point in doing so, since
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen # we'll sort actions globally during packaging operations.
71a74e26cf070a205d31cf6c6fae003f90027b63Timo Sirainen """Return a dictionary mapping variant tags to their values."""
71a74e26cf070a205d31cf6c6fae003f90027b63Timo Sirainen return variant.VariantCombinationTemplate(dict((
f0569d9fbb25c8437760be69f194595a841ad711Timo Sirainen """Returns the boolean of the value of the attribute 'key'."""
907723f35f4d3dfc774ca42d00a8a7b8ef90dd5dTimo Sirainen raise ValueError(_("Attribute value '%s' not 'true' or "
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen """Returns an integer representing the total size, in bytes, of
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen the Manifest's data payload.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen 'excludes' is a list of variants which should be allowed when
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen calculating the total.
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen """Private helper function to populate list of facets and
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen variants on-demand."""
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen # append any variants and facets to manifest dict
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen # Lists can't be set elements.
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen _("%(forv)s '%(v)s' specified multiple times") %
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen """Return the value for the package attribute 'key'."""
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen """Set the value for the package attribute 'key' to 'value'."""
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen if a.name == "set" and a.attrs["name"] == key:
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen new_attr = AttributeAction(None, name=key, value=value)
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen self.actions_bytype.setdefault("set", []).append(new_attr)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen facets = property(lambda self: self.__get_facets())
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen variants = property(lambda self: self.__get_variants())
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen """This class serves as a wrapper for the Manifest class for callers
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen that need efficient access to package data on a per-action type basis.
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen It achieves this by partitioning the manifest into multiple files (one
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen per action type) and then storing an on-disk cache of the directories
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen explictly and implicitly referenced by the manifest each tagged with
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen the appropriate variants/facets."""
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen def __init__(self, fmri, cache_root, contents=None, excludes=EmptyI,
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen """Raises KeyError exception if factored manifest is not present
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen and contents are None; delays reading of manifest until required
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen if cache file is present.
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen 'fmri' is a PkgFmri object representing the identity of the
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen 'cache_root' is the pathname of the directory where the manifest
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen and cache files should be stored or loaded from.
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen 'contents' is an optional string to use as the contents of the
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen manifest if a cached copy does not already exist.
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen 'excludes' is optional. If provided it must be a length two
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen list with the variants to be excluded as the first element and
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen the facets to be exclduded as the second element.
8907d617ce7c4f390c0f42f6f694db2fecdd5775Timo Sirainen 'pathname' is an optional string containing the pathname of a
99be58a447b69d62cbd9e764000a06226b9c9c89Timo Sirainen manifest. If not provided, it is assumed that the manifest is
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen stored in a file named 'manifest' in the directory indicated by
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen 'cache_root'. If provided, and contents is also provided, then
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen 'contents' will be stored in 'pathname' if it does not already
b397a802ec245a9169dab6b62efa4f7f877c07f6Timo Sirainen # Make sure that either no excludes were provided or that both
b397a802ec245a9169dab6b62efa4f7f877c07f6Timo Sirainen # variants and facet excludes were.
5edfc0f1c3c55e906d8316d9cdeaa3b0c7000c19Timo Sirainen # Do we have a cached copy?
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # we have no cached copy; save one
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # don't specify excludes so on-disk copy has
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen # all variants
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # we have a cached copy of the manifest
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen mdpath = self.__cache_path("manifest.dircache")
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # have we computed the dircache?
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if not os.path.exists(mdpath): # we're adding cache
292a66475ffe1037c2535063614f8beb71d266bfTimo Sirainen """Load all manifest contents from on-disk copy of manifest"""
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen self.set_content(excludes=self.excludes, pathname=self.pathname)
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen """Unload manifest; used to reduce peak memory comsumption
9e59a1f3f095b3099478562cf3f3970a24736970Timo Sirainen when downloading new manifests"""
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen """Finish loading.... this part of initialization is common
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen to multiple code paths"""
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen """ store the current action set; also create per-type
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen caches. Return True if data was saved, False if not"""
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen # this allows us to try to cache new manifests
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen # when non-root w/o failures.
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen """ create manifest.<typename> files to accelerate partial
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen parsing of manifests. Separate from __storeback code to
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen allow upgrade to reuse existing on disk manifests"""
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen # Ensure target cache directory and intermediates exist.
c263e92d73889da530b308c9ab28b4b74031550eTimo Sirainen # create per-action type cache; use rename to avoid
c263e92d73889da530b308c9ab28b4b74031550eTimo Sirainen # corrupt files if ^C'd in the middle
a393d9d6dabdc46cf724f8cb004a652b4036d53dTimo Sirainen fd, fn = tempfile.mkstemp(dir=t_dir, prefix=t_prefix)
9e59a1f3f095b3099478562cf3f3970a24736970Timo Sirainen portable.rename(fn, self.__cache_path("manifest.%s" % n))
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen create_cache("manifest.dircache", self._gen_dirs_to_str)
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen """Remove all manifest cache files found in the given directory
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen (excluding the manifest itself).
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen # Only raise error if failure wasn't due to
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen # cache directory not existing.
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen """Private helper function for loading arbitrary cached manifest
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen # no cached copy
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen # need to load from disk
319944c0f35b311c998854e96d6463a084fd90aeTimo Sirainen # we have cached copy on disk; use it
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen """ return a list of directories implicitly or explicitly
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen referenced by this object
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen return Manifest.get_directories(self, excludes)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen def gen_actions_by_type(self, atype, excludes=EmptyI):
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen """ generate actions of the specified type;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen use already in-memory stuff if already loaded,
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen otherwise use per-action types files"""
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen if self.loaded: #if already loaded, use in-memory cached version
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen # invoke subclass method to generate action by action
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen for a in Manifest.gen_actions_by_type(self, atype,
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen # This checks if we've already written out the factored
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen # manifest files. If so, we'll use it, and if not, then
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen # we'll load the full manifest.
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen mpath = self.__cache_path("manifest.dircache")
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen # no cached copy :-(
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen # get manifest from disk
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen # invoke subclass method to generate action by action
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen for a in Manifest.gen_actions_by_type(self, atype,
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen # we have a cached copy - use it
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen mpath = self.__cache_path("manifest.%s" % atype)
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen return # no such action in this manifest
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen """A generator function that yields set actions expressing the
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen set of possible mediations for this package.
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen self.__load_cached_data("manifest.mediatorcache")
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen """Load attributes dictionary from cached set actions;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen this speeds up pkg info a lot"""
d252f81a2ff1bdd5439f9d2b3df715b70a4bcd3dTimo Sirainen if not self.loaded and not self.__load_attributes():
63a61b7a739ae0f3f520215137d9c50f94d0f34fTimo Sirainen """No assignments to factored manifests allowed."""
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen assert "FactoredManifests are not dicts"
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen if not self.loaded and not self.__load_attributes():
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen if not self.loaded and not self.__load_attributes():
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen if not self.loaded and not self.__load_attributes():
f2b79667fc7a8f7c2c72cad18bd71d49730e36f6Timo Sirainen def search_dict(cache_path, excludes, return_line=False):
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen return Manifest.search_dict(cache_path, excludes,
f755bf320bfa321de210b85d6455eb6d7092bb4aTimo Sirainen return Manifest.gen_actions(self, excludes=excludes)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen return Manifest.duplicates(self, excludes=excludes)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen def difference(self, origin, origin_exclude=EmptyI,
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen """The absolute pathname of the file containing the manifest."""
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen return os.path.join(self.__cache_root, "manifest")
4d10cf8c7879ccd377e7fb136913b2a258ba8d93Timo Sirainen """Special class for pkgplan's need for a empty manifest;
4d10cf8c7879ccd377e7fb136913b2a258ba8d93Timo Sirainen the regular null manifest doesn't support get_directories
4d10cf8c7879ccd377e7fb136913b2a258ba8d93Timo Sirainen and making the factored manifest code handle this case is
4d10cf8c7879ccd377e7fb136913b2a258ba8d93Timo Sirainen too ugly..."""
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen def difference(self, origin, origin_exclude=EmptyI,
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen """Return three lists of action pairs representing origin and
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen destination actions. The first list contains the pairs
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen representing additions, the second list contains the pairs
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen representing updates, and the third list contains the pairs
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen representing removals. All three lists are in the order in
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen which they should be executed."""
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen # The difference for this case is simply everything in the
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen # origin has been removed. This is an optimization for
d9de52132072d80b8c268094b879c0ef5a108db3Timo Sirainen [(a, None) for a in origin.gen_actions(origin_exclude)])
de4288b7369945a31c4001add9445fd0195a358dTimo Sirainen # This method is overridden so that self.excludes is never set
de4288b7369945a31c4001add9445fd0195a358dTimo Sirainen # on the singleton NullFactoredManifest.
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen raise RuntimeError("Cannot call set_content on an "
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen "EmptyFactoredManifest")
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen """Simple Exception class to handle manifest specific errors"""