#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
#
# basic variant support
import copy
import itertools
import six
import types
# store information on variants; subclass dict
# and maintain set of keys for performance reasons
for i in init:
# allow_action is provided as a native function (see end of class
# declaration).
"""This is a wrapper-class used by other consumers that handles implicit
variant values. This class cannot be used by the VariantCombination*
classes since they rely on explicit values only to be found."""
"""Implement variant lookup algorithm here
Note that _allow_variant bypasses __getitem__ for performance
reasons; if __getitem__ changes, _allow_variant in _varcet.c
must also be updated.
We return a tuple of the form (<key>, <value>) where key is the
explicitly set variant name that matched the caller specific
variant name."""
raise KeyError("key must start w/ variant.")
# Unknown variants have a default implicit value 'false'.
return None, "false"
# The two classes which follow are used during dependency calculation when
# actions have variants, or the packages they're contained in do. The
# VariantCombinationTemplate corresponds to information that is encoded in
# the actions. Specifically, it records what types of variants exist
# (variant.arch or variant.debug) and what values are known to exist for them
# dictionary while the variant values are what the keys map to.
#
# The VariantCombinations class serves a different purpose. In order to
# determine whether a dependency is satisfied under all combinations of
# variants, it is necessary to track whether each combination has been
# satisfied. When a VariantCombinations is created, it is provided a
# VariantCombinationTemplate which it uses to seed the combinations of variants.
# To make a single combination instance, for each type of variant, it chooses
# one value and adds it to the instance. It creates all possible combination
# instances and these are what it uses to track whether all combinations have
# been satisfied. The class also provides methods for manipulating the
# instances while maintaining consistency between the satisfied set and the
# unsatisfied set.
"""Class for holding a template of variant types and their potential
values."""
return VariantCombinationTemplate(self)
"""Overrides _Variants.__setitem__ to ensure that all values are
sets."""
"""Returns whether self is a subset of variant var."""
res = VCTDifference([], [])
for k in self:
if k not in var:
else:
return res
"""Pull the values for unknown keys in var into self."""
"""Pull all unknown values of all keys in var into self."""
s = ""
s += " {0}={1}".format(k, t)
if s:
return s
else:
return " <none>"
# Namedtuple used to store the results of VariantCombinationTemplate
# differences. The type_diffs field stores the variant types which are in the
# caller and not in the argument to difference. The value_diffs field stores
# the values for particular types which are in the caller and not in the
# argument to difference.
"""Class for keeping track of which combinations of variant values have
and have not been satisfied for a particular action."""
"""Create an instance of VariantCombinations based on the
template provided.
The 'vct' parameter is the template from which to build the
combinations.
The 'satisfied' parameter is a boolean which determines whether
the combinations created from the template will be considered
satisfied or unsatisfied."""
tmp = []
# This builds all combinations of variant values presented in
# vct.
if not tmp:
# Initialize tmp with the key-value pairs for
# the first key in vct.
continue
# For each subsequent key in vct, append each of its
# key-value pairs to each of the existing combinations.
new_tmp = [
]
# Here is an example of how the loop above would handle a vct
# of { 1:["a", "b"], 2:["x", "y"], 3:["m", "n"] }
# First, tmp would be initialized as [[(1, "a")], [(1, "b")]]
# Next, a new list is created by adding (2, "x") to a copy
# of each item in tmp, and then (2, "y"). This produces
# [[(1, "a"), (2, "x")], [(1, "a"), (2, "y")],
# [(1, "b"), (2, "x")], [(1, "b"), (2, "y")]]
# That process is repeated one more time for the 3 key,
# resulting in:
# [[(1, "a"), (2, "x"), (3, "m")],
# [(1, "a"), (2, "x"), (3, "n")],
# [(1, "a"), (2, "y"), (3, "m")],
# [(1, "a"), (2, "y"), (3, "n")],
# [(1, "b"), (2, "x"), (3, "m")],
# [(1, "b"), (2, "x"), (3, "n")],
# [(1, "b"), (2, "y"), (3, "m")],
# [(1, "b"), (2, "y"), (3, "n")]]
if satisfied:
else:
self.__simpl_template = None
return self.__template
if not self.__simpl_template:
else:
if not self.__simpl_template:
return self.__not_sat_set
else:
return vc
"""Returns whether self was created with any potential variant
values."""
"""Returns whether the instances in self are a subset of the
instances in vc. 'satisfied' determines whether the instances
compared are drawn from the set of satisfied instances or the
set of unsatisfied instances."""
if satisfied:
else:
"""Returns whether an action whose variants are vc could satisfy
dependencies whose variants are self.
'only_not_sat' determines whether only the unsatisfied set of
variants for self is used for comparision. When only_not_sat
is True, then intersects returns wether vc would satisfy at
least one instance which is currently unsatisfied."""
return True
if only_not_sat:
"""Find those variant values in self that are also in var, and
return them."""
return res
"""Find those combinations of variants that are satisfied only
in self, in both self and vc, and only in vc."""
intersect = None
only_big = None
only_small = None
return None, self, None
else:
# If one template isn't a subset of, or identical to,
# the other, then no meaningful comparison can be
# performed.
"""For all instances in vc, mark those instances as being
satisfied. Returns a boolean indicating whether any changes
have been made."""
if not i:
return False
self.__not_sat_set -= i
return True
"""For all satisfied instances in vc, mark those instances as
being unsatisfied."""
if not i:
return False
self.__not_sat_set |= i
return True
"""Mark all instances as being satisfied."""
"""Returns whether all variant combinations for this package
have been satisfied."""
return not self.__not_sat_set
"""Store the provided VariantCombinationTemplate as the template
to use when simplifying the combinations."""
self.__simpl_template = {}
"template:{0}\nvct:{1}".format(
"""Create one VariantCombination object for each possible
combination of variants. This is useful when each combination
needs to be associated with other information."""
tmp = []
for c in self.__combinations:
# If there weren't any combinations, then this is an empty
# variant combination, so just return a copy of ourselves.
if not tmp:
return tmp
"""Create a copy of this variant combination, but make sure all
the variant combinations are marked as unsatisifed."""
"""Given VariantCombinationTemplate to be simplified against,
reduce the instances to the empty set if the instances cover all
possible combinations of the template provided.
A general approach to simplification is currently deemed to
difficult in the face of arbitrary numbers of variant types and
arbitrary numbers of variant."""
if not self.__simpl_template:
possibilities = 0
else:
possibilities = 1
for k in self.__simpl_template:
if sat:
else:
# If the size of sat_set or not_sat_set matches the number of
# possibilities a template can produce, then it can be
# simplified.
return set()
# If any dependencies are merged, then another pass over the
# variant types is necessary. 'keep_going' tracks whether that
# has happened.
while keep_going:
# For each variant type ...
# Sort to ensure variant_name is being visited in a
# reversed aliphatic order so that the logic below can
# get a deterministic simplified variant combination
# between Python 2 and 3.
# For example, "variant.opensolaris.zone" should be
# visited before "variant.arch".
return [
(k, v) for k, v in item
if k != variant_name
]
# For sanity, instead of modifying rel_set on
# the fly, a new working set is created to which
# members or collapsed members of rel_set are
# added.
new_rel_set = set()
# Put the combinations of variant values into
# groups so that all members of the group are
# identical except for the values for
# variant_name.
g = set(g)
# If there are fewer members in the
# group than there are values, then
# there's no way this value can be
# collapsed.
variant_name]):
new_rel_set |= g
continue
# 'expected' is the set of variant
# values that will need to be seen to
# collapse the combinations in g by
# removing the values associated with
# variant_name.
# Check to see whether all possible
# variant values are covered.
for tup in g:
if v_name != \
continue
# If not all the possible values have
# been seen, then the variant
# combinations can't be collapsed.
if expected:
new_rel_set |= g
continue
# If they have, then the variant
# combinations can be collapsed by
# removing variant_name. The key used
# to group the variant combinations,
# 'k', is identical to each of the
# variant combinations with the value
# for variant_name removed, so 'k' is
# added to the new result set. Since
# some variant combinations have been
# collapsed, then it's necessary to make
# another pass over the variant types as
# 'k' may be able to collapse with other
# variant combinations.
return rel_set