filter.py: Allow dependency on stack elements

Staging the artifact of a stack element currently creates an empty
filter. This commit changes the logic in a filter element to stage the
artifacts of dependencies of stack elements. We recursively expand stack
elements into their dependencies.
diff --git a/src/buildstream/plugins/elements/filter.py b/src/buildstream/plugins/elements/filter.py
index c2c2e01..556153b 100644
--- a/src/buildstream/plugins/elements/filter.py
+++ b/src/buildstream/plugins/elements/filter.py
@@ -216,35 +216,42 @@
     def assemble(self, sandbox):
         with self.timed_activity("Staging artifact", silent_nested=True):
             for dep in self.dependencies(Scope.BUILD, recurse=False):
-                # Check that all the included/excluded domains exist
-                pub_data = dep.get_public_data('bst')
-                split_rules = pub_data.get_mapping('split-rules', {})
-                unfound_includes = []
-                for domain in self.include:
-                    if domain not in split_rules:
-                        unfound_includes.append(domain)
-                unfound_excludes = []
-                for domain in self.exclude:
-                    if domain not in split_rules:
-                        unfound_excludes.append(domain)
-
-                detail = []
-                if unfound_includes:
-                    detail.append("Unknown domains were used in {}".format(self.include_node.get_provenance()))
-                    detail.extend([' - {}'.format(domain) for domain in unfound_includes])
-
-                if unfound_excludes:
-                    detail.append("Unknown domains were used in {}".format(self.exclude_node.get_provenance()))
-                    detail.extend([' - {}'.format(domain) for domain in unfound_excludes])
-
-                if detail:
-                    detail = '\n'.join(detail)
-                    raise ElementError("Unknown domains declared.", detail=detail)
-
-                dep.stage_artifact(sandbox, include=self.include,
-                                   exclude=self.exclude, orphans=self.include_orphans)
+                self._assemble_single(sandbox, dep)
         return ""
 
+    def _assemble_single(self, sandbox, dep):
+        # Check that all the included/excluded domains exist
+        if dep.get_kind() == 'stack':
+            for subdep in dep.dependencies(Scope.RUN, recurse=False):
+                self._assemble_single(sandbox, subdep)
+
+        pub_data = dep.get_public_data('bst')
+        split_rules = pub_data.get_mapping('split-rules', {})
+        unfound_includes = []
+        for domain in self.include:
+            if domain not in split_rules:
+                unfound_includes.append(domain)
+        unfound_excludes = []
+        for domain in self.exclude:
+            if domain not in split_rules:
+                unfound_excludes.append(domain)
+
+        detail = []
+        if unfound_includes:
+            detail.append("Unknown domains were used in {}".format(self.include_node.get_provenance()))
+            detail.extend([' - {}'.format(domain) for domain in unfound_includes])
+
+        if unfound_excludes:
+            detail.append("Unknown domains were used in {}".format(self.exclude_node.get_provenance()))
+            detail.extend([' - {}'.format(domain) for domain in unfound_excludes])
+
+        if detail:
+            detail = '\n'.join(detail)
+            raise ElementError("Unknown domains declared.", detail=detail)
+
+        dep.stage_artifact(sandbox, include=self.include,
+                           exclude=self.exclude, orphans=self.include_orphans)
+
     def _get_source_element(self):
         # Filter elements act as proxies for their sole build-dependency
         build_deps = list(self.dependencies(Scope.BUILD, recurse=False))