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