OLD | NEW |
(Empty) | |
| 1 """SCons.Tool.Packaging |
| 2 |
| 3 SCons Packaging Tool. |
| 4 """ |
| 5 |
| 6 # |
| 7 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The S
Cons Foundation |
| 8 # |
| 9 # Permission is hereby granted, free of charge, to any person obtaining |
| 10 # a copy of this software and associated documentation files (the |
| 11 # "Software"), to deal in the Software without restriction, including |
| 12 # without limitation the rights to use, copy, modify, merge, publish, |
| 13 # distribute, sublicense, and/or sell copies of the Software, and to |
| 14 # permit persons to whom the Software is furnished to do so, subject to |
| 15 # the following conditions: |
| 16 # |
| 17 # The above copyright notice and this permission notice shall be included |
| 18 # in all copies or substantial portions of the Software. |
| 19 # |
| 20 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY |
| 21 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
| 22 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| 23 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| 24 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| 25 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| 26 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 27 |
| 28 __revision__ = "src/engine/SCons/Tool/packaging/__init__.py 5134 2010/08/16 23:0
2:40 bdeegan" |
| 29 |
| 30 import SCons.Environment |
| 31 from SCons.Variables import * |
| 32 from SCons.Errors import * |
| 33 from SCons.Util import is_List, make_path_relative |
| 34 from SCons.Warnings import warn, Warning |
| 35 |
| 36 import os, imp |
| 37 import SCons.Defaults |
| 38 |
| 39 __all__ = [ 'src_targz', 'src_tarbz2', 'src_zip', 'tarbz2', 'targz', 'zip', 'rpm
', 'msi', 'ipk' ] |
| 40 |
| 41 # |
| 42 # Utility and Builder function |
| 43 # |
| 44 def Tag(env, target, source, *more_tags, **kw_tags): |
| 45 """ Tag a file with the given arguments, just sets the accordingly named |
| 46 attribute on the file object. |
| 47 |
| 48 TODO: FIXME |
| 49 """ |
| 50 if not target: |
| 51 target=source |
| 52 first_tag=None |
| 53 else: |
| 54 first_tag=source |
| 55 |
| 56 if first_tag: |
| 57 kw_tags[first_tag[0]] = '' |
| 58 |
| 59 if len(kw_tags) == 0 and len(more_tags) == 0: |
| 60 raise UserError("No tags given.") |
| 61 |
| 62 # XXX: sanity checks |
| 63 for x in more_tags: |
| 64 kw_tags[x] = '' |
| 65 |
| 66 if not SCons.Util.is_List(target): |
| 67 target=[target] |
| 68 else: |
| 69 # hmm, sometimes the target list, is a list of a list |
| 70 # make sure it is flattened prior to processing. |
| 71 # TODO: perhaps some bug ?!? |
| 72 target=env.Flatten(target) |
| 73 |
| 74 for t in target: |
| 75 for (k,v) in kw_tags.items(): |
| 76 # all file tags have to start with PACKAGING_, so we can later |
| 77 # differentiate between "normal" object attributes and the |
| 78 # packaging attributes. As the user should not be bothered with |
| 79 # that, the prefix will be added here if missing. |
| 80 #if not k.startswith('PACKAGING_'): |
| 81 if k[:10] != 'PACKAGING_': |
| 82 k='PACKAGING_'+k |
| 83 setattr(t, k, v) |
| 84 |
| 85 def Package(env, target=None, source=None, **kw): |
| 86 """ Entry point for the package tool. |
| 87 """ |
| 88 # check if we need to find the source files ourself |
| 89 if not source: |
| 90 source = env.FindInstalledFiles() |
| 91 |
| 92 if len(source)==0: |
| 93 raise UserError("No source for Package() given") |
| 94 |
| 95 # decide which types of packages shall be built. Can be defined through |
| 96 # four mechanisms: command line argument, keyword argument, |
| 97 # environment argument and default selection( zip or tar.gz ) in that |
| 98 # order. |
| 99 try: kw['PACKAGETYPE']=env['PACKAGETYPE'] |
| 100 except KeyError: pass |
| 101 |
| 102 if not kw.get('PACKAGETYPE'): |
| 103 from SCons.Script import GetOption |
| 104 kw['PACKAGETYPE'] = GetOption('package_type') |
| 105 |
| 106 if kw['PACKAGETYPE'] == None: |
| 107 if 'Tar' in env['BUILDERS']: |
| 108 kw['PACKAGETYPE']='targz' |
| 109 elif 'Zip' in env['BUILDERS']: |
| 110 kw['PACKAGETYPE']='zip' |
| 111 else: |
| 112 raise UserError("No type for Package() given") |
| 113 |
| 114 PACKAGETYPE=kw['PACKAGETYPE'] |
| 115 if not is_List(PACKAGETYPE): |
| 116 PACKAGETYPE=PACKAGETYPE.split(',') |
| 117 |
| 118 # load the needed packagers. |
| 119 def load_packager(type): |
| 120 try: |
| 121 file,path,desc=imp.find_module(type, __path__) |
| 122 return imp.load_module(type, file, path, desc) |
| 123 except ImportError, e: |
| 124 raise EnvironmentError("packager %s not available: %s"%(type,str(e))
) |
| 125 |
| 126 packagers=list(map(load_packager, PACKAGETYPE)) |
| 127 |
| 128 # set up targets and the PACKAGEROOT |
| 129 try: |
| 130 # fill up the target list with a default target name until the PACKAGETY
PE |
| 131 # list is of the same size as the target list. |
| 132 if not target: target = [] |
| 133 |
| 134 size_diff = len(PACKAGETYPE)-len(target) |
| 135 default_name = "%(NAME)s-%(VERSION)s" |
| 136 |
| 137 if size_diff>0: |
| 138 default_target = default_name%kw |
| 139 target.extend( [default_target]*size_diff ) |
| 140 |
| 141 if 'PACKAGEROOT' not in kw: |
| 142 kw['PACKAGEROOT'] = default_name%kw |
| 143 |
| 144 except KeyError, e: |
| 145 raise SCons.Errors.UserError( "Missing Packagetag '%s'"%e.args[0] ) |
| 146 |
| 147 # setup the source files |
| 148 source=env.arg2nodes(source, env.fs.Entry) |
| 149 |
| 150 # call the packager to setup the dependencies. |
| 151 targets=[] |
| 152 try: |
| 153 for packager in packagers: |
| 154 t=[target.pop(0)] |
| 155 t=packager.package(env,t,source, **kw) |
| 156 targets.extend(t) |
| 157 |
| 158 assert( len(target) == 0 ) |
| 159 |
| 160 except KeyError, e: |
| 161 raise SCons.Errors.UserError( "Missing Packagetag '%s' for %s packager"\ |
| 162 % (e.args[0],packager.__name__) ) |
| 163 except TypeError, e: |
| 164 # this exception means that a needed argument for the packager is |
| 165 # missing. As our packagers get their "tags" as named function |
| 166 # arguments we need to find out which one is missing. |
| 167 from inspect import getargspec |
| 168 args,varargs,varkw,defaults=getargspec(packager.package) |
| 169 if defaults!=None: |
| 170 args=args[:-len(defaults)] # throw away arguments with default value
s |
| 171 args.remove('env') |
| 172 args.remove('target') |
| 173 args.remove('source') |
| 174 # now remove any args for which we have a value in kw. |
| 175 args=[x for x in args if x not in kw] |
| 176 |
| 177 if len(args)==0: |
| 178 raise # must be a different error, so reraise |
| 179 elif len(args)==1: |
| 180 raise SCons.Errors.UserError( "Missing Packagetag '%s' for %s packag
er"\ |
| 181 % (args[0],packager.__name__) ) |
| 182 else: |
| 183 raise SCons.Errors.UserError( "Missing Packagetags '%s' for %s packa
ger"\ |
| 184 % (", ".join(args),packager.__name__)
) |
| 185 |
| 186 target=env.arg2nodes(target, env.fs.Entry) |
| 187 targets.extend(env.Alias( 'package', targets )) |
| 188 return targets |
| 189 |
| 190 # |
| 191 # SCons tool initialization functions |
| 192 # |
| 193 |
| 194 added = None |
| 195 |
| 196 def generate(env): |
| 197 from SCons.Script import AddOption |
| 198 global added |
| 199 if not added: |
| 200 added = 1 |
| 201 AddOption('--package-type', |
| 202 dest='package_type', |
| 203 default=None, |
| 204 type="string", |
| 205 action="store", |
| 206 help='The type of package to create.') |
| 207 |
| 208 try: |
| 209 env['BUILDERS']['Package'] |
| 210 env['BUILDERS']['Tag'] |
| 211 except KeyError: |
| 212 env['BUILDERS']['Package'] = Package |
| 213 env['BUILDERS']['Tag'] = Tag |
| 214 |
| 215 def exists(env): |
| 216 return 1 |
| 217 |
| 218 # XXX |
| 219 def options(opts): |
| 220 opts.AddVariables( |
| 221 EnumVariable( 'PACKAGETYPE', |
| 222 'the type of package to create.', |
| 223 None, allowed_values=list(map( str, __all__ )), |
| 224 ignorecase=2 |
| 225 ) |
| 226 ) |
| 227 |
| 228 # |
| 229 # Internal utility functions |
| 230 # |
| 231 |
| 232 def copy_attr(f1, f2): |
| 233 """ copies the special packaging file attributes from f1 to f2. |
| 234 """ |
| 235 #pattrs = [x for x in dir(f1) if not hasattr(f2, x) and\ |
| 236 # x.startswith('PACKAGING_')] |
| 237 copyit = lambda x: not hasattr(f2, x) and x[:10] == 'PACKAGING_' |
| 238 pattrs = list(filter(copyit, dir(f1))) |
| 239 for attr in pattrs: |
| 240 setattr(f2, attr, getattr(f1, attr)) |
| 241 def putintopackageroot(target, source, env, pkgroot, honor_install_location=1): |
| 242 """ Uses the CopyAs builder to copy all source files to the directory given |
| 243 in pkgroot. |
| 244 |
| 245 If honor_install_location is set and the copied source file has an |
| 246 PACKAGING_INSTALL_LOCATION attribute, the PACKAGING_INSTALL_LOCATION is |
| 247 used as the new name of the source file under pkgroot. |
| 248 |
| 249 The source file will not be copied if it is already under the the pkgroot |
| 250 directory. |
| 251 |
| 252 All attributes of the source file will be copied to the new file. |
| 253 """ |
| 254 # make sure the packageroot is a Dir object. |
| 255 if SCons.Util.is_String(pkgroot): pkgroot=env.Dir(pkgroot) |
| 256 if not SCons.Util.is_List(source): source=[source] |
| 257 |
| 258 new_source = [] |
| 259 for file in source: |
| 260 if SCons.Util.is_String(file): file = env.File(file) |
| 261 |
| 262 if file.is_under(pkgroot): |
| 263 new_source.append(file) |
| 264 else: |
| 265 if hasattr(file, 'PACKAGING_INSTALL_LOCATION') and\ |
| 266 honor_install_location: |
| 267 new_name=make_path_relative(file.PACKAGING_INSTALL_LOCATION) |
| 268 else: |
| 269 new_name=make_path_relative(file.get_path()) |
| 270 |
| 271 new_file=pkgroot.File(new_name) |
| 272 new_file=env.CopyAs(new_file, file)[0] |
| 273 copy_attr(file, new_file) |
| 274 new_source.append(new_file) |
| 275 |
| 276 return (target, new_source) |
| 277 |
| 278 def stripinstallbuilder(target, source, env): |
| 279 """ strips the install builder action from the source list and stores |
| 280 the final installation location as the "PACKAGING_INSTALL_LOCATION" of |
| 281 the source of the source file. This effectively removes the final installed |
| 282 files from the source list while remembering the installation location. |
| 283 |
| 284 It also warns about files which have no install builder attached. |
| 285 """ |
| 286 def has_no_install_location(file): |
| 287 return not (file.has_builder() and\ |
| 288 hasattr(file.builder, 'name') and\ |
| 289 (file.builder.name=="InstallBuilder" or\ |
| 290 file.builder.name=="InstallAsBuilder")) |
| 291 |
| 292 if len(list(filter(has_no_install_location, source))): |
| 293 warn(Warning, "there are files to package which have no\ |
| 294 InstallBuilder attached, this might lead to irreproducible packages") |
| 295 |
| 296 n_source=[] |
| 297 for s in source: |
| 298 if has_no_install_location(s): |
| 299 n_source.append(s) |
| 300 else: |
| 301 for ss in s.sources: |
| 302 n_source.append(ss) |
| 303 copy_attr(s, ss) |
| 304 setattr(ss, 'PACKAGING_INSTALL_LOCATION', s.get_path()) |
| 305 |
| 306 return (target, n_source) |
| 307 |
| 308 # Local Variables: |
| 309 # tab-width:4 |
| 310 # indent-tabs-mode:nil |
| 311 # End: |
| 312 # vim: set expandtab tabstop=4 shiftwidth=4: |
OLD | NEW |