Files
2018-07-17 14:15:37 +02:00

351 lines
10 KiB
Python

#!/usr/bin/python2
import os
import sys
import time
import errno
from dulwich.repo import Repo
from dulwich.diff_tree import tree_changes
import shutil
import subprocess
SOURCE_FOLDER = "source"
SOURCE_REPO = "git@github.com:BlindMindStudios/StarRuler2.git"
DEST_FOLDER = "dest"
DEST_REPO = "git@github.com:BlindMindStudios/SR2Beta.git"
LOG_FOLDER = "log"
CURRENT_COMMIT = ""
Source = None
Dest = None
SOURCE_DIRS = ["source/game/", "source/as_addons/",
"source/os/", "source/sound/", "source/util/",
"source/network/"]
SOURCE_LINUX = SOURCE_DIRS + ["source/linux/"]
SOURCE_WINDOWS = SOURCE_DIRS + [
"source/msvc10/", "source/angelscript/",
"source/glfw/", "source/libpng/", "source/freetype2/",
"source/linux/"]
class BuildStep(object):
@classmethod
def neededBy(cls, path):
return False
def execute(self):
pass
def finished(self):
return True
def finalize(self):
pass
class BuildGame32(BuildStep):
@classmethod
def neededBy(cls, path):
for d in SOURCE_LINUX:
if path.startswith(d):
return True
return False
def execute(self):
#Build 32-bit version
self.clean32 = subprocess.Popen(
["ARCH=32 make -f source/linux/Makefile clean_code"],
shell=True, cwd=SOURCE_FOLDER)
self.clean32.wait()
self.build32 = subprocess.Popen(
["ARCH=32 make -f source/linux/Makefile -j3 version compile"],
shell=True, cwd=SOURCE_FOLDER,
stdout=open(os.path.join(LOG_FOLDER, "build32_"+CURRENT_COMMIT+".log"), "w"),
stderr=subprocess.STDOUT)
def finished(self):
if self.build32.poll() == None:
return False
return True
def finalize(self):
if self.build32.returncode == 0:
copy_stage_folder("bin/lin32")
class BuildGame64(BuildStep):
@classmethod
def neededBy(cls, path):
for d in SOURCE_LINUX:
if path.startswith(d):
return True
return False
def execute(self):
self.clean64 = subprocess.Popen(
["ARCH=64 make -f source/linux/Makefile clean_code"],
shell=True, cwd=SOURCE_FOLDER)
self.clean64.wait()
self.build64 = subprocess.Popen(
["ARCH=64 make -f source/linux/Makefile -j3 version compile"],
shell=True, cwd=SOURCE_FOLDER,
stdout=open(os.path.join(LOG_FOLDER, "build64_"+CURRENT_COMMIT+".log"), "w"),
stderr=subprocess.STDOUT)
def finished(self):
if self.build64.poll() == None:
return False
return True
def finalize(self):
if self.build64.returncode == 0:
copy_stage_folder("bin/lin64")
class BuildAS32(BuildStep):
@classmethod
def neededBy(cls, path):
if path.startswith("source/angelscript/"):
return True
return False
def execute(self):
self.build32 = subprocess.Popen(
["ARCH=32 make -f source/linux/Makefile -j3 angelscript"],
shell=True, cwd=SOURCE_FOLDER,
stdout=open(os.path.join(LOG_FOLDER, "as32_"+CURRENT_COMMIT+".log"), "w"),
stderr=subprocess.STDOUT)
self.build32.wait();
class BuildAS64(BuildStep):
@classmethod
def neededBy(cls, path):
if path.startswith("source/angelscript/"):
return True
return False
def execute(self):
self.build64 = subprocess.Popen(
["ARCH=64 make -f source/linux/Makefile -j3 angelscript"],
shell=True, cwd=SOURCE_FOLDER,
stdout=open(os.path.join(LOG_FOLDER, "as64_"+CURRENT_COMMIT+".log"), "w"),
stderr=subprocess.STDOUT)
self.build64.wait();
class BuildWindows(BuildStep):
def __init__(self):
self.running = False
@classmethod
def neededBy(cls, path):
for d in SOURCE_WINDOWS:
if path.startswith(d):
return True
return False
def execute(self):
pass
def finished(self):
if not self.running:
#Create marker file for windows build
with open(os.path.join(SOURCE_FOLDER, "WIN_COMPILE"), "w") as f:
f.write("BUILD");
self.running = True
return False
return not os.path.exists(os.path.join(SOURCE_FOLDER, "WIN_COMPILE"))
def finalize(self):
#Copy over symbols
self.syncsymbols = subprocess.Popen(
["rsync -tavz symbols/ bms@glacicle.org:starruler2.com/symbols/"],
shell=True)
#Stage all the files needed
if os.path.exists(os.path.join(DEST_FOLDER, "Star Ruler 2.exe")):
Dest.stage("Star Ruler 2.exe")
for f in os.listdir(os.path.join(DEST_FOLDER, "bin/win32")):
if f.endswith(".exe") or f.endswith(".dll"):
Dest.stage("bin/win32/"+f)
for f in os.listdir(os.path.join(DEST_FOLDER, "bin/win64")):
if f.endswith(".exe") or f.endswith(".dll"):
Dest.stage("bin/win64/"+f)
#Rename the log to indicate the commit
if os.path.exists(os.path.join(LOG_FOLDER, "msvc32_build.log")):
os.rename(os.path.join(LOG_FOLDER, "msvc32_build.log"),
os.path.join(LOG_FOLDER, "msvc32_"+CURRENT_COMMIT+".log"))
if os.path.exists(os.path.join(LOG_FOLDER, "msvc64_build.log")):
os.rename(os.path.join(LOG_FOLDER, "msvc64_build.log"),
os.path.join(LOG_FOLDER, "msvc64_"+CURRENT_COMMIT+".log"))
BUILD_STEPS = [BuildAS32, BuildAS64, BuildGame32, BuildGame64, BuildWindows]
def publish_file(path):
if path.startswith("source/"):
return False
return True
def copy(path):
folder = os.path.dirname(path)
#Make the target folder
try:
os.makedirs(os.path.join(DEST_FOLDER, folder))
except OSError as exc:
pass
#Copy the file
shutil.copy2(os.path.join(SOURCE_FOLDER, path),
os.path.join(DEST_FOLDER, path))
def copy_stage_folder(path):
folder = os.path.join(SOURCE_FOLDER, path)
for root, folders, files in os.walk(folder):
for f in files:
fpath = os.path.join(root[len(SOURCE_FOLDER)+1:], f)
copy(fpath)
Dest.stage(fpath)
def setup():
#Clone the repositories first
#Note: This assumes source and dest are in a consistent state,
#which is something that needs to be guaranteed manually
if not os.path.exists(SOURCE_FOLDER):
p = subprocess.Popen(["git", "clone", SOURCE_REPO, SOURCE_FOLDER])
p.wait()
if not os.path.exists(DEST_FOLDER):
p = subprocess.Popen(["git", "clone", DEST_REPO, DEST_FOLDER])
p.wait()
if not os.path.exists(LOG_FOLDER):
os.mkdir(LOG_FOLDER)
#Prepare accessors
global Source
Source = Repo(SOURCE_FOLDER)
global Dest
Dest = Repo(DEST_FOLDER)
def updateRepo(folder):
p = subprocess.Popen(["git", "fetch", "origin", "master"], cwd=folder)
p.wait()
def pushRepo(folder):
p = subprocess.Popen(["git", "push", "origin", "master"], cwd=folder)
p.wait()
def listCommits(repo, front, back, l):
#Find all parent commits
if not isinstance(back, set):
stack = [repo.commit(back)]
back = set()
while stack:
c = stack.pop()
if c.id not in back:
back.add(c.id)
for parent in c._get_parents():
stack.append(repo.commit(parent))
#Make sure it isn't already done
if front in back:
return
#Check this commit
commit = repo.commit(front)
l.append(commit)
for parent in commit._get_parents():
listCommits(repo, parent, back, l)
def switchTo(folder, commit):
p = subprocess.Popen(["git", "reset", "--hard", commit], cwd=folder)
p.wait()
def main():
setup()
while True:
#Try to update the repo
updateRepo(SOURCE_FOLDER)
HEAD = Source.head();
FETCH_HEAD = Source.ref("FETCH_HEAD")
#Check if there are any updates
if HEAD == FETCH_HEAD:
time.sleep(60)
continue
#Build the tree of commits to build
commits = []
listCommits(Source, FETCH_HEAD, HEAD, commits)
commits.reverse()
base_tree = Source.commit(HEAD).tree
for c in commits:
global CURRENT_COMMIT
CURRENT_COMMIT = c.id
switchTo(SOURCE_FOLDER, c.id)
print("Executing commit "+c.id)
#Iterate through the difference tree
diff = tree_changes(Source, base_tree, c.tree)
steps = [False for x in BUILD_STEPS]
for d in diff:
#Handle deletes
if d.type == 'delete':
fname = d.old.path
if publish_file(fname):
os.remove(os.path.join(DEST_FOLDER, fname))
Dest.stage(fname)
continue
#Copy over all the files
fname = d.new.path
if not fname:
continue
if publish_file(fname):
copy(fname)
Dest.stage(fname)
for i, st in enumerate(BUILD_STEPS):
if st.neededBy(fname):
steps[i] = True
#Execute all build steps
cursteps = []
for i, needed in enumerate(steps):
if not needed:
continue
step = BUILD_STEPS[i]()
step.execute()
while not step.finished():
time.sleep(1)
cursteps.append(step)
#Finalize all build steps
for step in cursteps:
step.finalize()
#Set up the commit in the new repo
Dest.do_commit(
message=c.message+"\n\nOriginal Commit: BlindMindStudios/StarRuler2@"+c.id,
committer=c.committer,
author=c.author,
commit_timestamp=c._commit_time,
commit_timezone=c._commit_timezone,
author_timestamp=c._author_time,
author_timezone=c._author_timezone)
#Set up for next
base_tree = c.tree
#Push this individual commit
pushRepo(DEST_FOLDER)
if __name__ == '__main__':
main()
# vim: ff=unix sw=4 et: