mirror of
https://github.com/elliotnunn/BuildCubeE.git
synced 2024-09-27 04:55:00 +00:00
177 lines
6.5 KiB
Plaintext
177 lines
6.5 KiB
Plaintext
|
#!/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')
|