aecacb7fd15f2355ac59a4124ba8392f4f12c3cd
[obnox/samba/samba-obnox.git] / buildtools / wafsamba / samba_dist.py
1 # customised version of 'waf dist' for Samba tools
2 # uses git ls-files to get file lists
3
4 import Utils, os, sys, tarfile, stat, Scripting, Logs, Options
5 from samba_utils import *
6
7 dist_dirs = None
8 dist_files = None
9 dist_blacklist = ""
10
11 def add_symlink(tar, fname, abspath, basedir):
12     '''handle symlinks to directories that may move during packaging'''
13     if not os.path.islink(abspath):
14         return False
15     tinfo = tar.gettarinfo(name=abspath, arcname=fname)
16     tgt = os.readlink(abspath)
17
18     if dist_dirs:
19         # we need to find the target relative to the main directory
20         # this is here to cope with symlinks into the buildtools
21         # directory from within the standalone libraries in Samba. For example,
22         # a symlink to ../../builtools/scripts/autogen-waf.sh needs
23         # to be rewritten as a symlink to buildtools/scripts/autogen-waf.sh
24         # when the tarball for talloc is built
25
26         # the filename without the appname-version
27         rel_fname = '/'.join(fname.split('/')[1:])
28
29         # join this with the symlink target
30         tgt_full = os.path.join(os.path.dirname(rel_fname), tgt)
31
32         # join with the base directory
33         tgt_base = os.path.normpath(os.path.join(basedir, tgt_full))
34
35         # see if this is inside one of our dist_dirs
36         for dir in dist_dirs.split():
37             if dir.find(':') != -1:
38                 destdir=dir.split(':')[1]
39                 dir=dir.split(':')[0]
40             else:
41                 destdir = '.'
42             if dir == basedir:
43                 # internal links don't get rewritten
44                 continue
45             if dir == tgt_base[0:len(dir)] and tgt_base[len(dir)] == '/':
46                 new_tgt = destdir + tgt_base[len(dir):]
47                 tinfo.linkname = new_tgt
48                 break
49
50     tinfo.uid   = 0
51     tinfo.gid   = 0
52     tinfo.uname = 'root'
53     tinfo.gname = 'root'
54     tar.addfile(tinfo)
55     return True
56
57 def add_tarfile(tar, fname, abspath, basedir):
58     '''add a file to the tarball'''
59     if add_symlink(tar, fname, abspath, basedir):
60         return
61     try:
62         tinfo = tar.gettarinfo(name=abspath, arcname=fname)
63     except OSError:
64         Logs.error('Unable to find file %s - missing from git checkout?' % abspath)
65         sys.exit(1)
66     tinfo.uid   = 0
67     tinfo.gid   = 0
68     tinfo.uname = 'root'
69     tinfo.gname = 'root'
70     fh = open(abspath)
71     tar.addfile(tinfo, fileobj=fh)
72     fh.close()
73
74
75 def vcs_dir_contents(path):
76     """Return the versioned files under a path.
77
78     :return: List of paths relative to path
79     """
80     repo = path
81     while repo != "/":
82         if os.path.isdir(os.path.join(repo, ".git")):
83             ls_files_cmd = [ 'git', 'ls-files', '--full-name',
84                              os_path_relpath(path, repo) ]
85             cwd = None
86             env = dict(os.environ)
87             env["GIT_DIR"] = os.path.join(repo, ".git")
88             break
89         elif os.path.isdir(os.path.join(repo, ".bzr")):
90             ls_files_cmd = [ 'bzr', 'ls', '--recursive', '--versioned',
91                              os_path_relpath(path, repo)]
92             cwd = repo
93             env = None
94             break
95         repo = os.path.dirname(repo)
96     if repo == "/":
97         raise Exception("unsupported or no vcs for %s" % path)
98     return Utils.cmd_output(ls_files_cmd, cwd=cwd, env=env).split()
99
100
101 def dist(appname='', version=''):
102
103     def add_files_to_tarball(tar, srcdir, srcsubdir, dstdir, dstsubdir, blacklist, files):
104         if blacklist is None:
105             blacklist = []
106         for f in files:
107             abspath = os.path.join(srcdir, f)
108
109             if srcsubdir != '.':
110                 f = f[len(srcsubdir)+1:]
111
112             # Remove files in the blacklist
113             if f in blacklist:
114                 continue
115             blacklisted = False
116             # Remove directories in the blacklist
117             for d in blacklist:
118                 if f.startswith(d):
119                     blacklisted = True
120             if blacklisted:
121                 continue
122             if os.path.isdir(abspath):
123                 continue
124             if dstsubdir != '.':
125                 f = dstsubdir + '/' + f
126             fname = dstdir + '/' + f
127             add_tarfile(tar, fname, abspath, srcsubdir)
128
129
130     def list_directory_files(path):
131         curdir = os.getcwd()
132         os.chdir(srcdir)
133         out_files = []
134         for root, dirs, files in os.walk(path):
135             for f in files:
136                 out_files.append(os.path.join(root, f))
137         os.chdir(curdir)
138         return out_files
139
140
141     if not isinstance(appname, str) or not appname:
142         # this copes with a mismatch in the calling arguments for dist()
143         appname = Utils.g_module.APPNAME
144         version = Utils.g_module.VERSION
145     if not version:
146         version = Utils.g_module.VERSION
147
148     srcdir = os.path.normpath(os.path.join(os.path.dirname(Utils.g_module.root_path), Utils.g_module.srcdir))
149
150     if not dist_dirs:
151         Logs.error('You must use samba_dist.DIST_DIRS() to set which directories to package')
152         sys.exit(1)
153
154     dist_base = '%s-%s' % (appname, version)
155
156     if Options.options.SIGN_RELEASE:
157         dist_name = '%s.tar' % (dist_base)
158         tar = tarfile.open(dist_name, 'w')
159     else:
160         dist_name = '%s.tar.gz' % (dist_base)
161         tar = tarfile.open(dist_name, 'w:gz')
162
163     blacklist = dist_blacklist.split()
164
165     for dir in dist_dirs.split():
166         if dir.find(':') != -1:
167             destdir=dir.split(':')[1]
168             dir=dir.split(':')[0]
169         else:
170             destdir = '.'
171         absdir = os.path.join(srcdir, dir)
172         try:
173             files = vcs_dir_contents(absdir)
174         except Exception, e:
175             Logs.error('unable to get contents of %s: %s' % (absdir, e))
176             sys.exit(1)
177         add_files_to_tarball(tar, srcdir, dir, dist_base, destdir, blacklist, files)
178
179     if dist_files:
180         for file in dist_files.split():
181             if file.find(':') != -1:
182                 destfile = file.split(':')[1]
183                 file = file.split(':')[0]
184             else:
185                 destfile = file
186
187             absfile = os.path.join(srcdir, file)
188
189             if os.path.isdir(absfile):
190                 destdir = destfile
191                 dir = file
192                 files = list_directory_files(dir)
193                 add_files_to_tarball(tar, srcdir, dir, dist_base, destdir, blacklist, files)
194             else:
195                 fname = dist_base + '/' + destfile
196                 add_tarfile(tar, fname, absfile, destfile)
197
198     tar.close()
199
200     if Options.options.SIGN_RELEASE:
201         import gzip
202         try:
203             os.unlink(dist_name + '.asc')
204         except OSError:
205             pass
206
207         cmd = "gpg --detach-sign --armor " + dist_name
208         os.system(cmd)
209         uncompressed_tar = open(dist_name, 'rb')
210         compressed_tar = gzip.open(dist_name + '.gz', 'wb')
211         while 1:
212             buffer = uncompressed_tar.read(1048576)
213             if buffer:
214                 compressed_tar.write(buffer)
215             else:
216                 break
217         uncompressed_tar.close()
218         compressed_tar.close()
219         os.unlink(dist_name)
220         Logs.info('Created %s.gz %s.asc' % (dist_name, dist_name))
221         dist_name = dist_name + '.gz'
222     else:
223         Logs.info('Created %s' % dist_name)
224
225     return dist_name
226
227
228 @conf
229 def DIST_DIRS(dirs):
230     '''set the directories to package, relative to top srcdir'''
231     global dist_dirs
232     if not dist_dirs:
233         dist_dirs = dirs
234
235 @conf
236 def DIST_FILES(files, extend=False):
237     '''set additional files for packaging, relative to top srcdir'''
238     global dist_files
239     if not dist_files:
240         dist_files = files
241     elif extend:
242         dist_files = dist_files + " " + files
243
244 @conf
245 def DIST_BLACKLIST(blacklist):
246     '''set the files to exclude from packaging, relative to top srcdir'''
247     global dist_blacklist
248     if not dist_blacklist:
249         dist_blacklist = blacklist
250
251 Scripting.dist = dist