0,0 → 1,383 |
import os |
import sys |
|
VARS = {} |
TOPDIR = os.path.abspath(os.path.dirname(__file__)) |
TEST = False |
CLEAN = False |
BOOT = False |
CORE = ['tokenize','parse','encode','py2bc'] |
MODULES = [] |
|
def main(): |
chksize() |
if len(sys.argv) < 2: |
print HELP |
return |
|
global TEST,CLEAN,BOOT |
TEST = 'test' in sys.argv |
CLEAN = 'clean' in sys.argv |
BOOT = 'boot' in sys.argv |
CLEAN = CLEAN or BOOT |
TEST = TEST or BOOT |
|
get_libs() |
build_mymain() |
|
cmd = sys.argv[1] |
if cmd == 'linux': |
vars_linux() |
build_gcc() |
elif cmd == 'mingw': |
vars_windows() |
build_gcc() |
elif cmd == 'vs': |
build_vs() |
elif cmd == '64k': |
build_64k() |
elif cmd == 'blob': |
build_blob() |
else: |
print 'invalid command' |
|
HELP = """ |
python setup.py command [options] [modules] |
|
Commands: |
linux - build tinypy for linux |
mingw - build tinypy for mingw under windows |
vs - build tinypy using Visual Studio 2005 / 2008 |
|
64k - build a 64k version of the tinypy source |
blob - build a single tinypy.c and tinypy.h |
|
build - build CPython module *** |
install - install CPython module *** |
|
Options: |
test - run tests during build |
clean - rebuild all .tpc during build |
boot - fully bootstrap and test tinypy |
|
Modules: |
math - build math module |
random - build random module * |
pygame - build pygame module ** |
marshal - build marshal module *** |
jit - build jit module *** |
re - build re module *** |
|
* coming soon!! |
** proof-of-concept included |
*** vaporware |
""" |
|
def vars_linux(): |
VARS['$RM'] = 'rm -f' |
VARS['$VM'] = './vm' |
VARS['$TINYPY'] = './tinypy' |
VARS['$SYS'] = '-linux' |
VARS['$FLAGS'] = '' |
|
VARS['$WFLAGS'] = '-std=c89 -Wall -Wc++-compat' |
#-Wwrite-strings - i think this is included in -Wc++-compat |
|
if 'pygame' in MODULES: |
VARS['$FLAGS'] += ' `sdl-config --cflags --libs` ' |
|
def vars_windows(): |
VARS['$RM'] = 'del' |
VARS['$VM'] = 'vm' |
VARS['$TINYPY'] = 'tinypy' |
VARS['$FLAGS'] = '-lmingw32' |
VARS['$WFLAGS'] = '-Wwrite-strings -Wall' |
VARS['$SYS'] = '-mingw32' |
|
if 'pygame' in MODULES: |
VARS['$FLAGS'] += ' -Ic:\\mingw\\include\\SDL -lSDLmain -lSDL ' |
|
def do_cmd(cmd): |
for k,v in VARS.items(): |
cmd = cmd.replace(k,v) |
if '$' in cmd: |
print 'vars_error',cmd |
sys.exit(-1) |
|
print cmd |
r = os.system(cmd) |
if r: |
print 'exit_status',r |
sys.exit(r) |
|
def do_chdir(dest): |
print 'cd',dest |
os.chdir(dest) |
|
def build_bc(opt=False): |
out = [] |
for mod in CORE: |
out.append("""unsigned char tp_%s[] = {"""%mod) |
fname = mod+".tpc" |
data = open(fname,'rb').read() |
cols = 16 |
for n in xrange(0,len(data),cols): |
out.append(",".join([str(ord(v)) for v in data[n:n+cols]])+',') |
out.append("""};""") |
out.append("") |
f = open('bc.c','wb') |
f.write('\n'.join(out)) |
f.close() |
|
def open_tinypy(fname,*args): |
return open(os.path.join(TOPDIR,'tinypy',fname),*args) |
|
def build_blob(): |
mods = CORE[:] |
do_chdir(os.path.join(TOPDIR,'tinypy')) |
for mod in mods: do_cmd('python py2bc.py %s.py %s.tpc'%(mod,mod)) |
do_chdir(os.path.join(TOPDIR)) |
|
out = [] |
out.append("/*") |
out.extend([v.rstrip() for v in open(os.path.join(TOPDIR,'LICENSE.txt'),'r')]) |
out.append("*/") |
out.append("") |
|
out.append("#ifndef TINYPY_H") |
out.append("#define TINYPY_H") |
out.extend([v.rstrip() for v in open_tinypy('tp.h','r')]) |
for fname in ['list.c','dict.c','misc.c','string.c','builtins.c', |
'gc.c','ops.c','vm.c','tp.c']: |
for line in open_tinypy(fname,'r'): |
line = line.rstrip() |
if not len(line): continue |
if line[0] == '/': continue |
if line[0] == ' ': continue |
if line[0] == '\t': continue |
if line[-1] != '{': continue |
if 'enum' in line: continue |
if '=' in line: continue |
if '#' in line: continue |
line = line.replace('{',';') |
|
# Do not include prototypes already defined earlier, or gcc will |
# warn about doubled prototypes in user code. |
if '(' in line: |
line2 = line[:line.find('(') + 1] |
got_already = False |
for already in out: |
if already.startswith(line2): |
got_already = True |
break |
if got_already: continue |
out.append(line) |
out.append("#endif") |
out.append('') |
dest = os.path.join(TOPDIR,'build','tinypy.h') |
print 'writing %s'%dest |
f = open(dest,'w') |
f.write('\n'.join(out)) |
f.close() |
|
# we leave all the tinypy.h stuff at the top so that |
# if someone wants to include tinypy.c they don't have to have |
# tinypy.h cluttering up their folder |
|
for mod in CORE: |
out.append("""extern unsigned char tp_%s[];"""%mod) |
|
for fname in ['list.c','dict.c','misc.c','string.c','builtins.c', |
'gc.c','ops.c','vm.c','tp.c','bc.c']: |
for line in open_tinypy(fname,'r'): |
line = line.rstrip() |
if line.find('#include "') != -1: continue |
out.append(line) |
out.append('') |
dest = os.path.join(TOPDIR,'build','tinypy.c') |
print 'writing %s'%dest |
f = open(dest,'w') |
f.write('\n'.join(out)) |
f.close() |
|
def py2bc(cmd,mod): |
src = '%s.py'%mod |
dest = '%s.tpc'%mod |
if CLEAN or not os.path.exists(dest) or os.stat(src).st_mtime > os.stat(dest).st_mtime: |
cmd = cmd.replace('$SRC',src) |
cmd = cmd.replace('$DEST',dest) |
do_cmd(cmd) |
else: |
print '#',dest,'is up to date' |
|
def build_gcc(): |
mods = CORE[:] |
do_chdir(os.path.join(TOPDIR,'tinypy')) |
if TEST: |
mods.append('tests') |
do_cmd("gcc $WFLAGS -g vmmain.c $FLAGS -lm -o vm") |
do_cmd('python tests.py $SYS') |
for mod in mods: |
py2bc('python py2bc.py $SRC $DEST',mod) |
else: |
for mod in mods: |
py2bc('python py2bc.py $SRC $DEST -nopos',mod) |
if BOOT: |
do_cmd('$VM tests.tpc $SYS') |
for mod in mods: py2bc('$VM py2bc.tpc $SRC $DEST',mod) |
build_bc() |
do_cmd("gcc $WFLAGS -g tpmain.c $FLAGS -lm -o tinypy") |
#second pass - builts optimized binaries and stuff |
if BOOT: |
do_cmd('$TINYPY tests.py $SYS') |
for mod in mods: py2bc('$TINYPY py2bc.py $SRC $DEST -nopos',mod) |
build_bc(True) |
if BOOT: |
do_cmd("gcc $WFLAGS -O2 tpmain.c $FLAGS -lm -o tinypy") |
do_cmd('$TINYPY tests.py $SYS') |
print("# OK - we'll try -O3 for extra speed ...") |
do_cmd("gcc $WFLAGS -O3 tpmain.c $FLAGS -lm -o tinypy") |
do_cmd('$TINYPY tests.py $SYS') |
do_cmd("gcc $WFLAGS -O3 mymain.c $FLAGS -lm -o ../build/tinypy") |
do_chdir('..') |
if TEST: |
test_mods(os.path.join('.','build','tinypy')+' $TESTS') |
print("# OK") |
|
def get_libs(): |
modules = os.listdir('modules') |
for m in modules[:]: |
if m not in sys.argv: modules.remove(m) |
global MODULES |
MODULES = modules |
|
def build_mymain(): |
src = os.path.join(TOPDIR,'tinypy','tpmain.c') |
out = open(src,'r').read() |
dest = os.path.join(TOPDIR,'tinypy','mymain.c') |
|
vs = [] |
for m in MODULES: |
vs.append('#include "../modules/%s/init.c"'%m) |
out = out.replace('/* INCLUDE */','\n'.join(vs)) |
|
vs = [] |
for m in MODULES: |
vs.append('%s_init(tp);'%m) |
out = out.replace('/* INIT */','\n'.join(vs)) |
|
f = open(dest,'w') |
f.write(out) |
f.close() |
return True |
|
def test_mods(cmd): |
for m in MODULES: |
tests = os.path.join('modules',m,'tests.py') |
if not os.path.exists(tests): continue |
cmd = cmd.replace('$TESTS',tests) |
do_cmd(cmd) |
|
def build_vs(): |
# How to compile on windows with Visual Studio: |
# Call the batch script that sets environement variables for Visual Studio and |
# then run this script. |
# For VS 2005 the script is: |
# "C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\vsvars32.bat" |
# For VS 2008: "C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\vsvars32.bat" |
# Doesn't compile with vc6 (no variadic macros) |
# Note: /MD option causes to dynamically link with msvcrt80.dll. This dramatically |
# reduces size (for vm.exe 159k => 49k). Downside is that msvcrt80.dll must be |
# present on the system (and not all windows machine have it). You can either re-distribute |
# msvcrt80.dll or statically link with C runtime by changing /MD to /MT. |
mods = CORE[:]; mods.append('tests') |
os.chdir(os.path.join(TOPDIR,'tinypy')) |
do_cmd('cl vmmain.c /D "inline=" /Od /Zi /MD /Fdvm.pdb /Fmvm.map /Fevm.exe') |
do_cmd('python tests.py -win') |
for mod in mods: do_cmd('python py2bc.py %s.py %s.tpc'%(mod,mod)) |
do_cmd('vm.exe tests.tpc -win') |
for mod in mods: do_cmd('vm.exe py2bc.tpc %s.py %s.tpc'%(mod,mod)) |
build_bc() |
do_cmd('cl /Od tpmain.c /D "inline=" /Zi /MD /Fdtinypy.pdb /Fmtinypy.map /Fetinypy.exe') |
#second pass - builts optimized binaries and stuff |
do_cmd('tinypy.exe tests.py -win') |
for mod in mods: do_cmd('tinypy.exe py2bc.py %s.py %s.tpc -nopos'%(mod,mod)) |
build_bc(True) |
do_cmd('cl /Os vmmain.c /D "inline=__inline" /D "NDEBUG" /Gy /GL /Zi /MD /Fdvm.pdb /Fmvm.map /Fevm.exe /link /opt:ref /opt:icf') |
do_cmd('cl /Os tpmain.c /D "inline=__inline" /D "NDEBUG" /Gy /GL /Zi /MD /Fdtinypy.pdb /Fmtinypy.map /Fetinypy.exe /link /opt:ref,icf /OPT:NOWIN98') |
do_cmd("tinypy.exe tests.py -win") |
do_cmd("dir *.exe") |
|
def shrink(fname): |
f = open(fname,'r'); lines = f.readlines(); f.close() |
out = [] |
fixes = [ |
'vm','gc','params','STR', |
'int','float','return','free','delete','init', |
'abs','round','system','pow','div','raise','hash','index','printf','main'] |
passing = False |
for line in lines: |
#quit if we've already converted |
if '\t' in line: return ''.join(lines) |
|
#change " " into "\t" and remove blank lines |
if len(line.strip()) == 0: continue |
line = line.rstrip() |
l1,l2 = len(line),len(line.lstrip()) |
line = "\t"*((l1-l2)/4)+line.lstrip() |
|
#remove comments |
if '.c' in fname or '.h' in fname: |
#start block comment |
if line.strip()[:2] == '/*': |
passing = True; |
#end block comment |
if line.strip()[-2:] == '*/': |
passing = False; |
continue |
#skip lines inside block comments |
if passing: |
continue |
if '.py' in fname: |
if line.strip()[:1] == '#': continue |
|
#remove the "namespace penalty" from tinypy ... |
for name in fixes: |
line = line.replace('TP_'+name,'t'+name) |
line = line.replace('tp_'+name,'t'+name) |
line = line.replace('TP_','') |
line = line.replace('tp_','') |
|
out.append(line) |
return '\n'.join(out)+'\n' |
|
def chksize(): |
t1,t2 = 0,0 |
for fname in [ |
'tokenize.py','parse.py','encode.py','py2bc.py', |
'tp.h','list.c','dict.c','misc.c','string.c','builtins.c', |
'gc.c','ops.c','vm.c','tp.c','tpmain.c', |
]: |
fname = os.path.join(TOPDIR,'tinypy',fname) |
f = open(fname,'r'); t1 += len(f.read()); f.close() |
txt = shrink(fname) |
t2 += len(txt) |
print "#",t1,t2,t2-65536 |
return t2 |
|
def build_64k(): |
for fname in [ |
'tokenize.py','parse.py','encode.py','py2bc.py', |
'tp.h','list.c','dict.c','misc.c','string.c','builtins.c', |
'gc.c','ops.c','vm.c','tp.c','tpmain.c', |
]: |
src = os.path.join(TOPDIR,'tinypy',fname) |
dest = os.path.join(TOPDIR,'build',fname) |
txt = shrink(src) |
f = open(dest,'w') |
f.write(txt) |
f.close() |
print '%s saved to %s'%(src,dest) |
|
if __name__ == '__main__': |
main() |