From c8de3b4673aa78bcee3d82a307ea1c29fce4cdf1 Mon Sep 17 00:00:00 2001 From: Simon Date: Sun, 5 Feb 2023 23:13:43 +0000 Subject: [PATCH] Gradle changes to support building without dependency on the Meta OpenXR SDK Package --- .gitignore | 1 + Projects/Android/build.gradle | 2 +- Projects/Android/settings.gradle | 4 +- XrApp.gradle | 238 +++++++++++++++++++++++++++++++ build.gradle | 22 +++ gradle.properties | 3 + local.properties | 9 ++ 7 files changed, 276 insertions(+), 3 deletions(-) create mode 100644 XrApp.gradle create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 local.properties diff --git a/.gitignore b/.gitignore index 9570ebe..6b50d5e 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ assets/oculussig* Projects/Android/.cxx/* *.json *.bin +.gradle/* Projects/Android/.gradle/* *.ser Projects/Android/.idea/* diff --git a/Projects/Android/build.gradle b/Projects/Android/build.gradle index 23554f2..ef2d9b8 100644 --- a/Projects/Android/build.gradle +++ b/Projects/Android/build.gradle @@ -1,5 +1,5 @@ apply plugin: 'com.android.application' -apply from: "${rootProject.projectDir}/VrApp.gradle" +apply from: "${rootProject.projectDir}/XrApp.gradle" android { // This is the name of the generated apk file, which will have diff --git a/Projects/Android/settings.gradle b/Projects/Android/settings.gradle index e4d8c36..9f4aedf 100644 --- a/Projects/Android/settings.gradle +++ b/Projects/Android/settings.gradle @@ -1,4 +1,4 @@ -rootProject.projectDir = new File(settingsDir, '../../../..') +rootProject.projectDir = new File(settingsDir, '../..') rootProject.name = "JKQuest" -include ':', ':XrSamples:JKQuest:Projects:Android' +include ':', 'Projects:Android' diff --git a/XrApp.gradle b/XrApp.gradle new file mode 100644 index 0000000..d349e51 --- /dev/null +++ b/XrApp.gradle @@ -0,0 +1,238 @@ +import org.gradle.internal.os.OperatingSystem; +import com.android.ddmlib.AndroidDebugBridge +import com.android.ddmlib.IDevice +import com.android.ddmlib.CollectingOutputReceiver +import org.apache.tools.ant.taskdefs.condition.Os + +buildscript { + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.0.0' + } + + tasks.register("wrapper") +} + +class XrAppPlugin implements Plugin { + Project project = null + + void installApk( IDevice device, File apkFile, String applicationId ) { + project.logger.quiet "Installing ${applicationId} on device ${device.serialNumber}" + + String toinstall = "/data/local/tmp/toinstall.apk" + + try { + device.pushFile( apkFile.path, toinstall ) + } catch ( Exception e ) { + throw new RuntimeException( "Failed to push ${apkFile.path} to ${toinstall}. ${e}", e ) + } + + while ( true ) { + try { + device.installRemotePackage( toinstall, true ) + break + } catch ( Exception e ) { + project.logger.quiet "Failed to install ${applicationId} on device ${device.serialNumber} (${e}). Trying to uninstall first." + } + try { + device.uninstallPackage( applicationId ) + } catch ( Exception e ) { + throw new RuntimeException( "Failed to uninstall ${applicationId}. ${e}", e ) + } + } + } + + void stopApk( IDevice device, String packageName ) { + CollectingOutputReceiver receiver = new CollectingOutputReceiver() + device.executeShellCommand( "am force-stop ${packageName}", receiver ) + } + + void runApk( IDevice device, manifestFile ) { + CollectingOutputReceiver receiver = new CollectingOutputReceiver() + def activityClass = new XmlSlurper().parse( manifestFile ).application.activity.find{ it.'intent-filter'.find{ filter -> + return filter.action .find{it.'@android:name'.text() == 'android.intent.action.MAIN' } \ + && ( filter.category.find{it.'@android:name'.text() == 'android.intent.category.LAUNCHER'} \ + || filter.category.find{it.'@android:name'.text() == 'android.intent.category.INFO'} ) + }}.'@android:name' + def startActivity = "${project.android.defaultConfig.applicationId}/${activityClass}" + project.logger.quiet "Starting \'$startActivity\' on ${project.deviceMap.size()} devices:" + project.logger.quiet "- ${device.serialNumber}" + device.executeShellCommand( "am start $startActivity", receiver ) + } + + void apply( Project project ) { + this.project = project + + // FIXME: The Task.leftShift(Closure) method has been deprecated and is scheduled to be removed in Gradle 5.0. Please use Task.doLast(Action) instead. + project.task( "cleanWorkAround" ) { + description "Workaround for .externalNativeBuild not being deleted on clean" + }.doLast { + project.delete project.file( ".externalNativeBuild" ) + } + + project.android { + compileSdkVersion 26 + + defaultConfig { + minSdkVersion 24 + targetSdkVersion 25 + + externalNativeBuild { + ndk { + } + ndkBuild { + def numProcs = Runtime.runtime.availableProcessors() + arguments "V=0", "-j$numProcs", "-C$project.buildDir.parent", "APP_PLATFORM=android-24", "NDK_TOOLCHAIN_VERSION=clang", "APP_STL=c++_static" + } + } + } + + externalNativeBuild { + ndkBuild { + path 'jni/Android.mk' + } + } + + signingConfigs { + def keystorePath = (project.hasProperty('key.store')) ? + new File(project.getProperty('key.store')) : + project.file('android.debug.keystore') + + def keystorePassword = (project.hasProperty('key.store.password')) ? + project.getProperty('key.store.password') : 'android' + + def keystoreKeyAlias = (project.hasProperty('key.alias')) ? + project.getProperty('key.alias') : 'androiddebugkey' + + def keystoreKeyPassword = (project.hasProperty('key.alias.password')) ? + project.getProperty('key.alias.password') : 'android' + + debug { + storeFile keystorePath + storePassword keystorePassword + keyAlias keystoreKeyAlias + keyPassword keystoreKeyPassword + v2SigningEnabled true + } + + release { + storeFile keystorePath + storePassword keystorePassword + keyAlias keystoreKeyAlias + keyPassword keystoreKeyPassword + v2SigningEnabled true + } + } + + buildTypes { + debug { + signingConfig signingConfigs.debug + debuggable true + jniDebuggable true + + externalNativeBuild { + ndkBuild { + arguments "NDK_DEBUG=1","USE_ASAN=1" + } + } + } + + release { + signingConfig signingConfigs.release + debuggable false + jniDebuggable false + + externalNativeBuild { + ndkBuild { + arguments "NDK_DEBUG=0","USE_ASAN=0" + } + } + } + } + } + + // WORKAROUND: On Mac OS X, running ndk-build clean with a high num of parallel executions + // set may result in the following build error: rm: fts_read: No such file or directory. + // Currently, there isn't a good way to specify numProcs=1 only on clean. So, in order + // to work around the issue, delete the auto-generated .externalNativeBuild artifacts + // (where $numProcs specified) before executing the clean task. + project.clean.dependsOn project.cleanWorkAround + + project.clean { + // remove the auto-generated debug keystore (currently generated by python build script) + // delete "android.debug.keystore" + } + + project.afterEvaluate { + Task initDeviceList = project.task( "initDeviceList()" ).doLast { + project.ext.deviceMap = [ : ] + if (project.hasProperty( "should_install" ) == true) { + AndroidDebugBridge.initIfNeeded( false ) + AndroidDebugBridge bridge = AndroidDebugBridge.createBridge( project.android.getAdbExe().absolutePath, false ) + + long timeOut = 30000 // 30 sec + int sleepTime = 1000 + while ( !bridge.hasInitialDeviceList() && timeOut > 0 ) { + sleep( sleepTime ) + timeOut -= sleepTime + } + if ( timeOut <= 0 && !bridge.hasInitialDeviceList() ) { + throw new RuntimeException( "Timeout getting device list.", null ) + } + + // if a device is connected both physically and over the network, only include the physical ID + if ( project.hasProperty( "should_install" ) == true ) { + bridge.devices.split { it.getProperty( "ro.serialno" ) != it.serialNumber }.each { + it.collectEntries( project.deviceMap, { [ ( it.getProperty( "ro.serialno" )) : it ] } ) + } + } + } + } + + project.task( "stopApk", dependsOn: initDeviceList ) { + description "Stops app if currently running on device" + }.doLast { + project.deviceMap.each { deviceSerial, device -> + stopApk( device, android.defaultConfig.applicationId ) + } + } + + project.android.applicationVariants.all { variant -> + Task installAndRun = project.task( "installAndRun${variant.name.capitalize()}" ) { + dependsOn variant.assembleProvider.get() + dependsOn initDeviceList + onlyIf { project.hasProperty( "should_install" ) } + description "Installs and runs the APK file" + }.doLast { variant.outputs.each { output -> + if ( output.outputFile.exists() ) { + if ( project.deviceMap.size() == 0 ) { + project.logger.quiet "Install requested, but no devices found." + } else { + project.deviceMap.each { deviceSerial, device -> + installApk( device, output.outputFile, project.android.defaultConfig.applicationId ) + runApk( device, new File(output.processManifest.manifestOutputDirectory.get().asFile, "AndroidManifest.xml")) + } + } + } + } + } + variant.assembleProvider.get().finalizedBy installAndRun + } + } + } +} + +// Workaround to fix issue in Android Studio Chipmunk 2021.2.1 and later +// where opening a project would result in a 'prepareKotlinBuildScriptModel' +// not found error +if (!tasks.findByName("prepareKotlinBuildScriptModel")) { + tasks.register("prepareKotlinBuildScriptModel") {} +} + + + +apply plugin: XrAppPlugin diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..05092ac --- /dev/null +++ b/build.gradle @@ -0,0 +1,22 @@ +buildscript { + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.0.0' + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} + diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..b38e197 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +org.gradle.caching=true +org.gradle.configureondemand=true diff --git a/local.properties b/local.properties new file mode 100644 index 0000000..4494cdd --- /dev/null +++ b/local.properties @@ -0,0 +1,9 @@ +## This file must *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. +# +# Location of the SDK. This is only used by Gradle. +# For customization when using a Version Control System, please read the +# header note. +#Thu Dec 12 20:21:11 GMT 2019 +ndk.dir=C\:\\Users\\Simon\\AppData\\Local\\Android\\Sdk\\ndk\\21.1.6352462 +sdk.dir=C\:\\Users\\Simon\\AppData\\Local\\Android\\Sdk