-
-
Save jarikomppa/4c131f80aaf17d3f5a410b246b5b2805 to your computer and use it in GitHub Desktop.
using multiple processes now!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Sol's Stupidly Simple Build System v.2.1 | |
# (c) 2018 Jari Komppa http://iki.fi/sol | |
# | |
# This software is provided 'as-is', without any express or implied | |
# warranty. In no event will the authors be held liable for any damages | |
# arising from the use of this software. | |
# | |
# Permission is granted to anyone to use this software for any purpose, | |
# including commercial applications, and to alter it and redistribute it | |
# freely, subject to the following restrictions: | |
# | |
# 1. The origin of this software must not be misrepresented; you must not | |
# claim that you wrote the original software. If you use this software | |
# in a product, an acknowledgment in the product documentation would be | |
# appreciated but is not required. | |
# 2. Altered source versions must be plainly marked as such, and must not be | |
# misrepresented as being the original software. | |
# 3. This notice may not be removed or altered from any source distribution. | |
import os | |
import subprocess | |
# Add source files here. | |
# Special names: | |
# "*" is a sync point, causing all running processes to finish before | |
# continuing, so if you're generating files needed by later sources, | |
# add sync point before them. | |
# "+" is a literal shell command, like "+del *.bak" runs "del *.bak". | |
src = [ | |
"+cls", | |
"crt0.s", | |
"primdraw.c", | |
"data.c", | |
"drawstring.c", | |
"ingame.c", | |
"tour.c", | |
"heal.c", | |
"newcard.c", | |
"shop.c", | |
"app.c", | |
"tools.c" | |
] | |
# Add targets here. Note that if there's dependencies, | |
# you need the sync ("*") points. | |
product = [ | |
"crt0.ihx", | |
"*", | |
"engarde.tap", | |
"*", | |
"+speccy engarde.tap" | |
] | |
# Add file suffix based methods here: first param is the resulting | |
# suffix, second param is the command to execute. Tags are replaced | |
# before execution: %SRCFILE for source file, %DSTFILE for resulting | |
# file. %TARGETSUFFIX for lists of target files - so if you generate | |
# .obj files, %OBJ has list of all object files from src list. | |
methods = { | |
"c" : [ "rel", "..\\cc\\sdcc360\\bin\\sdcc -c -o %DSTFILE %SRCFILE -mz80 --no-std-crt0 --opt-code-speed --Werror --peep-asm --peep-return " ], | |
"s" : [ "rel", "..\\cc\\sdcc360\\bin\\sdasz80 -xlos -g %DSTFILE %SRCFILE" ], | |
"ihx" : [ "ihx", "..\\cc\\sdcc360\\bin\\sdcc -mz80 --no-std-crt0 --opt-code-speed --nostdlib --code-loc 0x6007 --data-loc 0x5b00 -Wl -b_HEADER=0x6000 %REL"], | |
"tap" : [ "tap", "..\\tools\\mackarel crt0.ihx %DSTFILE Engarde loader.scr -nosprestore -noei"] | |
} | |
# If you want to add tags, just add keys here, | |
# like "SDCC": "../sdcc350/bin/sdcc": | |
objectlists = {} | |
##################################################################### | |
##################################################################### | |
##################################################################### | |
class BuildTask: | |
def __init__(self, proc, dstfile): | |
self.proc = proc | |
self.dstfile = dstfile | |
procs = [] | |
changed = False | |
# If we want to track includes from .s files, we just need to | |
# make a variant of this.. | |
def scandeps_c(srcfile, reftime, ignore = None): | |
# Avoid infinite loops | |
if ignore is None: | |
ignore = set() | |
if srcfile not in ignore: | |
ignore.add(srcfile) | |
# It's ok if we don't find the file, we'll just skip it | |
if os.path.isfile(srcfile): | |
dtime = os.path.getmtime(srcfile) | |
if (dtime > reftime): | |
reftime = dtime | |
# Scan for includes and recurse | |
inc = [] | |
with open(srcfile, "r") as f: | |
for line in f: | |
if len(line.strip()) > 0 and line.strip()[0] == "#" and "include" in line and '"' in line: | |
inc.append(line) | |
for x in inc: | |
dtime = scandeps_c(x[x.find('"')+1:x.rfind('"')], reftime, ignore) | |
if (dtime > reftime): | |
reftime = dtime | |
# Return the biggest timestamp found | |
return reftime | |
def scandeps(srcfile, reftime): | |
if srcfile[srcfile.rfind(".")+1:] in "c h cpp hpp".split(): | |
return scandeps_c(srcfile, reftime) | |
return reftime | |
def agecheck(srcfile, dstfile): | |
if not os.path.isfile(dstfile): | |
return True | |
srctime = scandeps(srcfile, os.path.getmtime(srcfile)) | |
dsttime = os.path.getmtime(dstfile) | |
if (srctime > dsttime): | |
return True | |
return False | |
def build(srcfile, dstfile, method): | |
global changed | |
print "building " + dstfile | |
if os.path.isfile(dstfile): | |
os.remove(dstfile) | |
cmd = method.replace("%SRCFILE", srcfile).replace("%DSTFILE", dstfile) | |
for key, value in objectlists.iteritems(): | |
cmd = cmd.replace("%"+key.upper(), value) | |
procs.append(BuildTask(subprocess.Popen(cmd, shell=True, bufsize=8192, stdout=subprocess.PIPE, stderr=subprocess.STDOUT), dstfile)) | |
changed = True | |
def sync(): | |
global procs | |
failboat = False | |
livecount = 0 | |
for x in procs: | |
if x.proc.poll() is None: | |
livecount += 1 | |
# If more than one process is still running, print info. | |
if livecount > 1: | |
print "Waiting for", len(procs), "processes to finish.." | |
for x in procs: | |
(a,b) = x.proc.communicate() | |
# avoid printing empty lines.. | |
if a != "": | |
print a | |
if not os.path.isfile(x.dstfile): | |
print "Error: " + x.dstfile + " not generated." | |
failboat = True | |
procs = [] | |
if failboat: | |
exit() | |
##################################################################### | |
##################################################################### | |
##################################################################### | |
# scan all source files, build if changed | |
for x in src: | |
if x[0] == "*": | |
sync() | |
else: | |
if x[0] == "+": | |
os.system(x[1:]) | |
else: | |
if not os.path.isfile(x): | |
print x + " does not exist." | |
exit() | |
fname = x[:x.rfind(".")] | |
suffix = x[x.rfind(".")+1:] | |
if suffix not in methods: | |
print "Don't know how to handle files of type " + suffix + " (in " + x + ")." | |
exit() | |
if methods[suffix][0] not in objectlists: | |
objectlists[methods[suffix][0]] = fname + "." + methods[suffix][0]; | |
else: | |
objectlists[methods[suffix][0]] += " " + fname + "." + methods[suffix][0]; | |
if agecheck(x, fname + "." + methods[suffix][0]): | |
build(x, fname + "." + methods[suffix][0], methods[suffix][1]) | |
# Sync before moving to product phase | |
sync() | |
# If targets don't exist, build them | |
for x in product: | |
if x[0] not in "*+" and not os.path.isfile(x): | |
changed = True | |
# if source files have changed, or targets don't change, link | |
if changed: | |
for x in product: | |
if x[0] == "*": | |
sync() | |
else: | |
if x[0] == "+": | |
os.system(x[1:]) | |
else: | |
fname = x[:x.rfind(".")] | |
suffix = x[x.rfind(".")+1:] | |
if suffix not in methods: | |
print "Don't know how to handle files of type " + suffix + " (in " + x + ")." | |
build(x, fname + "." + methods[suffix][0], methods[suffix][1]) | |
else: | |
print "Nothing to do." | |
exit() | |
# Final sync just to be sure | |
sync() | |
print "All done." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment