3 # Thomas Nagy, 2005, 2006, 2007, 2008
11 demos = ['cpp', 'qt4', 'tex', 'ocaml', 'kde3', 'adv', 'cc', 'idl', 'docbook', 'xmlwaf', 'gnome']
12 zip_types = ['bz2', 'gz']
14 #from tokenize import *
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
23 Configure.autoconfig = 1
25 print ("------> Executing code from the top-level wscript <-----")
27 def sub_file(fname, lst):
33 for (key, val) in lst:
34 re_pat = re.compile(key, re.M)
35 txt = re_pat.sub(val, txt)
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), ))
49 pats.append(('^WAFVERSION=(.*)', 'WAFVERSION="%s"' % ver))
50 pats.append(('^HEXVERSION(.*)', 'HEXVERSION=%s' % hexver))
54 rev = Utils.cmd_output('svnversion').strip()
55 pats.append(('^WAFREVISION(.*)', 'WAFREVISION = "%s"' % rev))
59 sub_file('wafadmin/Constants.py', pats)
61 elif Options.options.waf:
63 elif Options.commands['check']:
64 # i have never used this (ita)
72 # this function is called before any other for parsing the command-line
76 opt.add_option('--make-waf', action='store_true', default=False,
77 help='creates the waf script', dest='waf')
79 opt.add_option('--zip-type', action='store', default='bz2',
80 help='specify the zip type [Allowed values: %s]' % ' '.join(zip_types), dest='zip')
82 opt.add_option('--make-batch', action='store_true', default=False,
83 help='creates a convenience waf.bat file (done automatically on win32 systems)',
86 opt.add_option('--yes', action='store_true', default=False,
87 help=optparse.SUPPRESS_HELP,
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')
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')
103 def compute_revision():
106 def visit(arg, dirname, names):
107 for pos, name in enumerate(names):
108 if name[0] == '.' or name in ['_build_', 'build']:
110 elif name.endswith('.py'):
111 arg.append(os.path.join(dirname, name))
113 os.path.walk('wafadmin', visit, sources)
116 for source in sources:
117 f = file(source,'rb')
120 readString = f.read(readBytes)
122 readBytes = len(readString)
124 REVISION = m.hexdigest()
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')
132 buf = [] # put the decorator lines
134 if line.startswith('@'):
137 name = deco_re.sub('\\2', line)
139 raise IOError, "decorator not followed by a function!"+line
141 all_deco.append("%s(%s)" % (x, name))
146 return "\n".join(accu+all_deco)
148 def process_imports(body):
149 header = '#! /usr/bin/env python\n# encoding: utf-8'
153 if body.find('set(') > -1:
154 impo += 'import sys\nif sys.hexversion < 0x020400f0: from sets import Set as set'
156 return "\n".join([header, impo, body, deco])
158 def process_tokens(tokens):
160 prev = tokenize.NEWLINE
166 for (type, token, start, end, line) in tokens:
167 if type == tokenize.NEWLINE:
169 accu.append(indent * '\t')
170 ln = "".join(line_buf)
171 if ln == 'if __name__=="__main__":': break
172 #ln = ln.replace('\n', '')
176 prev = tokenize.NEWLINE
177 elif type == tokenize.INDENT:
179 elif type == tokenize.DEDENT:
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:
192 elif type == tokenize.OP:
193 line_buf.append(token)
195 if token != "\n": line_buf.append(token)
205 if Options.options.strip_comments:
206 cnt = process_tokens(tokenize.generate_tokens(f.readline))
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()', '')
216 return (StringIO.StringIO(cnt), len(cnt), cnt)
219 print "-> preparing waf"
220 mw = 'tmp-waf-'+VERSION
224 zipType = Options.options.zip.strip().lower()
225 if zipType not in zip_types:
226 zipType = zip_types[0]
228 #open a file as tar.[extension] for writing
229 tar = tarfile.open('%s.tar.%s' % (mw, zipType), "w:%s" % zipType)
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):
238 if not k in add3rdparty:
240 if k.endswith('.py'):
241 files.append(os.path.join(dd, k))
243 tarinfo = tar.gettarinfo(x, x)
244 tarinfo.uid = tarinfo.gid = 0
245 tarinfo.uname = tarinfo.gname = "root"
246 (code, size, cnt) = sfilter(x)
248 tar.addfile(tarinfo, code)
251 f = open('waf-light', 'rb')
255 # now store the revision unique number in waf
257 #reg = re.compile('^REVISION=(.*)', re.M)
258 #code1 = reg.sub(r'REVISION="%s"' % REVISION, code1)
262 prefix = Build.bld.env['PREFIX'] or ''
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)
270 code1 = code1.replace('bunzip2', 'gzip -d')
272 f = open('%s.tar.%s' % (mw, zipType), 'rb')
276 # the REVISION value is the md5 sum of the binary blob (facilitate audits)
279 REVISION = m.hexdigest()
280 reg = re.compile('^REVISION=(.*)', re.M)
281 code1 = reg.sub(r'REVISION="%s"' % REVISION, code1)
283 def find_unused(kd, ch):
284 for i in xrange(35, 125):
285 for j in xrange(35, 125):
287 if i == 39 or j == 39: continue
288 if i == 92 or j == 92: continue
291 return (kd.replace(ch, s), s)
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))
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')
311 if sys.platform != 'win32':
312 os.chmod('waf', 0755)
313 os.unlink('%s.tar.%s' % (mw, zipType))
315 def make_copy(inf, outf):
316 (a, b, cnt) = sfilter(inf)
322 conf.check_tool('python')
323 conf.check_python_version((2,4))
330 if Options.commands['install']:
331 if sys.platform == 'win32':
332 print "Installing Waf on Windows is not possible."
335 if Options.is_install:
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)
343 dir = os.path.join('lib', 'waf-%s-%s' % (VERSION, REVISION), 'wafadmin')
345 wafadmin = bld(features = 'py')
346 wafadmin.find_sources_in_dirs('wafadmin', exts=['.py'])
347 wafadmin.install_path = os.path.join('${PREFIX}', dir)
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')
353 bld.install_files('${PREFIX}/bin', 'waf', chmod=0755)
355 #print "waf is now installed in %s [%s, %s]" % (prefix, wafadmindir, binpath)
356 #print "make sure the PATH contains %s/bin:$PATH" % prefix
361 # Scripting.g_dist_exts += ['Weak.py'] # shows how to exclude a file from dist
362 # Scripting.Dist(APPNAME, VERSION)