import org.apache.tools.ant.taskdefs.condition.Os buildscript { repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.3.0' } } allprojects { repositories { google() jcenter() mavenCentral() } } 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://hifi-public.s3.amazonaws.com/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> 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() } if (!fileName.isEmpty()) { directoryContents[pathName].add(fileName); } } DataOutputStream fos = new DataOutputStream(new FileOutputStream(outputFile)); for (Map.Entry> 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) { } // 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 result = [] Queue pending = new LinkedList<>() pending.add(elfBinary) Set 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() def qtDependencies = new HashSet() 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) } } } */