[Notes] [Git][BuildStream/buildstream][valentindavid/sysroot_dependencies] Add support for importing dependencies in sysroots.



Title: GitLab

Valentin David pushed to branch valentindavid/sysroot_dependencies at BuildStream / buildstream

Commits:

29 changed files:

Changes:

  • buildstream/_loader/loadelement.py
    ... ... @@ -72,10 +72,24 @@ class LoadElement():
    72 72
                 'variables', 'environment', 'environment-nocache',
    
    73 73
                 'config', 'public', 'description',
    
    74 74
                 'build-depends', 'runtime-depends',
    
    75
    +            'sysroots',
    
    75 76
             ])
    
    76 77
     
    
    78
    +        self.deps = []
    
    79
    +        sysroots = _yaml.node_get(node, list, 'sysroots', default_value=[])
    
    80
    +        for sysroot in sysroots:
    
    81
    +            _yaml.node_validate(sysroot, ['path', 'depends', 'build-depends'])
    
    82
    +            path = _yaml.node_get(sysroot, str, 'path')
    
    83
    +            for dep in _extract_depends_from_node(sysroot):
    
    84
    +                if dep.dep_type == Symbol.RUNTIME:
    
    85
    +                    raise LoadError(LoadErrorReason.INVALID_DATA,
    
    86
    +                                    "{}: Sysroot'ed dependencies can not be of type 'runtime'"
    
    87
    +                                    .format(dep.provenance))
    
    88
    +                self.deps.append((path, dep))
    
    89
    +
    
    77 90
             # Extract the Dependencies
    
    78
    -        self.deps = _extract_depends_from_node(self.node)
    
    91
    +        for dep in _extract_depends_from_node(self.node):
    
    92
    +            self.deps.append(('/', dep))
    
    79 93
     
    
    80 94
         # depends():
    
    81 95
         #
    
    ... ... @@ -101,7 +115,7 @@ class LoadElement():
    101 115
                 return
    
    102 116
     
    
    103 117
             self._dep_cache = {}
    
    104
    -        for dep in self.deps:
    
    118
    +        for _, dep in self.deps:
    
    105 119
                 elt = self._loader.get_element_for_dep(dep)
    
    106 120
     
    
    107 121
                 # Ensure the cache of the element we depend on
    

  • buildstream/_loader/loader.py
    ... ... @@ -121,7 +121,7 @@ class Loader():
    121 121
                     junction, name, loader = self._parse_name(target, rewritable, ticker,
    
    122 122
                                                               fetch_subprojects=fetch_subprojects)
    
    123 123
                     loader._load_file(name, rewritable, ticker, fetch_subprojects, yaml_cache)
    
    124
    -                deps.append(Dependency(name, junction=junction))
    
    124
    +                deps.append(('/', Dependency(name, junction=junction)))
    
    125 125
                     profile_end(Topics.LOAD_PROJECT, target)
    
    126 126
     
    
    127 127
             #
    
    ... ... @@ -269,7 +269,7 @@ class Loader():
    269 269
             self._elements[filename] = element
    
    270 270
     
    
    271 271
             # Load all dependency files for the new LoadElement
    
    272
    -        for dep in element.deps:
    
    272
    +        for _, dep in element.deps:
    
    273 273
                 if dep.junction:
    
    274 274
                     self._load_file(dep.junction, rewritable, ticker, fetch_subprojects, yaml_cache)
    
    275 275
                     loader = self._get_loader(dep.junction, rewritable=rewritable, ticker=ticker,
    
    ... ... @@ -330,7 +330,7 @@ class Loader():
    330 330
             # Push / Check each dependency / Pop
    
    331 331
             check_elements[element_name] = True
    
    332 332
             sequence.append(element_name)
    
    333
    -        for dep in element.deps:
    
    333
    +        for _, dep in element.deps:
    
    334 334
                 loader = self._get_loader_for_dep(dep)
    
    335 335
                 loader._check_circular_deps(dep.name, check_elements, validated, sequence)
    
    336 336
             del check_elements[element_name]
    
    ... ... @@ -365,14 +365,21 @@ class Loader():
    365 365
             if visited.get(element_name) is not None:
    
    366 366
                 return
    
    367 367
     
    
    368
    -        for dep in element.deps:
    
    368
    +        for _, dep in element.deps:
    
    369 369
                 loader = self._get_loader_for_dep(dep)
    
    370 370
                 loader._sort_dependencies(dep.name, visited=visited)
    
    371 371
     
    
    372
    -        def dependency_cmp(dep_a, dep_b):
    
    372
    +        def dependency_cmp(sdep_a, sdep_b):
    
    373
    +            sysroot_a, dep_a = sdep_a
    
    374
    +            sysroot_b, dep_b = sdep_b
    
    373 375
                 element_a = self.get_element_for_dep(dep_a)
    
    374 376
                 element_b = self.get_element_for_dep(dep_b)
    
    375 377
     
    
    378
    +            if sysroot_a < sysroot_b:
    
    379
    +                return -1
    
    380
    +            if sysroot_b < sysroot_a:
    
    381
    +                return 1
    
    382
    +
    
    376 383
                 # Sort on inter element dependency first
    
    377 384
                 if element_a.depends(element_b):
    
    378 385
                     return 1
    
    ... ... @@ -471,11 +478,11 @@ class Loader():
    471 478
             self._meta_elements[element_name] = meta_element
    
    472 479
     
    
    473 480
             # Descend
    
    474
    -        for dep in element.deps:
    
    481
    +        for sysroot, dep in element.deps:
    
    475 482
                 loader = self._get_loader_for_dep(dep)
    
    476 483
                 meta_dep = loader._collect_element(dep.name)
    
    477 484
                 if dep.dep_type != 'runtime':
    
    478
    -                meta_element.build_dependencies.append(meta_dep)
    
    485
    +                meta_element.build_dependencies.append((sysroot, meta_dep))
    
    479 486
                 if dep.dep_type != 'build':
    
    480 487
                     meta_element.dependencies.append(meta_dep)
    
    481 488
     
    

  • buildstream/buildelement.py
    ... ... @@ -216,8 +216,9 @@ class BuildElement(Element):
    216 216
             # Run any integration commands provided by the dependencies
    
    217 217
             # once they are all staged and ready
    
    218 218
             with self.timed_activity("Integrating sandbox"):
    
    219
    -            for dep in self.dependencies(Scope.BUILD):
    
    220
    -                dep.integrate(sandbox)
    
    219
    +            for sysroot, dep in self.dependencies(Scope.BUILD, with_sysroot=True):
    
    220
    +                if sysroot == '/':
    
    221
    +                    dep.integrate(sandbox)
    
    221 222
     
    
    222 223
             # Stage sources in the build root
    
    223 224
             self.stage_sources(sandbox, self.get_variable('build-root'))
    

  • buildstream/element.py
    ... ... @@ -376,7 +376,8 @@ class Element(Plugin):
    376 376
             for source in self.__sources:
    
    377 377
                 yield source
    
    378 378
     
    
    379
    -    def dependencies(self, scope, *, recurse=True, visited=None, recursed=False):
    
    379
    +    def dependencies(self, scope, *, recurse=True, visited=None, recursed=False,
    
    380
    +                     with_sysroot=False, sysroot='/'):
    
    380 381
             """dependencies(scope, *, recurse=True)
    
    381 382
     
    
    382 383
             A generator function which yields the dependencies of the given element.
    
    ... ... @@ -401,40 +402,56 @@ class Element(Plugin):
    401 402
     
    
    402 403
             scope_set = set((Scope.BUILD, Scope.RUN)) if scope == Scope.ALL else set((scope,))
    
    403 404
     
    
    404
    -        if full_name in visited and scope_set.issubset(visited[full_name]):
    
    405
    +        if with_sysroot:
    
    406
    +            key = (sysroot, full_name)
    
    407
    +        else:
    
    408
    +            key = full_name
    
    409
    +
    
    410
    +        if key in visited and scope_set.issubset(visited[key]):
    
    405 411
                 return
    
    406 412
     
    
    407 413
             should_yield = False
    
    408
    -        if full_name not in visited:
    
    409
    -            visited[full_name] = scope_set
    
    414
    +        if key not in visited:
    
    415
    +            visited[key] = scope_set
    
    410 416
                 should_yield = True
    
    411 417
             else:
    
    412
    -            visited[full_name] |= scope_set
    
    418
    +            visited[key] |= scope_set
    
    413 419
     
    
    414 420
             if recurse or not recursed:
    
    415 421
                 if scope == Scope.ALL:
    
    416
    -                for dep in self.__build_dependencies:
    
    422
    +                build_deps = []
    
    423
    +                for dep_sysroot, dep in self.__build_dependencies:
    
    424
    +                    new_sysroot = self._subst_string(dep_sysroot) if not recursed else sysroot
    
    417 425
                         yield from dep.dependencies(Scope.ALL, recurse=recurse,
    
    418
    -                                                visited=visited, recursed=True)
    
    426
    +                                                visited=visited, recursed=True,
    
    427
    +                                                sysroot=new_sysroot, with_sysroot=with_sysroot)
    
    428
    +                    build_deps.append(dep)
    
    419 429
     
    
    420 430
                     for dep in self.__runtime_dependencies:
    
    421
    -                    if dep not in self.__build_dependencies:
    
    431
    +                    if dep not in build_deps:
    
    422 432
                             yield from dep.dependencies(Scope.ALL, recurse=recurse,
    
    423
    -                                                    visited=visited, recursed=True)
    
    433
    +                                                    visited=visited, recursed=True,
    
    434
    +                                                    sysroot=sysroot, with_sysroot=with_sysroot)
    
    424 435
     
    
    425 436
                 elif scope == Scope.BUILD:
    
    426
    -                for dep in self.__build_dependencies:
    
    437
    +                for dep_sysroot, dep in self.__build_dependencies:
    
    438
    +                    new_sysroot = self._subst_string(dep_sysroot) if not recursed else sysroot
    
    427 439
                         yield from dep.dependencies(Scope.RUN, recurse=recurse,
    
    428
    -                                                visited=visited, recursed=True)
    
    440
    +                                                visited=visited, recursed=True,
    
    441
    +                                                sysroot=new_sysroot, with_sysroot=with_sysroot)
    
    429 442
     
    
    430 443
                 elif scope == Scope.RUN:
    
    431 444
                     for dep in self.__runtime_dependencies:
    
    432 445
                         yield from dep.dependencies(Scope.RUN, recurse=recurse,
    
    433
    -                                                visited=visited, recursed=True)
    
    446
    +                                                visited=visited, recursed=True,
    
    447
    +                                                sysroot=sysroot, with_sysroot=with_sysroot)
    
    434 448
     
    
    435 449
             # Yeild self only at the end, after anything needed has been traversed
    
    436 450
             if should_yield and (recurse or recursed) and (scope in (Scope.ALL, Scope.RUN)):
    
    437
    -            yield self
    
    451
    +            if with_sysroot:
    
    452
    +                yield sysroot, self
    
    453
    +            else:
    
    454
    +                yield self
    
    438 455
     
    
    439 456
         def search(self, scope, name):
    
    440 457
             """Search for a dependency by name
    
    ... ... @@ -633,7 +650,7 @@ class Element(Plugin):
    633 650
                 vbasedir = sandbox.get_virtual_directory()
    
    634 651
                 vstagedir = vbasedir \
    
    635 652
                     if path is None \
    
    636
    -                else vbasedir.descend(path.lstrip(os.sep).split(os.sep))
    
    653
    +                else vbasedir.descend(path.lstrip(os.sep).split(os.sep), create=True)
    
    637 654
     
    
    638 655
                 files = list(self.__compute_splits(include, exclude, orphans))
    
    639 656
     
    
    ... ... @@ -651,7 +668,8 @@ class Element(Plugin):
    651 668
             return link_result.combine(copy_result)
    
    652 669
     
    
    653 670
         def stage_dependency_artifacts(self, sandbox, scope, *, path=None,
    
    654
    -                                   include=None, exclude=None, orphans=True):
    
    671
    +                                   include=None, exclude=None, orphans=True,
    
    672
    +                                   build=True):
    
    655 673
             """Stage element dependencies in scope
    
    656 674
     
    
    657 675
             This is primarily a convenience wrapper around
    
    ... ... @@ -681,7 +699,14 @@ class Element(Plugin):
    681 699
             if self.__can_build_incrementally() and workspace.last_successful:
    
    682 700
                 old_dep_keys = self.__get_artifact_metadata_dependencies(workspace.last_successful)
    
    683 701
     
    
    684
    -        for dep in self.dependencies(scope):
    
    702
    +        def deps():
    
    703
    +            if build:
    
    704
    +                yield from self.dependencies(scope, with_sysroot=True)
    
    705
    +            else:
    
    706
    +                for dep in self.dependencies(scope, with_sysroot=False):
    
    707
    +                    yield '/', dep
    
    708
    +
    
    709
    +        for sysroot, dep in deps():
    
    685 710
                 # If we are workspaced, and we therefore perform an
    
    686 711
                 # incremental build, we must ensure that we update the mtimes
    
    687 712
                 # of any files created by our dependencies since the last
    
    ... ... @@ -706,8 +731,13 @@ class Element(Plugin):
    706 731
                         if utils._is_main_process():
    
    707 732
                             self._get_context().get_workspaces().save_config()
    
    708 733
     
    
    734
    +            if build:
    
    735
    +                sub_path = os.path.join(path, os.path.relpath(sysroot, '/')) if path else sysroot
    
    736
    +            else:
    
    737
    +                sub_path = path
    
    738
    +
    
    709 739
                 result = dep.stage_artifact(sandbox,
    
    710
    -                                        path=path,
    
    740
    +                                        path=sub_path,
    
    711 741
                                             include=include,
    
    712 742
                                             exclude=exclude,
    
    713 743
                                             orphans=orphans,
    
    ... ... @@ -906,9 +936,9 @@ class Element(Plugin):
    906 936
             for meta_dep in meta.dependencies:
    
    907 937
                 dependency = Element._new_from_meta(meta_dep)
    
    908 938
                 element.__runtime_dependencies.append(dependency)
    
    909
    -        for meta_dep in meta.build_dependencies:
    
    939
    +        for sysroot, meta_dep in meta.build_dependencies:
    
    910 940
                 dependency = Element._new_from_meta(meta_dep)
    
    911
    -            element.__build_dependencies.append(dependency)
    
    941
    +            element.__build_dependencies.append((sysroot, dependency))
    
    912 942
     
    
    913 943
             return element
    
    914 944
     
    
    ... ... @@ -1088,14 +1118,11 @@ class Element(Plugin):
    1088 1118
                 # Weak cache key includes names of direct build dependencies
    
    1089 1119
                 # but does not include keys of dependencies.
    
    1090 1120
                 if self.BST_STRICT_REBUILD:
    
    1091
    -                dependencies = [
    
    1092
    -                    e._get_cache_key(strength=_KeyStrength.WEAK)
    
    1093
    -                    for e in self.dependencies(Scope.BUILD)
    
    1094
    -                ]
    
    1121
    +                dependencies = [(sysroot, e._get_cache_key(strength=_KeyStrength.WEAK))
    
    1122
    +                                for sysroot, e in self.dependencies(Scope.BUILD, with_sysroot=True)]
    
    1095 1123
                 else:
    
    1096
    -                dependencies = [
    
    1097
    -                    e.name for e in self.dependencies(Scope.BUILD, recurse=False)
    
    1098
    -                ]
    
    1124
    +                dependencies = [(sysroot, e.name)
    
    1125
    +                                for sysroot, e in self.dependencies(Scope.BUILD, with_sysroot=True)]
    
    1099 1126
     
    
    1100 1127
                 self.__weak_cache_key = self.__calculate_cache_key(dependencies)
    
    1101 1128
     
    
    ... ... @@ -1123,9 +1150,8 @@ class Element(Plugin):
    1123 1150
                         return
    
    1124 1151
     
    
    1125 1152
             if self.__strict_cache_key is None:
    
    1126
    -            dependencies = [
    
    1127
    -                e.__strict_cache_key for e in self.dependencies(Scope.BUILD)
    
    1128
    -            ]
    
    1153
    +            dependencies = [(sysroot, e.__strict_cache_key)
    
    1154
    +                            for sysroot, e in self.dependencies(Scope.BUILD, with_sysroot=True)]
    
    1129 1155
                 self.__strict_cache_key = self.__calculate_cache_key(dependencies)
    
    1130 1156
     
    
    1131 1157
                 if self.__strict_cache_key is None:
    
    ... ... @@ -1165,10 +1191,8 @@ class Element(Plugin):
    1165 1191
                     strong_key, _ = self.__get_artifact_metadata_keys()
    
    1166 1192
                     self.__cache_key = strong_key
    
    1167 1193
                 elif self.__assemble_scheduled or self.__assemble_done:
    
    1168
    -                # Artifact will or has been built, not downloaded
    
    1169
    -                dependencies = [
    
    1170
    -                    e._get_cache_key() for e in self.dependencies(Scope.BUILD)
    
    1171
    -                ]
    
    1194
    +                dependencies = [(sysroot, e._get_cache_key())
    
    1195
    +                                for sysroot, e in self.dependencies(Scope.BUILD, with_sysroot=True)]
    
    1172 1196
                     self.__cache_key = self.__calculate_cache_key(dependencies)
    
    1173 1197
     
    
    1174 1198
                 if self.__cache_key is None:
    
    ... ... @@ -1333,7 +1357,7 @@ class Element(Plugin):
    1333 1357
                         # Stage deps in the sandbox root
    
    1334 1358
                         if deps == 'run':
    
    1335 1359
                             with self.timed_activity("Staging dependencies", silent_nested=True):
    
    1336
    -                            self.stage_dependency_artifacts(sandbox, scope)
    
    1360
    +                            self.stage_dependency_artifacts(sandbox, scope, build=False)
    
    1337 1361
     
    
    1338 1362
                             # Run any integration commands provided by the dependencies
    
    1339 1363
                             # once they are all staged and ready
    
    ... ... @@ -2011,8 +2035,11 @@ class Element(Plugin):
    2011 2035
         #
    
    2012 2036
         def __calculate_cache_key(self, dependencies):
    
    2013 2037
             # No cache keys for dependencies which have no cache keys
    
    2014
    -        if None in dependencies:
    
    2015
    -            return None
    
    2038
    +        for dep in dependencies:
    
    2039
    +            if dep[1] is None:
    
    2040
    +                return None
    
    2041
    +        # Do not break cache keys
    
    2042
    +        dependencies = [(sysroot, key) if sysroot != '/' else key for sysroot, key in dependencies]
    
    2016 2043
     
    
    2017 2044
             # Generate dict that is used as base for all cache keys
    
    2018 2045
             if self.__cache_key_dict is None:
    

  • buildstream/plugins/elements/compose.py
    ... ... @@ -122,8 +122,9 @@ class ComposeElement(Element):
    122 122
                         snapshot = set(vbasedir.list_relative_paths())
    
    123 123
                         vbasedir.mark_unmodified()
    
    124 124
     
    
    125
    -                for dep in self.dependencies(Scope.BUILD):
    
    126
    -                    dep.integrate(sandbox)
    
    125
    +                for sysroot, dep in self.dependencies(Scope.BUILD, with_sysroot=True):
    
    126
    +                    if sysroot == '/':
    
    127
    +                        dep.integrate(sandbox)
    
    127 128
     
    
    128 129
                     if require_split:
    
    129 130
                         # Calculate added, modified and removed files
    

  • doc/source/format_declaring.rst
    ... ... @@ -159,6 +159,57 @@ See :ref:`format_dependencies` for more information on the dependency model.
    159 159
     
    
    160 160
        The ``runtime-depends`` configuration is available since :ref:`format version 14 <project_format_version>`
    
    161 161
     
    
    162
    +Sysroot'ed dependencies
    
    163
    +~~~~~~~~~~~~~~~~~~~~~~~
    
    164
    +
    
    165
    +Sysroot'ed dependencies are intended for bootstraping base systems or
    
    166
    +cross-compiling.
    
    167
    +
    
    168
    +.. code:: yaml
    
    169
    +
    
    170
    +   # Specify some sysroot'ed dependencies
    
    171
    +   sysroots:
    
    172
    +   - path: /sysroot
    
    173
    +     depends:
    
    174
    +     - element1.bst
    
    175
    +     - element2.bst
    
    176
    +
    
    177
    +During build, or initialization of build shell, sysroot'ed build
    
    178
    +dependencies will be staged in the given sysroot path instead of '/'
    
    179
    +together with the runtime dependencies of those sysroot'ed build
    
    180
    +dependencies.
    
    181
    +
    
    182
    +It is possible to end up with indirect runtime dependencies in
    
    183
    +different sysroots if they are staged from build dependencies with
    
    184
    +different sysroots. They will be staged multiple times.
    
    185
    +
    
    186
    +Sysroot paths only apply to build dependencies. It is not possible to
    
    187
    +define runtime dependencies either with ``type: runtime`` or
    
    188
    +``runtime-depends``. It is possible to use ``all`` dependencies, but
    
    189
    +the sysroot part is only for the build part not the runtime.
    
    190
    +
    
    191
    +For example:
    
    192
    +
    
    193
    +.. code:: yaml
    
    194
    +
    
    195
    +   sysroots:
    
    196
    +   - path: /sysroot
    
    197
    +     depends:
    
    198
    +     - element.bst
    
    199
    +
    
    200
    +is equivalent to:
    
    201
    +
    
    202
    +.. code:: yaml
    
    203
    +
    
    204
    +   runtime-depends:
    
    205
    +   - element.bst
    
    206
    +   sysroots:
    
    207
    +   - path: /sysroot
    
    208
    +     build-depends:
    
    209
    +     - element.bst
    
    210
    +
    
    211
    +:ref:`Integration commands <public_integration>` are never executed for
    
    212
    +sysroot'ed dependencies.
    
    162 213
     
    
    163 214
     .. _format_sources:
    
    164 215
     
    

  • tests/loader/dependencies.py
    ... ... @@ -135,7 +135,7 @@ def test_build_dependency(datafiles):
    135 135
     
    
    136 136
         assert(len(element.build_dependencies) == 1)
    
    137 137
         firstdep = element.build_dependencies[0]
    
    138
    -    assert(isinstance(firstdep, MetaElement))
    
    138
    +    assert(isinstance(firstdep[1], MetaElement))
    
    139 139
     
    
    140 140
         assert(len(element.dependencies) == 0)
    
    141 141
     
    
    ... ... @@ -170,7 +170,7 @@ def test_build_runtime_dependency(datafiles):
    170 170
         firstdep = element.dependencies[0]
    
    171 171
         assert(isinstance(firstdep, MetaElement))
    
    172 172
         firstbuilddep = element.build_dependencies[0]
    
    173
    -    assert(firstdep == firstbuilddep)
    
    173
    +    assert(firstdep == firstbuilddep[1])
    
    174 174
     
    
    175 175
     
    
    176 176
     @pytest.mark.datafiles(DATA_DIR)
    
    ... ... @@ -187,7 +187,7 @@ def test_all_dependency(datafiles):
    187 187
         firstdep = element.dependencies[0]
    
    188 188
         assert(isinstance(firstdep, MetaElement))
    
    189 189
         firstbuilddep = element.build_dependencies[0]
    
    190
    -    assert(firstdep == firstbuilddep)
    
    190
    +    assert(firstdep == firstbuilddep[1])
    
    191 191
     
    
    192 192
     
    
    193 193
     @pytest.mark.datafiles(DATA_DIR)
    

  • tests/sysroot_depends/project/elements/a.bst
    1
    +kind: import
    
    2
    +sources:
    
    3
    +  - kind: local
    
    4
    +    path: files/a

  • tests/sysroot_depends/project/elements/b.bst
    1
    +kind: import
    
    2
    +sources:
    
    3
    +  - kind: local
    
    4
    +    path: files/b

  • tests/sysroot_depends/project/elements/base.bst
    1
    +kind: stack
    
    2
    +depends:
    
    3
    +- base/base-alpine.bst

  • tests/sysroot_depends/project/elements/base/base-alpine.bst
    1
    +kind: import
    
    2
    +
    
    3
    +description: |
    
    4
    +  Alpine Linux base for tests
    
    5
    +
    
    6
    +  Generated using the `tests/integration-tests/base/generate-base.sh` script.
    
    7
    +
    
    8
    +sources:
    
    9
    +  - kind: tar
    
    10
    +    url: alpine:integration-tests-base.v1.x86_64.tar.xz
    
    11
    +    base-dir: ''
    
    12
    +    ref: 3eb559250ba82b64a68d86d0636a6b127aa5f6d25d3601a79f79214dc9703639

  • tests/sysroot_depends/project/elements/compose-integration.bst
    1
    +kind: compose
    
    2
    +
    
    3
    +sysroots:
    
    4
    +- path: /sysroot
    
    5
    +  build-depends:
    
    6
    +  - integration.bst

  • tests/sysroot_depends/project/elements/compose-layers-with-sysroot.bst
    1
    +kind: compose
    
    2
    +
    
    3
    +sysroots:
    
    4
    +- path: /other-sysroot
    
    5
    +  build-depends:
    
    6
    +  - layer2.bst

  • tests/sysroot_depends/project/elements/compose-layers.bst
    1
    +kind: compose
    
    2
    +
    
    3
    +build-depends:
    
    4
    +- layer2.bst

  • tests/sysroot_depends/project/elements/integration.bst
    1
    +kind: manual
    
    2
    +
    
    3
    +depends:
    
    4
    +- base.bst
    
    5
    +
    
    6
    +config:
    
    7
    +  install-commands:
    
    8
    +    - echo 0 >"%{install-root}/integrated.txt"
    
    9
    +
    
    10
    +public:
    
    11
    +  bst:
    
    12
    +    integration-commands:
    
    13
    +    - echo 1 >/integrated.txt

  • tests/sysroot_depends/project/elements/layer1-files.bst
    1
    +kind: import
    
    2
    +sources:
    
    3
    +- kind: local
    
    4
    +  path: files/layer1

  • tests/sysroot_depends/project/elements/layer1.bst
    1
    +kind: stack
    
    2
    +
    
    3
    +depends:
    
    4
    +- layer1-files.bst

  • tests/sysroot_depends/project/elements/layer2-files.bst
    1
    +kind: import
    
    2
    +sources:
    
    3
    +- kind: local
    
    4
    +  path: files/layer2

  • tests/sysroot_depends/project/elements/layer2.bst
    1
    +kind: manual
    
    2
    +
    
    3
    +depends:
    
    4
    +- layer2-files.bst
    
    5
    +
    
    6
    +build-depends:
    
    7
    +- base.bst
    
    8
    +
    
    9
    +sysroots:
    
    10
    +- path: /sysroot
    
    11
    +  depends:
    
    12
    +  - layer1.bst
    
    13
    +
    
    14
    +config:
    
    15
    +  install-commands:
    
    16
    +  - mkdir -p "%{install-root}"
    
    17
    +  - |
    
    18
    +    for file in /*; do
    
    19
    +      if test -f "${file}"; then
    
    20
    +        cp "${file}" "%{install-root}"
    
    21
    +      fi
    
    22
    +    done

  • tests/sysroot_depends/project/elements/manual-integration-runtime.bst
    1
    +kind: manual
    
    2
    +
    
    3
    +depends:
    
    4
    +- base.bst
    
    5
    +
    
    6
    +sysroots:
    
    7
    +- path: /sysroot
    
    8
    +  depends:
    
    9
    +  - integration.bst
    
    10
    +
    
    11
    +config:
    
    12
    +  install-commands:
    
    13
    +    - mkdir -p "%{install-root}"
    
    14
    +    - echo dummy >"%{install-root}/dummy.txt"

  • tests/sysroot_depends/project/elements/manual-integration.bst
    1
    +kind: manual
    
    2
    +
    
    3
    +build-depends:
    
    4
    +- base.bst
    
    5
    +
    
    6
    +sysroots:
    
    7
    +- path: /sysroot
    
    8
    +  build-depends:
    
    9
    +  - integration.bst
    
    10
    +
    
    11
    +config:
    
    12
    +  install-commands:
    
    13
    +    - mkdir -p "%{install-root}/sysroot"
    
    14
    +    - if test -f /sysroot/integrated.txt; then cp /sysroot/integrated.txt "%{install-root}/sysroot"; fi
    
    15
    +    - if test -f /integrated.txt; then cp /integrated.txt "%{install-root}"; fi

  • tests/sysroot_depends/project/elements/target-variable.bst
    1
    +kind: compose
    
    2
    +
    
    3
    +build-depends:
    
    4
    +- a.bst
    
    5
    +
    
    6
    +variables:
    
    7
    +  mydir: test
    
    8
    +
    
    9
    +sysroots:
    
    10
    +- path: "/path/%{mydir}"
    
    11
    +  build-depends:
    
    12
    +  - b.bst

  • tests/sysroot_depends/project/elements/target.bst
    1
    +kind: compose
    
    2
    +
    
    3
    +build-depends:
    
    4
    +- a.bst
    
    5
    +
    
    6
    +sysroots:
    
    7
    +- path: /sysroot
    
    8
    +  build-depends:
    
    9
    +  - b.bst

  • tests/sysroot_depends/project/files/a/a.txt
    1
    +test

  • tests/sysroot_depends/project/files/b/b.txt
    1
    +test

  • tests/sysroot_depends/project/files/layer1/1
    1
    +1

  • tests/sysroot_depends/project/files/layer2/2
    1
    +2

  • tests/sysroot_depends/project/project.conf
    1
    +name: test
    
    2
    +element-path: elements
    
    3
    +aliases:
    
    4
    +  alpine: https://bst-integration-test-images.ams3.cdn.digitaloceanspaces.com/
    
    5
    +options:
    
    6
    +  linux:
    
    7
    +    type: bool
    
    8
    +    description: Whether to expect a linux platform
    
    9
    +    default: True

  • tests/sysroot_depends/sysroot_depends.py
    1
    +import os
    
    2
    +import pytest
    
    3
    +from tests.testutils import cli_integration as cli
    
    4
    +from tests.testutils.site import IS_LINUX, HAVE_BWRAP
    
    5
    +
    
    6
    +
    
    7
    +# Project directory
    
    8
    +DATA_DIR = os.path.join(
    
    9
    +    os.path.dirname(os.path.realpath(__file__)),
    
    10
    +    "project",
    
    11
    +)
    
    12
    +
    
    13
    +
    
    14
    +@pytest.mark.datafiles(DATA_DIR)
    
    15
    +def test_sysroot_dependency_smoke_test(datafiles, cli, tmpdir):
    
    16
    +    "Test simple sysroot use case without integration"
    
    17
    +
    
    18
    +    project = str(datafiles)
    
    19
    +    checkout = os.path.join(str(tmpdir), 'checkout')
    
    20
    +
    
    21
    +    result = cli.run(project=project,
    
    22
    +                     args=['build', 'target.bst'])
    
    23
    +    result.assert_success()
    
    24
    +
    
    25
    +    result = cli.run(project=project,
    
    26
    +                     args=['checkout', 'target.bst', checkout])
    
    27
    +    result.assert_success()
    
    28
    +    assert os.path.exists(os.path.join(checkout, 'a.txt'))
    
    29
    +    assert os.path.exists(os.path.join(checkout, 'sysroot', 'b.txt'))
    
    30
    +
    
    31
    +
    
    32
    +@pytest.mark.integration
    
    33
    +@pytest.mark.skipif(IS_LINUX and not HAVE_BWRAP, reason='Only available with bubblewrap on Linux')
    
    34
    +@pytest.mark.datafiles(DATA_DIR)
    
    35
    +def test_skip_integration_commands_compose(datafiles, cli, tmpdir):
    
    36
    +    "Integration commands are not run on sysroots"
    
    37
    +
    
    38
    +    project = str(datafiles)
    
    39
    +    checkout = os.path.join(str(tmpdir), 'checkout')
    
    40
    +
    
    41
    +    result = cli.run(project=project,
    
    42
    +                     args=['build', 'compose-integration.bst'])
    
    43
    +    result.assert_success()
    
    44
    +
    
    45
    +    result = cli.run(project=project,
    
    46
    +                     args=['checkout', 'compose-integration.bst', checkout])
    
    47
    +    result.assert_success()
    
    48
    +
    
    49
    +    integrated = os.path.join(checkout, 'sysroot', 'integrated.txt')
    
    50
    +    assert os.path.exists(integrated)
    
    51
    +    with open(integrated, 'r') as f:
    
    52
    +        assert f.read() == '0\n'
    
    53
    +
    
    54
    +
    
    55
    +@pytest.mark.integration
    
    56
    +@pytest.mark.skipif(IS_LINUX and not HAVE_BWRAP, reason='Only available with bubblewrap on Linux')
    
    57
    +@pytest.mark.datafiles(DATA_DIR)
    
    58
    +def test_skip_integration_commands_build_element(datafiles, cli, tmpdir):
    
    59
    +    "Integration commands are not run on sysroots"
    
    60
    +
    
    61
    +    project = str(datafiles)
    
    62
    +    checkout = os.path.join(str(tmpdir), 'checkout')
    
    63
    +
    
    64
    +    result = cli.run(project=project,
    
    65
    +                     args=['build', 'manual-integration.bst'])
    
    66
    +    result.assert_success()
    
    67
    +
    
    68
    +    result = cli.run(project=project,
    
    69
    +                     args=['checkout', 'manual-integration.bst', checkout])
    
    70
    +    result.assert_success()
    
    71
    +
    
    72
    +    sysroot_integrated = os.path.join(checkout, 'sysroot', 'integrated.txt')
    
    73
    +    integrated = os.path.join(checkout, 'integrated.txt')
    
    74
    +    assert os.path.exists(sysroot_integrated)
    
    75
    +    with open(sysroot_integrated, 'r') as f:
    
    76
    +        assert f.read() == '0\n'
    
    77
    +    # We need to make sure that integration command has not been run on / either.
    
    78
    +    assert not os.path.exists(integrated)
    
    79
    +
    
    80
    +
    
    81
    +@pytest.mark.integration
    
    82
    +@pytest.mark.skipif(IS_LINUX and not HAVE_BWRAP, reason='Only available with bubblewrap on Linux')
    
    83
    +@pytest.mark.datafiles(DATA_DIR)
    
    84
    +def test_sysroot_only_for_build(cli, tmpdir, datafiles):
    
    85
    +    project = str(datafiles)
    
    86
    +    checkout = os.path.join(str(tmpdir), 'checkout')
    
    87
    +
    
    88
    +    result = cli.run(project=project,
    
    89
    +                     args=['build', 'compose-layers.bst'])
    
    90
    +    result.assert_success()
    
    91
    +
    
    92
    +    result = cli.run(project=project,
    
    93
    +                     args=['checkout', 'compose-layers.bst', checkout])
    
    94
    +
    
    95
    +    result.assert_success()
    
    96
    +    assert os.path.exists(os.path.join(checkout, '1'))
    
    97
    +    assert os.path.exists(os.path.join(checkout, '2'))
    
    98
    +    assert not os.path.exists(os.path.join(checkout, 'sysroot', '1'))
    
    99
    +
    
    100
    +
    
    101
    +@pytest.mark.integration
    
    102
    +@pytest.mark.skipif(IS_LINUX and not HAVE_BWRAP, reason='Only available with bubblewrap on Linux')
    
    103
    +@pytest.mark.datafiles(DATA_DIR)
    
    104
    +def test_sysroot_only_for_build_with_sysroot(cli, tmpdir, datafiles):
    
    105
    +    project = str(datafiles)
    
    106
    +    checkout = os.path.join(str(tmpdir), 'checkout')
    
    107
    +
    
    108
    +    result = cli.run(project=project,
    
    109
    +                     args=['build', 'compose-layers-with-sysroot.bst'])
    
    110
    +    result.assert_success()
    
    111
    +
    
    112
    +    result = cli.run(project=project,
    
    113
    +                     args=['checkout', 'compose-layers-with-sysroot.bst', checkout])
    
    114
    +
    
    115
    +    result.assert_success()
    
    116
    +    assert os.path.exists(os.path.join(checkout, 'other-sysroot', '1'))
    
    117
    +    assert os.path.exists(os.path.join(checkout, 'other-sysroot', '2'))
    
    118
    +    assert not os.path.exists(os.path.join(checkout, 'sysroot', '1'))
    
    119
    +
    
    120
    +
    
    121
    +@pytest.mark.integration
    
    122
    +@pytest.mark.skipif(IS_LINUX and not HAVE_BWRAP, reason='Only available with bubblewrap on Linux')
    
    123
    +@pytest.mark.datafiles(DATA_DIR)
    
    124
    +def test_shell_no_sysroot(cli, tmpdir, datafiles):
    
    125
    +    "bst shell does not have sysroots and dependencies are integrated"
    
    126
    +
    
    127
    +    project = str(datafiles)
    
    128
    +
    
    129
    +    result = cli.run(project=project,
    
    130
    +                     args=['build', 'base.bst', 'manual-integration-runtime.bst'])
    
    131
    +    result.assert_success()
    
    132
    +
    
    133
    +    result = cli.run(project=project,
    
    134
    +                     args=['shell', 'manual-integration-runtime.bst', '--', 'cat', '/integrated.txt'])
    
    135
    +    result.assert_success()
    
    136
    +    assert result.output == '1\n'
    
    137
    +
    
    138
    +    result = cli.run(project=project,
    
    139
    +                     args=['shell', 'manual-integration-runtime.bst', '--', 'ls', '/sysroot/integrated.txt'])
    
    140
    +    assert result.exit_code != 0
    
    141
    +    assert result.output == ''
    
    142
    +
    
    143
    +
    
    144
    +@pytest.mark.integration
    
    145
    +@pytest.mark.skipif(IS_LINUX and not HAVE_BWRAP, reason='Only available with bubblewrap on Linux')
    
    146
    +@pytest.mark.datafiles(DATA_DIR)
    
    147
    +def test_shell_build_sysroot(cli, tmpdir, datafiles):
    
    148
    +    "Build shell should stage build dependencies sysroot'ed non integrated"
    
    149
    +
    
    150
    +    project = str(datafiles)
    
    151
    +
    
    152
    +    result = cli.run(project=project,
    
    153
    +                     args=['build', 'base.bst', 'integration.bst'])
    
    154
    +    result.assert_success()
    
    155
    +
    
    156
    +    result = cli.run(project=project,
    
    157
    +                     args=['shell', '-b', 'manual-integration.bst', '--', 'cat', '/sysroot/integrated.txt'])
    
    158
    +    result.assert_success()
    
    159
    +    assert result.output == '0\n'
    
    160
    +
    
    161
    +
    
    162
    +@pytest.mark.integration
    
    163
    +@pytest.mark.datafiles(DATA_DIR)
    
    164
    +def test_show_dependencies_only_once(cli, tmpdir, datafiles):
    
    165
    +    """Dependencies should not show up in status several times when they
    
    166
    +    are staged with multiple sysroots"""
    
    167
    +
    
    168
    +    project = str(datafiles)
    
    169
    +
    
    170
    +    result = cli.run(project=project,
    
    171
    +                     args=['show', '--format', '%{name}', 'manual-integration.bst'])
    
    172
    +    result.assert_success()
    
    173
    +    pipeline = result.output.splitlines()
    
    174
    +    assert pipeline == ['base/base-alpine.bst',
    
    175
    +                        'base.bst',
    
    176
    +                        'integration.bst',
    
    177
    +                        'manual-integration.bst']
    
    178
    +
    
    179
    +
    
    180
    +@pytest.mark.datafiles(DATA_DIR)
    
    181
    +def test_sysroot_path_subst_variable(datafiles, cli, tmpdir):
    
    182
    +    "Test that variables are expanded in sysroot path"
    
    183
    +
    
    184
    +    project = str(datafiles)
    
    185
    +    checkout = os.path.join(str(tmpdir), 'checkout')
    
    186
    +
    
    187
    +    result = cli.run(project=project,
    
    188
    +                     args=['build', 'target-variable.bst'])
    
    189
    +    result.assert_success()
    
    190
    +
    
    191
    +    result = cli.run(project=project,
    
    192
    +                     args=['checkout', 'target-variable.bst', checkout])
    
    193
    +    result.assert_success()
    
    194
    +
    
    195
    +    assert os.path.exists(os.path.join(checkout, 'a.txt'))
    
    196
    +    assert os.path.exists(os.path.join(checkout, 'path/test', 'b.txt'))



  • [Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]