Update build files based on move

This commit is contained in:
Brad Davis 2018-12-31 13:28:53 -08:00
parent b11afa48f8
commit 61ea0de742
19 changed files with 274 additions and 413 deletions

3
.gitignore vendored
View file

@ -14,11 +14,13 @@ Makefile
# Android Studio
*.iml
*.class
local.properties
android/gradle*
android/.gradle
android/**/src/main/jniLibs
android/**/libs
android/**/bin
android/**/src/main/res/values/libs.xml
android/**/src/main/assets
android/**/gradle*
@ -102,3 +104,4 @@ tools/unity-avatar-exporter/Logs
tools/unity-avatar-exporter/Packages
tools/unity-avatar-exporter/ProjectSettings
tools/unity-avatar-exporter/Temp

View file

@ -12,7 +12,7 @@ target_python()
if (HIFI_ANDROID )
execute_process(
COMMAND ${HIFI_PYTHON_EXEC} ${CMAKE_CURRENT_SOURCE_DIR}/prebuild.py --android --build-root ${CMAKE_BINARY_DIR}
COMMAND ${HIFI_PYTHON_EXEC} ${CMAKE_CURRENT_SOURCE_DIR}/prebuild.py --android ${HIFI_ANDROID_APP} --build-root ${CMAKE_BINARY_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
else()
@ -174,7 +174,7 @@ set_packaging_parameters()
# FIXME hack to work on the proper Android toolchain
if (ANDROID)
add_subdirectory(android/app)
add_subdirectory(android/apps/${HIFI_ANDROID_APP})
return()
endif()

View file

@ -4,10 +4,10 @@ link_hifi_libraries(shared task networking gl gpu qml image fbx hfm render-utils
target_opengl()
target_bullet()
set(INTERFACE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../interface")
set(INTERFACE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../interface")
add_subdirectory("${INTERFACE_DIR}" "libraries/interface")
include_directories("${INTERFACE_DIR}/src")
set(HIFI_CODEC_PLUGIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../plugins/hifiCodec")
set(HIFI_CODEC_PLUGIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/hifiCodec")
add_subdirectory("${HIFI_CODEC_PLUGIN_DIR}" "libraries/hifiCodecPlugin")
target_link_libraries(native-lib android log m interface)
@ -15,16 +15,3 @@ target_link_libraries(native-lib android log m interface)
set(GVR_ROOT "${HIFI_ANDROID_PRECOMPILED}/gvr/gvr-android-sdk-1.101.0/")
target_include_directories(native-lib PRIVATE "${GVR_ROOT}/libraries/headers" "libraries/ui/src")
target_link_libraries(native-lib "${GVR_ROOT}/libraries/libgvr.so" ui)
# finished libraries
# core -> qt
# networking -> openssl, tbb
# fbx -> draco
# physics -> bullet
# entities-renderer -> polyvox
# unfinished libraries
# image -> nvtt (doesn't look good, but can be made optional)
# script-engine -> quazip (probably not required for the android client)

View file

@ -1,9 +1,41 @@
import org.apache.tools.ant.taskdefs.condition.Os
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task renameHifiACTaskDebug() {
doLast {
def sourceFile = new File("${appDir}/build/intermediates/cmake/debug/obj/arm64-v8a/","libhifiCodec.so")
def destinationFile = new File("${appDir}/src/main/jniLibs/arm64-v8a", "libplugins_libhifiCodec.so")
copy { from sourceFile; into destinationFile.parent; rename(sourceFile.name, destinationFile.name) }
}
}
task renameHifiACTaskRelease(type: Copy) {
doLast {
def sourceFile = new File("${appDir}/build/intermediates/cmake/release/obj/arm64-v8a/","libhifiCodec.so")
def destinationFile = new File("${appDir}/src/main/jniLibs/arm64-v8a", "libplugins_libhifiCodec.so")
copy { from sourceFile; into destinationFile.parent; rename(sourceFile.name, destinationFile.name) }
}
}
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
compileSdkVersion 28
//buildToolsVersion '27.0.3'
def appVersionCode = Integer.valueOf(VERSION_CODE ?: 1)
@ -12,24 +44,24 @@ android {
defaultConfig {
applicationId "io.highfidelity.hifiinterface"
minSdkVersion 24
targetSdkVersion 26
targetSdkVersion 28
versionCode appVersionCode
versionName appVersionName
ndk { abiFilters 'arm64-v8a' }
externalNativeBuild {
cmake {
arguments '-DHIFI_ANDROID=1',
'-DHIFI_ANDROID_APP=interface',
'-DANDROID_PLATFORM=android-24',
'-DANDROID_TOOLCHAIN=clang',
'-DANDROID_STL=c++_shared',
'-DQT_CMAKE_PREFIX_PATH=' + HIFI_ANDROID_PRECOMPILED + '/qt/lib/cmake',
'-DHIFI_ANDROID_PRECOMPILED=' + HIFI_ANDROID_PRECOMPILED,
'-DRELEASE_NUMBER=' + RELEASE_NUMBER,
'-DRELEASE_TYPE=' + RELEASE_TYPE,
'-DSTABLE_BUILD=' + STABLE_BUILD,
'-DDISABLE_QML=OFF',
'-DDISABLE_KTX_CACHE=OFF',
'-DUSE_BREAKPAD=' + (System.getenv("CMAKE_BACKTRACE_URL") && System.getenv("CMAKE_BACKTRACE_TOKEN") ? 'ON' : 'OFF');
targets = ['native-lib']
}
}
signingConfigs {
@ -72,7 +104,7 @@ android {
externalNativeBuild {
cmake {
path '../../CMakeLists.txt'
path '../../../CMakeLists.txt'
}
}
@ -82,6 +114,7 @@ android {
variant.externalNativeBuildTasks.each { task ->
variant.mergeResources.dependsOn(task)
if (Os.isFamily(Os.FAMILY_UNIX)) {
// FIXME
def uploadDumpSymsTask = rootProject.getTasksByName("uploadBreakpadDumpSyms${variant.name.capitalize()}", false).first()
def runDumpSymsTask = rootProject.getTasksByName("runBreakpadDumpSyms${variant.name.capitalize()}", false).first()
def renameHifiACTask = rootProject.getTasksByName("renameHifiACTask${variant.name.capitalize()}", false).first()
@ -97,7 +130,7 @@ android {
// Copy the compiled resources generated by the external native build
copy {
from new File(projectDir, "../../interface/compiledResources")
from new File(projectDir, "../../../interface/compiledResources")
into outputDir
duplicatesStrategy DuplicatesStrategy.INCLUDE
eachFile { details ->
@ -108,7 +141,7 @@ android {
// Copy the scripts directory
copy {
from new File(projectDir, "../../scripts")
from new File(projectDir, "../../../scripts")
into new File(outputDir, "scripts")
duplicatesStrategy DuplicatesStrategy.INCLUDE
eachFile { details->
@ -123,12 +156,6 @@ android {
assetList.each { file -> out.println(file) }
}
}
variant.outputs.all {
if (RELEASE_NUMBER != '0') {
outputFileName = "app_" + RELEASE_NUMBER + "_" + RELEASE_TYPE + ".apk"
}
}
}
}
@ -157,5 +184,6 @@ dependencies {
api 'com.sothree.slidinguppanel:library:3.4.0'
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation fileTree(include: ['*.jar'], dir: '../../libraries/qt/libs')
implementation project(':qt')
}

View file

@ -42,378 +42,13 @@ ext {
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' : ''
QT5_DEPS = [
'Qt5Concurrent',
'Qt5Core',
'Qt5Gui',
'Qt5Multimedia',
'Qt5Network',
'Qt5OpenGL',
'Qt5Qml',
'Qt5Quick',
'Qt5QuickControls2',
'Qt5QuickTemplates2',
'Qt5Script',
'Qt5ScriptTools',
'Qt5Svg',
'Qt5WebChannel',
'Qt5WebSockets',
'Qt5Widgets',
'Qt5XmlPatterns',
// Android specific
'Qt5AndroidExtras',
'Qt5WebView',
]
}
def baseFolder = new File(HIFI_ANDROID_PRECOMPILED)
def appDir = new File(projectDir, 'app')
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")
def qtFile='qt-5.11.1_linux_armv8-libcpp_openssl_patched.tgz'
def qtChecksum='aa449d4bfa963f3bc9a9dfe558ba29df'
def qtVersionId='3S97HBM5G5Xw9EfE52sikmgdN3t6C2MN'
if (Os.isFamily(Os.FAMILY_MAC)) {
qtFile = 'qt-5.11.1_osx_armv8-libcpp_openssl_patched.tgz'
qtChecksum='c83cc477c08a892e00c71764dca051a0'
qtVersionId='OxBD7iKINv1HbyOXmAmDrBb8AF3N.Kup'
} else if (Os.isFamily(Os.FAMILY_WINDOWS)) {
qtFile = 'qt-5.11.1_win_armv8-libcpp_openssl_patched.tgz'
qtChecksum='0582191cc55431aa4f660848a542883e'
qtVersionId='JfWM0P_Mz5Qp0LwpzhrsRwN3fqlLSFeT'
}
def packages = [
qt: [
file: qtFile,
versionId: qtVersionId,
checksum: qtChecksum,
],
bullet: [
file: 'bullet-2.88_armv8-libcpp.tgz',
versionId: 'S8YaoED0Cl8sSb8fSV7Q2G1lQJSNDxqg',
checksum: '81642779ccb110f8c7338e8739ac38a0',
],
draco: [
file: 'draco_armv8-libcpp.tgz',
versionId: '3.B.uBj31kWlgND3_R2xwQzT_TP6Dz_8',
checksum: '617a80d213a5ec69fbfa21a1f2f738cd',
],
glad: [
file: 'glad_armv8-libcpp.zip',
versionId: 'r5Zran.JSCtvrrB6Q4KaqfIoALPw3lYY',
checksum: 'a8ee8584cf1ccd34766c7ddd9d5e5449',
],
gvr: [
file: 'gvrsdk_v1.101.0.tgz',
versionId: 'nqBV_j81Uc31rC7bKIrlya_Hah4v3y5r',
checksum: '57fd02baa069176ba18597a29b6b4fc7',
],
nvtt: [
file: 'nvtt_armv8-libcpp.zip',
versionId: 'lmkBVR5t4UF1UUwMwEirnk9H_8Nt90IO',
checksum: 'eb46d0b683e66987190ed124aabf8910',
sharedLibFolder: 'lib',
includeLibs: ['libnvtt.so', 'libnvmath.so', 'libnvimage.so', 'libnvcore.so'],
],
openssl: [
file: 'openssl-1.1.0g_armv8.tgz',
versionId: 'AiiPjmgUZTgNj7YV1EEx2lL47aDvvvAW',
checksum: 'cabb681fbccd79594f65fcc266e02f32',
],
polyvox: [
file: 'polyvox_armv8-libcpp.tgz',
versionId: 'A2kbKiNhpIenGq23bKRRzg7IMAI5BI92',
checksum: 'dba88b3a098747af4bb169e9eb9af57e',
sharedLibFolder: 'lib',
includeLibs: ['Release/libPolyVoxCore.so', 'libPolyVoxUtil.so'],
],
tbb: [
file: 'tbb-2018_U1_armv8_libcpp.tgz',
versionId: 'mrRbWnv4O4evcM1quRH43RJqimlRtaKB',
checksum: '20768f298f53b195e71b414b0ae240c4',
sharedLibFolder: 'lib/release',
includeLibs: ['libtbb.so', 'libtbbmalloc.so'],
],
hifiAC: [
baseUrl: 'http://s3.amazonaws.com/hifi-public/dependencies/',
file: 'codecSDK-android_armv8-2.0.zip',
checksum: '1cbef929675818fc64c4101b72f84a6a'
],
etc2comp: [
file: 'etc2comp-patched-armv8-libcpp.tgz',
versionId: 'bHhGECRAQR1vkpshBcK6ByNc1BQIM8gU',
checksum: '14b02795d774457a33bbc60e00a786bc'
],
breakpad: [
file: 'breakpad.tgz',
versionId: '8VrYXz7oyc.QBxNia0BVJOUBvrFO61jI',
checksum: 'ddcb23df336b08017042ba4786db1d9e',
sharedLibFolder: 'lib',
includeLibs: ['libbreakpad_client.a']
]
]
def options = [
files: new TreeSet<File>(),
features: new HashSet<String>(),
permissions: new HashSet<String>()
]
def qmlRoot = new File(HIFI_ANDROID_PRECOMPILED, 'qt')
def captureOutput = { String command, List<String> commandArgs ->
def result
new ByteArrayOutputStream().withStream { os ->
def execResult = exec {
executable = command
args = commandArgs
standardOutput = os
errorOutput = new ByteArrayOutputStream()
}
result = os.toString()
}
return result;
}
def relativize = { File root, File absolute ->
def relativeURI = root.toURI().relativize(absolute.toURI())
return new File(relativeURI.toString())
}
def scanQmlImports = { File qmlRootPath ->
def qmlImportCommandFile = new File(qmlRoot, 'bin/qmlimportscanner' + EXEC_SUFFIX)
if (!qmlImportCommandFile.exists()) {
throw new GradleException('Unable to find required qmlimportscanner executable at ' + qmlImportCommandFile.parent.toString())
}
def command = qmlImportCommandFile.absolutePath
def args = [
'-rootPath', qmlRootPath.absolutePath,
'-importPath', "${qmlRoot.absolutePath}/qml"
]
def commandResult = captureOutput(command, args)
new JsonSlurper().parseText(commandResult).each {
if (!it.containsKey('path')) {
println "Warning: QML import could not be resolved in any of the import paths: ${it.name}"
return
}
def file = new File(it.path)
// Ignore non-existent files
if (!file.exists()) {
return
}
// Ignore files in the import path
if (file.canonicalPath.startsWith(qmlRootPath.canonicalPath)) {
return
}
if (file.isFile()) {
options.files.add(file)
} else {
file.eachFileRecurse(FileType.FILES, {
options.files.add(it)
})
}
}
}
def parseQtDependencies = { List qtLibs ->
qtLibs.each({
def libFile = new File(qmlRoot, "lib/lib${it}.so")
options.files.add(libFile)
def androidDeps = new File(qmlRoot, "lib/${it}-android-dependencies.xml")
if (!libFile.exists()) return
if (!androidDeps.exists()) return
new XmlSlurper().parse(androidDeps).dependencies.lib.depends.'*'.each{ node ->
switch (node.name()) {
case 'lib':
case 'bundled':
def relativeFilename = node.@file.toString()
// Special case, since this is handled by qmlimportscanner instead
if (relativeFilename.startsWith('qml'))
return
def file = new File(qmlRoot, relativeFilename)
if (!file.exists())
return
if (file.isFile()) {
options.files.add(file)
} else {
file.eachFileRecurse(FileType.FILES, { options.files.add(it) })
}
break
case 'jar':
if (node.@bundling == "1") {
def jar = new File(qmlRoot, node.@file.toString())
if (!jar.exists()) {
throw new GradleException('Unable to find required JAR ' + jar.path)
}
options.files.add(jar)
}
break
case 'permission':
options.permissions.add(node.@name)
break
case 'feature':
options.features.add(node.@name)
break
default:
throw new GradleException('Unhandled Android Dependency node ' + node.name())
}
}
})
}
def generateLibsXml = {
def libDestinationDirectory = jniFolder
def jarDestinationDirectory = new File(appDir, 'libs')
def assetDestinationDirectory = new File(appDir, 'src/main/assets/--Added-by-androiddeployqt--');
def libsXmlFile = new File(appDir, 'src/main/res/values/libs.xml')
def libPrefix = 'lib' + File.separator
def jarPrefix = 'jar' + File.separator
def xmlParser = new XmlParser()
def libsXmlRoot = xmlParser.parseText('<?xml version="1.0" encoding="UTF-8"?><resources/>')
def qtLibsNode = xmlParser.createNode(libsXmlRoot, 'array', [name: 'qt_libs'])
def bundledLibsNode = xmlParser.createNode(libsXmlRoot, 'array', [name: 'bundled_in_lib'])
def bundledAssetsNode = xmlParser.createNode(libsXmlRoot, 'array', [name: 'bundled_in_assets'])
options.files.each {
def sourceFile = it
if (!sourceFile.exists()) {
throw new GradleException("Unable to find dependency file " + sourceFile.toString())
}
def relativePath = relativize( qmlRoot, sourceFile ).toString()
def destinationFile
if (relativePath.endsWith('.so')) {
def garbledFileName
if (relativePath.startsWith(libPrefix)) {
garbledFileName = relativePath.substring(libPrefix.size())
Pattern p = ~/lib(Qt5.*).so/
Matcher m = p.matcher(garbledFileName)
assert m.matches()
def libName = m.group(1)
xmlParser.createNode(qtLibsNode, 'item', [:]).setValue(libName)
} else {
garbledFileName = 'lib' + relativePath.replace(File.separator, '_'[0])
xmlParser.createNode(bundledLibsNode, 'item', [:]).setValue("${garbledFileName}:${relativePath}".replace(File.separator, '/'))
}
destinationFile = new File(libDestinationDirectory, garbledFileName)
} else if (relativePath.startsWith('jar')) {
destinationFile = new File(jarDestinationDirectory, relativePath.substring(jarPrefix.size()))
} else {
xmlParser.createNode(bundledAssetsNode, 'item', [:]).setValue("--Added-by-androiddeployqt--/${relativePath}:${relativePath}".replace(File.separator, '/'))
destinationFile = new File(assetDestinationDirectory, relativePath)
}
copy { from sourceFile; into destinationFile.parent; rename(sourceFile.name, destinationFile.name) }
assert destinationFile.exists() && destinationFile.isFile()
}
def xml = XmlUtil.serialize(libsXmlRoot)
new FileWriter(libsXmlFile).withPrintWriter { writer ->
writer.write(xml)
}
}
task downloadDependencies {
doLast {
packages.each { entry ->
def filename = entry.value['file'];
def dependencyBaseUrl = entry.value['baseUrl']
def url = (dependencyBaseUrl?.trim() ? dependencyBaseUrl : baseUrl) + filename;
if (entry.value.containsKey('versionId')) {
url = url + '?versionId=' + entry.value['versionId']
}
download {
src url
dest new File(baseFolder, filename)
onlyIfNewer true
}
}
}
}
task verifyQt(type: Verify) { def p = packages['qt']; src new File(baseFolder, p['file']); checksum p['checksum']; }
task verifyBullet(type: Verify) { def p = packages['bullet']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyDraco(type: Verify) { def p = packages['draco']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyGvr(type: Verify) { def p = packages['gvr']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyOpenSSL(type: Verify) { def p = packages['openssl']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyPolyvox(type: Verify) { def p = packages['polyvox']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyTBB(type: Verify) { def p = packages['tbb']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyHifiAC(type: Verify) { def p = packages['hifiAC']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyEtc2Comp(type: Verify) { def p = packages['etc2comp']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyBreakpad(type: Verify) { def p = packages['breakpad']; src new File(baseFolder, p['file']); checksum p['checksum'] }
task verifyDependencyDownloads(dependsOn: downloadDependencies) { }
verifyDependencyDownloads.dependsOn verifyQt
verifyDependencyDownloads.dependsOn verifyBullet
verifyDependencyDownloads.dependsOn verifyDraco
verifyDependencyDownloads.dependsOn verifyGvr
verifyDependencyDownloads.dependsOn verifyOpenSSL
verifyDependencyDownloads.dependsOn verifyPolyvox
verifyDependencyDownloads.dependsOn verifyTBB
verifyDependencyDownloads.dependsOn verifyHifiAC
verifyDependencyDownloads.dependsOn verifyEtc2Comp
verifyDependencyDownloads.dependsOn verifyBreakpad
task extractDependencies(dependsOn: verifyDependencyDownloads) {
doLast {
packages.each { entry ->
def folder = entry.key
def filename = entry.value['file']
def localFile = new File(HIFI_ANDROID_PRECOMPILED, filename)
def localFolder = new File(HIFI_ANDROID_PRECOMPILED, folder)
def fileTree;
if (filename.endsWith('zip')) {
fileTree = zipTree(localFile)
} else {
fileTree = tarTree(resources.gzip(localFile))
}
copy {
from fileTree
into localFolder
}
}
}
}
// Copies the non Qt dependencies. Qt dependencies (primary libraries and plugins) are handled by the qtBundle task
task copyDependencies() {
doLast {
packages.each { entry ->
def packageName = entry.key
def currentPackage = entry.value;
if (currentPackage.containsKey('sharedLibFolder')) {
def localFolder = new File(baseFolder, packageName + '/' + currentPackage['sharedLibFolder'])
def tree = fileTree(localFolder);
if (currentPackage.containsKey('includeLibs')) {
currentPackage['includeLibs'].each { includeSpec -> tree.include includeSpec }
}
tree.visit { element ->
if (!element.file.isDirectory()) {
println "Copying " + element.file + " to " + jniFolder
copy { from element.file; into jniFolder }
}
}
}
}
}
}
task extractGvrBinaries() {
doLast {
def gvrLibFolder = new File(HIFI_ANDROID_PRECOMPILED, 'gvr/gvr-android-sdk-1.101.0/libraries');
@ -500,13 +135,11 @@ task qtBundle {
}
}
task setupDependencies(dependsOn: [copyDependencies, extractGvrBinaries, qtBundle]) { }
task setupDependencies() {
// migrated to python
}
task cleanDependencies(type: Delete) {
delete HIFI_ANDROID_PRECOMPILED
delete 'app/src/main/jniLibs/arm64-v8a'
delete 'app/src/main/assets/--Added-by-androiddeployqt--'
delete 'app/src/main/res/values/libs.xml'
}
def runBreakpadDumpSyms = { buildType ->

View file

@ -1,4 +1,11 @@
#!/usr/bin/env bash
set -xeuo pipefail
./gradlew -PHIFI_ANDROID_PRECOMPILED=${HIFI_ANDROID_PRECOMPILED} -PVERSION_CODE=${VERSION_CODE} -PRELEASE_NUMBER=${RELEASE_NUMBER} -PRELEASE_TYPE=${RELEASE_TYPE} setupDependencies
./gradlew -PHIFI_ANDROID_PRECOMPILED=${HIFI_ANDROID_PRECOMPILED} -PVERSION_CODE=${VERSION_CODE} -PRELEASE_NUMBER=${RELEASE_NUMBER} -PRELEASE_TYPE=${RELEASE_TYPE} app:${ANDROID_BUILD_TARGET}
./gradlew -PHIFI_ANDROID_PRECOMPILED=${HIFI_ANDROID_PRECOMPILED} -PVERSION_CODE=${VERSION_CODE} -PRELEASE_NUMBER=${RELEASE_NUMBER} -PRELEASE_TYPE=${RELEASE_TYPE} ${ANDROID_APP}:${ANDROID_BUILD_TARGET}
# This is the actual output from gradle, which no longer attempts to muck with the naming of the APK
OUTPUT_APK=./apps/${ANDROID_APP}/build/outputs/apk/${ANDROID_BUILD_DIR}/${ANDROID_APP}-${ANDROID_BUILD_DIR}.apk
# This is the APK name requested by Jenkins
TARGET_APK=./${ANDROID_APK_NAME}
# Make sure this matches up with the new ARTIFACT_EXPRESSION for jenkins builds, which should be "android/*.apk"
cp ${OUTPUT_APK} ${TARGET_APK}

View file

@ -5,12 +5,21 @@ DOCKER_IMAGE_NAME="hifi_androidbuild"
docker build --build-arg BUILD_UID=`id -u` -t "${DOCKER_IMAGE_NAME}" -f docker/Dockerfile docker
# The Jenkins PR builds use VERSION_CODE, but the release builds use VERSION
# So make sure we use VERSION_CODE consistently
if [-z "$VERSION_CODE"]; then
export VERSION_CODE=$VERSION
fi
docker run \
--rm \
--security-opt seccomp:unconfined \
--security-opt seccomp:unconfined \
-v "${WORKSPACE}":/home/jenkins/hifi \
-v /home/jenkins/.gradle:/home/jenkins/.gradle \
-e RELEASE_NUMBER \
-e RELEASE_TYPE \
-e ANDROID_APP \
-e ANDROID_APK_NAME \
-e ANDROID_BUILD_TARGET \
-e ANDROID_BUILD_DIR \
-e CMAKE_BACKTRACE_URL \

13
android/docker/update.txt Normal file
View file

@ -0,0 +1,13 @@
git fetch
git checkout feature/quest_move_interface
export VERSION_CODE=1
export RELEASE_NUMBER=1
export RELEASE_TYPE=DEV
export ANDROID_APP=interface
touch ~/.gradle/gradle.properties
echo HIFI_ANDROID_KEYSTORE=/home/jenkins/keystore.jks > ~/.gradle/gradle.properties
echo HIFI_ANDROID_KEYSTORE_PASSWORD=password >> ~/.gradle/gradle.properties
echo HIFI_ANDROID_KEY_ALIAS=key0 >> ~/.gradle/gradle.properties
echo HIFI_ANDROID_KEY_PASSWORD=password >> ~/.gradle/gradle.properties
./build_android.sh
cp ./apps/${ANDROID_APP}/build/outputs/apk/release/${ANDROID_APP}-release.apk ${ANDROID_APP}.apk

View file

@ -0,0 +1,22 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 24
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
api 'com.google.guava:guava:23.0'
}

View file

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.highfidelity.shared.qt" />

View file

@ -0,0 +1,69 @@
package io.highfidelity.utils;
import android.content.res.AssetManager;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.LinkedList;
public class HifiUtils {
private static LinkedList<String> readAssetLines(AssetManager assetManager, String asset) throws IOException {
LinkedList<String> assets = new LinkedList<>();
InputStream is = assetManager.open(asset);
BufferedReader in = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String line;
while ((line=in.readLine()) != null) {
assets.add(line);
}
in.close();
return assets;
}
private static void copyAsset(AssetManager assetManager, String asset, String destFileName) throws IOException {
try (InputStream is = assetManager.open(asset)) {
try (OutputStream os = Files.asByteSink(new File(destFileName)).openStream()) {
ByteStreams.copy(is, os);
}
}
}
public static void upackAssets(AssetManager assetManager, String destDir) {
try {
if (!destDir.endsWith("/"))
destDir = destDir + "/";
LinkedList<String> assets = readAssetLines(assetManager, "cache_assets.txt");
String dateStamp = assets.poll();
String dateStampFilename = destDir + dateStamp;
File dateStampFile = new File(dateStampFilename);
if (dateStampFile.exists()) {
return;
}
for (String fileToCopy : assets) {
String destFileName = destDir + fileToCopy;
{
File destFile = new File(destFileName);
File destFolder = destFile.getParentFile();
if (!destFolder.exists()) {
destFolder.mkdirs();
}
if (destFile.exists()) {
destFile.delete();
}
}
copyAsset(assetManager, fileToCopy, destFileName);
}
Files.write("touch".getBytes(), dateStampFile);
} catch (IOException e){
throw new RuntimeException(e);
}
}
}

View file

@ -1 +1,5 @@
include ':app'
include ':qt'
project(':qt').projectDir = new File(settingsDir, 'libraries/qt')
include ':interface'
project(':interface').projectDir = new File(settingsDir, 'apps/interface')

View file

@ -6,6 +6,7 @@ import re
import shutil
import xml.etree.ElementTree as ET
import functools
import zipfile
print = functools.partial(print, flush=True)
@ -163,6 +164,31 @@ def copyAndroidLibs(packagePath, appPath):
print("Copying {}".format(lib))
shutil.copy(sourceFile, destFile)
gvrLibFolder = os.path.join(packagePath, 'gvr/gvr-android-sdk-1.101.0/libraries')
audioSoOut = os.path.join(gvrLibFolder, 'libgvr_audio.so')
if not os.path.isfile(audioSoOut):
audioAar = os.path.join(gvrLibFolder, 'sdk-audio-1.101.0.aar')
with zipfile.ZipFile(audioAar) as z:
with z.open('jni/arm64-v8a/libgvr_audio.so') as f:
with open(audioSoOut, 'wb') as of:
shutil.copyfileobj(f, of)
audioSoOut2 = os.path.join(jniPath, 'libgvr_audio.so')
if not os.path.isfile(audioSoOut2):
shutil.copy(audioSoOut, audioSoOut2)
baseSoOut = os.path.join(gvrLibFolder, 'libgvr.so')
if not os.path.isfile(baseSoOut):
baseAar = os.path.join(gvrLibFolder, 'sdk-base-1.101.0.aar')
with zipfile.ZipFile(baseAar) as z:
with z.open('jni/arm64-v8a/libgvr.so') as f:
with open(baseSoOut, 'wb') as of:
shutil.copyfileobj(f, of)
baseSoOut2 = os.path.join(jniPath, 'libgvr.so')
if not os.path.isfile(baseSoOut2):
shutil.copy(baseSoOut, baseSoOut2)
class QtPackager:
def __init__(self, appPath, qtRootPath):
self.appPath = appPath
@ -170,6 +196,7 @@ class QtPackager:
self.jniPath = os.path.join(self.appPath, 'src/main/jniLibs/arm64-v8a')
self.assetPath = os.path.join(self.appPath, 'src/main/assets')
self.qtAssetPath = os.path.join(self.assetPath, '--Added-by-androiddeployqt--')
self.qtAssetCacheList = os.path.join(self.qtAssetPath, 'qt_cache_pregenerated_file_list')
# Jars go into the qt library
self.jarPath = os.path.realpath(os.path.join(self.appPath, '../../libraries/qt/libs'))
self.xmlFile = os.path.join(self.appPath, 'src/main/res/values/libs.xml')
@ -277,10 +304,43 @@ class QtPackager:
tree = ET.ElementTree(libsXmlRoot)
tree.write(self.xmlFile, 'UTF-8', True)
def generateAssetsFileList(self):
print("Implement asset file list")
# outputFilename = os.path.join(self.qtAssetPath, "qt_cache_pregenerated_file_list")
# fileList = hifi_utils.recursiveFileList(self.qtAssetPath)
# fileMap = {}
# for fileName in fileList:
# relativeFileName = os.path.relpath(fileName, self.assetPath)
# dirName, localFileName = os.path.split(relativeFileName)
# if not dirName in fileMap:
# fileMap[dirName] = []
# fileMap[dirName].append(localFileName)
# for dirName in fileMap:
# for localFileName in fileMap[dirName]:
# ????
#
# Gradle version
#
# 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);
# }
# }
def bundle(self):
if not os.path.isfile(self.xmlFile) or True:
if not os.path.isfile(self.xmlFile):
self.copyQtDeps()
self.scanQmlImports()
self.processFiles()
# if not os.path.isfile(self.qtAssetCacheList):
# self.generateAssetsFileList()

View file

@ -97,16 +97,12 @@ def downloadFile(url, hash=None, hasher=hashlib.sha512(), retries=3):
else:
tempFileName, headers = urllib.request.urlretrieve(url)
# for some reason the hash we get back from the downloaded file is sometimes wrong if we check it right away
# but if we examine the file later, it is correct.
time.sleep(3)
downloadHash = hashFile(tempFileName, hasher)
# Verify the hash
if hash is not None and hash != downloadHash:
print("Try {}: Downloaded file {} hash {} does not match expected hash {} for url {}".format(i + 1, tempFileName, downloadHash, hash, url))
os.remove(tempFileName)
continue
return tempFileName
raise RuntimeError("Downloaded file hash {} does not match expected hash {} for\n{}".format(downloadHash, hash, url))

View file

@ -189,6 +189,18 @@ endif()
#hifi_utils.downloadAndExtract(url, dest, hash)
hifi_utils.downloadAndExtract(url, dest)
print("Installing additional android archives")
androidPackages = hifi_android.getPlatformPackages()
for packageName in androidPackages:
package = androidPackages[packageName]
dest = os.path.join(self.androidPackagePath, packageName)
if os.path.isdir(dest):
continue
url = hifi_android.getPackageUrl(package)
zipFile = package['file'].endswith('.zip')
print("Android archive {}".format(package['file']))
hifi_utils.downloadAndExtract(url, dest, isZip=zipFile, hash=package['checksum'], hasher=hashlib.md5())
def writeTag(self):
print("Writing tag {} to {}".format(self.tagContents, self.tagFile))
with open(self.tagFile, 'w') as f:
@ -203,6 +215,12 @@ endif()
cmakeTemplate = VcpkgRepo.CMAKE_TEMPLATE
if not self.args.android:
cmakeTemplate += VcpkgRepo.CMAKE_TEMPLATE_NON_ANDROID
else:
precompiled = os.path.realpath(os.path.join(self.path, 'android'))
qtCmakePrefix = os.path.realpath(os.path.join(precompiled, 'qt/lib/cmake'))
cmakeTemplate += 'set(HIFI_ANDROID_PRECOMPILED "{}")\n'.format(precompiled)
cmakeTemplate += 'set(QT_CMAKE_PREFIX_PATH "{}")\n'.format(qtCmakePrefix)
cmakeConfig = cmakeTemplate.format(cmakeScript, cmakeScript, installPath, toolsPath).replace('\\', '/')
with open(self.configFilePath, 'w') as f:
f.write(cmakeConfig)

View file

@ -43,8 +43,7 @@ def parse_args():
defaultPortsPath = hifi_utils.scriptRelative('cmake', 'ports')
from argparse import ArgumentParser
parser = ArgumentParser(description='Prepare build dependencies.')
parser.add_argument('--android', action='store_true')
#parser.add_argument('--android', type=str)
parser.add_argument('--android', type=str)
parser.add_argument('--debug', action='store_true')
parser.add_argument('--force-bootstrap', action='store_true')
parser.add_argument('--force-build', action='store_true')
@ -87,6 +86,17 @@ def main():
# here shouldn't invalidte the vcpkg install)
pm.cleanBuilds()
# If we're running in android mode, we also need to grab a bunch of additional binaries
# (this logic is all migrated from the old setupDependencies tasks in gradle)
if args.android:
# Find the target location
appPath = hifi_utils.scriptRelative('android/apps/' + args.android)
# Copy the non-Qt libraries specified in the config in hifi_android.py
hifi_android.copyAndroidLibs(pm.androidPackagePath, appPath)
# Determine the Qt package path
qtPath = os.path.join(pm.androidPackagePath, 'qt')
hifi_android.QtPackager(appPath, qtPath).bundle()
# Write the vcpkg config to the build directory last
pm.writeConfig()