1

I have a library with common code which uses android.util.Log:

java_library(
    name = "common",
    srcs = glob(["*.java"]),
)

And I have an j2objc rule for iOS which works just fine:

j2objc_library(
    name = "common_ios",
    deps = ["//common"],
    jre_deps = ["@bazel_j2objc//:android_util_lib"],
)

But when I use common in my Android project:

android_binary(
    name = "app",
    srcs = glob(["*.java"]),
    manifest = "//android:manifest",
    resource_files = ["//android:resources"],
    deps = ["//common"],
)

But when I run bazel build //android:app, I get:

common/MyVeryOwnLogger.java:3: error: package android.util does not exist
import android.util.Log;

Which makes sense, as android.* libs should not be available in a java_library rule. Am I missing something? Is this not the recommended way to setup a project?

Thanks!

Vitor Pereira
  • 77
  • 1
  • 9
  • you may provide custom `android.util.Log` implementation or move `MyVeryOwnLogger` implementation out of common – Selvin Jun 07 '18 at 13:05
  • @Selvin sure but then I'm not reusing code. The idea is to use the `android_util` library in iOS and the native `android.util` package in Android. – Vitor Pereira Jun 07 '18 at 13:09
  • I would 1. create java_library called `android_util` with one file `android/util/Log.java` 2. adds it as `deps` (`deps = ["//common", "android_util"]`) in common_ios ... – Selvin Jun 07 '18 at 13:17
  • The iOS code is working as intended. I probably didn't make that clear enough. You can see that it depends on `//common` but also `android_util_lib` for logging. The issue is that, on Android, the `//common` rule can't find `android.*` classes, even though it is depended on in an `android_binary` rule. This makes me think that my bazel setup is not following the correct practices. EDIT: added the bazel command which gives the error. – Vitor Pereira Jun 07 '18 at 13:27

1 Answers1

1

A java_library won't be able to compile code that depends on Android because it won't have any of the Android dependencies that android_library provides.

Have you tried running your iOS app with a dependency on common that uses Android classes? I'm a little surprised that that works.

In any case, I recommend moving things that are platform dependent out of common and into platform-specific rules.

So, for example, say you have some kind of business logic Model class in common that requires a logger, create some interface like Logger in common, and have Model take an instance of Logger. Then you can have an android_library rule that depends on common and provides an implementation of Logger for Android that uses all the classes in android.util.*. Then your android_binary rule depends on both common and the android_library. In your app code, you can then instantiate an instance of the Android-specific logger and pass that to Model.

For the iOS half of things, you can similarly have an objective-c rule that provides iOS-specific logging (though I'm less familiar with how all that would work in objective-c or iOS).

You might also consider breaking up common into separate rules, which will improve incrementality (e.g. putting logging into its own rule). This all depends on the structure of your code.

ahumesky
  • 4,203
  • 8
  • 12
  • The `j2objc_library` rule depends on `@bazel_j2objc//:android_util_lib` which is an implementation of a couple useful `android.*` classes made available by j2objc (see https://developers.google.com/j2objc/guides/required-link-flags#other_j2objc_libraries). On the Android side, there is obviously no need for such a reimplementation as the runtime has all of these classes available. So the issue is more of a "how do I tell this to bazel". – Vitor Pereira Jun 07 '18 at 20:05
  • Thanks for the pointer, I'll remove that bit from my answer since it's irrelevant. I think the clean solution to getting the `java_library` to compile is still to split it up into separate components, with the android parts in an `android_library`. A `java_library` on its own won't be able to compile android code. That said, it is possible to depend directly on `android.jar` using e.g. `@androidsdk//:platforms/android-25/android.jar`, however that won't be in sync with the `--android_sdk` flag. – ahumesky Jun 07 '18 at 22:40
  • 1
    There is, however, an undocumented rule you can use that does take into account `--android_sdk`: ``` java_import( name = "android_jar", jars = [":android_tools_defaults_jar"], neverlink = True, ) android_tools_defaults_jar( name = "android_tools_defaults_jar" )``` – ahumesky Jun 07 '18 at 22:41
  • 1
    `android_tools_defaults_jar` is undocumented mostly because it's just used internally and we might want to get rid of it one day and replace it with an appropriate set of `config_setting`s + `select`s, but we have no immediate plans to do so as far as I know. – ahumesky Jun 07 '18 at 22:43
  • 1
    Also, in either case, you'll want to use a `java_import` with `neverlink = True` so that your code is only compiled against the classes in the jar, and they don't end up anywhere. – ahumesky Jun 07 '18 at 22:46
  • I'd still prefer not to depend on internal APIs. Tom Ball, from j2objc, suggested having the type of rule depend on which target is being built. Either put my code in an `android_library` or a `java_library` + `j2objc_library`. That's what I ended up doing and it's working fine, for now. Btw, thanks for telling me about `neverlink`! – Vitor Pereira Jun 08 '18 at 11:36