| |
| 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 protobufInstallDir() { |
| return new File("./third_party/protobuf-3.0.0/install/linux-x86").getCanonicalPath() |
| } |
| |
| 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 getExternalPath() + '/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"] |
| } |
| exec { |
| commandLine "dpkg-query" |
| args = ["--list", "*env*"] |
| } |
| } |
| } |
| |
| buildscript { |
| repositories { |
| google() |
| jcenter() |
| } |
| dependencies { |
| classpath 'com.android.tools.build:gradle:3.2.1' |
| } |
| } |
| |
| allprojects { |
| buildDir = getOutPath() |
| repositories { |
| google() |
| jcenter() |
| } |
| } |
| |
| abstract class Toolchain { |
| 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_ + '_' + 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] |
| } |
| } |
| } |
| class SpecificToolchain extends Toolchain { |
| SpecificToolchain(String androidVersion, String ndkVersion) { |
| androidVersion_ = androidVersion |
| ndkVersion_ = ndkVersion |
| } |
| public String getAndroidNDKPath() { |
| return new File("../prebuilts/ndk/" + ndkVersion_).getCanonicalPath() |
| } |
| public String getCMakePath() { |
| return new File("../prebuilts/cmake/linux-x86/bin/cmake").getCanonicalPath() |
| } |
| public String getNinjaPath() { |
| return new File("../prebuilts/cmake/linux-x86/bin/ninja").getCanonicalPath() |
| } |
| } |
| class LocalToolchain extends Toolchain { |
| Project project_; |
| 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("./build").getCanonicalPath() |
| } |
| |
| def getOutPath() { |
| return new File("../out").getCanonicalPath() |
| } |
| |
| def getPackagePath() { |
| return new File("../package").getCanonicalPath() |
| } |
| |
| def getTempPath() { |
| return new File("../.temp").getCanonicalPath() |
| } |
| |
| def getExternalPath() { |
| def f = new File("../external/"); |
| if ( !f.exists() ) |
| f = new File("../../../external/"); |
| return f.getCanonicalPath() |
| } |
| |
| def useNinja() { |
| return true; |
| } |
| |
| def cmake(projectFolder, workingFolder, outputFolder, arch, toolchain, stl, |
| threadChecks, buildTuningFork) { |
| def ndkPath = toolchain.getAndroidNDKPath() |
| def toolchainFilePath = ndkPath + "/build/cmake/android.toolchain.cmake" |
| def externalPath = getExternalPath(); |
| 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 cmdLine = [toolchain.getCMakePath(), |
| "$projectFolder", |
| "-DCMAKE_BUILD_TYPE=Release", |
| "-DANDROID_PLATFORM=android-$androidVersion", |
| "-DANDROID_NDK=$ndkPath", |
| "-DANDROID_STL=$stl", |
| "-DANDROID_ABI=$arch", |
| "-DCMAKE_CXX_FLAGS=", |
| "-DCMAKE_TOOLCHAIN_FILE=$toolchainFilePath", |
| "-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=$outputFolder", |
| "-DGAMESDK_BUILD_TUNINGFORK=$buildtfval", |
| "-DEXTERNAL_ROOT=$externalPath", |
| threadFlags] |
| if (useNinja()) { |
| cmdLine += ["-DCMAKE_MAKE_PROGRAM=" + "$ninjaPath", |
| "-GNinja"] |
| } |
| exec { |
| environment "PATH", "$protocBinDir:${environment.PATH}" |
| workingDir workingFolder |
| commandLine cmdLine |
| } |
| } |
| |
| def buildNativeModules(arch, Toolchain toolchain, stl, threadChecks, buildtf, subdir = "src") { |
| 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) |
| |
| 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) { |
| return buildNativeModules(arch, |
| new SpecificToolchain(androidVersion, ndkVersion), |
| stl, threadChecks, buildtf) |
| } |
| |
| 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 -qc $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', |
| 'libswappyVk_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 16b and SDK 21 with gnu stl |
| def defaultAbis() { return ["armeabi-v7a", "arm64-v8a", "x86", "x86_64"] } |
| |
| def unityNativeBuild(withTuningFork=false) { |
| def threadChecks = false |
| return defaultAbis().collect { |
| buildNativeModules(it, "21", "r16", "gnustl_static", threadChecks, withTuningFork) |
| } |
| } |
| |
| def sdkNativeBuild(withTuningFork = true) { |
| def threadChecks = false |
| return defaultAbis().collectMany { |
| [ buildNativeModules(it, "26", "r16", "gnustl_static", threadChecks, withTuningFork) ] + |
| [ buildNativeModules(it, "28", "r17", "gnustl_static", threadChecks, withTuningFork) ] + |
| [ buildNativeModules(it, "28", "r17", "c++_static", threadChecks, withTuningFork) ] |
| } |
| } |
| |
| 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") { |
| def toolchain = getLocalToolchain(); |
| def stl = "c++_static" |
| def threadChecks = true |
| return defaultAbis().collect { |
| buildNativeModules(it, toolchain , stl, threadChecks, withTuningFork, subdir) |
| } |
| } |
| |
| 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 = true |
| ext.withStaticLibs = true |
| ext.withFullBuildKey = false |
| ext.nativeBuild = { tf -> unityNativeBuild(tf) } |
| } |
| |
| // 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 = true |
| ext.withStaticLibs = false |
| ext.withFullBuildKey = false |
| ext.nativeBuild = { tf -> unityNativeBuild(tf) } |
| } |
| |
| // 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.nativeBuild = { tf -> sdkNativeBuild(tf) } |
| } |
| |
| // 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.nativeBuild = { tf -> localNativeBuild(tf) } |
| } |
| |
| // 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.nativeBuild = { tf -> localNativeBuild(tf) } |
| } |
| |
| tasks.withType(BuildTask) { |
| doLast { |
| nativeBuild(withTuningFork).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" |
| } |
| } |
| } |
| |
| // 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("unityZip", unityBuild, "builds") |
| addZipTask("archiveZip", localBuild, "gamesdk") |
| addZipTask("archiveTfZip", localTfBuild, "gamesdk") |
| addZipTask("gamesdkZip", sdkBuild, "gamesdk") |