Merge "Adds "element-provides" support to element dependencies"
This commit is contained in:
commit
0749cf4d11
14
README.md
14
README.md
@ -325,9 +325,17 @@ scripts in each phase.
|
|||||||
|
|
||||||
### Dependencies ###
|
### Dependencies ###
|
||||||
|
|
||||||
Each element has a file named element-deps: a plain text, newline separated
|
Each element can use the following files to define or affect dependencies:
|
||||||
list of elements which will be added to the list of elements built into the
|
|
||||||
image at image creation time.
|
* 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 ###
|
### First-boot files ###
|
||||||
|
|
||||||
|
@ -24,21 +24,12 @@ def get_elements_dir():
|
|||||||
return os.environ['ELEMENTS_PATH']
|
return os.environ['ELEMENTS_PATH']
|
||||||
|
|
||||||
|
|
||||||
def dependencies(element, elements_dir=None):
|
def _get_set(element, fname, 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.
|
|
||||||
"""
|
|
||||||
if elements_dir is None:
|
if elements_dir is None:
|
||||||
elements_dir = get_elements_dir()
|
elements_dir = get_elements_dir()
|
||||||
|
|
||||||
for path in elements_dir.split(':'):
|
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:
|
try:
|
||||||
with open(element_deps_path) as element_deps:
|
with open(element_deps_path) as element_deps:
|
||||||
return set([line.strip() for line in element_deps])
|
return set([line.strip() for line in element_deps])
|
||||||
@ -55,6 +46,32 @@ def dependencies(element, elements_dir=None):
|
|||||||
exit(-1)
|
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):
|
def expand_dependencies(user_elements, elements_dir=None):
|
||||||
"""Expand user requested elements using element-deps files.
|
"""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)
|
final_elements = set(user_elements)
|
||||||
check_queue = list(user_elements)
|
check_queue = list(user_elements)
|
||||||
|
provided = set()
|
||||||
|
|
||||||
while check_queue:
|
while check_queue:
|
||||||
element = check_queue.pop()
|
element = check_queue.pop()
|
||||||
|
if element in provided:
|
||||||
|
continue
|
||||||
deps = dependencies(element, elements_dir)
|
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)
|
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):
|
def main(argv):
|
||||||
|
@ -24,12 +24,15 @@ data_dir = os.path.abspath(
|
|||||||
os.path.join(os.path.dirname(__file__), 'test-elements'))
|
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)
|
element_home = os.path.join(element_dir, element_name)
|
||||||
os.mkdir(element_home)
|
os.mkdir(element_home)
|
||||||
deps_path = os.path.join(element_home, 'element-deps')
|
deps_path = os.path.join(element_home, 'element-deps')
|
||||||
with open(deps_path, 'w') as deps_file:
|
with open(deps_path, 'w') as deps_file:
|
||||||
deps_file.write("\n".join(element_deps))
|
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):
|
class TestElementDeps(testtools.TestCase):
|
||||||
@ -43,6 +46,12 @@ class TestElementDeps(testtools.TestCase):
|
|||||||
'requires-requires-foo',
|
'requires-requires-foo',
|
||||||
['requires-foo'])
|
['requires-foo'])
|
||||||
_populate_element(self.element_dir, 'self', ['self'])
|
_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, 'circular1', ['circular2'])
|
||||||
_populate_element(self.element_dir, 'circular2', ['circular1'])
|
_populate_element(self.element_dir, 'circular2', ['circular1'])
|
||||||
|
|
||||||
@ -79,6 +88,18 @@ class TestElementDeps(testtools.TestCase):
|
|||||||
['circular1'], elements_dir=self.element_dir)
|
['circular1'], elements_dir=self.element_dir)
|
||||||
self.assertEqual(set(['circular1', 'circular2']), result)
|
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):
|
class TestElements(testtools.TestCase):
|
||||||
def test_depends_on_env(self):
|
def test_depends_on_env(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user