Skip to content

Commit ca42c9d

Browse files
authored
Merge pull request #24 from MovingBlocks/android/gestalt-di-fix-android
Android compilation fixes for MovingBlocks/DestinationSol#622
2 parents 1b1c0db + 178770f commit ca42c9d

File tree

3 files changed

+117
-246
lines changed

3 files changed

+117
-246
lines changed

build.gradle

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ dependencies {
7171
}
7272
// Android-compatible JOML variant
7373
implementation "org.joml:joml-jdk3:1.9.25"
74-
implementation(group: 'org.terasology.gestalt', name: 'gestalt-android', version: '7.2.0-SNAPSHOT')
74+
implementation(group: 'org.terasology.gestalt', name: 'gestalt-android', version: gestaltVersion)
7575
// TODO: Needed for gestalt because of an internal dependency but since that dependency is never
7676
// exposed in a public API, I have no idea why it's needed for compilation.
7777
implementation "com.github.zafarkhaja:java-semver:0.10.0"
@@ -89,8 +89,14 @@ dependencies {
8989
natives "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-x86"
9090
natives "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-x86_64"
9191

92+
// Add reflections purely for NUI. NUI uses the ReflectionUtils class from it.
93+
implementation "org.terasology:reflections:0.9.12-MB"
94+
9295
// Android-compatible logging
9396
implementation group: 'org.slf4j', name: 'slf4j-android', version: '1.7.25'
97+
98+
// Backport some Java 8 APIs like java.time for gestalt
99+
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
94100
}
95101

96102
ext {
@@ -127,6 +133,9 @@ android {
127133

128134
// Make it clear we're compiling for Java 8
129135
compileOptions {
136+
// Backport some Java 8 APIs like java.time for gestalt
137+
coreLibraryDesugaringEnabled true
138+
130139
sourceCompatibility JavaVersion.VERSION_1_8
131140
targetCompatibility JavaVersion.VERSION_1_8
132141
}
@@ -153,6 +162,15 @@ android {
153162
abortOnError false
154163
}
155164

165+
packagingOptions {
166+
merge 'META-INF/annotations/*'
167+
merge 'META-INF/subtypes/*'
168+
merge 'META-INF/services/*'
169+
170+
// The contents of the "gestalt-indexes-present" file is never read, so just pick any one of them
171+
pickFirst 'META-INF/gestalt-indexes-present'
172+
}
173+
156174
defaultConfig {
157175
targetSdkVersion(project.ext.targetSdk)
158176
minSdkVersion(project.ext.minSdk)
@@ -225,9 +243,9 @@ def deleteDir(File dir) {
225243
dir.delete()
226244
}
227245

228-
task modulesReflections
246+
task modulesJar
229247
rootProject.destinationSolModules().each { module ->
230-
modulesReflections.dependsOn ":modules:$module.name" + ":cacheReflections"
248+
modulesJar.dependsOn ":modules:$module.name" + ":jar"
231249
}
232250

233251
task exportModules() {
@@ -243,8 +261,7 @@ task exportModules() {
243261

244262
outputs.dir("$projectDir/assets/modules")
245263

246-
dependsOn ":engine:cacheReflections"
247-
dependsOn modulesReflections
264+
dependsOn modulesJar
248265

249266
doLast {
250267
dexModules()
@@ -277,7 +294,6 @@ task exportModules() {
277294
Files.createSymbolicLink(Paths.get("$projectDir/assets/modules/${module.name}/module.json"), Paths.get("$rootDir/modules/${module.name}/module.json"))
278295

279296
file("$projectDir/assets/modules/${module.name}/build/classes").mkdirs()
280-
Files.createSymbolicLink(Paths.get("$projectDir/assets/modules/${module.name}/build/classes/reflections.cache"), Paths.get("$rootDir/modules/${module.name}/build/classes/reflections.cache"))
281297
Files.createSymbolicLink(Paths.get("$projectDir/assets/modules/${module.name}/build/dexes"), Paths.get("$rootDir/modules/${module.name}/build/dexes"))
282298
}
283299
} else {
@@ -297,8 +313,7 @@ task exportModules() {
297313
include "${module.name}/assets/**"
298314
include "${module.name}/overrides/**"
299315
include "${module.name}/deltas/**"
300-
include "${module.name}/build/dexes/*.dex"
301-
include "${module.name}/build/classes/reflections.cache"
316+
include "${module.name}/build/dexes/*.jar"
302317
}
303318
}
304319
}
@@ -326,9 +341,9 @@ def dexModules() {
326341
}
327342
def dex = "${buildToolsVersions[0]}/$dexCommand"
328343
for (module in rootProject.destinationSolModules()) {
329-
def moduleClassesDir = "${rootProject.projectDir}/modules/${module.name}/build/classes/"
344+
def moduleClassesDir = file("${rootProject.projectDir}/modules/${module.name}/build/classes/")
330345
def moduleClassesFiles = fileTree(moduleClassesDir).filter { it.isFile() && it.name.endsWith('.class')}.files
331-
def classesRootPath = new File(moduleClassesDir).toPath()
346+
def classesRootPath = moduleClassesDir.toPath()
332347
def moduleClasses = []
333348
for (file in moduleClassesFiles) {
334349
moduleClasses.add(classesRootPath.relativize(file.toPath()))
@@ -339,12 +354,30 @@ def dexModules() {
339354
}
340355
def moduleDexesDir = "${rootProject.projectDir}/modules/${module.name}/build/dexes/"
341356
mkdir(moduleDexesDir)
357+
def jarOutputPath = "$moduleDexesDir/${module.name}.jar"
342358
exec {
343359
workingDir moduleClassesDir
344360
commandLine (["$dex"] + moduleClasses + ['--classpath', "$rootDir/engine/build/classes",
345361
'--lib', "$path/platforms/android-$compileSdk/android.jar",
346362
'--min-api', "$minSdk",
347-
'--output', "$moduleDexesDir"])
363+
'--output', "$jarOutputPath"])
364+
}
365+
FileSystem jarFileSystem
366+
try {
367+
jarFileSystem = FileSystems.newFileSystem(URI.create("jar:" + new File(jarOutputPath).toURI()), new HashMap<String, Object>())
368+
fileTree(moduleClassesDir).filter {
369+
it.isFile() && !it.name.endsWith(".class") && !it.path.startsWith("assets/")
370+
}.each {
371+
def destinationPath = jarFileSystem.getPath("/${moduleClassesDir.relativePath(it)}")
372+
Files.createDirectories(destinationPath.parent)
373+
Files.copy(it.toPath(), destinationPath)
374+
}
375+
} catch (Exception e) {
376+
e.printStackTrace()
377+
} finally {
378+
if (jarFileSystem != null) {
379+
jarFileSystem.close()
380+
}
348381
}
349382
}
350383
}
Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,95 @@
11
package com.miloshpetrov.sol2.android;
22

3+
import android.content.res.AssetManager;
34
import android.os.Bundle;
45
import android.util.Log;
56

67
import com.badlogic.gdx.backends.android.AndroidApplication;
78
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;
9+
import com.google.common.reflect.Reflection;
810
import org.destinationsol.SolApplication;
9-
import org.destinationsol.modules.ModuleManager;
10-
import org.destinationsol.android.AndroidModuleManager;
11+
import org.destinationsol.modules.FacadeModuleConfig;
12+
import org.terasology.context.Lifetime;
13+
import org.terasology.gestalt.android.AndroidAssetsFileSource;
14+
import org.terasology.gestalt.android.AndroidModuleClassLoader;
15+
import org.terasology.gestalt.android.AndroidModulePathScanner;
16+
import org.terasology.gestalt.di.ServiceRegistry;
17+
import org.terasology.gestalt.di.index.UrlClassIndex;
18+
import org.terasology.gestalt.module.Module;
19+
import org.terasology.gestalt.module.ModuleEnvironment;
20+
import org.terasology.gestalt.module.ModuleMetadataJsonAdapter;
21+
import org.terasology.gestalt.module.ModulePathScanner;
22+
23+
import java.io.File;
24+
import java.io.InputStream;
25+
import java.io.InputStreamReader;
26+
import java.util.Collections;
1127

1228
public class SolAndroid extends AndroidApplication {
1329
@Override
1430
public void onCreate(Bundle savedInstanceState) {
1531
super.onCreate(savedInstanceState);
1632
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
1733

18-
ModuleManager manager = new AndroidModuleManager(this);
19-
2034
try {
21-
manager.init();
35+
initialize(new SolApplication(60.0f, new AndroidServices(getAssets(), getCodeCacheDir())), config);
2236
} catch (Exception e) {
23-
Log.e("DESTINATION_SOL_INIT", "Failed to initialise ModuleManager.");
37+
Log.e("DESTINATION_SOL", "FATAL ERROR: Forced abort!", e);
2438
}
39+
}
2540

26-
try {
27-
initialize(new SolApplication(manager, 60.0f), config);
28-
} catch (Exception e) {
29-
Log.e("DESTINATION_SOL", "FATAL ERROR: Forced abort!", e);
41+
private static class AndroidServices extends ServiceRegistry {
42+
public AndroidServices(AssetManager assets, File codeCacheDir) {
43+
this.with(FacadeModuleConfig.class).lifetime(Lifetime.Singleton).use(() -> new AndroidModuleConfig(assets, codeCacheDir));
44+
this.with(ModulePathScanner.class).lifetime(Lifetime.Singleton).use(() -> new AndroidModulePathScanner(assets, codeCacheDir));
45+
}
46+
}
47+
48+
private static class AndroidModuleConfig implements FacadeModuleConfig {
49+
private final AssetManager assets;
50+
private final File codeCacheDir;
51+
52+
public AndroidModuleConfig(AssetManager assets, File codeCacheDir) {
53+
this.assets = assets;
54+
this.codeCacheDir = codeCacheDir;
55+
}
56+
57+
@Override
58+
public File getModulesPath() {
59+
return new File("modules");
60+
}
61+
62+
// Android does not allow changing the system security policy.
63+
// Modules should still be restricted via classpath filtering though.
64+
@Override
65+
public boolean useSecurityManager() {
66+
return false;
67+
}
68+
69+
@Override
70+
public Module createEngineModule() {
71+
try {
72+
InputStream engineModuleMetadataStream = assets.open("engine/module.json");
73+
return new Module(new ModuleMetadataJsonAdapter().read(new InputStreamReader(engineModuleMetadataStream)),
74+
new AndroidAssetsFileSource(assets, "engine"),
75+
Collections.emptyList(), UrlClassIndex.byClassLoaderPrefix("org.destinationsol"), x -> {
76+
String classPackageName = Reflection.getPackageName(x);
77+
return "org.destinationsol".equals(classPackageName) || classPackageName.startsWith("org.destinationsol.");
78+
});
79+
} catch (Exception e) {
80+
Log.e("DestinationSol", "Error loading engine module!");
81+
return null;
82+
}
83+
}
84+
85+
@Override
86+
public ModuleEnvironment.ClassLoaderSupplier getClassLoaderSupplier() {
87+
return (module, parent, permissionProvider) -> AndroidModuleClassLoader.create(module, parent, permissionProvider, codeCacheDir);
88+
}
89+
90+
@Override
91+
public Class<?>[] getAPIClasses() {
92+
return new Class<?>[0];
3093
}
3194
}
3295
}

0 commit comments

Comments
 (0)