diff options
author | Jacek Antonelli | 2008-08-15 23:44:50 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:44:50 -0500 |
commit | 89fe5dab825a62a0e3fd8d248cbc91c65eb2a426 (patch) | |
tree | bcff14b7888d04a2fec799c59369f6095224bd08 /linden/indra/lib/python | |
parent | Second Life viewer sources 1.13.3.2 (diff) | |
download | meta-impy-89fe5dab825a62a0e3fd8d248cbc91c65eb2a426.zip meta-impy-89fe5dab825a62a0e3fd8d248cbc91c65eb2a426.tar.gz meta-impy-89fe5dab825a62a0e3fd8d248cbc91c65eb2a426.tar.bz2 meta-impy-89fe5dab825a62a0e3fd8d248cbc91c65eb2a426.tar.xz |
Second Life viewer sources 1.14.0.0
Diffstat (limited to 'linden/indra/lib/python')
-rw-r--r-- | linden/indra/lib/python/indra/llmanifest.py | 562 |
1 files changed, 562 insertions, 0 deletions
diff --git a/linden/indra/lib/python/indra/llmanifest.py b/linden/indra/lib/python/indra/llmanifest.py new file mode 100644 index 0000000..6697299 --- /dev/null +++ b/linden/indra/lib/python/indra/llmanifest.py | |||
@@ -0,0 +1,562 @@ | |||
1 | #!/usr/bin/python | ||
2 | # @file llmanifest.py | ||
3 | # @author Ryan Williams | ||
4 | # @brief Library for specifying operations on a set of files. | ||
5 | # | ||
6 | # Copyright (c) 2006-2007, Linden Research, Inc. | ||
7 | # | ||
8 | # The source code in this file ("Source Code") is provided by Linden Lab | ||
9 | # to you under the terms of the GNU General Public License, version 2.0 | ||
10 | # ("GPL"), unless you have obtained a separate licensing agreement | ||
11 | # ("Other License"), formally executed by you and Linden Lab. Terms of | ||
12 | # the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
13 | # online at http://secondlife.com/developers/opensource/gplv2 | ||
14 | # | ||
15 | # There are special exceptions to the terms and conditions of the GPL as | ||
16 | # it is applied to this Source Code. View the full text of the exception | ||
17 | # in the file doc/FLOSS-exception.txt in this software distribution, or | ||
18 | # online at http://secondlife.com/developers/opensource/flossexception | ||
19 | # | ||
20 | # By copying, modifying or distributing this software, you acknowledge | ||
21 | # that you have read and understood your obligations described above, | ||
22 | # and agree to abide by those obligations. | ||
23 | # | ||
24 | # ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
25 | # WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
26 | # COMPLETENESS OR PERFORMANCE. | ||
27 | |||
28 | import commands | ||
29 | import filecmp | ||
30 | import fnmatch | ||
31 | import getopt | ||
32 | import glob | ||
33 | import os | ||
34 | import os.path | ||
35 | import re | ||
36 | import shutil | ||
37 | import sys | ||
38 | import tarfile | ||
39 | |||
40 | def path_ancestors(path): | ||
41 | path = os.path.normpath(path) | ||
42 | result = [] | ||
43 | while len(path) > 0: | ||
44 | result.append(path) | ||
45 | path, sub = os.path.split(path) | ||
46 | return result | ||
47 | |||
48 | def proper_windows_path(path, current_platform = sys.platform): | ||
49 | """ This function takes an absolute Windows or Cygwin path and | ||
50 | returns a path appropriately formatted for the platform it's | ||
51 | running on (as determined by sys.platform)""" | ||
52 | path = path.strip() | ||
53 | drive_letter = None | ||
54 | rel = None | ||
55 | match = re.match("/cygdrive/([a-z])/(.*)", path) | ||
56 | if(not match): | ||
57 | match = re.match('([a-zA-Z]):\\\(.*)', path) | ||
58 | if(not match): | ||
59 | return None # not an absolute path | ||
60 | drive_letter = match.group(1) | ||
61 | rel = match.group(2) | ||
62 | if(current_platform == "cygwin"): | ||
63 | return "/cygdrive/" + drive_letter.lower() + '/' + rel.replace('\\', '/') | ||
64 | else: | ||
65 | return drive_letter.upper() + ':\\' + rel.replace('/', '\\') | ||
66 | |||
67 | def get_default_platform(dummy): | ||
68 | return {'linux2':'linux', | ||
69 | 'linux1':'linux', | ||
70 | 'cygwin':'windows', | ||
71 | 'win32':'windows', | ||
72 | 'darwin':'darwin' | ||
73 | }[sys.platform] | ||
74 | |||
75 | def get_default_version(srctree): | ||
76 | # look up llversion.h and parse out the version info | ||
77 | paths = [os.path.join(srctree, x, 'llversion.h') for x in ['llcommon', '../llcommon', '../../indra/llcommon.h']] | ||
78 | for p in paths: | ||
79 | if os.path.exists(p): | ||
80 | contents = open(p, 'r').read() | ||
81 | major = re.search("LL_VERSION_MAJOR\s=\s([0-9]+)", contents).group(1) | ||
82 | minor = re.search("LL_VERSION_MINOR\s=\s([0-9]+)", contents).group(1) | ||
83 | patch = re.search("LL_VERSION_PATCH\s=\s([0-9]+)", contents).group(1) | ||
84 | build = re.search("LL_VERSION_BUILD\s=\s([0-9]+)", contents).group(1) | ||
85 | return major, minor, patch, build | ||
86 | |||
87 | |||
88 | ARGUMENTS=[ | ||
89 | dict(name='actions', | ||
90 | description="""This argument specifies the actions that are to be taken when the | ||
91 | script is run. The meaningful actions are currently: | ||
92 | copy - copies the files specified by the manifest into the | ||
93 | destination directory. | ||
94 | package - bundles up the files in the destination directory into | ||
95 | an installer for the current platform | ||
96 | unpacked - bundles up the files in the destination directory into | ||
97 | a simple tarball | ||
98 | Example use: %(name)s --actions="copy unpacked" """, | ||
99 | default="copy package"), | ||
100 | dict(name='arch', | ||
101 | description="""This argument is appended to the platform string for | ||
102 | determining which manifest class to run. | ||
103 | Example use: %(name)s --arch=i686 | ||
104 | On Linux this would try to use Linux_i686Manifest.""", | ||
105 | default=""), | ||
106 | dict(name='configuration', | ||
107 | description="""The build configuration used. Only used on OS X for | ||
108 | now, but it could be used for other platforms as well.""", | ||
109 | default="Universal"), | ||
110 | dict(name='grid', | ||
111 | description="""Which grid the client will try to connect to. Even | ||
112 | though it's not strictly a grid, 'firstlook' is also an acceptable | ||
113 | value for this parameter.""", | ||
114 | default=""), | ||
115 | dict(name='installer_name', | ||
116 | description=""" The name of the file that the installer should be | ||
117 | packaged up into. Only used on Linux at the moment.""", | ||
118 | default=None), | ||
119 | dict(name='login_url', | ||
120 | description="""The url that the login screen displays in the client.""", | ||
121 | default=None), | ||
122 | dict(name='platform', | ||
123 | description="""The current platform, to be used for looking up which | ||
124 | manifest class to run.""", | ||
125 | default=get_default_platform), | ||
126 | dict(name='version', | ||
127 | description="""This specifies the version of Second Life that is | ||
128 | being packaged up.""", | ||
129 | default=get_default_version) | ||
130 | ] | ||
131 | |||
132 | def usage(srctree=""): | ||
133 | nd = {'name':sys.argv[0]} | ||
134 | print """Usage: | ||
135 | %(name)s [options] [destdir] | ||
136 | Options: | ||
137 | """ % nd | ||
138 | for arg in ARGUMENTS: | ||
139 | default = arg['default'] | ||
140 | if hasattr(default, '__call__'): | ||
141 | default = "(computed value) \"" + str(default(srctree)) + '"' | ||
142 | elif default is not None: | ||
143 | default = '"' + default + '"' | ||
144 | print "\t--%s Default: %s\n\t%s\n" % ( | ||
145 | arg['name'], | ||
146 | default, | ||
147 | arg['description'] % nd) | ||
148 | |||
149 | def main(argv=None, srctree='.', dsttree='./dst'): | ||
150 | if(argv == None): | ||
151 | argv = sys.argv | ||
152 | |||
153 | print "Source tree:", srctree | ||
154 | print "Destination tree:", dsttree | ||
155 | |||
156 | option_names = [arg['name'] + '=' for arg in ARGUMENTS] | ||
157 | option_names.append('help') | ||
158 | options, remainder = getopt.getopt(argv[1:], "", option_names) | ||
159 | if len(remainder) >= 1: | ||
160 | dsttree = remainder[0] | ||
161 | |||
162 | # convert options to a hash | ||
163 | args = {} | ||
164 | for opt in options: | ||
165 | args[opt[0].replace("--", "")] = opt[1] | ||
166 | |||
167 | # early out for help | ||
168 | if args.has_key('help'): | ||
169 | # *TODO: it is a huge hack to pass around the srctree like this | ||
170 | usage(srctree) | ||
171 | return | ||
172 | |||
173 | # defaults | ||
174 | for arg in ARGUMENTS: | ||
175 | if not args.has_key(arg['name']): | ||
176 | default = arg['default'] | ||
177 | if hasattr(default, '__call__'): | ||
178 | default = default(srctree) | ||
179 | if default is not None: | ||
180 | args[arg['name']] = default | ||
181 | |||
182 | # fix up version | ||
183 | if args.has_key('version') and type(args['version']) == str: | ||
184 | args['version'] = args['version'].split('.') | ||
185 | |||
186 | # default and agni are default | ||
187 | if args['grid'] in ['default', 'agni']: | ||
188 | args['grid'] = '' | ||
189 | |||
190 | if args.has_key('actions'): | ||
191 | args['actions'] = args['actions'].split() | ||
192 | |||
193 | # debugging | ||
194 | for opt in args: | ||
195 | print "Option:", opt, "=", args[opt] | ||
196 | |||
197 | wm = LLManifest.for_platform(args['platform'], args.get('arch'))(srctree, dsttree, args) | ||
198 | wm.do(*args['actions']) | ||
199 | return 0 | ||
200 | |||
201 | class LLManifestRegistry(type): | ||
202 | def __init__(cls, name, bases, dct): | ||
203 | super(LLManifestRegistry, cls).__init__(name, bases, dct) | ||
204 | match = re.match("(\w+)Manifest", name) | ||
205 | if(match): | ||
206 | cls.manifests[match.group(1).lower()] = cls | ||
207 | |||
208 | class LLManifest(object): | ||
209 | __metaclass__ = LLManifestRegistry | ||
210 | manifests = {} | ||
211 | def for_platform(self, platform, arch = None): | ||
212 | if arch: | ||
213 | platform = platform + '_' + arch | ||
214 | return self.manifests[platform.lower()] | ||
215 | for_platform = classmethod(for_platform) | ||
216 | |||
217 | def __init__(self, srctree, dsttree, args): | ||
218 | super(LLManifest, self).__init__() | ||
219 | self.args = args | ||
220 | self.file_list = [] | ||
221 | self.excludes = [] | ||
222 | self.actions = [] | ||
223 | self.src_prefix = [srctree] | ||
224 | self.dst_prefix = [dsttree] | ||
225 | self.created_paths = [] | ||
226 | |||
227 | def construct(self): | ||
228 | """ Meant to be overriden by LLManifest implementors with code that | ||
229 | constructs the complete destination hierarchy.""" | ||
230 | pass # override this method | ||
231 | |||
232 | def exclude(self, glob): | ||
233 | """ Excludes all files that match the glob from being included | ||
234 | in the file list by path().""" | ||
235 | self.excludes.append(glob) | ||
236 | |||
237 | def prefix(self, src='', dst=None): | ||
238 | """ Pushes a prefix onto the stack. Until end_prefix is | ||
239 | called, all relevant method calls (esp. to path()) will prefix | ||
240 | paths with the entire prefix stack. Source and destination | ||
241 | prefixes can be different, though if only one is provided they | ||
242 | are both equal. To specify a no-op, use an empty string, not | ||
243 | None.""" | ||
244 | if(dst == None): | ||
245 | dst = src | ||
246 | self.src_prefix.append(src) | ||
247 | self.dst_prefix.append(dst) | ||
248 | return True # so that you can wrap it in an if to get indentation | ||
249 | |||
250 | def end_prefix(self, descr=None): | ||
251 | """Pops a prefix off the stack. If given an argument, checks | ||
252 | the argument against the top of the stack. If the argument | ||
253 | matches neither the source or destination prefixes at the top | ||
254 | of the stack, then misnesting must have occurred and an | ||
255 | exception is raised.""" | ||
256 | # as an error-prevention mechanism, check the prefix and see if it matches the source or destination prefix. If not, improper nesting may have occurred. | ||
257 | src = self.src_prefix.pop() | ||
258 | dst = self.dst_prefix.pop() | ||
259 | if descr and not(src == descr or dst == descr): | ||
260 | raise ValueError, "End prefix '" + descr + "' didn't match '" +src+ "' or '" +dst + "'" | ||
261 | |||
262 | def get_src_prefix(self): | ||
263 | """ Returns the current source prefix.""" | ||
264 | return os.path.join(*self.src_prefix) | ||
265 | |||
266 | def get_dst_prefix(self): | ||
267 | """ Returns the current destination prefix.""" | ||
268 | return os.path.join(*self.dst_prefix) | ||
269 | |||
270 | def src_path_of(self, relpath): | ||
271 | """Returns the full path to a file or directory specified | ||
272 | relative to the source directory.""" | ||
273 | return os.path.join(self.get_src_prefix(), relpath) | ||
274 | |||
275 | def dst_path_of(self, relpath): | ||
276 | """Returns the full path to a file or directory specified | ||
277 | relative to the destination directory.""" | ||
278 | return os.path.join(self.get_dst_prefix(), relpath) | ||
279 | |||
280 | def ensure_src_dir(self, reldir): | ||
281 | """Construct the path for a directory relative to the | ||
282 | source path, and ensures that it exists. Returns the | ||
283 | full path.""" | ||
284 | path = os.path.join(self.get_src_prefix(), reldir) | ||
285 | self.cmakedirs(path) | ||
286 | return path | ||
287 | |||
288 | def ensure_dst_dir(self, reldir): | ||
289 | """Construct the path for a directory relative to the | ||
290 | destination path, and ensures that it exists. Returns the | ||
291 | full path.""" | ||
292 | path = os.path.join(self.get_dst_prefix(), reldir) | ||
293 | self.cmakedirs(path) | ||
294 | return path | ||
295 | |||
296 | def run_command(self, command): | ||
297 | """ Runs an external command, and returns the output. Raises | ||
298 | an exception if the command reurns a nonzero status code. For | ||
299 | debugging/informational purpoases, prints out the command's | ||
300 | output as it is received.""" | ||
301 | print "Running command:", command | ||
302 | fd = os.popen(command, 'r') | ||
303 | lines = [] | ||
304 | while True: | ||
305 | lines.append(fd.readline()) | ||
306 | if(lines[-1] == ''): | ||
307 | break | ||
308 | else: | ||
309 | print lines[-1], | ||
310 | output = ''.join(lines) | ||
311 | status = fd.close() | ||
312 | if(status): | ||
313 | raise RuntimeError, "Command " + command + " returned non-zero status (" + str(status) + ")" | ||
314 | return output | ||
315 | |||
316 | def created_path(self, path): | ||
317 | """ Declare that you've created a path in order to | ||
318 | a) verify that you really have created it | ||
319 | b) schedule it for cleanup""" | ||
320 | if not os.path.exists(path): | ||
321 | raise RuntimeError, "Should be something at path " + path | ||
322 | self.created_paths.append(path) | ||
323 | |||
324 | def put_in_file(self, contents, dst): | ||
325 | # write contents as dst | ||
326 | f = open(self.dst_path_of(dst), "wbU") | ||
327 | f.write(contents) | ||
328 | f.close() | ||
329 | |||
330 | def replace_in(self, src, dst=None, searchdict={}): | ||
331 | if(dst == None): | ||
332 | dst = src | ||
333 | # read src | ||
334 | f = open(self.src_path_of(src), "rbU") | ||
335 | contents = f.read() | ||
336 | f.close() | ||
337 | # apply dict replacements | ||
338 | for old, new in searchdict.iteritems(): | ||
339 | contents = contents.replace(old, new) | ||
340 | self.put_in_file(contents, dst) | ||
341 | self.created_paths.append(dst) | ||
342 | |||
343 | def copy_action(self, src, dst): | ||
344 | if(src and (os.path.exists(src) or os.path.islink(src))): | ||
345 | # ensure that destination path exists | ||
346 | self.cmakedirs(os.path.dirname(dst)) | ||
347 | self.created_paths.append(dst) | ||
348 | if(not os.path.isdir(src)): | ||
349 | self.ccopy(src,dst) | ||
350 | else: | ||
351 | # src is a dir | ||
352 | self.ccopytree(src,dst) | ||
353 | else: | ||
354 | print "Doesn't exist:", src | ||
355 | |||
356 | def package_action(self, src, dst): | ||
357 | pass | ||
358 | |||
359 | def copy_finish(self): | ||
360 | pass | ||
361 | |||
362 | def package_finish(self): | ||
363 | pass | ||
364 | |||
365 | def unpacked_finish(self): | ||
366 | unpacked_file_name = "unpacked_%(plat)s_%(vers)s.tar" % { | ||
367 | 'plat':self.args['platform'], | ||
368 | 'vers':'_'.join(self.args['version'])} | ||
369 | print "Creating unpacked file:", unpacked_file_name | ||
370 | # could add a gz here but that doubles the time it takes to do this step | ||
371 | tf = tarfile.open(self.src_path_of(unpacked_file_name), 'w:') | ||
372 | # add the entire installation package, at the very top level | ||
373 | tf.add(self.get_dst_prefix(), "") | ||
374 | tf.close() | ||
375 | |||
376 | def cleanup_finish(self): | ||
377 | """ Delete paths that were specified to have been created by this script""" | ||
378 | for c in self.created_paths: | ||
379 | # *TODO is this gonna be useful? | ||
380 | print "Cleaning up " + c | ||
381 | |||
382 | def process_file(self, src, dst): | ||
383 | if(self.includes(src, dst)): | ||
384 | # print src, "=>", dst | ||
385 | for action in self.actions: | ||
386 | methodname = action + "_action" | ||
387 | method = getattr(self, methodname, None) | ||
388 | if method is not None: | ||
389 | method(src, dst) | ||
390 | self.file_list.append([src, dst]) | ||
391 | else: | ||
392 | print "Excluding: ", src, dst | ||
393 | |||
394 | |||
395 | def process_directory(self, src, dst): | ||
396 | if(not self.includes(src, dst)): | ||
397 | print "Excluding: ", src, dst | ||
398 | return | ||
399 | names = os.listdir(src) | ||
400 | self.cmakedirs(dst) | ||
401 | errors = [] | ||
402 | for name in names: | ||
403 | srcname = os.path.join(src, name) | ||
404 | dstname = os.path.join(dst, name) | ||
405 | if os.path.isdir(srcname): | ||
406 | self.process_directory(srcname, dstname) | ||
407 | else: | ||
408 | self.process_file(srcname, dstname) | ||
409 | |||
410 | |||
411 | |||
412 | def includes(self, src, dst): | ||
413 | if src: | ||
414 | for excl in self.excludes: | ||
415 | if fnmatch.fnmatch(src, excl): | ||
416 | return False | ||
417 | return True | ||
418 | |||
419 | def remove(self, *paths): | ||
420 | for path in paths: | ||
421 | if(os.path.exists(path)): | ||
422 | print "Removing path", path | ||
423 | if(os.path.isdir(path)): | ||
424 | shutil.rmtree(path) | ||
425 | else: | ||
426 | os.remove(path) | ||
427 | |||
428 | def ccopy(self, src, dst): | ||
429 | """ Copy a single file or symlink. Uses filecmp to skip copying for existing files.""" | ||
430 | if os.path.islink(src): | ||
431 | linkto = os.readlink(src) | ||
432 | if(os.path.islink(dst) or os.path.exists(dst)): | ||
433 | os.remove(dst) # because symlinking over an existing link fails | ||
434 | os.symlink(linkto, dst) | ||
435 | else: | ||
436 | # Don't recopy file if it's up-to-date. | ||
437 | # If we seem to be not not overwriting files that have been | ||
438 | # updated, set the last arg to False, but it will take longer. | ||
439 | if(os.path.exists(dst) and filecmp.cmp(src, dst, True)): | ||
440 | return | ||
441 | # only copy if it's not excluded | ||
442 | if(self.includes(src, dst)): | ||
443 | shutil.copy2(src, dst) | ||
444 | |||
445 | def ccopytree(self, src, dst): | ||
446 | """Direct copy of shutil.copytree with the additional | ||
447 | feature that the destination directory can exist. It | ||
448 | is so dumb that Python doesn't come with this. Also it | ||
449 | implements the excludes functionality.""" | ||
450 | if(not self.includes(src, dst)): | ||
451 | return | ||
452 | names = os.listdir(src) | ||
453 | self.cmakedirs(dst) | ||
454 | errors = [] | ||
455 | for name in names: | ||
456 | srcname = os.path.join(src, name) | ||
457 | dstname = os.path.join(dst, name) | ||
458 | try: | ||
459 | if os.path.isdir(srcname): | ||
460 | self.ccopytree(srcname, dstname) | ||
461 | else: | ||
462 | self.ccopy(srcname, dstname) | ||
463 | # XXX What about devices, sockets etc.? | ||
464 | except (IOError, os.error), why: | ||
465 | errors.append((srcname, dstname, why)) | ||
466 | if errors: | ||
467 | raise RuntimeError, errors | ||
468 | |||
469 | |||
470 | def cmakedirs(self, path): | ||
471 | """Ensures that a directory exists, and doesn't throw an exception | ||
472 | if you call it on an existing directory.""" | ||
473 | # print "making path: ", path | ||
474 | path = os.path.normpath(path) | ||
475 | self.created_paths.append(path) | ||
476 | if not os.path.exists(path): | ||
477 | os.makedirs(path) | ||
478 | |||
479 | def find_existing_file(self, *list): | ||
480 | for f in list: | ||
481 | if(os.path.exists(f)): | ||
482 | return f | ||
483 | |||
484 | def contents_of_tar(self, src_tar, dst_dir): | ||
485 | """ Extracts the contents of the tarfile (specified | ||
486 | relative to the source prefix) into the directory | ||
487 | specified relative to the destination directory.""" | ||
488 | self.check_file_exists(src_tar) | ||
489 | tf = tarfile.open(self.src_path_of(src_tar), 'r') | ||
490 | for member in tf.getmembers(): | ||
491 | tf.extract(member, self.ensure_dst_dir(dst_dir)) | ||
492 | # TODO get actions working on these dudes, perhaps we should extract to a temporary directory and then process_directory on it? | ||
493 | self.file_list.append([src_tar, | ||
494 | self.dst_path_of(os.path.join(dst_dir,member.name))]) | ||
495 | tf.close() | ||
496 | |||
497 | |||
498 | def wildcard_regex(self, src_glob, dst_glob): | ||
499 | # print "regex_pair:", src_glob, dst_glob | ||
500 | src_re = re.escape(src_glob) | ||
501 | src_re = src_re.replace('\*', '([-a-zA-Z0-9._ ]+)') | ||
502 | dst_temp = dst_glob | ||
503 | i = 1 | ||
504 | while(dst_temp.count("*") > 0): | ||
505 | dst_temp = dst_temp.replace('*', '\g<' + str(i) + '>', 1) | ||
506 | i = i+1 | ||
507 | # print "regex_result:", src_re, dst_temp | ||
508 | return re.compile(src_re), dst_temp | ||
509 | |||
510 | def check_file_exists(self, path): | ||
511 | if(not os.path.exists(path) and not os.path.islink(path)): | ||
512 | raise RuntimeError, "Path " + path + " doesn't exist" | ||
513 | |||
514 | |||
515 | wildcard_pattern = re.compile('\*') | ||
516 | def expand_globs(self, src, dst): | ||
517 | def fw_slash(str): | ||
518 | return str.replace('\\', '/') | ||
519 | def os_slash(str): | ||
520 | return str.replace('/', os.path.sep) | ||
521 | dst = fw_slash(dst) | ||
522 | src = fw_slash(src) | ||
523 | src_list = glob.glob(src) | ||
524 | src_re, d_template = self.wildcard_regex(src, dst) | ||
525 | for s in src_list: | ||
526 | s = fw_slash(s) | ||
527 | d = src_re.sub(d_template, s) | ||
528 | #print "s:",s, "d_t", d_template, "dst", dst, "d", d | ||
529 | yield os_slash(s), os_slash(d) | ||
530 | |||
531 | def path(self, src, dst=None): | ||
532 | print "Processing", src, "=>", dst | ||
533 | if dst == None: | ||
534 | dst = src | ||
535 | dst = os.path.join(self.get_dst_prefix(), dst) | ||
536 | src = os.path.join(self.get_src_prefix(), src) | ||
537 | |||
538 | # expand globs | ||
539 | if(self.wildcard_pattern.search(src)): | ||
540 | for s,d in self.expand_globs(src, dst): | ||
541 | self.process_file(s, d) | ||
542 | else: | ||
543 | # if we're specifying a single path (not a glob), | ||
544 | # we should error out if it doesn't exist | ||
545 | self.check_file_exists(src) | ||
546 | # if it's a directory, recurse through it | ||
547 | if(os.path.isdir(src)): | ||
548 | self.process_directory(src, dst) | ||
549 | else: | ||
550 | self.process_file(src, dst) | ||
551 | |||
552 | |||
553 | def do(self, *actions): | ||
554 | self.actions = actions | ||
555 | self.construct() | ||
556 | # perform finish actions | ||
557 | for action in self.actions: | ||
558 | methodname = action + "_finish" | ||
559 | method = getattr(self, methodname, None) | ||
560 | if method is not None: | ||
561 | method() | ||
562 | return self.file_list | ||