overte-lubosz/android/build.gradle

308 lines
11 KiB
Groovy

import de.undercouch.gradle.tasks.download.Download
import de.undercouch.gradle.tasks.download.Verify
import groovy.io.FileType
import groovy.json.JsonSlurper
import groovy.xml.XmlUtil
import org.apache.tools.ant.taskdefs.condition.Os
import java.util.regex.Matcher
import java.util.regex.Pattern
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
}
}
plugins {
id 'de.undercouch.download' version '3.3.0'
id "cz.malohlava" version "1.0.3"
id "io.github.http-builder-ng.http-plugin" version "0.1.1"
}
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
ext {
RELEASE_NUMBER = project.hasProperty('RELEASE_NUMBER') ? project.getProperty('RELEASE_NUMBER') : '0'
VERSION_CODE = project.hasProperty('VERSION_CODE') ? project.getProperty('VERSION_CODE') : '0'
RELEASE_TYPE = project.hasProperty('RELEASE_TYPE') ? project.getProperty('RELEASE_TYPE') : 'DEV'
STABLE_BUILD = project.hasProperty('STABLE_BUILD') ? project.getProperty('STABLE_BUILD') : '0'
EXEC_SUFFIX = Os.isFamily(Os.FAMILY_WINDOWS) ? '.exe' : ''
appVersionCode = Integer.valueOf(VERSION_CODE ?: 1)
appVersionName = RELEASE_NUMBER ?: "1.0"
}
def appDir = new File(projectDir, 'apps/interface')
def jniFolder = new File(appDir, 'src/main/jniLibs/arm64-v8a')
def baseUrl = 'https://build-deps.overte.org/dependencies/android/'
def breakpadDumpSymsDir = new File("${appDir}/build/tmp/breakpadDumpSyms")
task extractGvrBinaries() {
doLast {
def gvrLibFolder = new File(HIFI_ANDROID_PRECOMPILED, 'gvr/gvr-android-sdk-1.101.0/libraries');
zipTree(new File(HIFI_ANDROID_PRECOMPILED, 'gvr/gvr-android-sdk-1.101.0/libraries/sdk-audio-1.101.0.aar')).visit { element ->
def fileName = element.file.toString();
if (fileName.endsWith('libgvr_audio.so') && fileName.contains('arm64-v8a')) {
copy { from element.file; into gvrLibFolder }
}
}
zipTree(new File(HIFI_ANDROID_PRECOMPILED, 'gvr/gvr-android-sdk-1.101.0/libraries/sdk-base-1.101.0.aar')).visit { element ->
def fileName = element.file.toString();
if (fileName.endsWith('libgvr.so') && fileName.contains('arm64-v8a')) {
copy { from element.file; into gvrLibFolder }
}
}
fileTree(gvrLibFolder).visit { element ->
if (element.file.toString().endsWith('.so')) {
copy { from element.file; into jniFolder }
}
}
}
}
def generateAssetsFileList = {
def assetsPath = "${appDir}/src/main/assets/"
def addedByAndroidDeployQtName = "--Added-by-androiddeployqt--/"
def addedByAndroidDeployQtPath = assetsPath + addedByAndroidDeployQtName
def addedByAndroidDeployQt = new File(addedByAndroidDeployQtPath)
if (!addedByAndroidDeployQt.exists() && !addedByAndroidDeployQt.mkdirs()) {
throw new GradleScriptException("Failed to create directory " + addedByAndroidDeployQtPath, null);
}
def outputFilename = "/qt_cache_pregenerated_file_list"
def outputFile = new File(addedByAndroidDeployQtPath + outputFilename);
Map<String, List<String>> directoryContents = new TreeMap<>();
def dir = new File(assetsPath)
dir.eachFileRecurse (FileType.ANY) { file ->
def name = file.path.substring(assetsPath.length())
int slashIndex = name.lastIndexOf('/')
def pathName = slashIndex >= 0 ? name.substring(0, slashIndex) : "/"
def fileName = slashIndex >= 0 ? name.substring(pathName.length() + 1) : name
if (!fileName.isEmpty() && file.isDirectory() && !fileName.endsWith("/")) {
fileName += "/"
}
if (!directoryContents.containsKey(pathName)) {
directoryContents[pathName] = new ArrayList<String>()
}
if (!fileName.isEmpty()) {
directoryContents[pathName].add(fileName);
}
}
DataOutputStream fos = new DataOutputStream(new FileOutputStream(outputFile));
for (Map.Entry<String, List<String>> e: directoryContents.entrySet()) {
def entryList = e.getValue()
fos.writeInt(e.key.length()*2); // 2 bytes per char
fos.writeChars(e.key);
fos.writeInt(entryList.size());
for (String entry: entryList) {
fos.writeInt(entry.length()*2);
fos.writeChars(entry);
}
}
fos.close();
}
// Copy required Qt main libraries and required plugins based on the predefined list here
// FIXME eventually we would like to use the readelf functionality to automatically detect dependencies
// from our built applications and use that during the full build process. However doing so would mean
// hooking existing Android build tasks since the output from the qtBundle logic adds JNI libs, asset
// files and resources files and potentially modifies the AndroidManifest.xml
task qtBundle {
doLast {
parseQtDependencies(QT5_DEPS)
def qmlImportFolder = new File("${appDir}/../../interface/resources/qml/")
//def qmlImportFolder = new File("${projectDir}/app/src/main/cpp")
scanQmlImports(qmlImportFolder)
generateLibsXml()
generateAssetsFileList()
}
}
task setupDependencies() {
// migrated to python
}
task cleanDependencies(type: Delete) {
}
def runBreakpadDumpSyms = { buildType ->
gradle.startParameter.showStacktrace = ShowStacktrace.ALWAYS
def objDir = new File("${appDir}/build/intermediates/cmake/${buildType}/obj/arm64-v8a")
def stripDebugSymbol = "${appDir}/build/intermediates/transforms/stripDebugSymbol/${buildType}/0/lib/arm64-v8a/"
def outputDir = new File(breakpadDumpSymsDir, buildType)
if (!outputDir.exists()) {
outputDir.mkdirs()
}
objDir.eachFileRecurse (FileType.FILES) { file ->
if (file.name.endsWith('.so')) {
def output = file.name + ".sym"
def cmdArgs = [
file.toString(),
stripDebugSymbol
]
def result = exec {
workingDir HIFI_ANDROID_PRECOMPILED + '/breakpad/bin'
commandLine './dump_syms'
args cmdArgs
ignoreExitValue true
standardOutput = new BufferedOutputStream(new FileOutputStream(new File(outputDir, output)))
}
}
}
}
task runBreakpadDumpSymsDebug() {
doLast {
runBreakpadDumpSyms("debug");
}
}
task runBreakpadDumpSymsRelease() {
doLast {
runBreakpadDumpSyms("release");
}
}
task zipDumpSymsDebug(type: Zip, dependsOn: runBreakpadDumpSymsDebug) {
from (new File(breakpadDumpSymsDir, "debug").absolutePath)
archiveName "symbols-${RELEASE_NUMBER}-debug.zip"
destinationDir(new File("${appDir}/build/tmp/"))
}
task zipDumpSymsRelease(type: Zip, dependsOn: runBreakpadDumpSymsRelease) {
from (new File(breakpadDumpSymsDir, "release").absolutePath)
archiveName "symbols-${RELEASE_NUMBER}-release.zip"
destinationDir(new File("${appDir}/build/tmp/"))
}
task uploadBreakpadDumpSymsDebug(type:io.github.httpbuilderng.http.HttpTask, dependsOn: zipDumpSymsDebug) {
onlyIf {
System.getenv("CMAKE_BACKTRACE_URL") && System.getenv("CMAKE_BACKTRACE_SYMBOLS_TOKEN")
}
config {
request.uri = System.getenv("CMAKE_BACKTRACE_URL")
}
post {
request.uri.path = '/post'
request.uri.query = [format: 'symbols', token: System.getenv("CMAKE_BACKTRACE_SYMBOLS_TOKEN")]
request.body = new File("${appDir}/build/tmp/", "symbols-${RELEASE_NUMBER}-debug.zip").bytes
request.contentType = 'application/octet-stream'
response.success {
println ("${appDir}/build/tmp/symbols-${RELEASE_NUMBER}-debug.zip uploaded")
}
}
}
task uploadBreakpadDumpSymsRelease(type:io.github.httpbuilderng.http.HttpTask, dependsOn: zipDumpSymsRelease) {
onlyIf {
System.getenv("CMAKE_BACKTRACE_URL") && System.getenv("CMAKE_BACKTRACE_SYMBOLS_TOKEN")
}
config {
request.uri = System.getenv("CMAKE_BACKTRACE_URL")
}
post {
request.uri.path = '/post'
request.uri.query = [format: 'symbols', token: System.getenv("CMAKE_BACKTRACE_SYMBOLS_TOKEN")]
request.body = new File("${appDir}/build/tmp/", "symbols-${RELEASE_NUMBER}-release.zip").bytes
request.contentType = 'application/octet-stream'
response.success {
println ("${appDir}/build/tmp/symbols-${RELEASE_NUMBER}-release.zip uploaded")
}
}
}
// FIXME this code is prototyping the desired functionality for doing build time binary dependency resolution.
// See the comment on the qtBundle task above
/*
// FIXME derive the path from the gradle environment
def toolchain = [
version: '4.9',
prefix: 'aarch64-linux-android',
// FIXME derive from the host OS
ndkHost: 'windows-x86_64',
]
def findDependentLibrary = { String name ->
def libFolders = [
new File(qmlRoot, 'lib'),
new File("${HIFI_ANDROID_PRECOMPILED}/tbb/lib/release"),
new File("${HIFI_ANDROID_PRECOMPILED}/polyvox/lib/Release"),
new File("${HIFI_ANDROID_PRECOMPILED}/polyvox/lib/"),
new File("${HIFI_ANDROID_PRECOMPILED}/gvr/gvr-android-sdk-1.101.0/libraries"),
]
}
def readElfBinary = new File(android.ndkDirectory, "/toolchains/${toolchain.prefix}-${toolchain.version}/prebuilt/${toolchain.ndkHost}/bin/${toolchain.prefix}-readelf${EXEC_SUFFIX}")
def getDependencies = { File elfBinary ->
Set<File> result = []
Queue<File> pending = new LinkedList<>()
pending.add(elfBinary)
Set<File> scanned = []
Pattern p = ~/.*\(NEEDED\).*Shared library: \[(.*\.so)\]/
while (!pending.isEmpty()) {
File current = pending.remove()
if (scanned.contains(current)) {
continue
}
scanned.add(current)
def command = "${readElfBinary} -d -W ${current.absolutePath}"
captureOutput(command).split('[\r\n]').each { line ->
Matcher m = p.matcher(line)
if (!m.matches()) {
return
}
def libName = m.group(1)
def file = new File(qmlRoot, "lib/${libName}")
if (file.exists()) {
result.add(file)
pending.add(file)
}
}
}
return result
}
task testElf (dependsOn: 'externalNativeBuildDebug') {
doLast {
def appLibraries = new HashSet<File>()
def qtDependencies = new HashSet<File>()
externalNativeBuildDebug.nativeBuildConfigurationsJsons.each { File file ->
def json = new JsonSlurper().parse(file)
json.libraries.each { node ->
def outputFile = new File(node.value.output)
if (outputFile.canonicalPath.startsWith(projectDir.canonicalPath)) {
appLibraries.add(outputFile)
}
}
}
appLibraries.each { File file ->
println getDependencies(file)
}
}
}
*/