Fixed a Python 2.5 incompatibility introduced by r6f106cb9f5c9d2ea933dda68481220c85c5...
[third_party/waf.waf15] / wscript
1 #! /usr/bin/env python
2 # encoding: utf-8
3 # Thomas Nagy, 2005, 2006, 2007, 2008
4
5 VERSION="1.5.19"
6 APPNAME='waf'
7 REVISION=''
8 top = '.'
9 out = 'build'
10
11 demos = ['cpp', 'qt4', 'tex', 'ocaml', 'kde3', 'adv', 'cc', 'idl', 'docbook', 'xmlwaf', 'gnome']
12 zip_types = ['bz2', 'gz']
13
14 #from tokenize import *
15 import tokenize
16
17 import os, sys, base64, shutil, re, random, StringIO, optparse, tempfile
18 import Utils, Options, Build
19 try: from hashlib import md5
20 except ImportError: from md5 import md5
21
22 import Configure
23 Configure.autoconfig = 1
24
25 print ("------> Executing code from the top-level wscript <-----")
26
27 def sub_file(fname, lst):
28
29         f = open(fname, 'rU')
30         txt = f.read()
31         f.close()
32
33         for (key, val) in lst:
34                 re_pat = re.compile(key, re.M)
35                 txt = re_pat.sub(val, txt)
36
37         f = open(fname, 'w')
38         f.write(txt)
39         f.close()
40
41 def init():
42         if Options.options.setver: # maintainer only (ita)
43                 ver = Options.options.setver
44                 hexver = '0x'+ver.replace('.','0')
45                 sub_file('wscript', (('^VERSION=(.*)', 'VERSION="%s"' % ver), ))
46                 sub_file('waf-light', (('^VERSION=(.*)', 'VERSION="%s"' % ver), ))
47
48                 pats = []
49                 pats.append(('^WAFVERSION=(.*)', 'WAFVERSION="%s"' % ver))
50                 pats.append(('^HEXVERSION(.*)', 'HEXVERSION=%s' % hexver))
51
52                 try:
53                         import Utils
54                         rev = Utils.cmd_output('svnversion').strip()
55                         pats.append(('^WAFREVISION(.*)', 'WAFREVISION = "%s"' % rev))
56                 except:
57                         pass
58
59                 sub_file('wafadmin/Constants.py', pats)
60                 sys.exit(0)
61         elif Options.options.waf:
62                 create_waf()
63         elif Options.commands['check']:
64                 # i have never used this (ita)
65                 sys.path.insert(0,'')
66                 import test.Test
67                 test.Test.run_tests()
68         else:
69                 return
70         sys.exit(0)
71
72 # this function is called before any other for parsing the command-line
73 def set_options(opt):
74
75         # generate waf
76         opt.add_option('--make-waf', action='store_true', default=False,
77                 help='creates the waf script', dest='waf')
78
79         opt.add_option('--zip-type', action='store', default='bz2',
80                 help='specify the zip type [Allowed values: %s]' % ' '.join(zip_types), dest='zip')
81
82         opt.add_option('--make-batch', action='store_true', default=False,
83                 help='creates a convenience waf.bat file (done automatically on win32 systems)',
84                 dest='make_batch')
85
86         opt.add_option('--yes', action='store_true', default=False,
87                 help=optparse.SUPPRESS_HELP,
88                 dest='yes')
89
90         # those ones are not too interesting
91         opt.add_option('--set-version', default='',
92                 help='sets the version number for waf releases (for the maintainer)', dest='setver')
93
94         opt.add_option('--strip', action='store_true', default=True,
95                 help='shrinks waf (strip docstrings, saves ~33kb)',
96                 dest='strip_comments')
97         opt.add_option('--nostrip', action='store_false', help='no shrinking',
98                 dest='strip_comments')
99         opt.add_option('--tools', action='store', help='3rd party tools to add [Default: "boost,fluid,rvds"]',
100                 dest='add3rdparty', default='boost,fluid,rvds')
101         opt.tool_options('python')
102
103 def compute_revision():
104         global REVISION
105
106         def visit(arg, dirname, names):
107                 for pos, name in enumerate(names):
108                         if name[0] == '.' or name in ['_build_', 'build']:
109                                 del names[pos]
110                         elif name.endswith('.py'):
111                                 arg.append(os.path.join(dirname, name))
112         sources = []
113         os.path.walk('wafadmin', visit, sources)
114         sources.sort()
115         m = md5()
116         for source in sources:
117                 f = file(source,'rb')
118                 readBytes = 100000
119                 while (readBytes):
120                         readString = f.read(readBytes)
121                         m.update(readString)
122                         readBytes = len(readString)
123                 f.close()
124         REVISION = m.hexdigest()
125
126 #deco_re = re.compile('def\\s+([a-zA-Z_]+)\\(')
127 deco_re = re.compile('(def|class)\\s+(\w+)\\(.*')
128 def process_decorators(body):
129         lst = body.split('\n')
130         accu = []
131         all_deco = []
132         buf = [] # put the decorator lines
133         for line in lst:
134                 if line.startswith('@'):
135                         buf.append(line[1:])
136                 elif buf:
137                         name = deco_re.sub('\\2', line)
138                         if not name:
139                                 raise IOError, "decorator not followed by a function!"+line
140                         for x in buf:
141                                 all_deco.append("%s(%s)" % (x, name))
142                         accu.append(line)
143                         buf = []
144                 else:
145                         accu.append(line)
146         return "\n".join(accu+all_deco)
147
148 def process_imports(body):
149         header = '#! /usr/bin/env python\n# encoding: utf-8'
150         impo = ''
151         deco = ''
152
153         if body.find('set(') > -1:
154                 impo += 'import sys\nif sys.hexversion < 0x020400f0: from sets import Set as set'
155
156         return "\n".join([header, impo, body, deco])
157
158 def process_tokens(tokens):
159         accu = []
160         prev = tokenize.NEWLINE
161
162         accu_deco = []
163         indent = 0
164         line_buf = []
165
166         for (type, token, start, end, line) in tokens:
167                 if type == tokenize.NEWLINE:
168                         if line_buf:
169                                 accu.append(indent * '\t')
170                                 ln = "".join(line_buf)
171                                 if ln == 'if __name__=="__main__":': break
172                                 #ln = ln.replace('\n', '')
173                                 accu.append(ln)
174                                 accu.append('\n')
175                                 line_buf = []
176                                 prev = tokenize.NEWLINE
177                 elif type == tokenize.INDENT:
178                         indent += 1
179                 elif type == tokenize.DEDENT:
180                         indent -= 1
181                 elif type == tokenize.NAME:
182                         if prev == tokenize.NAME or prev == tokenize.NUMBER: line_buf.append(' ')
183                         line_buf.append(token)
184                 elif type == tokenize.NUMBER:
185                         if prev == tokenize.NAME or prev == tokenize.NUMBER: line_buf.append(' ')
186                         line_buf.append(token)
187                 elif type == tokenize.STRING:
188                         if not line_buf and token.startswith('"'): pass
189                         else: line_buf.append(token)
190                 elif type == tokenize.COMMENT:
191                         pass
192                 elif type == tokenize.OP:
193                         line_buf.append(token)
194                 else:
195                         if token != "\n": line_buf.append(token)
196
197                 if token != '\n':
198                         prev = type
199
200         body = "".join(accu)
201         return body
202
203 def sfilter(path):
204         f = open(path, "r")
205         if Options.options.strip_comments:
206                 cnt = process_tokens(tokenize.generate_tokens(f.readline))
207         else:
208                 cnt = f.read()
209         f.close()
210
211         cnt = process_decorators(cnt)
212         cnt = process_imports(cnt)
213         if path.endswith('Options.py') or path.endswith('Scripting.py'):
214                 cnt = cnt.replace('Utils.python_24_guard()', '')
215
216         return (StringIO.StringIO(cnt), len(cnt), cnt)
217
218 def create_waf():
219         print "-> preparing waf"
220         mw = 'tmp-waf-'+VERSION
221
222         import tarfile, re
223
224         zipType = Options.options.zip.strip().lower()
225         if zipType not in zip_types:
226                 zipType = zip_types[0]
227
228         #open a file as tar.[extension] for writing
229         tar = tarfile.open('%s.tar.%s' % (mw, zipType), "w:%s" % zipType)
230         tarFiles=[]
231
232         files = []
233         add3rdparty = [x + '.py' for x in Options.options.add3rdparty.split(',')]
234         for d in '. Tools 3rdparty'.split():
235                 dd = os.path.join('wafadmin', d)
236                 for k in os.listdir(dd):
237                         if d == '3rdparty':
238                                 if not k in add3rdparty:
239                                         continue
240                         if k.endswith('.py'):
241                                 files.append(os.path.join(dd, k))
242         for x in files:
243                 tarinfo = tar.gettarinfo(x, x)
244                 tarinfo.uid = tarinfo.gid = 0
245                 tarinfo.uname = tarinfo.gname = "root"
246                 (code, size, cnt) = sfilter(x)
247                 tarinfo.size = size
248                 tar.addfile(tarinfo, code)
249         tar.close()
250
251         f = open('waf-light', 'rb')
252         code1 = f.read()
253         f.close()
254
255         # now store the revision unique number in waf
256         #compute_revision()
257         #reg = re.compile('^REVISION=(.*)', re.M)
258         #code1 = reg.sub(r'REVISION="%s"' % REVISION, code1)
259
260         prefix = ''
261         if Build.bld:
262                 prefix = Build.bld.env['PREFIX'] or ''
263
264         reg = re.compile('^INSTALL=(.*)', re.M)
265         code1 = reg.sub(r'INSTALL=%r' % prefix, code1)
266         #change the tarfile extension in the waf script
267         reg = re.compile('bz2', re.M)
268         code1 = reg.sub(zipType, code1)
269         if zipType == 'gz':
270                 code1 = code1.replace('bunzip2', 'gzip -d')
271
272         f = open('%s.tar.%s' % (mw, zipType), 'rb')
273         cnt = f.read()
274         f.close()
275
276         # the REVISION value is the md5 sum of the binary blob (facilitate audits)
277         m = md5()
278         m.update(cnt)
279         REVISION = m.hexdigest()
280         reg = re.compile('^REVISION=(.*)', re.M)
281         code1 = reg.sub(r'REVISION="%s"' % REVISION, code1)
282
283         def find_unused(kd, ch):
284                 for i in xrange(35, 125):
285                         for j in xrange(35, 125):
286                                 if i==j: continue
287                                 if i == 39 or j == 39: continue
288                                 if i == 92 or j == 92: continue
289                                 s = chr(i) + chr(j)
290                                 if -1 == kd.find(s):
291                                         return (kd.replace(ch, s), s)
292                 raise
293
294         # The reverse order prevent collisions
295         (cnt, C2) = find_unused(cnt, '\r')
296         (cnt, C1) = find_unused(cnt, '\n')
297         f = open('waf', 'wb')
298         f.write(code1.replace("C1='x'", "C1='%s'" % C1).replace("C2='x'", "C2='%s'" % C2))
299         f.write('#==>\n')
300         f.write('#')
301         f.write(cnt)
302         f.write('\n')
303         f.write('#<==\n')
304         f.close()
305
306         if sys.platform == 'win32' or Options.options.make_batch:
307                 f = open('waf.bat', 'wb')
308                 f.write('@python -x %~dp0waf %* & exit /b\n')
309                 f.close()
310
311         if sys.platform != 'win32':
312                 os.chmod('waf', 0755)
313         os.unlink('%s.tar.%s' % (mw, zipType))
314
315 def make_copy(inf, outf):
316         (a, b, cnt) = sfilter(inf)
317         f = open(outf, "wb")
318         f.write(cnt)
319         f.close()
320
321 def configure(conf):
322         conf.check_tool('python')
323         conf.check_python_version((2,4))
324
325
326 def build(bld):
327
328         import shutil, re
329
330         if Options.commands['install']:
331                 if sys.platform == 'win32':
332                         print "Installing Waf on Windows is not possible."
333                         sys.exit(0)
334
335         if Options.is_install:
336                 compute_revision()
337
338         if Options.commands['install']:
339                 val = Options.options.yes or (not sys.stdin.isatty() or raw_input("Installing Waf is discouraged. Proceed? [y/n]"))
340                 if val != True and val != "y": sys.exit(1)
341                 create_waf()
342
343         dir = os.path.join('lib', 'waf-%s-%s' % (VERSION, REVISION), 'wafadmin')
344
345         wafadmin = bld(features = 'py')
346         wafadmin.find_sources_in_dirs('wafadmin', exts=['.py'])
347         wafadmin.install_path = os.path.join('${PREFIX}', dir)
348
349         tools = bld(features = 'py')
350         tools.find_sources_in_dirs('wafadmin/Tools', exts=['.py'])
351         tools.install_path = os.path.join('${PREFIX}', dir, 'Tools')
352
353         bld.install_files('${PREFIX}/bin', 'waf', chmod=0755)
354
355         #print "waf is now installed in %s [%s, %s]" % (prefix, wafadmindir, binpath)
356         #print "make sure the PATH contains %s/bin:$PATH" % prefix
357
358
359 #def dist():
360 #       import Scripting
361 #       Scripting.g_dist_exts += ['Weak.py'] # shows how to exclude a file from dist
362 #       Scripting.Dist(APPNAME, VERSION)
363