Daniel Carvalho has submitted this change. ( https://gem5-review.googlesource.com/c/public/gem5/+/43587 )

Change subject: scons: Allow declaring dependencies of tags
......................................................................

scons: Allow declaring dependencies of tags

It was quite incovenient to declare tags. tags either
had to be added to the Source declaration of included
files, or using (with_tag()) would need to be added
with all the indirect sub-tags. For example,

Assume:
- B.cc refers to symbols in A.cc
- C.cc refers to symbols in B.cc (i.e., it needs A transitively)
- D.cc refers to symbols in A.cc and C.cc (i.e., it needs B trans.)

So either their SConscript would be:
  Source('A.cc', add_tags=['B', 'D'])
  Source('B.cc', add_tags='B')
  Source('C.cc', add_tags='C')
  Source('D.cc', add_tags='D')

  GTest('A.test', 'A.test.cc', 'a.cc')
  GTest('B.test', 'B.test.cc', with_tag('B'))
  GTest('C.test', 'C.test.cc', with_any_tags('B', 'C'))
  GTest('D.test', 'D.test.cc', with_any_tags('B', 'C', 'D'))

or:
  Source('A.cc', add_tags=['B', 'C', 'D'])
  Source('B.cc', add_tags=['B', 'C', 'D'])
  Source('C.cc', add_tags=['C', 'D'])
  Source('D.cc', add_tags='D')

  GTest('A.test', 'A.test.cc', 'a.cc')
  GTest('B.test', 'B.test.cc', with_tag('B'))
  GTest('C.test', 'C.test.cc', with_tag('C'))
  GTest('D.test', 'D.test.cc', with_tag('D'))

This change makes it simpler. The tag should be added
only to the files directly included by the functionality
being tagged. Using the same example:

  Source('A.cc', add_tags=['B', 'D'])
  Source('B.cc', add_tags='B')
  Source('C.cc', add_tags='C')
  Source('D.cc', add_tags='D')

  env.TagImplies('B', 'A')
  env.TagImplies('C', 'B')
  env.TagImplies('D', ['A', 'C'])

  GTest('A.test', 'A.test.cc', 'a.cc')
  GTest('B.test', 'B.test.cc', with_tag('B'))
  GTest('C.test', 'C.test.cc', with_tag('C'))
  GTest('D.test', 'D.test.cc', with_tag('D'))

This also means that when a file no longer refers to
symbols from other file, or when it starts refering to
symbols from another file, one only needs to change
the dependencies of the tag directly being modified,
not the tags that rely on (imply) them. That is, on
the previous example, if C stops refering to symbols
from B, then tags that imply C do not need to be
modified:

  env.TagImplies('B','A')
  env.TagImplies('D', ['A', 'C'])

Change-Id: I5be07b01864f8d5df83f59002dfd2f01c73d5e09
Signed-off-by: Daniel R. Carvalho <oda...@yahoo.com.br>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/43587
Reviewed-by: Gabe Black <gabe.bl...@gmail.com>
Maintainer: Gabe Black <gabe.bl...@gmail.com>
Tested-by: kokoro <noreply+kok...@google.com>
---
M src/SConscript
1 file changed, 97 insertions(+), 20 deletions(-)

Approvals:
  Gabe Black: Looks good to me, approved; Looks good to me, approved
  kokoro: Regressions pass



diff --git a/src/SConscript b/src/SConscript
index 31fce0c..831f5c6 100644
--- a/src/SConscript
+++ b/src/SConscript
@@ -71,17 +71,91 @@
 # When specifying a source file of some type, a set of tags can be
 # specified for that file.

+def tag_implies(env, tag, tag_list):
+    '''
+    Associates a tag X to a list of tags which are implied by X.
+
+    For example, assume:
+    - Each file <X>.cc is tagged with the tag "Tag <X>".
+    - B.cc refers to symbols from A.cc
+    - C.cc refers to symbols from B.cc
+    - D.cc refers to symbols from A.cc and C.cc
+
+    Then:
+    - "Tag A" is implied by "Tag B"
+    - "Tag B" is implied by "Tag C"
+    - "Tag A" is transitively implied by "Tag C" (from "Tag B")
+    - "Tag A" and "Tag C" are implied by "Tag D"
+    - "Tag B" is transitively implied by "Tag D" (from "Tag C")
+    - "Tag A" is transitively implied by "Tag D" (from transitive "Tag B")
+
+    All of these implications are simply declared as:
+        env.TagImplies("Tag B", "Tag A")
+        env.TagImplies("Tag C", "Tag B")
+        env.TagImplies("Tag D", ["Tag A", "Tag C"])
+
+    So that any use of a tag will automatically include its transitive tags
+    after being resolved.
+    '''
+
+    env.SetDefault(_tag_implies={})
+    implications = env['_tag_implies']
+
+    if isinstance(tag_list, str):
+        tag_list = frozenset([tag_list])
+    if not isinstance(tag_list, frozenset):
+        tag_list = frozenset(tag_list)
+    if tag in implications:
+        implications[tag] |= tag_list
+    else:
+        implications[tag] = tag_list
+
+    # Check if any of the tags on which the new tag depends on already
+    # has a list of implications. If so, add the list to the new tag's
+    # implications
+    for t in tag_list:
+        if t in implications:
+            implications[tag] |= implications[t]
+
+    # Check if another tag depends on this tag. If so, add this tag's
+    # implications to that tag.
+    for t,implied in implications.items():
+        if tag in implied:
+            implications[t] |= implications[tag]
+
+env.AddMethod(tag_implies, 'TagImplies')
+
+def resolve_tags(env, tags):
+    '''
+    Returns the complete set of tags implied (dependencies) by the
+    supplied tags.
+    '''
+
+    implications = env.SetDefault(_tag_implies={})
+    implications = env['_tag_implies']
+
+    if isinstance(tags, str):
+        tags = frozenset([tags])
+    if not isinstance(tags, frozenset):
+        tags = frozenset(tags)
+
+    tags = tags.copy()
+    for tag in tags:
+        if tag in implications:
+            tags |= implications[tag]
+    return tags
+
 class SourceFilter(object):
     def __init__(self, predicate):
         self.predicate = predicate

     def __or__(self, other):
-        return SourceFilter(lambda tags: self.predicate(tags) or
-                                         other.predicate(tags))
+        return SourceFilter(lambda env, tags: self.predicate(env, tags) or
+                                              other.predicate(env, tags))

     def __and__(self, other):
-        return SourceFilter(lambda tags: self.predicate(tags) and
-                                         other.predicate(tags))
+        return SourceFilter(lambda env, tags: self.predicate(env, tags) and
+                                              other.predicate(env, tags))

 def with_tags_that(predicate):
     '''Return a list of sources with tags that satisfy a predicate.'''
@@ -89,23 +163,25 @@

 def with_any_tags(*tags):
     '''Return a list of sources with any of the supplied tags.'''
-    return SourceFilter(lambda stags: len(set(tags) & stags) > 0)
+    return SourceFilter(lambda env, stags: \
+        len(resolve_tags(env, tags) & stags) > 0)

 def with_all_tags(*tags):
     '''Return a list of sources with all of the supplied tags.'''
-    return SourceFilter(lambda stags: set(tags) <= stags)
+ return SourceFilter(lambda env, stags: resolve_tags(env, tags) <= stags)

 def with_tag(tag):
     '''Return a list of sources with the supplied tag.'''
-    return SourceFilter(lambda stags: tag in stags)
+    return with_any_tags(*[tag])

 def without_tags(*tags):
     '''Return a list of sources without any of the supplied tags.'''
-    return SourceFilter(lambda stags: len(set(tags) & stags) == 0)
+    return SourceFilter(lambda env, stags: \
+        len(resolve_tags(env, tags) & stags) == 0)

 def without_tag(tag):
-    '''Return a list of sources with the supplied tag.'''
-    return SourceFilter(lambda stags: tag not in stags)
+    '''Return a list of sources without the supplied tag.'''
+    return without_tags(*[tag])

 source_filter_factories = {
     'with_tags_that': with_tags_that,
@@ -119,9 +195,9 @@
 Export(source_filter_factories)

 class SourceList(list):
-    def apply_filter(self, f):
+    def apply_filter(self, env, f):
         def match(source):
-            return f.predicate(source.tags)
+            return f.predicate(env, source.tags)
         return SourceList(filter(match, self))

     def __getattr__(self, name):
@@ -130,8 +206,8 @@
             raise AttributeError

         @functools.wraps(func)
-        def wrapper(*args, **kwargs):
-            return self.apply_filter(func(*args, **kwargs))
+        def wrapper(env, *args, **kwargs):
+            return self.apply_filter(env, func(*args, **kwargs))
         return wrapper

 class SourceMeta(type):
@@ -523,7 +599,7 @@
     def declare(self, env):
         sources = list(self.sources)
         for f in self.filters:
-            sources += Source.all.apply_filter(f)
+            sources += Source.all.apply_filter(env, f)
         objs = self.srcs_to_objs(env, sources) + env['STATIC_OBJS']
         if self.main:
             objs += env['MAIN_OBJS']
@@ -542,7 +618,7 @@
         env = env.Clone()
         env.Append(LIBS=env['GTEST_LIBS'])
         env.Append(CPPFLAGS=env['GTEST_CPPFLAGS'])
-        env['GTEST_LIB_SOURCES'] = Source.all.with_tag('gtest lib')
+        env['GTEST_LIB_SOURCES'] = Source.all.with_tag(env, 'gtest lib')
         env['GTEST_OUT_DIR'] = \
             Dir(env['BUILDDIR']).Dir('unittests.' + env['EXE_SUFFIX'])
         return super(GTest, cls).declare_all(env)
@@ -552,7 +628,7 @@
         if not self.skip_lib:
             sources += env['GTEST_LIB_SOURCES']
         for f in self.filters:
-            sources += Source.all.apply_filter(f)
+            sources += Source.all.apply_filter(env, f)
         objs = self.srcs_to_objs(env, sources)

         binary = super(GTest, self).declare(env, objs)
@@ -1286,12 +1362,12 @@
     new_env.Label = label
     new_env.Append(**kwargs)

-    lib_sources = Source.all.with_tag('gem5 lib')
+    lib_sources = Source.all.with_tag(new_env, 'gem5 lib')

     # Without Python, leave out all Python content from the library
     # builds.  The option doesn't affect gem5 built as a program
     if GetOption('without_python'):
-        lib_sources = lib_sources.without_tag('python')
+        lib_sources = lib_sources.without_tag(new_env, 'python')

     static_objs = list([ s.static(new_env) for s in lib_sources ])
     shared_objs = list([ s.shared(new_env) for s in lib_sources ])
@@ -1304,7 +1380,8 @@
     new_env.Depends(shared_date, shared_objs)
     shared_objs.extend(shared_date)

-    main_objs = [ s.static(new_env) for s in Source.all.with_tag('main') ]
+    main_objs = [ s.static(new_env) for s in
+            Source.all.with_tag(new_env, 'main') ]

     # First make a library of everything but main() so other programs can
     # link against m5.



4 is the latest approved patch-set.
No files were changed between the latest approved patch-set and the submitted one.
--
To view, visit https://gem5-review.googlesource.com/c/public/gem5/+/43587
To unsubscribe, or for help writing mail filters, visit https://gem5-review.googlesource.com/settings

Gerrit-Project: public/gem5
Gerrit-Branch: develop
Gerrit-Change-Id: I5be07b01864f8d5df83f59002dfd2f01c73d5e09
Gerrit-Change-Number: 43587
Gerrit-PatchSet: 6
Gerrit-Owner: Daniel Carvalho <oda...@yahoo.com.br>
Gerrit-Reviewer: Daniel Carvalho <oda...@yahoo.com.br>
Gerrit-Reviewer: Gabe Black <gabe.bl...@gmail.com>
Gerrit-Reviewer: kokoro <noreply+kok...@google.com>
Gerrit-MessageType: merged
_______________________________________________
gem5-dev mailing list -- gem5-dev@gem5.org
To unsubscribe send an email to gem5-dev-le...@gem5.org
%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s

Reply via email to