1# 2# Copyright 2012, The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16"""Data structure for processing makefiles.""" 17 18import os 19 20import android_build 21import android_mk 22import errors 23 24class MakeNode(object): 25 """Represents single node in make tree.""" 26 27 def __init__(self, name, parent): 28 self._name = name 29 self._children_map = {} 30 self._is_leaf = False 31 self._parent = parent 32 self._includes_submake = None 33 if parent: 34 self._path = os.path.join(parent._GetPath(), name) 35 else: 36 self._path = "" 37 38 def _AddPath(self, path_segs): 39 """Adds given path to this node. 40 41 Args: 42 path_segs: list of path segments 43 """ 44 if not path_segs: 45 # done processing path 46 return self 47 current_seg = path_segs.pop(0) 48 child = self._children_map.get(current_seg) 49 if not child: 50 child = MakeNode(current_seg, self) 51 self._children_map[current_seg] = child 52 return child._AddPath(path_segs) 53 54 def _SetLeaf(self, is_leaf): 55 self._is_leaf = is_leaf 56 57 def _GetPath(self): 58 return self._path 59 60 def _DoesIncludesSubMake(self): 61 if self._includes_submake is None: 62 if self._is_leaf: 63 path = os.path.join(android_build.GetTop(), self._path) 64 mk_parser = android_mk.CreateAndroidMK(path) 65 self._includes_submake = mk_parser.IncludesMakefilesUnder() 66 else: 67 self._includes_submake = False 68 return self._includes_submake 69 70 def _DoesParentIncludeMe(self): 71 return self._parent and self._parent._DoesIncludesSubMake() 72 73 def _BuildPrunedMakeList(self, make_list): 74 if self._is_leaf and not self._DoesParentIncludeMe(): 75 make_list.append(os.path.join(self._path, "Android.mk")) 76 for child in self._children_map.itervalues(): 77 child._BuildPrunedMakeList(make_list) 78 79 80class MakeTree(MakeNode): 81 """Data structure for building a non-redundant set of Android.mk paths. 82 83 Used to collapse set of Android.mk files to use to prevent issuing make 84 command that include same module multiple times due to include rules. 85 """ 86 87 def __init__(self): 88 super(MakeTree, self).__init__("", None) 89 90 def AddPath(self, path): 91 """Adds make directory path to tree. 92 93 Will have no effect if path is already included in make set. 94 95 Args: 96 path: filesystem path to directory to build, relative to build root. 97 """ 98 path = os.path.normpath(path) 99 mk_path = os.path.join(android_build.GetTop(), path, "Android.mk") 100 if not os.path.isfile(mk_path): 101 raise errors.AbortError("%s does not exist" % mk_path) 102 path_segs = path.split(os.sep) 103 child = self._AddPath(path_segs) 104 child._SetLeaf(True) 105 106 def GetPrunedMakeList(self): 107 """Return as list of the minimum set of Android.mk files necessary to 108 build all leaf nodes in tree. 109 """ 110 make_list = [] 111 self._BuildPrunedMakeList(make_list) 112 return make_list 113 114 def IsEmpty(self): 115 return not self._children_map 116 117