BuildCubeE/FillGaps

177 lines
6.5 KiB
Plaintext
Raw Normal View History

2018-11-05 23:44:15 +00:00
#!/usr/bin/env python3
# Script to insert AmphibianDNA binaries into the build tree
import argparse
import os
import os.path as path
import time
import shutil
import re
########################################################################
RE1 = re.compile(rb'^(\w+)\s*=\s*(.+)')
RE2 = re.compile(rb'{(\w+)}')
def extract_makefile_defines(makefile, seed={}):
makefile = makefile.replace(b'\r', b'\n') # tolerate all sorts of crud
vardict = dict(seed)
grabber = lambda m: vardict.get(m.group(1).decode('ascii'), '').encode('ascii')
for line in text.split(b'\n'):
m = RE1.match(line)
if m:
try:
left = m.group(1).decode('ascii')
right = RE2.sub(grabber, m.group(2)).decode('ascii')
vardict[left] = right
except UnicodeDecodeError:
pass
return vardict
########################################################################
# Argparse
defaultdir = path.dirname(path.abspath(__file__))
args = argparse.ArgumentParser(description='''
Copy this CubeE source tree into another folder, which could then be
packed into an HFS volume with MakeHFS
(https://pypi.org/project/machfs/) to be compiled with MPW on a real
Mac or emulator. Difficult parts of the build tree can be elegantly
replaced by dropping pre-built objects or binaries into the
AmphibianDNA folder. Every file in AmphibianDNA will be spliced into
the build system by rewriting the makefile rule that seems to target
it, or by direct copying if no rule is found. All necessary
BuildResults folders are also created.
''')
args.add_argument('-i', '--src', action='store', default=defaultdir, help='Override source (default: my parent directory)')
args.add_argument('-o', '--dest', action='store', default=None, help='Override destination (default: MacStoopid/)')
args = args.parse_args()
if args.dest is None: args.dest = path.join(args.src, 'MacStoopid')
########################################################################
print('Copying source tree', flush=True)
try:
shutil.rmtree(args.dest)
except FileNotFoundError:
pass
myignore = shutil.ignore_patterns('FillGaps', 'MacStoopid', '.*', '*.dmg', '*.dsk', '*.sh', '*.py')
# copy2 preserves mod times, which we need to eventually allow MPW Make to work right
shutil.copytree(args.src, args.dest, ignore=myignore, copy_function=shutil.copy2)
all_makefiles = [path.join(root, f) for root, dirs, files in os.walk(args.dest) for f in files if f.lower().endswith('.make')]
########################################################################
print('Creating build folders', flush=True) # I have better code for this!
main_makefiles = []
for mkfile in all_makefiles:
with open(path.join(args.dest, mkfile), 'rb') as f:
text = f.read()
defines = extract_makefile_defines(text)
if 'BuildDir' not in defines: continue
main_makefiles.append(mkfile)
for key, macpath in defines.items():
macpath = macpath.replace('"', '')
if key.endswith('Dir') and macpath.startswith('BuildResults:'):
nativepath = path.join(args.dest, *macpath.split(':')[:-1])
os.makedirs(nativepath, exist_ok=True)
########################################################################
print('Splicing amphibian DNA', flush=True) # Ugly, but keeps src tree clean
OVERDIR = 'AmphibianDNA'
try:
overs = [n for n in os.listdir(path.join(args.dest, OVERDIR)) if path.splitext(n)[1].lower() not in ('.idump', '.rdump')]
except FileNotFoundError:
overs = []
remaining = list(overs)
overs_re = '|'.join(re.escape(x) for x in overs)
overs_re = r'^[^#\s]*\b(Thing.lib)"?\s*ƒ\s*'.replace('Thing.lib', overs_re)
overs_re = re.compile(overs_re, re.IGNORECASE)
for f in all_makefiles:
mfile = open(f).read().split('\n')
havechanged = False
newmfile = []
idx = -1
while idx + 1 < len(mfile):
idx += 1
m = overs_re.match(mfile[idx])
if m:
thefile = m.group(1)
havechanged = True
newmfile.append('# Rule replaced at build time by ' + path.basename(__file__))
remaining = [x for x in remaining if x.upper() != thefile.upper()]
srcfile = '{Sources}%s:%s' % (OVERDIR, thefile)
newmfile.append(m.group(0) + srcfile)
newmfile.append('\tDuplicate -y {Deps} {Targ}')
lastidx = idx # how many "old" lines should be commented out?
if mfile[idx].endswith('∂'):
while lastidx + 1 < len(mfile) and mfile[lastidx + 1].endswith('∂'):
lastidx += 1 # capture continuations of first line
while lastidx + 1 < len(mfile) and mfile[lastidx + 1].startswith('\t'):
lastidx += 1 # capture build lines starting with tab
while idx <= lastidx:
newmfile.append('#\t' + mfile[idx])
idx += 1
else:
newmfile.append(mfile[idx])
if havechanged:
open(f, 'w').write('\n'.join(newmfile))
if remaining: # try to find where these override files with *no build rule* should go
found_locations = {k: [] for k in remaining}
overs_re = '|'.join(re.escape(x) for x in remaining)
overs_re = r'^[^#]*"({\w+}(?:\w+:)*)(Thing.lib)"'.replace('Thing.lib', overs_re)
overs_re = re.compile(overs_re, re.IGNORECASE)
for f in all_makefiles:
mfile = open(f).read().split('\n')
for line in mfile:
m = overs_re.match(line)
if m:
orig_name = next(x for x in remaining if x.upper() == m.group(2).upper())
found_loc = m.group(1)+m.group(2)
if found_loc.upper() not in (x.upper() for x in found_locations[orig_name]):
found_locations[orig_name].append(found_loc)
for f in main_makefiles:
with open(f, 'a') as fd:
fd.write('\n# Rules created at build time by %s\n' % path.basename(__file__))
for orig_name, found_locs in found_locations.items():
if len(found_locs) == 1:
remaining = [x for x in remaining if x != orig_name]
fd.write(found_locs[0])
fd.write(' ƒ {Sources}%s:%s\n' % (OVERDIR, orig_name))
fd.write('\tDuplicate -y {Deps} {Targ}\n')
diag = 'Successfully spliced: %d/%d' % (len(overs)-len(remaining), len(overs))
if remaining: diag += '; Failed: ' + ' '.join(remaining)
print(diag)
########################################################################
print('Suggestion: MakeHFS -s 256M -n MacStoopid -i MacStoopid/ MacStoopid.dmg')