OLD | NEW |
---|---|
(Empty) | |
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 """Creates a tree of hardlinks, symlinks or copy the inputs files.""" | |
6 | |
7 import ctypes | |
8 import logging | |
9 import os | |
10 import shutil | |
11 import sys | |
12 | |
13 | |
14 # Types of action accepted by recreate_tree(). | |
15 DRY_RUN, HARDLINK, SYMLINK, COPY = range(5)[1:] | |
16 | |
17 | |
18 class MappingError(OSError): | |
19 """Failed to recreate the tree.""" | |
20 pass | |
21 | |
22 | |
23 def os_link(source, link_name): | |
24 """Add support for os.link() on Windows.""" | |
25 if sys.platform == 'win32': | |
26 if not ctypes.windll.kernel32.CreateHardLinkW( | |
27 unicode(link_name), unicode(source), 0): | |
28 raise OSError() | |
29 else: | |
30 os.link(source, link_name) | |
31 | |
32 | |
33 def _process_item(outdir, indir, relfile, action, blacklist): | |
34 """Processes an input file. | |
35 | |
36 Returns True if processed. | |
37 """ | |
38 logging.debug( | |
39 '_process_item(%s, %s, %s, %s, %s)' % ( | |
40 outdir, indir, relfile, action, blacklist)) | |
41 if blacklist and blacklist(relfile): | |
42 return False | |
43 infile = os.path.normpath(os.path.join(indir, relfile)) | |
44 if not os.path.isfile(infile): | |
45 raise MappingError('%s doesn\'t exist' % infile) | |
46 | |
47 if action == DRY_RUN: | |
48 logging.info('Verified input: %s' % infile) | |
49 return True | |
50 | |
51 outfile = os.path.normpath(os.path.join(outdir, relfile)) | |
52 logging.debug('Mapping %s to %s' % (infile, outfile)) | |
53 outsubdir = os.path.dirname(outfile) | |
54 if not os.path.isdir(outsubdir): | |
55 os.makedirs(outsubdir) | |
56 | |
57 if os.path.isfile(outfile): | |
58 raise MappingError('%s already exist' % outfile) | |
59 | |
60 if action == COPY: | |
61 shutil.copy(infile, outfile) | |
62 elif action == SYMLINK and sys.platform != 'win32': | |
63 os.symlink(infile, outfile) | |
64 elif action == HARDLINK: | |
65 try: | |
66 os_link(infile, outfile) | |
67 except OSError: | |
68 # Probably a different file system. | |
69 logging.warn( | |
70 'Failed to hardlink, failing back to copy %s to %s' % ( | |
71 infile, outfile)) | |
72 shutil.copy(infile, outfile) | |
73 else: | |
74 raise ValueError('Unknown mapping action %s' % action) | |
75 return True | |
76 | |
77 | |
78 def _recurse_dir(outdir, indir, subdir, action, blacklist): | |
79 """Processes a directory and all its decendents.""" | |
80 logging.debug( | |
81 '_recurse_dir(%s, %s, %s, %s, %s)' % ( | |
82 outdir, indir, subdir, action, blacklist)) | |
83 root = os.path.join(indir, subdir) | |
84 for dirpath, dirnames, filenames in os.walk(root): | |
85 # Convert the absolute path to subdir + relative subdirectory. | |
86 relpath = dirpath[len(indir)+1:] | |
87 | |
88 for filename in filenames: | |
89 relfile = os.path.join(relpath, filename) | |
90 _process_item(outdir, indir, relfile, action, blacklist) | |
91 | |
92 for index, dirname in enumerate(dirnames): | |
93 # Do not process blacklisted directories. | |
94 if blacklist(os.path.normpath(os.path.join(relpath, dirname))): | |
95 del dirnames[index] | |
96 | |
97 | |
98 def recreate_tree(outdir, indir, infiles, action, blacklist): | |
99 """Creates a new tree with only the input files in it. | |
100 | |
101 Arguments: | |
102 outdir: Temporary directory to create the files in. | |
103 indir: Root directory the infiles are based in. | |
104 infiles: List of files to map from |indir| to |outdir|. | |
105 action: See assert below. | |
106 blacklist: Files to inconditionally ignore. | |
Roger Tawa OOO till Jul 10th
2012/03/02 16:47:49
inconditionally -->unconditionally
M-A Ruel
2012/03/02 21:28:30
done
| |
107 """ | |
108 logging.debug( | |
109 'recreate_tree(%s, %s, %s, %s, %s)' % ( | |
110 outdir, indir, infiles, action, blacklist)) | |
Roger Tawa OOO till Jul 10th
2012/03/02 16:47:49
is the indenting OK here?
M-A Ruel
2012/03/02 21:28:30
That's the indenting I generally use, +4 for funct
| |
111 logging.info('Mapping from %s to %s' % (indir, outdir)) | |
112 | |
113 assert action in (DRY_RUN, HARDLINK, SYMLINK, COPY) | |
114 # Both need to be a local path. | |
115 indir = os.path.normpath(indir) | |
116 if not os.path.isdir(indir): | |
117 raise MappingError('%s is not a directory' % indir) | |
118 | |
119 # Do not call abspath until it was verified the directory exists. | |
120 indir = os.path.abspath(indir) | |
121 | |
122 if action != DRY_RUN: | |
123 outdir = os.path.normpath(outdir) | |
124 if not os.path.isdir(outdir): | |
125 logging.info ('Creating %s' % outdir) | |
126 os.makedirs(outdir) | |
127 # Do not call abspath until the directory exists. | |
128 outdir = os.path.abspath(outdir) | |
129 | |
130 for relfile in infiles: | |
131 if os.path.isabs(relfile): | |
132 raise MappingError('Can\'t map absolute path %s' % relfile) | |
133 infile = os.path.normpath(os.path.join(indir, relfile)) | |
134 if not infile.startswith(indir): | |
135 raise MappingError('Can\'t map file %s outside %s' % (infile, indir)) | |
136 | |
137 isdir = os.path.isdir(infile) | |
138 if isdir and not relfile.endswith('/'): | |
139 raise MappingError( | |
140 'Input directory %s must have a trailing slash' % infile) | |
141 if not isdir and relfile.endswith('/'): | |
142 raise MappingError( | |
143 'Input file %s must not have a trailing slash' % infile) | |
144 if isdir: | |
145 _recurse_dir(outdir, indir, relfile, action, blacklist) | |
146 else: | |
147 _process_item(outdir, indir, relfile, action, blacklist) | |
148 | |
149 | |
150 def _set_write_bit(path, read_only): | |
151 mode = os.stat(path).st_mode | |
152 if read_only: | |
153 mode = mode & 0500 | |
154 else: | |
155 mode = mode | 0200 | |
156 if hasattr(os, 'lchmod'): | |
157 os.lchmod(path, mode) # pylint: disable=E1101 | |
158 else: | |
159 # TODO(maruel): Implement proper DACL modification on Windows. | |
160 os.chmod(path, mode) | |
161 | |
162 | |
163 def make_writable(root, read_only): | |
164 """Toggle the writable bit on a directory tree.""" | |
165 for dirpath, dirnames, filenames in os.walk(root, topdown=True): | |
166 relpath = os.path.join(root, dirpath) | |
Roger Tawa OOO till Jul 10th
2012/03/02 16:47:49
is this relative or absolute?
M-A Ruel
2012/03/02 21:28:30
This was indeed wrong, fixed.
| |
167 | |
168 for filename in filenames: | |
169 _set_write_bit(os.path.join(relpath, filename), read_only) | |
170 | |
171 for dirname in dirnames: | |
172 _set_write_bit(os.path.join(relpath, dirname), read_only) | |
OLD | NEW |