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