Merge "Adds "element-provides" support to element dependencies"
This commit is contained in:
commit
0749cf4d11
3 changed files with 73 additions and 17 deletions
14
README.md
14
README.md
|
@ -325,9 +325,17 @@ scripts in each phase.
|
|||
|
||||
### Dependencies ###
|
||||
|
||||
Each element has a file named element-deps: a plain text, newline separated
|
||||
list of elements which will be added to the list of elements built into the
|
||||
image at image creation time.
|
||||
Each element can use the following files to define or affect dependencies:
|
||||
|
||||
* element-deps: a plain text, newline separated list of elements which will
|
||||
be added to the list of elements built into the image at image creation time.
|
||||
|
||||
* element-provides: A plain text, newline separated list of elements which
|
||||
are provided by this element. These elements will be excluded from elements
|
||||
built into the image at image creation time. For example if element A depends
|
||||
on element B and element C includes element B in its "element-provides"
|
||||
file and A and C are included when building an image, then B is not used.
|
||||
|
||||
|
||||
### First-boot files ###
|
||||
|
||||
|
|
|
@ -24,21 +24,12 @@ def get_elements_dir():
|
|||
return os.environ['ELEMENTS_PATH']
|
||||
|
||||
|
||||
def dependencies(element, elements_dir=None):
|
||||
"""Return the non-transitive list of dependencies for a single element.
|
||||
|
||||
:param user_elements: iterable enumerating elements a user has requested
|
||||
:param elements_dir: the elements dir to read from. If not supplied,
|
||||
inferred by calling get_elements_dir().
|
||||
|
||||
:return: a set just containing all elements that the specified element
|
||||
depends on.
|
||||
"""
|
||||
def _get_set(element, fname, elements_dir=None):
|
||||
if elements_dir is None:
|
||||
elements_dir = get_elements_dir()
|
||||
|
||||
for path in elements_dir.split(':'):
|
||||
element_deps_path = (os.path.join(path, element, 'element-deps'))
|
||||
element_deps_path = (os.path.join(path, element, fname))
|
||||
try:
|
||||
with open(element_deps_path) as element_deps:
|
||||
return set([line.strip() for line in element_deps])
|
||||
|
@ -55,6 +46,32 @@ def dependencies(element, elements_dir=None):
|
|||
exit(-1)
|
||||
|
||||
|
||||
def provides(element, elements_dir=None):
|
||||
"""Return the set of elements provided by the specified element.
|
||||
|
||||
:param element: name of a single element
|
||||
:param elements_dir: the elements dir to read from. If not supplied,
|
||||
inferred by calling get_elements_dir().
|
||||
|
||||
:return: a set just containing all elements that the specified element
|
||||
provides.
|
||||
"""
|
||||
return _get_set(element, 'element-provides', elements_dir)
|
||||
|
||||
|
||||
def dependencies(element, elements_dir=None):
|
||||
"""Return the non-transitive set of dependencies for a single element.
|
||||
|
||||
:param element: name of a single element
|
||||
:param elements_dir: the elements dir to read from. If not supplied,
|
||||
inferred by calling get_elements_dir().
|
||||
|
||||
:return: a set just containing all elements that the specified element
|
||||
depends on.
|
||||
"""
|
||||
return _get_set(element, 'element-deps', elements_dir)
|
||||
|
||||
|
||||
def expand_dependencies(user_elements, elements_dir=None):
|
||||
"""Expand user requested elements using element-deps files.
|
||||
|
||||
|
@ -68,14 +85,24 @@ def expand_dependencies(user_elements, elements_dir=None):
|
|||
"""
|
||||
final_elements = set(user_elements)
|
||||
check_queue = list(user_elements)
|
||||
provided = set()
|
||||
|
||||
while check_queue:
|
||||
element = check_queue.pop()
|
||||
if element in provided:
|
||||
continue
|
||||
deps = dependencies(element, elements_dir)
|
||||
check_queue.extend(deps - final_elements)
|
||||
provided.update(provides(element, elements_dir))
|
||||
check_queue.extend(deps - (final_elements | provided))
|
||||
final_elements.update(deps)
|
||||
|
||||
return final_elements
|
||||
conflicts = set(user_elements) & provided
|
||||
if conflicts:
|
||||
sys.stderr.write("ERROR: Following elements were explicitly required "
|
||||
"but are provided by other included elements: %s\n" %
|
||||
", ".join(conflicts))
|
||||
exit(-1)
|
||||
return final_elements - provided
|
||||
|
||||
|
||||
def main(argv):
|
||||
|
|
|
@ -24,12 +24,15 @@ data_dir = os.path.abspath(
|
|||
os.path.join(os.path.dirname(__file__), 'test-elements'))
|
||||
|
||||
|
||||
def _populate_element(element_dir, element_name, element_deps=[]):
|
||||
def _populate_element(element_dir, element_name, element_deps=[], provides=[]):
|
||||
element_home = os.path.join(element_dir, element_name)
|
||||
os.mkdir(element_home)
|
||||
deps_path = os.path.join(element_home, 'element-deps')
|
||||
with open(deps_path, 'w') as deps_file:
|
||||
deps_file.write("\n".join(element_deps))
|
||||
provides_path = os.path.join(element_home, 'element-provides')
|
||||
with open(provides_path, 'w') as provides_file:
|
||||
provides_file.write("\n".join(provides))
|
||||
|
||||
|
||||
class TestElementDeps(testtools.TestCase):
|
||||
|
@ -43,6 +46,12 @@ class TestElementDeps(testtools.TestCase):
|
|||
'requires-requires-foo',
|
||||
['requires-foo'])
|
||||
_populate_element(self.element_dir, 'self', ['self'])
|
||||
_populate_element(self.element_dir,
|
||||
'provides_virtual',
|
||||
[],
|
||||
['virtual'])
|
||||
_populate_element(self.element_dir, 'requires_virtual', ['virtual'])
|
||||
_populate_element(self.element_dir, 'virtual', [])
|
||||
_populate_element(self.element_dir, 'circular1', ['circular2'])
|
||||
_populate_element(self.element_dir, 'circular2', ['circular1'])
|
||||
|
||||
|
@ -79,6 +88,18 @@ class TestElementDeps(testtools.TestCase):
|
|||
['circular1'], elements_dir=self.element_dir)
|
||||
self.assertEqual(set(['circular1', 'circular2']), result)
|
||||
|
||||
def test_provide(self):
|
||||
result = element_dependencies.expand_dependencies(
|
||||
['requires_virtual', 'provides_virtual'],
|
||||
elements_dir=self.element_dir)
|
||||
self.assertEqual(set(['requires_virtual', 'provides_virtual']), result)
|
||||
|
||||
def test_provide_conflict(self):
|
||||
self.assertRaises(SystemExit,
|
||||
element_dependencies.expand_dependencies,
|
||||
['virtual', 'provides_virtual'],
|
||||
self.element_dir)
|
||||
|
||||
|
||||
class TestElements(testtools.TestCase):
|
||||
def test_depends_on_env(self):
|
||||
|
|
Loading…
Reference in a new issue