Skip to content

Add AOT output dir to compile classpath - #1

Open
mogenson wants to merge 1 commit into
clj-android:mainfrom
mogenson:add-dest-dir-to-compile-classpath
Open

Add AOT output dir to compile classpath#1
mogenson wants to merge 1 commit into
clj-android:mainfrom
mogenson:add-dest-dir-to-compile-classpath

Conversation

@mogenson

@mogenson mogenson commented Sep 2, 2026

Copy link
Copy Markdown

Problem

ClojureCompileTask sets the Clojure compile output directory only via the clojure.compile.path system property (the destination for generated .class files). It is not added to the javaexec classpath.

Consequently, when a namespace (:import ...)s or directly constructs a gen-class'd class produced by a sibling namespace compiled earlier in the same task, the AOT compiler's DynamicClassLoader cannot resolve it — the sibling's .class file exists on disk but the output dir is not a classpath root. Compilation fails with:

Caused by: java.lang.ClassNotFoundException: com.example.MyGeneratedClass
  at clojure.lang.RT.classForName(RT.java:...)
  at com.example.my_service$loading__...invoke(my_service.clj:1)   ; the ns form

This is common for apps with multiple gen-class'd Android entry points — e.g. a WallpaperService that constructs its gen-class'd Engine, or a Worker referenced by name. (Discovered while porting a live-wallpaper app whose BingWallpaperService builds its BingEngine.)

Fix

Prepend destinationDir to the javaexec classpath. Classes generated by earlier-compiled namespaces are now on the classpath, so a later namespace can :import / construct them directly. This removes the need to resolve such classes reflectively via Class/forName.

Test

Ported com.binglivewallpaper.wallpaper-service from a reflective Class/forName lookup to a direct (BingEngine. this) constructor call. With this change, compileDebugClojure succeeds (it previously threw ClassNotFoundException: com.binglivewallpaper.BingEngine), all three gen-class'd entry-point classes are emitted, the full assembleDebug APK builds, and the unit tests pass.

Prepend the ClojureCompileTask destination directory to the javaexec
classpath so that classes generated by earlier-compiled namespaces are
resolvable when a later namespace references them during its own
compilation.

Previously the output directory was set only as clojure.compile.path
(the *destination* for generated .class files) and was not a classpath
root. As a result, a namespace that (:import ...)s or directly
constructs a gen-class'd class produced by a sibling namespace failed at
AOT time with ClassNotFoundException — the sibling's .class file existed
on disk but the compiler's DynamicClassLoader could not load it.

This is common for apps with multiple gen-class'd Android entry points
(e.g. a WallpaperService that constructs its gen-class'd Engine, or a
Worker referenced by name). The workaround of resolving such classes
reflectively via Class/forName is no longer necessary.

Tested against a live-wallpaper app whose BingWallpaperService directly
constructs its gen-class'd BingEngine: compilation now succeeds and all
entry-point classes are emitted.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant