Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting pure-java modules with ThreeTenBP to play nice with ThreeTenABP? #47

Closed
kiwiandroiddev opened this issue Feb 2, 2017 · 8 comments

Comments

@kiwiandroiddev
Copy link

Disclaimer: this is probably a gradle config issue rather than an issue with this library per-se. But hopefully this issue is relevant to android developers wanting to use JSR 310 in multi-module projects.

I have a pure java (non-android) gradle module called common with some utility functions for building ZonedDateTime objects. It depends on the standard jvm backport library (org.threeten:threetenbp).

What I'd like is to have the android app module depend on the common module to make use of these platform-independent utility functions, but depend on threetenABP instead for performance. Should this be possible? Attempting to do it naiively gives this build error:

Error:Execution failed for task ':app:transformResourcesWithMergeJavaResForDevelopDebug'.
> com.android.build.api.transform.TransformException: com.android.builder.packaging.DuplicateFileException: Duplicate files copied in APK org/threeten/bp/format/ChronologyText.properties
  	File1: /Users/matthewcl/.gradle/caches/modules-2/files-2.1/org.threeten/threetenbp/1.3.1/5769e9c27cd5ba74cd3a73785dde0bbb5a2d3c0d/threetenbp-1.3.1.jar
  	File2: /Users/matthewcl/.gradle/caches/modules-2/files-2.1/org.threeten/threetenbp/1.3.1/fecd59cfa6acebf3d0f2f41f55a1cc3e24e59726/threetenbp-1.3.1-no-tzdb.jar

From what I understand, the same public classes and interfaces are in use in both modules (e.g. ZonedDateTime). It seems like something like gradle's provided keyword to have compilation succeed and yet use ABP at runtime would work, but I haven't had much success with it.

Any thoughts?

@JakeWharton
Copy link
Owner

In the Android app you can do something like:

configurations.all {
  resolutionStrategy {
    dependencySubstitution {
      substitute module('org.threeten:threetenbp') with module('com.jakewharton.threetenabp:threetenabp')
    }
  }
}

Which should work, but I've never tested it.

@JakeWharton
Copy link
Owner

@kiwiandroiddev
Copy link
Author

Thanks for the hint. I applied that snippet to app's build script and it's currently giving:

Error:A problem occurred configuring project ':app'.
> Failed to notify project evaluation listener.
   > java.lang.StackOverflowError (no error message)

Could it be applying the substitution to threetenabp's internal dependency on org.threeten:threetenbp and causing recursion..? I'm creating a small sample project to continue investigating

@JakeWharton
Copy link
Owner

Ah, yes. It likely is because it isn't considering the classifier of the dependency. I'm not sure if there's a means of doing that or if that's a Gradle bug.

@JakeWharton JakeWharton reopened this Feb 2, 2017
@tmtron
Copy link

tmtron commented Feb 20, 2017

I got it working like this:

  • In the Android application module "app" (which builds the apk): use ThreeTen Backport for Android (including timezone-info):
    implementation 'com.jakewharton.threetenabp:threetenabp:1.0.5'
  • In Android library modules and plain Java modules (e.g. "common"):
    • use ThreeTen Backport without time-zone info for compilation only:
      compileOnly 'org.threeten:threetenbp:1.3.6:no-tzdb'
    • and use the java ThreeTen Backport with time-zone info for testing:
      testImplementation 'org.threeten:threetenbp:1.3.6'

I think, the substitution that @JakeWharton mentions in this comment should only be required when you use an external library which uses 'org.threeten:threetenbp' to avoid the slow loading of the time-zone info.

@kiwiandroiddev
Copy link
Author

@tmtron thanks, your solution worked for me.

This had a nice cascading benefit in that we could then convert 3 modules to pure-java modules, which brought our build time down from 4:19 to 3:41 (!)

@Byronium
Copy link

Byronium commented Mar 6, 2018

@tmtron Correct me if I'm wrong, but the reason your solution works is the compileOnly declaration. compileOnly dependencies are not included on the runtime class-path and are non-transitive (meaning they will not be included in dependent projects). However, in most cases, wouldn't you need the ThreeTen library to be accessible at runtime by the Java-only module common? Also, is there any particular reason you're using the no-tzdb version of ThreeTen Backport?

Anyways, in my case, I needed the time-zone version of ThreeTenBackport in my Java-only module and I also couldn't make it a compileOnly dependency, so I just excluded it in my Android module app where I define the common module as a dependency.

In my Java-only common module build.gradle:

implementation 'org.threeten:threetenbp:1.3.3'

In my Android app module build.gradle:

implementation (project(":common")) {
    // exclude the threetenbp dependency from the `common` module
    exclude group: 'org.threeten', module: 'threetenbp' 
}

implementation 'com.jakewharton.threetenabp:threetenabp:1.0.5'

(sidenote: I'm using the new implementation syntax instead of compile)

Just another solution in case it helps anyone!

@tmtron
Copy link

tmtron commented Mar 6, 2018

Correct me if I'm wrong, but the reason your solution works is the compileOnly declaration. compileOnly dependencies are not included on the runtime class-path and are non-transitive (meaning they will not be included in dependent projects). However, in most cases, wouldn't you need the ThreeTen library to be accessible at runtime by the Java-only module common?

That's right.
My philosophy is that I only use for compilation what I really need and whoever consumes the common module must provide the correct implementation (threetenbp or treetenabp).

In your case you use threetenbp as default and whoever consumes the common module on android must "substitute" it with treetenabp. So if your common module is used by many java-builds but only one Android build, this may be more convenient. But when you forget this on Android, you will get strange errors (see original question).

I think which of the 2 ways you use depends on your project and is to some degree a matter of taste.

Also, is there any particular reason you're using the no-tzdb version of ThreeTen Backport?

Well, the tz-info is not needed for compilation and no-tzdb is only half the size of the full jar file, so I see no particular reason not to use it. I guess using one or the other will not have dramatic performance differences.

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

No branches or pull requests

4 participants