build: cope with symlinks between build components in waf dist
[metze/samba/wip.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
5 from samba_utils import *
6
7 dist_dirs = None
8
9 def add_symlink(tar, fname, abspath, basedir):
10     '''handle symlinks to directories that may move during packaging'''
11     if not os.path.islink(abspath):
12         return False
13     tinfo = tar.gettarinfo(name=abspath, arcname=fname)
14     tgt = os.readlink(abspath)
15
16     if dist_dirs:
17         # we need to find the target relative to the main directory
18         # this is here to cope with symlinks into the buildtools
19         # directory from within the standalone libraries in Samba. For example,
20         # a symlink to ../../builtools/scripts/autogen-waf.sh needs
21         # to be rewritten as a symlink to buildtools/scripts/autogen-waf.sh
22         # when the tarball for talloc is built
23
24         # the filename without the appname-version
25         rel_fname = '/'.join(fname.split('/')[1:])
26
27         # join this with the symlink target
28         tgt_full = os.path.join(os.path.dirname(rel_fname), tgt)
29
30         # join with the base directory
31         tgt_base = os.path.normpath(os.path.join(basedir, tgt_full))
32
33         # see if this is inside one of our dist_dirs
34         for dir in dist_dirs.split():
35             if dir.find(':') != -1:
36                 destdir=dir.split(':')[1]
37                 dir=dir.split(':')[0]
38             else:
39                 destdir = '.'
40             if dir == basedir:
41                 # internal links don't get rewritten
42                 continue
43             if dir == tgt_base[0:len(dir)] and tgt_base[len(dir)] == '/':
44                 new_tgt = destdir + tgt_base[len(dir):]
45                 tinfo.linkname = new_tgt
46                 break
47
48     tinfo.uid   = 0
49     tinfo.gid   = 0
50     tinfo.uname = 'root'
51     tinfo.gname = 'root'
52     tar.addfile(tinfo)
53     return True
54
55 def add_tarfile(tar, fname, abspath, basedir):
56     '''add a file to the tarball'''
57     if add_symlink(tar, fname, abspath, basedir):
58         return
59     try:
60         tinfo = tar.gettarinfo(name=abspath, arcname=fname)
61     except OSError:
62         Logs.error('Unable to find file %s - missing from git checkout?' % abspath)
63         sys.exit(1)
64     tinfo.uid   = 0
65     tinfo.gid   = 0
66     tinfo.uname = 'root'
67     tinfo.gname = 'root'
68     fh = open(abspath)
69     tar.addfile(tinfo, fileobj=fh)
70     fh.close()
71
72
73 def dist(appname='',version=''):
74     if not isinstance(appname, str) or not appname:
75         # this copes with a mismatch in the calling arguments for dist()
76         appname = Utils.g_module.APPNAME
77         version = Utils.g_module.VERSION
78     if not version:
79         version = Utils.g_module.VERSION
80
81     srcdir = os.path.normpath(os.path.join(os.path.dirname(Utils.g_module.root_path), Utils.g_module.srcdir))
82
83     if not dist_dirs:
84         Logs.error('You must use samba_dist.DIST_DIRS() to set which directories to package')
85         sys.exit(1)
86
87     dist_base = '%s-%s' % (appname, version)
88     dist_name = '%s.tar.gz' % (dist_base)
89
90     tar = tarfile.open(dist_name, 'w:gz')
91
92     for dir in dist_dirs.split():
93         if dir.find(':') != -1:
94             destdir=dir.split(':')[1]
95             dir=dir.split(':')[0]
96         else:
97             destdir = '.'
98         absdir = os.path.join(srcdir, dir)
99         git_cmd = [ 'git', 'ls-files', '--full-name', absdir ]
100         try:
101             files = Utils.cmd_output(git_cmd).split()
102         except:
103             Logs.error('git command failed: %s' % ' '.join(git_cmd))
104             sys.exit(1)
105         for f in files:
106             abspath = os.path.join(srcdir, f)
107             if dir != '.':
108                 f = f[len(dir)+1:]
109             if destdir != '.':
110                 f = destdir + '/' + f
111             fname = dist_base + '/' + f
112             add_tarfile(tar, fname, abspath, dir)
113
114     tar.close()
115
116     Logs.info('Created %s' % dist_name)
117     return dist_name
118
119
120 @conf
121 def DIST_DIRS(dirs):
122     '''set the directories to package, relative to top srcdir'''
123     global dist_dirs
124     if not dist_dirs:
125         dist_dirs = dirs
126
127 Scripting.dist = dist