Make coverage work without test defs.
Change-Id: I946df038e97dc5c2f40a4610d4076e13ab6bde37
diff --git a/testrunner/make_tree.py b/testrunner/make_tree.py
new file mode 100644
index 0000000..c8bac17
--- /dev/null
+++ b/testrunner/make_tree.py
@@ -0,0 +1,116 @@
+#
+# Copyright 2012, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Data structure for processing makefiles."""
+
+import os
+
+import android_build
+import android_mk
+import errors
+
+class MakeNode(object):
+ """Represents single node in make tree."""
+
+ def __init__(self, name, parent):
+ self._name = name
+ self._children_map = {}
+ self._is_leaf = False
+ self._parent = parent
+ self._includes_submake = None
+ if parent:
+ self._path = os.path.join(parent._GetPath(), name)
+ else:
+ self._path = ""
+
+ def _AddPath(self, path_segs):
+ """Adds given path to this node.
+
+ Args:
+ path_segs: list of path segments
+ """
+ if not path_segs:
+ # done processing path
+ return self
+ current_seg = path_segs.pop(0)
+ child = self._children_map.get(current_seg)
+ if not child:
+ child = MakeNode(current_seg, self)
+ self._children_map[current_seg] = child
+ return child._AddPath(path_segs)
+
+ def _SetLeaf(self, is_leaf):
+ self._is_leaf = is_leaf
+
+ def _GetPath(self):
+ return self._path
+
+ def _DoesIncludesSubMake(self):
+ if self._includes_submake is None:
+ if self._is_leaf:
+ path = os.path.join(android_build.GetTop(), self._path)
+ mk_parser = android_mk.CreateAndroidMK(path)
+ self._includes_submake = mk_parser.IncludesMakefilesUnder()
+ else:
+ self._includes_submake = False
+ return self._includes_submake
+
+ def _DoesParentIncludeMe(self):
+ return self._parent and self._parent._DoesIncludesSubMake()
+
+ def _BuildPrunedMakeList(self, make_list):
+ if self._is_leaf and not self._DoesParentIncludeMe():
+ make_list.append(os.path.join(self._path, "Android.mk"))
+ for child in self._children_map.itervalues():
+ child._BuildPrunedMakeList(make_list)
+
+
+class MakeTree(MakeNode):
+ """Data structure for building a non-redundant set of Android.mk paths.
+
+ Used to collapse set of Android.mk files to use to prevent issuing make
+ command that include same module multiple times due to include rules.
+ """
+
+ def __init__(self):
+ super(MakeTree, self).__init__("", None)
+
+ def AddPath(self, path):
+ """Adds make directory path to tree.
+
+ Will have no effect if path is already included in make set.
+
+ Args:
+ path: filesystem path to directory to build, relative to build root.
+ """
+ path = os.path.normpath(path)
+ mk_path = os.path.join(android_build.GetTop(), path, "Android.mk")
+ if not os.path.isfile(mk_path):
+ raise errors.AbortError("%s does not exist" % mk_path)
+ path_segs = path.split(os.sep)
+ child = self._AddPath(path_segs)
+ child._SetLeaf(True)
+
+ def GetPrunedMakeList(self):
+ """Return as list of the minimum set of Android.mk files necessary to
+ build all leaf nodes in tree.
+ """
+ make_list = []
+ self._BuildPrunedMakeList(make_list)
+ return make_list
+
+ def IsEmpty(self):
+ return not self._children_map
+