blob: 74c0c19a386baead6c04451a7e856a651a635dc9 [file] [log] [blame]
import java.nio.file.Paths
import org.gradle.internal.logging.text.StyledTextOutput;
import org.gradle.internal.logging.text.StyledTextOutputFactory;
import static org.gradle.internal.logging.text.StyledTextOutput.Style;
import org.gradle.internal.os.OperatingSystem;
import org.gradle.api.Project;
defaultTasks 'cleanPath', 'archiveZip'
def platformString() {
if (OperatingSystem.current().isMacOsX())
return "mac";
else if (OperatingSystem.current().isLinux())
return "linux-x86";
else
throw new GradleException("Only MacOsX and Linux are supported")
}
def protobufInstallDir() {
return new File("$projectDir/third_party/protobuf-3.0.0/install/" + platformString()).getPath()
}
def joinPath(String first, String... more) {
return Paths.get(first, more).toString()
}
task prepare_proto_before {
def protocBinDir = protobufInstallDir() + "/bin"
doLast {
// Install python-protobuf
exec {
workingDir "./third_party/protobuf-3.0.0/python"
environment 'PATH', "$protocBinDir:${environment.PATH}"
commandLine "python", "setup.py", "install", "--user"
}
// Generate nano-pb requirements
exec {
workingDir '../external/nanopb-c/generator/proto'
environment 'PATH', "$protocBinDir:${environment.PATH}"
commandLine 'make'
}
}
}
task prepare_proto(dependsOn: prepare_proto_before) {
doLast {
exec {
commandLine "python"
args = ["ab_info.py"]
}
}
}
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
}
}
allprojects {
buildDir = getOutPath()
repositories {
google()
jcenter()
}
}
abstract class Toolchain {
protected Project project_;
protected String androidVersion_;
protected String ndkVersion_;
abstract String getAndroidNDKPath();
abstract String getCMakePath();
abstract String getNinjaPath();
public String getAndroidVersion() { return androidVersion_; }
public String getNdkVersion() { return ndkVersion_; }
public String getBuildKey(String arch, String stl) {
return arch + '_SDK' + androidVersion_ + "_NDK" + ndkVersion_ + '_' + sanitize(stl)
}
protected String getNdkVersionFromPropertiesFile() {
def f = new File(getAndroidNDKPath(), 'source.properties')
if (!f.exists()) {
println "Warning: can't get NDK version"
return "UNKNOWN"
}
else {
Properties props = new Properties()
f.withInputStream {
props.load(it)
}
def ver = props."Pkg.Revision"
def parts = ver.findAll("[^.]+")
if (parts.size()>1)
return parts[0] + "." + parts[1]
else
return parts[0]
}
}
private String sanitize(String s) {
return s.replaceAll(/[+]/, "p")
}
}
class SpecificToolchain extends Toolchain {
SpecificToolchain(Project project, String androidVersion, String ndkVersion) {
project_ = project
androidVersion_ = androidVersion
ndkVersion_ = ndkVersion
}
public String getAndroidNDKPath() {
return new File("${project_.projectDir}/../prebuilts/ndk/" + ndkVersion_).getPath()
}
public String getCMakePath() {
return new File("${project_.projectDir}/../prebuilts/cmake/"
+ project_.platformString() + "/bin/cmake").getPath()
}
public String getNinjaPath() {
return new File("${project_.projectDir}/../prebuilts/cmake/"
+ project_.platformString() + "/bin/ninja").getPath()
}
}
class LocalToolchain extends Toolchain {
String sdkPath_;
String ndkPath_;
String cmakePath_;
String ninjaPath_;
String adbPath_;
LocalToolchain(Project project, String androidVersion) {
project_ = project
androidVersion_ = androidVersion
sdkPath_ = System.getenv("ANDROID_HOME")
if(sdkPath_ == null) {
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
sdkPath_ = properties.getProperty('sdk.dir')
}
if(sdkPath_ == null)
throw new GradleException('Must set ANDROID_HOME or sdk.dir in local.properties')
ndkPath_ = System.getenv("ANDROID_NDK")
if(ndkPath_ == null) {
ndkPath_ = project_.joinPath(sdkPath_, 'ndk-bundle')
if (!project_.file(ndkPath_).exists())
throw new GradleException('No NDK found in SDK: must set ANDROID_NDK')
}
ndkVersion_ = getNdkVersionFromPropertiesFile()
cmakePath_ = findCMakeTool("CMAKE", "cmake")
ninjaPath_ = findCMakeTool("NINJA", "ninja")
adbPath_ = project_.joinPath(sdkPath_, 'platform-tools', 'adb')
}
String getAndroidNDKPath() {
return ndkPath_;
}
String getCMakePath() {
return cmakePath_;
}
String getNinjaPath() {
return ninjaPath_;
}
String getAdbPath() {
return adbPath_;
}
String findCMakeTool(String envVar, String name) {
def tool = System.getenv(envVar);
if (tool) {
return tool;
}
def osname = OperatingSystem.current().isWindows() ? name + '.exe' : name
def tools = project_.fileTree( dir: project_.joinPath(sdkPath_, 'cmake'),
include: ['**/bin/'+osname] )
if (tools==null || tools.size() == 0) {
throw new GradleException('No ' + osname + ' found in ' + sdkPath_ + '/cmake')
}
return tools.getFiles().last().toString();
}
}
def getBuildPath() {
return new File("$projectDir/build").getPath()
}
def getOutPath() {
return new File("$projectDir/../out").getPath()
}
def getPackagePath() {
def outputBaseDir = "$projectDir/../package";
def distDir = System.getenv('DIST_DIR');
if (distDir != null) {
// Running in build server, use DIST_DIR for artifacts
outputBaseDir = distDir;
}
return new File(outputBaseDir).getPath();
}
def getTempPath() {
return new File("$projectDir/../.temp").getPath()//.getAbsolutePath()
}
def useNinja() {
return true;
}
def cmake(projectFolder, workingFolder, outputFolder, arch, toolchain, stl,
threadChecks, buildTuningFork, buildType = "Release") {
def ndkPath = toolchain.getAndroidNDKPath()
def toolchainFilePath = ndkPath + "/build/cmake/android.toolchain.cmake"
def androidVersion = toolchain.getAndroidVersion()
def ninjaPath = toolchain.getNinjaPath()
def buildtfval = buildTuningFork ? "ON" : "OFF"
def protocBinDir = protobufInstallDir() + "/bin"
mkdir workingFolder
mkdir outputFolder
def threadFlags = ""
if (threadChecks) {
threadFlags = "-DGAMESDK_THREAD_CHECKS=1"
} else {
threadFlags = "-DGAMESDK_THREAD_CHECKS=0"
}
def cxx_flags = ""
if (stl=="gnustl_static")
cxx_flags = "-DANDROID_GNUSTL"
def cmdLine = [toolchain.getCMakePath(),
"$projectFolder",
"-DCMAKE_BUILD_TYPE=$buildType",
"-DANDROID_PLATFORM=android-$androidVersion",
"-DANDROID_NDK=$ndkPath",
"-DANDROID_STL=$stl",
"-DANDROID_ABI=$arch",
"-DCMAKE_CXX_FLAGS=$cxx_flags",
"-DCMAKE_TOOLCHAIN_FILE=$toolchainFilePath",
"-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=$outputFolder",
"-DGAMESDK_BUILD_TUNINGFORK=$buildtfval",
threadFlags]
if (useNinja()) {
cmdLine += ["-DCMAKE_MAKE_PROGRAM=" + "$ninjaPath",
"-GNinja"]
}
exec {
environment "PATH", "$protocBinDir:${environment.PATH}"
workingDir workingFolder
commandLine cmdLine
}
}
def hostCmake(projectFolder, workingFolder, outputFolder, toolchain, buildType) {
mkdir workingFolder
mkdir outputFolder
def cmdLine = [toolchain.getCMakePath(),
"$projectFolder",
"-DCMAKE_BUILD_TYPE=$buildType",
"-DCMAKE_CXX_FLAGS=",
"-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=$outputFolder"
]
exec {
workingDir workingFolder
commandLine cmdLine
}
}
def buildHostModule(subdir, buildType) {
def toolchain = getLocalToolchain();
def buildKey = "host"
def workingFolder = joinPath(getTempPath(), buildKey, '.cmake')
def outputFolder = joinPath(getOutPath(), buildKey)
def cmakeProjectLocation = joinPath("$projectDir", subdir)
hostCmake(cmakeProjectLocation, workingFolder, outputFolder, toolchain, buildType)
def cmdLine = ["make", "-j"]
exec {
workingDir workingFolder
commandLine cmdLine
}
return [buildKey: buildKey, outputFolder: outputFolder]
}
def buildNativeModules(arch, Toolchain toolchain, stl, threadChecks, buildtf, subdir = "src", buildType = "Release") {
def buildKey = toolchain.getBuildKey(arch, stl)
def workingFolder = joinPath(getTempPath(), buildKey, '.cmake')
def outputFolder = joinPath(getOutPath(), buildKey)
def cmakeProjectLocation = joinPath("$projectDir", subdir)
cmake(cmakeProjectLocation, workingFolder, outputFolder, arch, toolchain,
stl, threadChecks, buildtf, buildType)
def cmdLine = useNinja() ? [toolchain.getNinjaPath()] : ["make", "-j"]
exec {
workingDir workingFolder
commandLine cmdLine
}
return [arch: arch, buildKey: buildKey]
}
def buildNativeModules(arch, androidVersion, ndkVersion, stl, threadChecks,
buildtf, buildType) {
return buildNativeModules(arch,
new SpecificToolchain(project, androidVersion, ndkVersion),
stl, threadChecks, buildtf, "src", buildType)
}
task cleanPath(type: Delete) {
delete getOutPath()
delete getPackagePath()
delete getTempPath()
delete getBuildPath()
}
// Create outAr from the contents of inArs
// All files taken/created in dir
def repackArchives(dir, inArs, outAr) {
def cmd = /pushd $dir &&/
inArs.each {
cmd <<= /ar -x $it &&/
}
cmd <<= /ar -rcs $outAr *.o && rm *.o && popd/
['/bin/bash', '-c', cmd.toString()].execute().waitFor()
}
def sdkCopy(buildInfo, outFolder, all = true, staticToo = false,
useFullBuildKey = false, flattenLibDirs = false, shared = true) {
def arch = buildInfo.arch
def buildKey = buildInfo.buildKey
def cmakeFolder = joinPath(getTempPath(), buildKey, '.cmake')
def buildFolder = joinPath(getPackagePath(), outFolder)
def libBuildFolder = joinPath(buildFolder, 'libs',
useFullBuildKey ? buildKey : arch, 'lib')
if (shared) {
copy {
from file(cmakeFolder)
include all ? "*/lib*.so" : "swappy*/lib*.so"
into file(libBuildFolder)
includeEmptyDirs = false
if (flattenLibDirs) {
eachFile {
path = name
}
}
}
}
if (staticToo) {
def staticsFolder = joinPath(getOutPath(), buildKey)
def staticLibsBuildFolder = joinPath(buildFolder, 'libs', buildKey)
def staticLibs = ['libswappy_static.a']
if (all)
staticLibs += 'libtuningfork_static.a'
repackArchives(staticsFolder, staticLibs, 'libgamesdk.a')
copy {
from file(staticsFolder)
include "libgamesdk.a"
into file(staticLibsBuildFolder)
}
}
}
def copyExtras(outFolder, all = true) {
def buildFolder = getPackagePath() + '/' + outFolder
def headerFolder = './include'
def aarFolder = joinPath(getOutPath(), 'outputs', 'aar')
def includeBuildFolder = joinPath(buildFolder, 'include')
def aarBuildFolder = joinPath(buildFolder, 'aar')
copy {
from file(headerFolder)
include all ? "*/*.h" : "swappy*/*.h"
into file(includeBuildFolder)
includeEmptyDirs = false
}
copy {
from file(aarFolder)
into file(aarBuildFolder)
includeEmptyDirs = false
}
}
// The latest Unity is using NDK 19 and SDK 21 with c++ stl
def defaultAbis() { return ["armeabi-v7a", "arm64-v8a", "x86", "x86_64"] }
def unityNativeBuild(withTuningFork=false, buildType="Release") {
def threadChecks = false
return defaultAbis().collect {
buildNativeModules(it, "21", "r19", "c++_static", threadChecks, withTuningFork, buildType)
}
}
def sdkNativeBuild(withTuningFork = true, buildType="Release") {
def threadChecks = false
return defaultAbis().collectMany {
[ buildNativeModules(it, "14", "r16", "c++_static", threadChecks, withTuningFork, buildType) ] +
[ buildNativeModules(it, "16", "r17", "c++_static", threadChecks, withTuningFork, buildType) ] +
[ buildNativeModules(it, "26", "r16", "gnustl_static", threadChecks, withTuningFork, buildType) ] +
[ buildNativeModules(it, "28", "r17", "gnustl_static", threadChecks, withTuningFork, buildType) ] +
[ buildNativeModules(it, "28", "r17", "c++_static", threadChecks, withTuningFork, buildType) ]
}
}
def getLocalToolchain() {
def kLocalMinSdk = project.hasProperty("GAMESDK_ANDROID_SDK_VERSION") ?
project.GAMESDK_ANDROID_SDK_VERSION : "26"
return new LocalToolchain(project, kLocalMinSdk)
}
def localNativeBuild(withTuningFork = false, subdir = "src", buildType="Release") {
def toolchain = getLocalToolchain();
def stl = "c++_static"
def threadChecks = true
return defaultAbis().collect {
buildNativeModules(it, toolchain , stl, threadChecks, withTuningFork, subdir, buildType)
}
}
class BuildTask extends DefaultTask {
}
// Just build swappy shared libraries
task swappyUnityBuild(type: BuildTask) {
dependsOn ':extras:assembleRelease'
ext.packageName = 'swappyUnity'
ext.flattenLibs = true
ext.withTuningFork = false
ext.withSharedLibs = false
ext.withStaticLibs = true
ext.withFullBuildKey = true
ext.buildType = "Release";
ext.nativeBuild = { tf,bt -> unityNativeBuild(tf,bt) }
}
task swappyUnityDebugBuild(type: BuildTask) {
dependsOn ':extras:assembleRelease'
ext.packageName = 'swappyUnity'
ext.flattenLibs = true
ext.withTuningFork = false
ext.withSharedLibs = true
ext.withStaticLibs = true
ext.withFullBuildKey = true
ext.buildType = "Debug";
ext.nativeBuild = { tf,bt -> unityNativeBuild(tf,bt) }
}
// Full build including tuning fork for unity, shared libraries only
task unityBuild(type: BuildTask) {
dependsOn ':extras:assembleRelease', prepare_proto
ext.packageName = 'unity'
ext.flattenLibs = true
ext.withTuningFork = true
ext.withSharedLibs = false
ext.withStaticLibs = true
ext.withFullBuildKey = true
ext.buildType = "Release";
ext.nativeBuild = { tf,bt -> unityNativeBuild(tf,bt) }
}
// Build everything
task sdkBuild(type: BuildTask) {
dependsOn ':extras:assembleRelease', prepare_proto
ext.packageName = 'gamesdk'
ext.flattenLibs = false
ext.withTuningFork = true
ext.withSharedLibs = true
ext.withStaticLibs = true
ext.withFullBuildKey = true
ext.buildType = "Release";
ext.nativeBuild = { tf,bt -> sdkNativeBuild(tf,bt) }
}
// Build using local SDK, no tuning fork
task localBuild(type: BuildTask) {
dependsOn ':extras:assembleRelease'
ext.packageName = 'local'
ext.flattenLibs = false
ext.withTuningFork = false;
ext.withSharedLibs = true
ext.withStaticLibs = true;
ext.withFullBuildKey = false;
ext.buildType = "Release";
ext.nativeBuild = { tf,bt -> localNativeBuild(tf, "src", bt) }
}
// Build using local SDK, with tuning fork
task localTfBuild(type: BuildTask) {
dependsOn ':extras:assembleRelease', prepare_proto
ext.packageName = 'localtf'
ext.flattenLibs = false
ext.withTuningFork = true;
ext.withSharedLibs = true
ext.withStaticLibs = true;
ext.withFullBuildKey = false;
ext.buildType = "Release";
ext.nativeBuild = { tf,bt -> localNativeBuild(tf, "src", bt) }
}
tasks.withType(BuildTask) {
doLast {
nativeBuild(withTuningFork, buildType).each {
sdkCopy(it, packageName, withTuningFork, withStaticLibs,
withFullBuildKey, flattenLibs, withSharedLibs)
}
copyExtras(packageName, withTuningFork)
}
}
task localUnitTests {
// These unit tests require a connected ARM64 device to run
doLast {
def buildInfo = localNativeBuild(true, "test")
def buildKey = buildInfo.buildKey.findAll{it.contains('arm64-v8a')}[0]
def cmakeFolder = getTempPath() + '/' + buildKey + '/.cmake/tuningfork'
def toolchain = getLocalToolchain();
def adb = toolchain.getAdbPath();
exec {
workingDir cmakeFolder
commandLine adb, "push", "tuningfork_test", "/data/local/tmp"
}
exec {
workingDir cmakeFolder
commandLine adb, "shell", "/data/local/tmp/tuningfork_test"
}
}
}
task localDeviceInfoUnitTests {
doLast {
def buildInfo = buildHostModule("test/device_info", "Release")
exec {
workingDir buildInfo.outputFolder
commandLine "./device_info_test"
}
}
}
// Zipping things up
def addZipTask(name, buildTask, zipName, rootName = "gamesdk") {
def packPath = buildTask.packageName
tasks.register(name, Zip) {
dependsOn buildTask
def buildFolder = joinPath(getPackagePath(), packPath)
baseName = joinPath(buildFolder, zipName)
from fileTree(buildFolder)
exclude "*.zip"
into rootName
doLast {
def outFolder = getBuildPath();
mkdir outFolder;
copy {
from file(baseName + '.zip')
into outFolder
}
def out = services.get(StyledTextOutputFactory).create("output")
out.style(Style.Identifier).text('\n' + rootName +'.zip is in ')
.style(Style.ProgressStatus)
.println(baseName + '.zip' );
}
}
}
addZipTask("swappyUnityZip", swappyUnityBuild, "builds")
addZipTask("swappyUnityDebugZip", swappyUnityDebugBuild, "builds")
addZipTask("unityZip", unityBuild, "builds")
addZipTask("archiveZip", localBuild, "gamesdk")
addZipTask("archiveTfZip", localTfBuild, "gamesdk")
addZipTask("gamesdkZip", sdkBuild, "gamesdk")