Merge branch 'master' of ctdb into 'master' of samba
[samba.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(abspath):
131         out_files = []
132         for root, dirs, files in os.walk(abspath):
133             for f in files:
134                 out_files.append(os.path.join(root, f))
135         return out_files
136
137
138     if not isinstance(appname, str) or not appname:
139         # this copes with a mismatch in the calling arguments for dist()
140         appname = Utils.g_module.APPNAME
141         version = Utils.g_module.VERSION
142     if not version:
143         version = Utils.g_module.VERSION
144
145     srcdir = os.path.normpath(os.path.join(os.path.dirname(Utils.g_module.root_path), Utils.g_module.srcdir))
146
147     if not dist_dirs:
148         Logs.error('You must use samba_dist.DIST_DIRS() to set which directories to package')
149         sys.exit(1)
150
151     dist_base = '%s-%s' % (appname, version)
152
153     if Options.options.SIGN_RELEASE:
154         dist_name = '%s.tar' % (dist_base)
155         tar = tarfile.open(dist_name, 'w')
156     else:
157         dist_name = '%s.tar.gz' % (dist_base)
158         tar = tarfile.open(dist_name, 'w:gz')
159
160     blacklist = dist_blacklist.split()
161
162     for dir in dist_dirs.split():
163         if dir.find(':') != -1:
164             destdir=dir.split(':')[1]
165             dir=dir.split(':')[0]
166         else:
167             destdir = '.'
168         absdir = os.path.join(srcdir, dir)
169         try:
170             files = vcs_dir_contents(absdir)
171         except Exception, e:
172             Logs.error('unable to get contents of %s: %s' % (absdir, e))
173             sys.exit(1)
174         add_files_to_tarball(tar, srcdir, dir, dist_base, destdir, blacklist, files)
175
176     if dist_files:
177         for file in dist_files.split():
178             if file.find(':') != -1:
179                 destfile = file.split(':')[1]
180                 file = file.split(':')[0]
181             else:
182                 destfile = file
183
184             absfile = os.path.join(srcdir, file)
185
186             if os.path.isdir(absfile):
187                 destdir = destfile
188                 dir = file
189                 files = list_directory_files(dir)
190                 add_files_to_tarball(tar, srcdir, dir, dist_base, destdir, blacklist, files)
191             else:
192                 fname = dist_base + '/' + destfile
193                 add_tarfile(tar, fname, absfile, destfile)
194
195     tar.close()
196
197     if Options.options.SIGN_RELEASE:
198         import gzip
199         try:
200             os.unlink(dist_name + '.asc')
201         except OSError:
202             pass
203
204         cmd = "gpg --detach-sign --armor " + dist_name
205         os.system(cmd)
206         uncompressed_tar = open(dist_name, 'rb')
207         compressed_tar = gzip.open(dist_name + '.gz', 'wb')
208         while 1:
209             buffer = uncompressed_tar.read(1048576)
210             if buffer:
211                 compressed_tar.write(buffer)
212             else:
213                 break
214         uncompressed_tar.close()
215         compressed_tar.close()
216         os.unlink(dist_name)
217         Logs.info('Created %s.gz %s.asc' % (dist_name, dist_name))
218         dist_name = dist_name + '.gz'
219     else:
220         Logs.info('Created %s' % dist_name)
221
222     return dist_name
223
224
225 @conf
226 def DIST_DIRS(dirs):
227     '''set the directories to package, relative to top srcdir'''
228     global dist_dirs
229     if not dist_dirs:
230         dist_dirs = dirs
231
232 @conf
233 def DIST_FILES(files, extend=False):
234     '''set additional files for packaging, relative to top srcdir'''
235     global dist_files
236     if not dist_files:
237         dist_files = files
238     elif extend:
239         dist_files = dist_files + " " + files
240
241 @conf
242 def DIST_BLACKLIST(blacklist):
243     '''set the files to exclude from packaging, relative to top srcdir'''
244     global dist_blacklist
245     if not dist_blacklist:
246         dist_blacklist = blacklist
247
248 Scripting.dist = dist