|  | # | 
|  | # 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 | 
|  |  |