| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/python2.4 | |
| 2 # Copyright 2008, Google Inc. | |
| 3 # All rights reserved. | |
| 4 # | |
| 5 # Redistribution and use in source and binary forms, with or without | |
| 6 # modification, are permitted provided that the following conditions are | |
| 7 # met: | |
| 8 # | |
| 9 # * Redistributions of source code must retain the above copyright | |
| 10 # notice, this list of conditions and the following disclaimer. | |
| 11 # * Redistributions in binary form must reproduce the above | |
| 12 # copyright notice, this list of conditions and the following disclaimer | |
| 13 # in the documentation and/or other materials provided with the | |
| 14 # distribution. | |
| 15 # * Neither the name of Google Inc. nor the names of its | |
| 16 # contributors may be used to endorse or promote products derived from | |
| 17 # this software without specific prior written permission. | |
| 18 # | |
| 19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 30 | |
| 31 """Set up tools for environments for for software construction toolkit. | |
| 32 | |
| 33 This module is a SCons tool which should be include in all environments. It | |
| 34 will automatically be included by the component_setup tool. | |
| 35 """ | |
| 36 | |
| 37 | |
| 38 import os | |
| 39 import SCons | |
| 40 | |
| 41 | |
| 42 #------------------------------------------------------------------------------ | |
| 43 | |
| 44 | |
| 45 def FilterOut(self, **kw): | |
| 46 """Removes values from existing construction variables in an Environment. | |
| 47 | |
| 48 The values to remove should be a list. For example: | |
| 49 | |
| 50 self.FilterOut(CPPDEFINES=['REMOVE_ME', 'ME_TOO']) | |
| 51 | |
| 52 Args: | |
| 53 self: Environment to alter. | |
| 54 kw: (Any other named arguments are values to remove). | |
| 55 """ | |
| 56 | |
| 57 kw = SCons.Environment.copy_non_reserved_keywords(kw) | |
| 58 for key, val in kw.items(): | |
| 59 envval = self.get(key, None) | |
| 60 if envval is None: | |
| 61 # No existing variable in the environment, so nothing to delete. | |
| 62 continue | |
| 63 | |
| 64 for vremove in val: | |
| 65 # Use while not if, so we can handle duplicates. | |
| 66 while vremove in envval: | |
| 67 envval.remove(vremove) | |
| 68 | |
| 69 self[key] = envval | |
| 70 | |
| 71 # TODO(sgk): SCons.Environment.Append() has much more logic to deal | |
| 72 # with various types of values. We should handle all those cases in here | |
| 73 # too. (If variable is a dict, etc.) | |
| 74 | |
| 75 #------------------------------------------------------------------------------ | |
| 76 | |
| 77 | |
| 78 def Overlap(self, values1, values2): | |
| 79 """Checks for overlap between the values. | |
| 80 | |
| 81 Args: | |
| 82 self: Environment to use for variable substitution. | |
| 83 values1: First value(s) to compare. May be a string or list of strings. | |
| 84 values2: Second value(s) to compare. May be a string or list of strings. | |
| 85 | |
| 86 Returns: | |
| 87 The list of values in common after substitution, or an empty list if | |
| 88 the values do not overlap. | |
| 89 | |
| 90 Converts the values to a set of plain strings via self.SubstList2() before | |
| 91 comparison, so SCons $ variables are evaluated. | |
| 92 """ | |
| 93 set1 = set(self.SubstList2(values1)) | |
| 94 set2 = set(self.SubstList2(values2)) | |
| 95 return list(set1.intersection(set2)) | |
| 96 | |
| 97 #------------------------------------------------------------------------------ | |
| 98 | |
| 99 | |
| 100 def ApplySConscript(self, sconscript_file): | |
| 101 """Applies a SConscript to the current environment. | |
| 102 | |
| 103 Args: | |
| 104 self: Environment to modify. | |
| 105 sconscript_file: Name of SConscript file to apply. | |
| 106 | |
| 107 Returns: | |
| 108 The return value from the call to SConscript(). | |
| 109 | |
| 110 ApplySConscript() should be used when an existing SConscript which sets up an | |
| 111 environment gets too large, or when there is common setup between multiple | |
| 112 environments which can't be reduced into a parent environment which the | |
| 113 multiple child environments Clone() from. The latter case is necessary | |
| 114 because env.Clone() only enables single inheritance for environments. | |
| 115 | |
| 116 ApplySConscript() is NOT intended to replace the Tool() method. If you need | |
| 117 to add methods or builders to one or more environments, do that as a tool | |
| 118 (and write unit tests for them). | |
| 119 | |
| 120 ApplySConscript() is equivalent to the following SCons call: | |
| 121 SConscript(sconscript_file, exports={'env':self}) | |
| 122 | |
| 123 The called SConscript should import the 'env' variable to get access to the | |
| 124 calling environment: | |
| 125 Import('env') | |
| 126 | |
| 127 Changes made to env in the called SConscript will be applied to the | |
| 128 environment calling ApplySConscript() - that is, env in the called SConscript | |
| 129 is a reference to the calling environment. | |
| 130 | |
| 131 If you need to export multiple variables to the called SConscript, or return | |
| 132 variables from it, use the existing SConscript() function. | |
| 133 """ | |
| 134 return self.SConscript(sconscript_file, exports={'env': self}) | |
| 135 | |
| 136 #------------------------------------------------------------------------------ | |
| 137 | |
| 138 | |
| 139 def BuildSConscript(self, sconscript_file): | |
| 140 """Builds a SConscript based on the current environment. | |
| 141 | |
| 142 Args: | |
| 143 self: Environment to clone and pass to the called SConscript. | |
| 144 sconscript_file: Name of SConscript file to build. If this is a directory, | |
| 145 this method will look for sconscript_file+'/build.scons', and if that | |
| 146 is not found, sconscript_file+'/SConscript'. | |
| 147 | |
| 148 Returns: | |
| 149 The return value from the call to SConscript(). | |
| 150 | |
| 151 BuildSConscript() should be used when an existing SConscript which builds a | |
| 152 project gets too large, or when a group of SConscripts are logically related | |
| 153 but should not directly affect each others' environments (for example, a | |
| 154 library might want to build a number of unit tests which exist in | |
| 155 subdirectories, but not allow those tests' SConscripts to affect/pollute the | |
| 156 library's environment. | |
| 157 | |
| 158 BuildSConscript() is NOT intended to replace the Tool() method. If you need | |
| 159 to add methods or builders to one or more environments, do that as a tool | |
| 160 (and write unit tests for them). | |
| 161 | |
| 162 BuildSConscript() is equivalent to the following SCons call: | |
| 163 SConscript(sconscript_file, exports={'env':self.Clone()}) | |
| 164 or if sconscript_file is a directory: | |
| 165 SConscript(sconscript_file+'/build.scons', exports={'env':self.Clone()}) | |
| 166 | |
| 167 The called SConscript should import the 'env' variable to get access to the | |
| 168 calling environment: | |
| 169 Import('env') | |
| 170 | |
| 171 Changes made to env in the called SConscript will NOT be applied to the | |
| 172 environment calling BuildSConscript() - that is, env in the called SConscript | |
| 173 is a clone/copy of the calling environment, not a reference to that | |
| 174 environment. | |
| 175 | |
| 176 If you need to export multiple variables to the called SConscript, or return | |
| 177 variables from it, use the existing SConscript() function. | |
| 178 """ | |
| 179 # Need to look for the source node, since by default SCons will look for the | |
| 180 # entry in the variant_dir, which won't exist (and thus won't be a directory | |
| 181 # or a file). This isn't a problem in BuildComponents(), since the variant | |
| 182 # dir is only set inside its call to SConscript(). | |
| 183 if self.Entry(sconscript_file).srcnode().isdir(): | |
| 184 # Building a subdirectory, so look for build.scons or SConscript | |
| 185 script_file = sconscript_file + '/build.scons' | |
| 186 if not self.File(script_file).srcnode().exists(): | |
| 187 script_file = sconscript_file + '/SConscript' | |
| 188 else: | |
| 189 script_file = sconscript_file | |
| 190 | |
| 191 self.SConscript(script_file, exports={'env': self.Clone()}) | |
| 192 | |
| 193 #------------------------------------------------------------------------------ | |
| 194 | |
| 195 | |
| 196 def SubstList2(self, *args): | |
| 197 """Replacement subst_list designed for flags/parameters, not command lines. | |
| 198 | |
| 199 Args: | |
| 200 self: Environment context. | |
| 201 args: One or more strings or lists of strings. | |
| 202 | |
| 203 Returns: | |
| 204 A flattened, substituted list of strings. | |
| 205 | |
| 206 SCons's built-in subst_list evaluates (substitutes) variables in its | |
| 207 arguments, and returns a list of lists (one per positional argument). Since | |
| 208 it is designed for use in command line expansion, the list items are | |
| 209 SCons.Subst.CmdStringHolder instances. These instances can't be passed into | |
| 210 env.File() (or subsequent calls to env.subst(), either). The returned | |
| 211 nested lists also need to be flattened via env.Flatten() before the caller | |
| 212 can iterate over the contents. | |
| 213 | |
| 214 SubstList2() does a subst_list, flattens the result, then maps the flattened | |
| 215 list to strings. | |
| 216 | |
| 217 It is better to do: | |
| 218 for x in env.SubstList2('$MYPARAMS'): | |
| 219 than to do: | |
| 220 for x in env.get('MYPARAMS', []): | |
| 221 and definitely better than: | |
| 222 for x in env['MYPARAMS']: | |
| 223 which will throw an exception if MYPARAMS isn't defined. | |
| 224 """ | |
| 225 return map(str, self.Flatten(self.subst_list(args))) | |
| 226 | |
| 227 | |
| 228 #------------------------------------------------------------------------------ | |
| 229 | |
| 230 | |
| 231 def RelativePath(self, source, target, sep=os.sep, source_is_file=False): | |
| 232 """Calculates the relative path from source to target. | |
| 233 | |
| 234 Args: | |
| 235 self: Environment context. | |
| 236 source: Source path or node. | |
| 237 target: Target path or node. | |
| 238 sep: Path separator to use in returned relative path. | |
| 239 source_is_file: If true, calculates the relative path from the directory | |
| 240 containing the source, rather than the source itself. Note that if | |
| 241 source is a node, you can pass in source.dir instead, which is shorter. | |
| 242 | |
| 243 Returns: | |
| 244 The relative path from source to target. | |
| 245 """ | |
| 246 # Split source and target into list of directories | |
| 247 source = self.Entry(str(source)) | |
| 248 if source_is_file: | |
| 249 source = source.dir | |
| 250 source = source.abspath.split(os.sep) | |
| 251 target = self.Entry(str(target)).abspath.split(os.sep) | |
| 252 | |
| 253 # Handle source and target identical | |
| 254 if source == target: | |
| 255 if source_is_file: | |
| 256 return source[-1] # Bare filename | |
| 257 else: | |
| 258 return '.' # Directory pointing to itself | |
| 259 | |
| 260 # TODO(rspangler): Handle UNC paths and drive letters (fine if they're the | |
| 261 # same, but if they're different, there IS no relative path) | |
| 262 | |
| 263 # Remove common elements | |
| 264 while source and target and source[0] == target[0]: | |
| 265 source.pop(0) | |
| 266 target.pop(0) | |
| 267 # Join the remaining elements | |
| 268 return sep.join(['..'] * len(source) + target) | |
| 269 | |
| 270 | |
| 271 #------------------------------------------------------------------------------ | |
| 272 | |
| 273 | |
| 274 def generate(env): | |
| 275 # NOTE: SCons requires the use of this name, which fails gpylint. | |
| 276 """SCons entry point for this tool.""" | |
| 277 | |
| 278 # Add methods to environment | |
| 279 env.AddMethod(ApplySConscript) | |
| 280 env.AddMethod(BuildSConscript) | |
| 281 env.AddMethod(FilterOut) | |
| 282 env.AddMethod(Overlap) | |
| 283 env.AddMethod(RelativePath) | |
| 284 env.AddMethod(SubstList2) | |
| OLD | NEW |