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