#! /usr/bin/env python
# encoding: utf-8
-# Thomas Nagy, 2005, 2006 (ita)
+# Thomas Nagy, 2005-2010
"""
-This script is not a good example:
- * it is complicated
- * it does not build anything, it just exits in the init method
+to make a custom waf file use the option --tools
-Have a look at demos/cc/wscript instead
-For configuration examples: demos/adv/wscript
-For a project without subdirectory: demos/python/wscript
+To add a tool that does not exist in the folder compat15, pass an absolute path:
+./waf-light --make-waf --tools=compat15,/comp/waf/aba.py --prelude=$'\tfrom waflib.extras import aba\n\taba.foo()'
"""
-VERSION="1.4.2"
+
+VERSION="1.6.8"
APPNAME='waf'
REVISION=''
-srcdir='.'
-blddir='build'
+
+top = '.'
+out = 'build'
demos = ['cpp', 'qt4', 'tex', 'ocaml', 'kde3', 'adv', 'cc', 'idl', 'docbook', 'xmlwaf', 'gnome']
zip_types = ['bz2', 'gz']
-# exclude these modules
-forbidden = [x+'.py' for x in 'Test Weak'.split()]
+PRELUDE = '\timport waflib.extras.compat15'
+
+#from tokenize import *
+import tokenize
+
+import os, sys, re, io, optparse
+
+from waflib import Utils, Options
+from hashlib import md5
+
+from waflib import Configure
+Configure.autoconfig = 1
-from tokenize import *
+def sub_file(fname, lst):
-import os, sys, base64, shutil, re, random, StringIO, optparse
-import Params, Utils, Options, Common
-try: from hashlib import md5
-except ImportError: from md5 import md5
+ f = open(fname, 'rU')
+ txt = f.read()
+ f.close()
+
+ for (key, val) in lst:
+ re_pat = re.compile(key, re.M)
+ txt = re_pat.sub(val, txt)
+
+ f = open(fname, 'w')
+ f.write(txt)
+ f.close()
-pyFileExp = re.compile(".*\.py$")
+print("------> Executing code from the top-level wscript <-----")
+def init(*k, **kw):
+ if Options.options.setver: # maintainer only (ita)
+ ver = Options.options.setver
+ hexver = Utils.num2ver(ver)
+ hexver = '0x%x'%hexver
+ sub_file('wscript', (('^VERSION=(.*)', 'VERSION="%s"' % ver), ))
+ sub_file('waf-light', (('^VERSION=(.*)', 'VERSION="%s"' % ver), ))
+
+ pats = []
+ pats.append(('^WAFVERSION=(.*)', 'WAFVERSION="%s"' % ver))
+ pats.append(('^HEXVERSION(.*)', 'HEXVERSION=%s' % hexver))
+
+ try:
+ rev = k[0].cmd_and_log('svnversion').strip().replace('M', '')
+ pats.append(('^WAFREVISION(.*)', 'WAFREVISION="%s"' % rev))
+ except:
+ pass
-print "------> Executing code from the top-level wscript <-----"
+ sub_file('waflib/Context.py', pats)
+
+ sys.exit(0)
+ elif Options.options.waf:
+ create_waf()
+ sys.exit(0)
+
+def check(ctx):
+ sys.path.insert(0,'')
+ # some tests clobber g_module. We must preserve it here, otherwise we get an error
+ # about an undefined shutdown function
+ mod = Utils.g_module
+ import test.Test
+ test.Test.run_tests()
+ Utils.g_module = mod
# this function is called before any other for parsing the command-line
-def set_options(opt):
+def options(opt):
# generate waf
opt.add_option('--make-waf', action='store_true', default=False,
help='specify the zip type [Allowed values: %s]' % ' '.join(zip_types), dest='zip')
opt.add_option('--make-batch', action='store_true', default=False,
- help='creates a waf.bat file that calls the waf script. (this is done automatically on win32 systems)',
+ help='creates a convenience waf.bat file (done automatically on win32 systems)',
dest='make_batch')
opt.add_option('--yes', action='store_true', default=False,
# those ones are not too interesting
opt.add_option('--set-version', default='',
- help='set the version number for waf releases (for the maintainer)', dest='setver')
+ help='sets the version number for waf releases (for the maintainer)', dest='setver')
- opt.add_option('--strip', action='store_true', default=False,
- help='Shrink waf (strip docstrings, saves 33kb)',
+ opt.add_option('--strip', action='store_true', default=True,
+ help='shrinks waf (strip docstrings, saves 33kb)',
dest='strip_comments')
-
-def encodeAscii85(s):
- out=[]
- app=out.append
- v=[(0,16777216L),(1,65536),(2,256),(3,1)]
- cnt,r = divmod(len(s),4)
- stop=4*cnt
- p1,p2=s[0:stop],s[stop:]
- for i in range(cnt):
- offset=i*4
- num=0
- for (j,mul) in v: num+=mul*ord(p1[offset+j])
- if num==0: out.append('z')
- else:
- x,e=divmod(num,85)
- x,d=divmod(x,85)
- x,c=divmod(x,85)
- a,b=divmod(x,85)
- app(chr(a+33)+chr(b+33)+chr(c+33)+chr(d+33)+chr(e+33))
- if r>0:
- while len(p2)<4: p2=p2+'\x00'
- num=0
- for (j,mul) in v: num+=mul*ord(p2[j])
- x,e=divmod(num,85)
- x,d=divmod(x,85)
- x,c=divmod(x,85)
- a,b=divmod(x,85)
- end=chr(a+33)+chr(b+33)+chr(c+33)+chr(d+33)+chr(e+33)
- app(end[0:1+r])
- return ''.join(out)
+ opt.add_option('--nostrip', action='store_false', help='no shrinking',
+ dest='strip_comments')
+ opt.add_option('--tools', action='store', help='Comma-separated 3rd party tools to add, eg: "compat,ocaml" [Default: "compat15"]',
+ dest='add3rdparty', default='compat15')
+ opt.add_option('--prelude', action='store', help='Code to execute before calling waf', dest='prelude', default=PRELUDE)
+ opt.load('python')
def compute_revision():
global REVISION
elif name.endswith('.py'):
arg.append(os.path.join(dirname, name))
sources = []
- os.path.walk('wafadmin', visit, sources)
+ os.path.walk('waflib', visit, sources)
sources.sort()
m = md5()
for source in sources:
f.close()
REVISION = m.hexdigest()
-#deco_re = re.compile('def\\s+([a-zA-Z_]+)\\(')
-deco_re = re.compile('(def|class)\\s+(\w+)\\(.*')
-def process_decorators(body):
- "modify the python 2.4 decorators"
- lst = body.split('\n')
- accu = []
- all_deco = []
- buf = [] # put the decorator lines
- for line in lst:
- if line.startswith('@'):
- buf.append(line[1:])
- elif buf:
- name = deco_re.sub('\\2', line)
- if not name:
- raise IOError, "decorator not followed by a function!"+line
- for x in buf:
- all_deco.append("%s(%s)" % (x, name))
- accu.append(line)
- buf = []
- else:
- accu.append(line)
- return "\n".join(accu+all_deco)
-
-def process_imports(body):
- "add the python 2.3 fixes to the redistributable waf"
- header = '#! /usr/bin/env python\n# encoding: utf-8'
- impo = ''
- deco = ''
-
- if body.find('set(') > -1:
- impo += 'import sys\nif sys.hexversion < 0x020400f0: from sets import Set as set'
-
- return "\n".join([header, impo, body, deco])
-
def process_tokens(tokens):
accu = []
- prev = NEWLINE
+ prev = tokenize.NEWLINE
- accu_deco = []
indent = 0
line_buf = []
for (type, token, start, end, line) in tokens:
- if type == NEWLINE:
+ token = token.replace('\r\n', '\n')
+ if type == tokenize.NEWLINE:
if line_buf:
accu.append(indent * '\t')
ln = "".join(line_buf)
accu.append(ln)
accu.append('\n')
line_buf = []
- prev = NEWLINE
- elif type == INDENT:
+ prev = tokenize.NEWLINE
+ elif type == tokenize.INDENT:
indent += 1
- elif type == DEDENT:
+ elif type == tokenize.DEDENT:
indent -= 1
- elif type == NAME:
- if prev == NAME or prev == NUMBER: line_buf.append(' ')
+ elif type == tokenize.NAME:
+ if prev == tokenize.NAME or prev == tokenize.NUMBER: line_buf.append(' ')
line_buf.append(token)
- elif type == NUMBER:
- if prev == NAME or prev == NUMBER: line_buf.append(' ')
+ elif type == tokenize.NUMBER:
+ if prev == tokenize.NAME or prev == tokenize.NUMBER: line_buf.append(' ')
line_buf.append(token)
- elif type == STRING:
+ elif type == tokenize.STRING:
if not line_buf and token.startswith('"'): pass
else: line_buf.append(token)
- elif type == COMMENT:
+ elif type == tokenize.COMMENT:
pass
- elif type == OP:
+ elif type == tokenize.OP:
line_buf.append(token)
else:
if token != "\n": line_buf.append(token)
body = "".join(accu)
return body
+deco_re = re.compile('(def|class)\\s+(\w+)\\(.*')
+def process_decorators(body):
+ lst = body.split('\n')
+ accu = []
+ all_deco = []
+ buf = [] # put the decorator lines
+ for line in lst:
+ if line.startswith('@'):
+ buf.append(line[1:])
+ elif buf:
+ name = deco_re.sub('\\2', line)
+ if not name:
+ raise IOError("decorator not followed by a function!" + line)
+ for x in buf:
+ all_deco.append("%s(%s)" % (x, name))
+ accu.append(line)
+ buf = []
+ else:
+ accu.append(line)
+ return "\n".join(accu+all_deco)
+
def sfilter(path):
- f = open(path, "r")
- if Params.g_options.strip_comments:
- cnt = process_tokens(generate_tokens(f.readline))
+ if sys.version_info[0] >= 3 and Options.options.strip_comments:
+ f = open(path, "rb")
+ tk = tokenize.tokenize(f.readline)
+ next(tk) # the first one is always tokenize.ENCODING for Python 3, ignore it
+ cnt = process_tokens(tk)
+ elif Options.options.strip_comments and path.endswith('.py'):
+ f = open(path, "r")
+ cnt = process_tokens(tokenize.generate_tokens(f.readline))
else:
+ f = open(path, "r")
cnt = f.read()
+
f.close()
+ if path.endswith('.py') :
+ cnt = process_decorators(cnt)
- cnt = process_decorators(cnt)
- cnt = process_imports(cnt)
- if path.endswith('Scripting.py'):
- cnt = cnt.replace('Utils.python_24_guard()', '')
+ if cnt.find('set(') > -1:
+ cnt = 'import sys\nif sys.hexversion < 0x020400f0: from sets import Set as set\n' + cnt
+ cnt = '#! /usr/bin/env python\n# encoding: utf-8\n# WARNING! Do not edit! http://waf.googlecode.com/svn/docs/wafbook/single.html#_obtaining_the_waf_file\n\n' + cnt
- return (StringIO.StringIO(cnt), len(cnt), cnt)
+ return (io.BytesIO(cnt.encode('utf-8')), len(cnt), cnt)
-def create_waf():
- print "-> preparing waf"
+def create_waf(*k, **kw):
+ #print("-> preparing waf")
mw = 'tmp-waf-'+VERSION
import tarfile, re
- zipType = Params.g_options.zip.strip().lower()
+ zipType = Options.options.zip.strip().lower()
if zipType not in zip_types:
zipType = zip_types[0]
#open a file as tar.[extension] for writing
tar = tarfile.open('%s.tar.%s' % (mw, zipType), "w:%s" % zipType)
- tarFiles=[]
- lst = os.listdir('wafadmin')
- files = [os.path.join('wafadmin', s) for s in lst if pyFileExp.match(s) and not s in forbidden]
- tooldir = os.path.join('wafadmin', 'Tools')
- lst = os.listdir(tooldir)
- files += [os.path.join(tooldir, s) for s in lst if pyFileExp.match(s) and not s in forbidden]
+ files = []
+ add3rdparty = []
+ for x in Options.options.add3rdparty.split(','):
+ if os.path.isabs(x):
+ files.append(x)
+ else:
+ add3rdparty.append(x + '.py')
+
+ for d in '. Tools extras'.split():
+ dd = os.path.join('waflib', d)
+ for k in os.listdir(dd):
+ if k == '__init__.py':
+ files.append(os.path.join(dd, k))
+ continue
+ if d == 'extras':
+ if not k in add3rdparty:
+ continue
+ if k.endswith('.py'):
+ files.append(os.path.join(dd, k))
+
for x in files:
tarinfo = tar.gettarinfo(x, x)
- tarinfo.uid=tarinfo.gid=1000
- tarinfo.uname=tarinfo.gname="bozo"
+ tarinfo.uid = tarinfo.gid = 0
+ tarinfo.uname = tarinfo.gname = 'root'
(code, size, cnt) = sfilter(x)
tarinfo.size = size
+
+ if os.path.isabs(x):
+ tarinfo.name = 'waflib/extras/' + os.path.split(x)[1]
+
tar.addfile(tarinfo, code)
tar.close()
- f = open('waf-light', 'rb')
+ f = open('waf-light', 'rU')
code1 = f.read()
f.close()
# now store the revision unique number in waf
- compute_revision()
- reg = re.compile('^REVISION=(.*)', re.M)
- code1 = reg.sub(r'REVISION="%s"' % REVISION, code1)
-
- prefix = Params.g_options.prefix
- # if the prefix is the default, let's be nice and be platform-independent
- # just in case the created waf is used on either windows or unix
- if prefix == Options.default_prefix:
- prefix = "sys.platform=='win32' and 'c:/temp' or '/usr/local'"
- else:
- prefix = '"%s"' % prefix #encase in quotes
+ #compute_revision()
+ #reg = re.compile('^REVISION=(.*)', re.M)
+ #code1 = reg.sub(r'REVISION="%s"' % REVISION, code1)
+ code1 = code1.replace("if sys.hexversion<0x206000f:\n\traise ImportError('Python >= 2.6 is required to create the waf file')\n", '')
+ code1 = code1.replace('\timport waflib.extras.compat15#PRELUDE', Options.options.prelude)
+ prefix = ''
reg = re.compile('^INSTALL=(.*)', re.M)
- code1 = reg.sub(r'INSTALL=%s' % prefix, code1)
+ code1 = reg.sub(r'INSTALL=%r' % prefix, code1)
#change the tarfile extension in the waf script
reg = re.compile('bz2', re.M)
code1 = reg.sub(zipType, code1)
+ if zipType == 'gz':
+ code1 = code1.replace('bunzip2', 'gzip -d')
f = open('%s.tar.%s' % (mw, zipType), 'rb')
cnt = f.read()
f.close()
- code2 = encodeAscii85(cnt)
+
+ # the REVISION value is the md5 sum of the binary blob (facilitate audits)
+ m = md5()
+ m.update(cnt)
+ REVISION = m.hexdigest()
+ reg = re.compile('^REVISION=(.*)', re.M)
+ code1 = reg.sub(r'REVISION="%s"' % REVISION, code1)
+
+ def find_unused(kd, ch):
+ for i in range(35, 125):
+ for j in range(35, 125):
+ if i==j: continue
+ if i == 39 or j == 39: continue
+ if i == 92 or j == 92: continue
+ s = chr(i) + chr(j)
+ if -1 == kd.find(s.encode()):
+ return (kd.replace(ch.encode(), s.encode()), s)
+ raise
+
+ # The reverse order prevent collisions
+ (cnt, C2) = find_unused(cnt, '\r')
+ (cnt, C1) = find_unused(cnt, '\n')
f = open('waf', 'wb')
- f.write(code1)
- f.write('#==>\n')
- f.write('#')
- f.write(code2)
- f.write('\n')
- f.write('#<==\n')
+
+ ccc = code1.replace("C1='x'", "C1='%s'" % C1).replace("C2='x'", "C2='%s'" % C2)
+
+ f.write(ccc.encode())
+ f.write(b'#==>\n')
+ f.write(b'#')
+ f.write(cnt)
+ f.write(b'\n')
+ f.write(b'#<==\n')
f.close()
- if sys.platform == 'win32' or Params.g_options.make_batch:
- f = open('waf.bat', 'wb')
- f.write('@python -x waf %* & exit /b\n')
+ if sys.platform == 'win32' or Options.options.make_batch:
+ f = open('waf.bat', 'w')
+ f.write('@python -x "%~dp0waf" %* & exit /b\n')
f.close()
if sys.platform != 'win32':
- os.chmod('waf', 0755)
+ os.chmod('waf', Utils.O755)
os.unlink('%s.tar.%s' % (mw, zipType))
def make_copy(inf, outf):
f.close()
def configure(conf):
- conf.check_tool('python')
+ conf.load('python')
conf.check_python_version((2,4))
def build(bld):
- import shutil, re
-
- if Params.g_commands['install']:
- if sys.platform == 'win32':
- print "Installing Waf on Windows is not possible."
- sys.exit(0)
-
- if Params.g_commands['install']:
- val = Params.g_options.yes or (not sys.stdin.isatty() or raw_input("Installing Waf is discouraged. Proceed? [y/n]"))
- if val != True and val != "y": sys.exit(1)
-
- compute_revision()
-
- create_waf()
-
- wafadmin = bld.create_obj('py')
- wafadmin.find_sources_in_dirs('wafadmin', exts=['.py'])
- wafadmin.inst_var = 'PREFIX'
- wafadmin.inst_dir = os.path.join('lib', 'waf-%s-%s' % (VERSION, REVISION), 'wafadmin')
-
- tools = bld.create_obj('py')
- tools.find_sources_in_dirs('wafadmin/Tools', exts=['.py'])
- tools.inst_var = 'PREFIX'
- tools.inst_dir = os.path.join(wafadmin.inst_dir, 'Tools')
-
- Common.install_files('PREFIX', 'bin', 'waf', chmod=0755)
-
- #print "waf is now installed in %s [%s, %s]" % (prefix, wafadmindir, binpath)
- #print "make sure the PATH contains %s/bin:$PATH" % prefix
-
-
-# the init function is called right after the command-line arguments are parsed
-# it is run before configure(), build() and shutdown()
-# in this case it calls sys.exit(0) to terminate the program
-def init():
- if Params.g_options.setver: # maintainer only (ita)
- ver = Params.g_options.setver
- hexver = '0x'+ver.replace('.','0')
- os.popen("""perl -pi -e 's/^VERSION=(.*)?$/VERSION="%s"/' wscript""" % ver).close()
- os.popen("""perl -pi -e 's/^VERSION=(.*)?$/VERSION="%s"/' waf-light""" % ver).close()
- os.popen("""perl -pi -e 's/^g_version(.*)?$/g_version="%s"/' wafadmin/Params.py""" % ver).close()
- os.popen("""perl -pi -e 's/^HEXVERSION(.*)?$/HEXVERSION = %s/' wafadmin/Constants.py""" % hexver).close()
- sys.exit(0)
- elif Params.g_options.waf:
- create_waf()
- sys.exit(0)
- elif Params.g_commands['check']:
- import Test
- Test.run_tests()
- sys.exit(0)
+ waf = bld.path.make_node('waf') # create the node right here
+ bld(name='create_waf', rule=create_waf, target=waf, always=True, color='PINK', update_outputs=True)
#def dist():
# import Scripting