arewefastyet/slave/build.py

393 строки
14 KiB
Python

import sys
import json
import urllib2
import urllib
import re
import os
import shutil
import socket
import utils
import puller
import platform
import subprocess
import stat
from utils import Run
import tarfile
import zipfile
socket.setdefaulttimeout(120)
class Environment(object):
def __init__(self):
self.env_ = os.environ.copy()
self.add("CC", "gcc")
self.add("CXX", "g++")
self.add("LINK", "g++")
self.ccoption = []
def add(self, name, data):
self.env_[name] = data
def remove(self, name):
del self.env_[name]
def addCCOption(self, option):
self.ccoption.append(option)
def get(self):
env = self.env_.copy()
if len(self.ccoption) > 0:
env["CC"] += " " + " ".join(self.ccoption)
env["CXX"] += " " + " ".join(self.ccoption)
return env
class Builder(object):
def __init__(self, config, folder):
self.env = Environment()
self.config = config
self.folder = folder
if platform.system() == "Darwin":
self.installClang()
self.env.add("CC", os.path.abspath("clang-3.8.0/bin/clang"))
self.env.add("CXX", os.path.abspath("clang-3.8.0/bin/clang++"))
self.env.add("LINK", os.path.abspath("clang-3.8.0/bin/clang++"))
def installClang(self):
# The standard clang version on mac is outdated.
# Retrieve a better one.
if os.path.exists("clang-3.8.0"):
return
urllib.urlretrieve("http://llvm.org/releases/3.8.0/clang+llvm-3.8.0-x86_64-apple-darwin.tar.xz", "./clang-3.8.0.tar.xz")
utils.run_realtime(["tar", "xf", "clang-3.8.0.tar.xz"])
shutil.move("clang+llvm-3.8.0-x86_64-apple-darwin", "clang-3.8.0")
os.unlink("clang-3.8.0.tar.xz")
def installNdk(self):
# Retrieve the ndk needed to build an android app.
# Using version 12, since that still supports gcc (Couldn't get clang working).
assert platform.system() == "Linux"
assert platform.architecture()[0] == "64bit"
with utils.FolderChanger(self.folder):
if os.path.exists("android-ndk-r12"):
print "already installed: ", os.path.join(self.folder, "android-ndk-r12")
return
print "installing"
urllib.urlretrieve("https://dl.google.com/android/repository/android-ndk-r12-linux-x86_64.zip", "./android-ndk.zip")
Run(["unzip", "android-ndk.zip"])
def unlinkBinary(self):
try:
os.unlink(self.binary())
except:
pass
def unlinkObjdir(self):
try:
shutil.rmtree(self.objdir())
except:
pass
def successfullyBuild(self):
return os.path.isfile(self.binary())
def reconf(self):
return
def build(self, puller):
self.unlinkBinary()
try:
self.make()
except:
pass
if not self.successfullyBuild():
self.reconf()
self.make()
assert self.successfullyBuild()
info = self.retrieveInfo()
info["revision"] = puller.identify()
# Default 'shell' to True only if it isn't set yet!
if 'shell' not in info:
info["shell"] = True
info["binary"] = os.path.abspath(self.binary())
fp = open(os.path.join(self.folder, "info.json"), "w")
json.dump(info, fp)
fp.close()
class MozillaBuilder(Builder):
def __init__(self, config, folder):
super(MozillaBuilder, self).__init__(config, folder);
if platform.architecture()[0] == "64bit" and self.config == "32bit":
self.env.add("AR",'ar')
self.env.add("CROSS_COMPILE", '1')
self.env.addCCOption("-m32")
def retrieveInfo(self):
info = {}
info["engine_type"] = "firefox"
if self.config.startswith("android"):
info["platform"] = "android"
return info
def objdir(self):
return os.path.join(self.folder, 'js', 'src', 'Opt')
def binary(self):
return os.path.join(self.objdir(), 'dist', 'bin', 'js')
def reconf(self):
# Step 0. install ndk if needed.
if self.config.startswith("android"):
self.env.remove("CC")
self.env.remove("CXX")
self.env.remove("LINK")
self.installNdk()
# Step 1. autoconf.
with utils.FolderChanger(os.path.join(self.folder, 'js', 'src')):
if platform.system() == "Darwin":
utils.run_realtime("autoconf213", shell=True)
elif platform.system() == "Linux":
utils.run_realtime("autoconf2.13", shell=True)
elif platform.system() == "Windows":
utils.run_realtime("autoconf-2.13", shell=True)
# Step 2. configure
if os.path.exists(os.path.join(self.folder, 'js', 'src', 'Opt')):
shutil.rmtree(os.path.join(self.folder, 'js', 'src', 'Opt'))
os.mkdir(os.path.join(self.folder, 'js', 'src', 'Opt'))
args = ['--enable-optimize', '--disable-debug']
if self.config == "android":
args.append("--target=arm-linux-androideabi")
args.append("--with-android-ndk="+os.path.abspath(self.folder)+"/android-ndk-r12/")
args.append("--with-android-version=24")
args.append("--enable-pie")
if self.config == "android64":
args.append("--target=aarch64-linux-androideabi")
args.append("--with-android-ndk="+os.path.abspath(self.folder)+"/android-ndk-r12/")
args.append("--with-android-version=24")
args.append("--enable-pie")
if platform.architecture()[0] == "64bit" and self.config == "32bit":
if platform.system() == "Darwin":
args.append("--target=i686-apple-darwin10.0.0")
elif platform.system() == "Linux":
args.append("--target=i686-pc-linux-gnu")
else:
assert False
with utils.FolderChanger(os.path.join(self.folder, 'js', 'src', 'Opt')):
utils.Run(['../configure'] + args, self.env.get())
return True
def make(self):
if not os.path.exists(os.path.join(self.folder, 'js', 'src', 'Opt')):
return
utils.run_realtime("make -j6 -C " + os.path.join(self.folder, 'js', 'src', 'Opt'), shell=True)
class WebkitBuilder(Builder):
def retrieveInfo(self):
info = {}
info["engine_type"] = "webkit"
return info
def patch(self):
with utils.FolderChanger(self.folder):
# Hack 1: Remove reporting errors for warnings that currently are present.
Run(["sed","-i.bac","s/GCC_TREAT_WARNINGS_AS_ERRORS = YES;/GCC_TREAT_WARNINGS_AS_ERRORS=NO;/","Source/JavaScriptCore/Configurations/Base.xcconfig"])
Run(["sed","-i.bac","s/GCC_TREAT_WARNINGS_AS_ERRORS = YES;/GCC_TREAT_WARNINGS_AS_ERRORS=NO;/","Source/bmalloc/Configurations/Base.xcconfig"])
Run(["sed","-i.bac","s/GCC_TREAT_WARNINGS_AS_ERRORS = YES;/GCC_TREAT_WARNINGS_AS_ERRORS=NO;/","Source/WTF/Configurations/Base.xcconfig"])
Run(["sed","-i.bac","s/std::numeric_limits<unsigned char>::max()/255/","Source/bmalloc/bmalloc/SmallLine.h"])
#Run(["sed","-i.bac","s/std::numeric_limits<unsigned char>::max()/255/","Source/bmalloc/bmalloc/SmallRun.h"])
# Hack 2: This check fails currently. Disable checking to still have a build.
os.remove("Tools/Scripts/check-for-weak-vtables-and-externals")
def clean(self):
with utils.FolderChanger(self.folder):
Run(["svn","revert","Tools/Scripts/check-for-weak-vtables-and-externals"])
Run(["svn","revert","Source/JavaScriptCore/Configurations/Base.xcconfig"])
Run(["svn","revert","Source/bmalloc/Configurations/Base.xcconfig"])
Run(["svn","revert","Source/WTF/Configurations/Base.xcconfig"])
Run(["svn","revert","Source/bmalloc/bmalloc/SmallLine.h"])
#Run(["svn","revert","Source/bmalloc/bmalloc/SmallPage.h"])
def make(self):
try:
self.patch()
with utils.FolderChanger(os.path.join(self.folder, 'Tools', 'Scripts')):
args = ['/usr/bin/perl', 'build-jsc']
if self.config == '32bit':
args += ['--32-bit']
Run(args, self.env.get())
finally:
self.clean()
Run(["install_name_tool", "-change", "/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/JavaScriptCore", self.objdir()+"/JavaScriptCore.framework/JavaScriptCore", self.objdir() + "/jsc"])
def objdir(self):
return os.path.join(self.folder, 'WebKitBuild', 'Release')
def binary(self):
return os.path.join(self.objdir(), 'jsc')
class V8Builder(Builder):
def __init__(self, config, folder):
super(V8Builder, self).__init__(config, folder)
self.env.add("PATH", os.path.realpath(os.path.join(self.folder, 'depot_tools'))+":"+self.env.get()["PATH"])
self.env.remove("CC")
self.env.remove("CXX")
self.env.remove("LINK")
if self.config.startswith("android"):
if "target_os = ['android']" not in open(folder + '/.gclient').read():
with open(folder + "/.gclient", "a") as myfile:
myfile.write("target_os = ['android']")
def retrieveInfo(self):
info = {}
info["engine_type"] = "chrome"
info["args"] = ['--expose-gc']
if self.config == "android":
info["platform"] = "android"
return info
def make(self):
if self.config == "android":
objdir = os.path.realpath(self.objdir())
if not os.path.isdir(objdir):
os.mkdir(objdir)
with utils.FolderChanger(os.path.join(self.folder, 'v8')):
config = [
'is_component_build = false',
'is_debug = false',
'symbol_level = 1',
'target_cpu = "arm"',
'target_os = "android"',
'v8_android_log_stdout = true',
'v8_test_isolation_mode = "prepare"',
]
args = 'gn gen '+objdir+' --args=\''+" ".join(config)+'\''
Run(args, self.env.get(), shell=True)
Run(["ninja", "-C", objdir], self.env.get())
return
args = ['make', '-j6']
if self.config == '32bit':
args += ['ia32.release']
elif self.config == '64bit':
args += ['x64.release']
else:
assert True
with utils.FolderChanger(os.path.join(self.folder, 'v8')):
Run(args, self.env.get())
def objdir(self):
if self.config == 'android':
return os.path.join(self.folder, 'v8', 'out', 'android_arm.release')
if self.config == '64bit':
return os.path.join(self.folder, 'v8', 'out', 'x64.release')
elif self.config == '32bit':
return os.path.join(self.folder, 'v8', 'out', 'ia32.release')
else:
assert False
def binary(self):
return os.path.join(self.objdir(), 'd8')
class ServoBuilder(Builder):
def __init__(self, config, folder):
super(ServoBuilder, self).__init__(config, folder);
# Some other config here
def retrieveInfo(self):
info = {}
info["engine_type"] = "servo"
info['shell'] = False
return info
def objdir(self):
return os.path.join(self.folder, 'target')
def binary(self):
return os.path.join(self.objdir(), 'release', 'servo')
def make(self):
with utils.FolderChanger(self.folder):
args = [os.path.join('.', 'mach'), 'build' ,'--release']
Run(args, self.env.get())
def getBuilder(config, path):
# fingerprint the known builders
if os.path.exists(os.path.join(path, "js", "src")):
return MozillaBuilder(config, path)
if os.path.exists(os.path.join(path, "Source", "JavaScriptCore")):
return WebkitBuilder(config, path)
if os.path.exists(os.path.join(path, "v8", "LICENSE.v8")):
return V8Builder(config, path)
if os.path.exists(os.path.join(path, "components", "servo")):
return ServoBuilder(config, path)
raise Exception("Unknown builder")
if __name__ == "__main__":
from optparse import OptionParser
parser = OptionParser(usage="usage: %prog [options]")
parser.add_option("-s", "--source", dest="repo",
help="The url of the repo to fetch or one of the known repos name. (mozilla, v8 and webkit are supported.)", default='mozilla')
parser.add_option("-r", "--rev", dest="revision",
help="Force this revision to get build")
parser.add_option("-o", "--output", dest="output",
help="download to DIR, default=output/", metavar="DIR", default='output')
parser.add_option("-c", "--config", dest="config",
help="auto, 32bit, 64bit, android, android64", default='auto')
parser.add_option("-f", "--force", dest="force", action="store_true", default=False,
help="Force runs even without source changes")
(options, args) = parser.parse_args()
if options.repo is None:
print "Please provide the source repository to pull"
exit()
if not options.output.endswith("/"):
options.output += "/"
if options.config not in ["auto", "32bit", "64bit", "android", "android64"]:
print "Please provide a valid config"
exit()
if options.config == "auto":
options.config, _ = platform.architecture()
if options.config == "64bit" and platform.architecture()[0] == "32bit":
print "Cannot compile a 64bit binary on 32bit architecture"
exit()
puller = puller.getPuller(options.repo, options.output)
puller.update(options.revision)
builder = getBuilder(options.config, options.output)
if options.force:
builder.unlinkObjdir()
builder.build(puller)