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