| OLD | NEW |
| 1 #!/usr/bin/python2.4 | 1 #!/usr/bin/python2.4 |
| 2 # Copyright 2008, Google Inc. | 2 # Copyright 2008, Google Inc. |
| 3 # All rights reserved. | 3 # All rights reserved. |
| 4 # | 4 # |
| 5 # Redistribution and use in source and binary forms, with or without | 5 # Redistribution and use in source and binary forms, with or without |
| 6 # modification, are permitted provided that the following conditions are | 6 # modification, are permitted provided that the following conditions are |
| 7 # met: | 7 # met: |
| 8 # | 8 # |
| 9 # * Redistributions of source code must retain the above copyright | 9 # * Redistributions of source code must retain the above copyright |
| 10 # notice, this list of conditions and the following disclaimer. | 10 # notice, this list of conditions and the following disclaimer. |
| (...skipping 18 matching lines...) Expand all Loading... |
| 29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 | 30 |
| 31 """Defer tool for SCons.""" | 31 """Defer tool for SCons.""" |
| 32 | 32 |
| 33 | 33 |
| 34 import os | 34 import os |
| 35 import sys | 35 import sys |
| 36 import types | 36 import types |
| 37 import SCons.Errors | 37 import SCons.Errors |
| 38 | 38 |
| 39 __defer_groups = {} | 39 |
| 40 class DeferGroup: |
| 41 """Named list of functions to be deferred.""" |
| 42 # If we derive DeferGroup from object, instances of it return type |
| 43 # <class 'defer.DeferGroup'>, which prevents SCons.Util.semi_deepcopy() |
| 44 # from calling its __semi_deepcopy__ function. |
| 45 # TODO(sgk): Make semi_deepcopy() capable of handling classes derived from |
| 46 # object. |
| 47 |
| 48 def __init__(self): |
| 49 """Initialize deferred function object.""" |
| 50 self.func_env_cwd = [] |
| 51 self.after = set() |
| 52 |
| 53 def __semi_deepcopy__(self): |
| 54 """Makes a semi-deep-copy of this object. |
| 55 |
| 56 Returns: |
| 57 A semi-deep-copy of this object. |
| 58 |
| 59 This means it copies the sets and lists contained by this object, but |
| 60 doesn't make copies of the function pointers and environments pointed to by |
| 61 those lists. |
| 62 |
| 63 Needed so env.Clone() makes a copy of the defer list, so that functions |
| 64 and after-relationships subsequently added to the clone are not added to |
| 65 the parent. |
| 66 """ |
| 67 c = DeferGroup() |
| 68 c.func_env_cwd = self.func_env_cwd[:] |
| 69 c.after = self.after.copy() |
| 70 return c |
| 40 | 71 |
| 41 | 72 |
| 42 def _InitializeDefer(self): | 73 def SetDeferRoot(self): |
| 43 """Re-initializes deferred function handling. | 74 """Sets the current environment as the root environment for defer. |
| 44 | 75 |
| 45 Args: | 76 Args: |
| 46 self: Parent environment | 77 self: Current environment context. |
| 78 |
| 79 Functions deferred by environments cloned from the root environment (that is, |
| 80 function deferred by children of the root environment) will be executed when |
| 81 ExecuteDefer() is called from the root environment. |
| 82 |
| 83 Functions deferred by environments from which the root environment was cloned |
| 84 (that is, functions deferred by parents of the root environment) will be |
| 85 passed the root environment instead of the original parent environment. |
| 86 (Otherwise, they would have no way to determine the root environment.) |
| 47 """ | 87 """ |
| 48 # Clear the list of deferred groups | 88 # Set the current environment as the root for holding defer groups |
| 49 __defer_groups.clear() | 89 self['_DEFER_ROOT_ENV'] = self |
| 90 |
| 91 # Deferred functions this environment got from its parents will be run in the |
| 92 # new root context. |
| 93 for group in GetDeferGroups(self).values(): |
| 94 new_list = [(func, self, cwd) for (func, env, cwd) in group.func_env_cwd] |
| 95 group.func_env_cwd = new_list |
| 50 | 96 |
| 51 | 97 |
| 52 def _ExecuteDefer(self): | 98 def GetDeferRoot(self): |
| 99 """Returns the root environment for defer. |
| 100 |
| 101 Args: |
| 102 self: Current environment context. |
| 103 |
| 104 Returns: |
| 105 The root environment for defer. If one of this environment's parents |
| 106 called SetDeferRoot(), returns that environment. Otherwise returns the |
| 107 current environment. |
| 108 """ |
| 109 return self.get('_DEFER_ROOT_ENV', self) |
| 110 |
| 111 |
| 112 def GetDeferGroups(env): |
| 113 """Returns the dict of defer groups from the root defer environment. |
| 114 |
| 115 Args: |
| 116 env: Environment context. |
| 117 |
| 118 Returns: |
| 119 The dict of defer groups from the root defer environment. |
| 120 """ |
| 121 return env.GetDeferRoot()['_DEFER_GROUPS'] |
| 122 |
| 123 |
| 124 def ExecuteDefer(self): |
| 53 """Executes deferred functions. | 125 """Executes deferred functions. |
| 54 | 126 |
| 55 Args: | 127 Args: |
| 56 self: Parent environment | 128 self: Current environment context. |
| 57 """ | 129 """ |
| 58 # Save directory, so SConscript functions can occur in the right subdirs | 130 # Save directory, so SConscript functions can occur in the right subdirs |
| 59 oldcwd = os.getcwd() | 131 oldcwd = os.getcwd() |
| 60 | 132 |
| 133 # If defer root is set and isn't this environment, we're being called from a |
| 134 # sub-environment. That's not where we should be called. |
| 135 if self.GetDeferRoot() != self: |
| 136 print ('Warning: Ignoring call to ExecuteDefer() from child of the ' |
| 137 'environment passed to SetDeferRoot().') |
| 138 return |
| 139 |
| 140 # Get list of defer groups from ourselves. |
| 141 defer_groups = GetDeferGroups(self) |
| 142 |
| 61 # Loop through deferred functions | 143 # Loop through deferred functions |
| 62 while __defer_groups: | 144 while defer_groups: |
| 63 did_work = False | 145 did_work = False |
| 64 for name, group in __defer_groups.items(): | 146 for name, group in defer_groups.items(): |
| 65 if group.after.intersection(__defer_groups.keys()): | 147 if group.after.intersection(defer_groups.keys()): |
| 66 continue # Still have dependencies | 148 continue # Still have dependencies |
| 67 if group.func_env_cwd: | 149 if group.func_env_cwd: |
| 68 # Run all the functions in our named group | 150 # Run all the functions in our named group |
| 69 for func, env, cwd in group.func_env_cwd: | 151 for func, env, cwd in group.func_env_cwd: |
| 70 os.chdir(cwd) | 152 os.chdir(cwd) |
| 71 func(env) | 153 func(env) |
| 72 did_work = True | 154 did_work = True |
| 73 del __defer_groups[name] | 155 del defer_groups[name] |
| 74 break | 156 break |
| 75 if not did_work: | 157 if not did_work: |
| 76 errmsg = 'Error in _ExecuteDefer: dependency cycle detected.\n' | 158 errmsg = 'Error in ExecuteDefer: dependency cycle detected.\n' |
| 77 for name, group in __defer_groups.items(): | 159 for name, group in defer_groups.items(): |
| 78 errmsg += ' %s after: %s\n' % (name, group.after) | 160 errmsg += ' %s after: %s\n' % (name, group.after) |
| 79 raise SCons.Errors.UserError(errmsg) | 161 raise SCons.Errors.UserError(errmsg) |
| 80 | 162 |
| 81 # Restore directory | 163 # Restore directory |
| 82 os.chdir(oldcwd) | 164 os.chdir(oldcwd) |
| 83 | 165 |
| 84 | 166 |
| 85 class DeferFunc(object): | 167 def PrintDefer(self, print_functions=True): |
| 86 """Named list of functions to be deferred.""" | 168 """Prints the current defer dependency graph. |
| 87 | 169 |
| 88 def __init__(self): | 170 Args: |
| 89 """Initialize deferred function object.""" | 171 self: Environment in which PrintDefer() was called. |
| 90 object.__init__(self) | 172 print_functions: Print individual functions in defer groups. |
| 91 self.func_env_cwd = [] | 173 """ |
| 92 self.after = set() | 174 # Get the defer dict |
| 175 # Get list of defer groups from ourselves. |
| 176 defer_groups = GetDeferGroups(self) |
| 177 dgkeys = defer_groups.keys() |
| 178 dgkeys.sort() |
| 179 for k in dgkeys: |
| 180 print ' +- %s' % k |
| 181 group = defer_groups[k] |
| 182 after = list(group.after) |
| 183 if after: |
| 184 print ' | after' |
| 185 after.sort() |
| 186 for a in after: |
| 187 print ' | +- %s' % a |
| 188 if print_functions and group.func_env_cwd: |
| 189 print ' functions' |
| 190 for func, env, cwd in group.func_env_cwd: |
| 191 print ' | +- %s %s' % (func.__name__, cwd) |
| 93 | 192 |
| 94 | 193 |
| 95 def Defer(self, *args, **kwargs): | 194 def Defer(self, *args, **kwargs): |
| 96 """Adds a deferred function or modifies defer dependencies. | 195 """Adds a deferred function or modifies defer dependencies. |
| 97 | 196 |
| 98 Args: | 197 Args: |
| 99 self: Environment in which Defer() was called | 198 self: Environment in which Defer() was called |
| 100 args: Positional arguments | 199 args: Positional arguments |
| 101 kwargs: Named arguments | 200 kwargs: Named arguments |
| 102 | 201 |
| 103 The deferred function will be passed the environment used to call Defer(), | 202 The deferred function will be passed the environment used to call Defer(), |
| 104 and will be executed in the same working directory as the calling SConscript. | 203 and will be executed in the same working directory as the calling SConscript. |
| 204 (Exception: if this environment is cloned and the clone calls SetDeferRoot() |
| 205 and then ExecuteDefer(), the function will be passed the root environment, |
| 206 instead of the environment used to call Defer().) |
| 105 | 207 |
| 106 All deferred functions run after all SConscripts. Additional dependencies | 208 All deferred functions run after all SConscripts. Additional dependencies |
| 107 may be specified with the after= keyword. | 209 may be specified with the after= keyword. |
| 108 | 210 |
| 109 Usage: | 211 Usage: |
| 110 | 212 |
| 111 env.Defer(func) | 213 env.Defer(func) |
| 112 # Defer func() until after all SConscripts | 214 # Defer func() until after all SConscripts |
| 113 | 215 |
| 114 env.Defer(func, after=otherfunc) | 216 env.Defer(func, after=otherfunc) |
| (...skipping 20 matching lines...) Expand all Loading... |
| 135 name = None | 237 name = None |
| 136 func = None | 238 func = None |
| 137 for a in args: | 239 for a in args: |
| 138 if isinstance(a, str): | 240 if isinstance(a, str): |
| 139 name = a | 241 name = a |
| 140 elif isinstance(a, types.FunctionType): | 242 elif isinstance(a, types.FunctionType): |
| 141 func = a | 243 func = a |
| 142 if func and not name: | 244 if func and not name: |
| 143 name = func.__name__ | 245 name = func.__name__ |
| 144 | 246 |
| 247 # TODO(rspangler): Why not allow multiple functions? Should be ok |
| 248 |
| 145 # Get list of names and/or functions this function should defer until after | 249 # Get list of names and/or functions this function should defer until after |
| 146 after = [] | 250 after = [] |
| 147 for a in self.Flatten(kwargs.get('after')): | 251 for a in self.Flatten(kwargs.get('after')): |
| 148 if isinstance(a, str): | 252 if isinstance(a, str): |
| 253 # TODO(rspangler): Should check if '$' in a, and if so, subst() it and |
| 254 # recurse into it. |
| 149 after.append(a) | 255 after.append(a) |
| 150 elif isinstance(a, types.FunctionType): | 256 elif isinstance(a, types.FunctionType): |
| 151 after.append(a.__name__) | 257 after.append(a.__name__) |
| 152 elif a is not None: | 258 elif a is not None: |
| 153 # Deferring | 259 # Deferring |
| 154 raise ValueError('Defer after=%r is not a function or name' % a) | 260 raise ValueError('Defer after=%r is not a function or name' % a) |
| 155 | 261 |
| 156 # Find the deferred function | 262 # Find the deferred function |
| 157 if name not in __defer_groups: | 263 defer_groups = GetDeferGroups(self) |
| 158 __defer_groups[name] = DeferFunc() | 264 if name not in defer_groups: |
| 159 group = __defer_groups[name] | 265 defer_groups[name] = DeferGroup() |
| 266 group = defer_groups[name] |
| 160 | 267 |
| 161 # If we were given a function, also save environment and current directory | 268 # If we were given a function, also save environment and current directory |
| 162 if func: | 269 if func: |
| 163 group.func_env_cwd.append((func, self, os.getcwd())) | 270 group.func_env_cwd.append((func, self, os.getcwd())) |
| 164 | 271 |
| 165 # Add dependencies for the function | 272 # Add dependencies for the function |
| 166 group.after.update(after) | 273 group.after.update(after) |
| 167 | 274 |
| 168 | 275 |
| 169 def generate(env): | 276 def generate(env): |
| 170 # NOTE: SCons requires the use of this name, which fails gpylint. | 277 # NOTE: SCons requires the use of this name, which fails gpylint. |
| 171 """SCons entry point for this tool.""" | 278 """SCons entry point for this tool.""" |
| 279 env.Append(_DEFER_GROUPS={}) |
| 172 | 280 |
| 173 env.AddMethod(_InitializeDefer) | |
| 174 env.AddMethod(_ExecuteDefer) | |
| 175 env.AddMethod(Defer) | 281 env.AddMethod(Defer) |
| 282 env.AddMethod(ExecuteDefer) |
| 283 env.AddMethod(GetDeferRoot) |
| 284 env.AddMethod(PrintDefer) |
| 285 env.AddMethod(SetDeferRoot) |
| OLD | NEW |