1 |
# Accumulate |
2 |
# |
3 |
# Based on code from http://www.scons.org/wiki/AccumulateBuilder |
4 |
|
5 |
# The goal here was to be able to distribute a source code tarball that |
6 |
# contains all the required source code to build our program, plus a LIMITED |
7 |
# subset of our test/sample code, because we have many sample files that are |
8 |
# not yet ready for distribution. We wanted to manage our list of distributable |
9 |
# files using a simple plain text-file system that would be informative to |
10 |
# end users and not tied to a particular build system (SCons). |
11 |
|
12 |
# This tool faciltates creation of tarballs with some files excluded using |
13 |
# a convenient text-file based mechanism. By default, the tool collects copies |
14 |
# of the files you list using the syntax below, and places them in your |
15 |
# 'destination' directory: |
16 |
# |
17 |
# env.Accumulate('dist/temp/src',"src") |
18 |
# env.Accumulate('dist/temp',['prog1.exe','prog2.exe']) |
19 |
# |
20 |
# There is a special exception however. If, while recursing into directories |
21 |
# listed in the Accumulate source list, a file named 'PACKAGE' is found, then |
22 |
# only files listed therein will be copied to the destination directory. |
23 |
# The 'PACKAGE' file can contain comment lines; they must begin with a '#'. |
24 |
# Once a directory has been found to contain a 'PACKAGE' file, subdirectories |
25 |
# of that directory will not be recursed into unless they also contain a |
26 |
# 'PACKAGE' file. |
27 |
|
28 |
# Converted to SCons 'tool' format by John Pye, 10 Sept 2007. |
29 |
|
30 |
import os, os.path, shutil |
31 |
import SCons.Node.FS |
32 |
|
33 |
def copypackaged(src,dest, symlinks=False): |
34 |
"""Recursive copy of files listed in a local file named PACKAGE |
35 |
|
36 |
Look in the current directory for a file named PACKAGE. If found, |
37 |
copy the files listed there. |
38 |
|
39 |
Then recurse into subdirectories. |
40 |
|
41 |
Behaviour is intended to facilitate packaging of a user-contributed |
42 |
code library, where many files are 'experimental' but some files are |
43 |
tested and intended to distribution. Suitable for software plugins, |
44 |
code examples, documentation fragments, etc. |
45 |
""" |
46 |
|
47 |
print "Entering directory '%s'" % src |
48 |
|
49 |
files = os.listdir(src) |
50 |
|
51 |
pfiles = [] |
52 |
if 'PACKAGE' in files: |
53 |
plistfile = os.path.join(src,'PACKAGE') |
54 |
plist = file(plistfile) |
55 |
for line in plist: |
56 |
l = line.strip() |
57 |
if not len(l) or l[0]=="#": |
58 |
continue |
59 |
if l in files: |
60 |
if os.path.isdir(os.path.join(src,l)): |
61 |
print "Package file '%s' ignored (is a directory)" % l |
62 |
continue |
63 |
print "Package '%s'" % l |
64 |
pfiles.append(l) |
65 |
else: |
66 |
print "Not found: '%s'" % l |
67 |
|
68 |
# copy any listed files in the current directory first |
69 |
if pfiles: |
70 |
if not os.path.exists(dest): |
71 |
os.makedirs(dest) |
72 |
for f in pfiles: |
73 |
srcPath = os.path.join(src, f) |
74 |
if os.path.islink(srcPath) and symlinks: |
75 |
linkto = os.readlink(f) |
76 |
os.symlink(linkto, dest) |
77 |
else: |
78 |
shutil.copy2(srcPath, dest) |
79 |
|
80 |
# now recurse into subdirectories |
81 |
for f in files: |
82 |
srcPath = os.path.join(src, f) |
83 |
if os.path.isdir(srcPath): |
84 |
# a directory must be recursed into, but defer creating the dir |
85 |
srcBasename = os.path.basename(srcPath) |
86 |
destDirPath = os.path.join(dest, srcBasename) |
87 |
copypackaged(srcPath, destDirPath, symlinks) |
88 |
|
89 |
print "Leaving directory '%s'" % src |
90 |
|
91 |
|
92 |
def my_copytree(src, dest, env, symlinks=False): |
93 |
"""My own copyTree which does not fail if the directory exists. |
94 |
|
95 |
Recursively copy a directory tree using copy2(). |
96 |
|
97 |
If the optional symlinks flag is true, symbolic links in the |
98 |
source tree result in symbolic links in the destination tree; if |
99 |
it is false, the contents of the files pointed to by symbolic |
100 |
links are copied. |
101 |
|
102 |
Behavior is meant to be identical to GNU 'cp -R'. |
103 |
""" |
104 |
def copyItems(src, dest, symlinks=False): |
105 |
"""Function that does all the work. |
106 |
|
107 |
It is necessary to handle the two 'cp' cases: |
108 |
- destination does exist |
109 |
- destination does not exist |
110 |
|
111 |
See 'cp -R' documentation for more details |
112 |
""" |
113 |
files = os.listdir(src) |
114 |
if 'PACKAGE' in files: |
115 |
copypackaged(src,dest,symlinks) |
116 |
return |
117 |
|
118 |
for item in files: |
119 |
srcPath = os.path.join(src, item) |
120 |
if os.path.isdir(srcPath): |
121 |
print "DIR = %s" % srcPath |
122 |
srcBasename = os.path.basename(srcPath) |
123 |
destDirPath = os.path.join(dest, srcBasename) |
124 |
if not os.path.exists(destDirPath): |
125 |
#print "CREATE DIR %s" % destDirPath |
126 |
os.makedirs(destDirPath) |
127 |
#print "RECURSE INTO %s" % srcPath |
128 |
copyItems(srcPath, destDirPath |
129 |
, symlinks |
130 |
) |
131 |
elif os.path.islink(item) and symlinks: |
132 |
print "LINK = %s" % srcPath |
133 |
linkto = os.readlink(item) |
134 |
os.symlink(linkto, dest) |
135 |
else: |
136 |
print "FILE = %s" % srcPath |
137 |
shutil.copy2(srcPath, dest) |
138 |
|
139 |
# case 'cp -R src/ dest/' where dest/ already exists |
140 |
if os.path.exists(dest): |
141 |
destPath = os.path.join(dest, os.path.basename(src)) |
142 |
if not os.path.exists(destPath): |
143 |
os.makedirs(destPath) |
144 |
# case 'cp -R src/ dest/' where dest/ does not exist |
145 |
else: |
146 |
os.makedirs(dest) |
147 |
destPath = dest |
148 |
# actually copy the files |
149 |
copyItems(src, destPath) |
150 |
|
151 |
|
152 |
## |
153 |
## AccumulatorAction.py |
154 |
## |
155 |
|
156 |
def accumulatorFunction(target, source, env): |
157 |
"""Function called when builder is called""" |
158 |
destDir = str(target[0]) |
159 |
#print "DEST DIR = %s" % destDir |
160 |
if not os.path.exists(destDir): |
161 |
#print "CREATE DIR %s" % destDir |
162 |
os.makedirs(destDir) |
163 |
#print "SOURCES: %s" % source |
164 |
for s in source: |
165 |
s = str(s) |
166 |
if os.path.isdir(s): |
167 |
#print "COPYTREE from source %s" % s |
168 |
my_copytree(s, destDir, env, symlinks = False) |
169 |
else: |
170 |
#print "COPY FILE from source %s" % s |
171 |
shutil.copy2(s, destDir) |
172 |
|
173 |
## |
174 |
## register the above builders... |
175 |
## |
176 |
|
177 |
def generate(env): |
178 |
|
179 |
# add builder to accumulate files |
180 |
accuBuilder = env.Builder(action=accumulatorFunction, |
181 |
source_factory=SCons.Node.FS.default_fs.Entry, |
182 |
target_factory=SCons.Node.FS.default_fs.Entry, |
183 |
multi=1) |
184 |
env['BUILDERS']['Accumulate'] = accuBuilder |
185 |
|
186 |
def exists(env): |
187 |
""" |
188 |
Make sure this tool exists. |
189 |
""" |
190 |
try: |
191 |
import distutils.archive_util |
192 |
import shutil |
193 |
import os, os.path |
194 |
except ImportError: |
195 |
return False |
196 |
else: |
197 |
return True |
198 |
|