estevenson | c0b135ff | 2016-11-17 16:03:51 | [diff] [blame] | 1 | # Accessing C++ Enums In Java |
| 2 | |
| 3 | [TOC] |
| 4 | |
| 5 | ## Introduction |
| 6 | |
| 7 | Accessing C++ enums in Java is implemented via a Python script which analyzes |
| 8 | the C++ enum and spits out the corresponding Java class. The enum needs to be |
| 9 | annotated in a particular way. By default, the generated class name will be the |
| 10 | same as the name of the enum. If all the names of the enum values are prefixed |
| 11 | with the MACRO\_CASED\_ name of the enum those prefixes will be stripped from |
| 12 | the Java version. |
| 13 | |
| 14 | ## Features |
| 15 | * Customize the package name of the generated class using the |
| 16 | `GENERATED_JAVA_ENUM_PACKAGE` directive (required) |
| 17 | * Customize the class name using the `GENERATED_JAVA_CLASS_NAME_OVERRIDE` |
| 18 | directive (optional) |
| 19 | * Strip enum entry prefixes to make the generated classes less verbose using |
| 20 | the `GENERATED_JAVA_PREFIX_TO_STRIP` directive (optional) |
Nate Fischer | bce81715 | 2021-07-14 02:14:15 | [diff] [blame] | 21 | * Follows best practices by using |
| 22 | [IntDef Instead of Enum](/styleguide/java/java.md#IntDef-Instead-of-Enum) |
Keigo Oka | c517460 | 2024-11-19 15:55:19 | [diff] [blame] | 23 | * Generate the `flag` attribute using the `GENERATED_JAVA_IS_FLAG` directive (optional) |
qyearsley | c0dc6f4 | 2016-12-02 22:13:39 | [diff] [blame] | 24 | * Copies comments that directly precede enum entries into the generated Java |
estevenson | c0b135ff | 2016-11-17 16:03:51 | [diff] [blame] | 25 | class |
| 26 | |
| 27 | ## Usage |
| 28 | |
Nate Fischer | bce81715 | 2021-07-14 02:14:15 | [diff] [blame] | 29 | 1. Add directives to your C++ enum. Only the `GENERATED_JAVA_ENUM_PACKAGE` |
| 30 | directive is required: |
estevenson | c0b135ff | 2016-11-17 16:03:51 | [diff] [blame] | 31 | |
| 32 | ```cpp |
| 33 | // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome |
| 34 | // GENERATED_JAVA_CLASS_NAME_OVERRIDE: FooBar |
| 35 | // GENERATED_JAVA_PREFIX_TO_STRIP: BAR_ |
Keigo Oka | c517460 | 2024-11-19 15:55:19 | [diff] [blame] | 36 | // GENERATED_JAVA_IS_FLAG: true |
estevenson | c0b135ff | 2016-11-17 16:03:51 | [diff] [blame] | 37 | enum SomeEnum { |
Keigo Oka | c517460 | 2024-11-19 15:55:19 | [diff] [blame] | 38 | BAR_A = 1 << 0, |
| 39 | BAR_B = 1 << 1, |
estevenson | c0b135ff | 2016-11-17 16:03:51 | [diff] [blame] | 40 | BAR_C = BAR_B, |
| 41 | }; |
| 42 | ``` |
| 43 | |
Nate Fischer | bce81715 | 2021-07-14 02:14:15 | [diff] [blame] | 44 | 2. Add a new build target and add it to the `srcjar_deps` of an |
| 45 | `android_library` target: |
estevenson | c0b135ff | 2016-11-17 16:03:51 | [diff] [blame] | 46 | |
Nate Fischer | 93c5915 | 2019-02-05 01:25:08 | [diff] [blame] | 47 | ```gn |
Nate Fischer | bce81715 | 2021-07-14 02:14:15 | [diff] [blame] | 48 | if (is_android) { |
| 49 | import("//build/config/android/rules.gni") |
| 50 | } |
estevenson | c0b135ff | 2016-11-17 16:03:51 | [diff] [blame] | 51 | |
Nate Fischer | bce81715 | 2021-07-14 02:14:15 | [diff] [blame] | 52 | if (is_android) { |
| 53 | java_cpp_enum("java_enum_srcjar") { |
| 54 | # External code should depend on ":foo_java" instead. |
| 55 | visibility = [ ":*" ] |
| 56 | sources = [ |
| 57 | # Include the .h or .cc file(s) which defines the enum(s). |
| 58 | "base/android/native_foo_header.h", |
| 59 | ] |
| 60 | } |
| 61 | |
| 62 | # If there's already an android_library target, you can add |
| 63 | # java_enum_srcjar to that target's srcjar_deps. Otherwise, the best |
| 64 | # practice is to create a new android_library just for this target. |
| 65 | android_library("foo_java") { |
| 66 | srcjar_deps = [ ":java_enum_srcjar" ] |
| 67 | |
| 68 | # Important: the generated enum uses the @IntDef annotation provided by |
| 69 | # this dependency. |
| 70 | deps = [ "//third_party/androidx:androidx_annotation_annotation_java" ] |
| 71 | } |
estevenson | c0b135ff | 2016-11-17 16:03:51 | [diff] [blame] | 72 | } |
| 73 | ``` |
| 74 | |
Nate Fischer | bce81715 | 2021-07-14 02:14:15 | [diff] [blame] | 75 | 3. The generated file `org/chromium/chrome/FooBar.java` would contain: |
estevenson | c0b135ff | 2016-11-17 16:03:51 | [diff] [blame] | 76 | |
| 77 | ```java |
| 78 | package org.chromium.chrome; |
| 79 | |
Nate Fischer | bce81715 | 2021-07-14 02:14:15 | [diff] [blame] | 80 | import androidx.annotation.IntDef; |
estevenson | c0b135ff | 2016-11-17 16:03:51 | [diff] [blame] | 81 | |
| 82 | import java.lang.annotation.Retention; |
| 83 | import java.lang.annotation.RetentionPolicy; |
| 84 | |
Keigo Oka | c517460 | 2024-11-19 15:55:19 | [diff] [blame] | 85 | @IntDef(flag = true, value = { |
dgn | 2b5c5e82 | 2017-05-12 20:33:39 | [diff] [blame] | 86 | FooBar.A, FooBar.B, FooBar.C |
| 87 | }) |
| 88 | @Retention(RetentionPolicy.SOURCE) |
| 89 | public @interface FooBar { |
Keigo Oka | c517460 | 2024-11-19 15:55:19 | [diff] [blame] | 90 | int A = 1 << 0; |
| 91 | int B = 1 << 1; |
| 92 | int C = 1 << 1; |
estevenson | c0b135ff | 2016-11-17 16:03:51 | [diff] [blame] | 93 | } |
| 94 | ``` |
| 95 | |
| 96 | ## Formatting Notes |
| 97 | |
| 98 | * Handling long package names: |
| 99 | |
Nate Fischer | 93c5915 | 2019-02-05 01:25:08 | [diff] [blame] | 100 | ```cpp |
estevenson | c0b135ff | 2016-11-17 16:03:51 | [diff] [blame] | 101 | // GENERATED_JAVA_ENUM_PACKAGE: ( |
| 102 | // org.chromium.chrome.this.package.is.too.long.to.fit.on.a.single.line) |
| 103 | ``` |
| 104 | |
| 105 | * Enum entries |
| 106 | * Single line enums should look like this: |
| 107 | |
Nate Fischer | 93c5915 | 2019-02-05 01:25:08 | [diff] [blame] | 108 | ```cpp |
| 109 | // GENERATED_JAVA_ENUM_PACKAGE: org.foo |
| 110 | enum NotificationActionType { BUTTON, TEXT }; |
| 111 | ``` |
estevenson | c0b135ff | 2016-11-17 16:03:51 | [diff] [blame] | 112 | |
| 113 | * Multi-line enums should have one enum entry per line, like this: |
| 114 | |
Nate Fischer | 93c5915 | 2019-02-05 01:25:08 | [diff] [blame] | 115 | ```cpp |
| 116 | // GENERATED_JAVA_ENUM_PACKAGE: org.foo |
| 117 | enum NotificationActionType { |
| 118 | BUTTON, |
| 119 | TEXT |
| 120 | }; |
| 121 | ``` |
estevenson | c0b135ff | 2016-11-17 16:03:51 | [diff] [blame] | 122 | |
| 123 | * Multi-line enum entries are allowed but should be formatted like this: |
| 124 | |
Nate Fischer | 93c5915 | 2019-02-05 01:25:08 | [diff] [blame] | 125 | ```cpp |
| 126 | // GENERATED_JAVA_ENUM_PACKAGE: org.foo |
| 127 | enum NotificationActionType { |
| 128 | LongKeyNumberOne, |
| 129 | LongKeyNumberTwo, |
| 130 | ... |
| 131 | LongKeyNumberThree = |
| 132 | LongKeyNumberOne | LongKeyNumberTwo | ... |
| 133 | }; |
| 134 | ``` |
estevenson | c0b135ff | 2016-11-17 16:03:51 | [diff] [blame] | 135 | |
| 136 | * Preserving comments |
| 137 | |
| 138 | ```cpp |
| 139 | // GENERATED_JAVA_ENUM_PACKAGE: org.chromium |
| 140 | enum CommentEnum { |
| 141 | // This comment will be preserved. |
| 142 | ONE, |
| 143 | TWO, // This comment will NOT be preserved. |
| 144 | THREE |
| 145 | } |
| 146 | ``` |
| 147 | |
| 148 | ```java |
| 149 | ... |
dgn | 2b5c5e82 | 2017-05-12 20:33:39 | [diff] [blame] | 150 | public @interface CommentEnum { |
estevenson | c0b135ff | 2016-11-17 16:03:51 | [diff] [blame] | 151 | ... |
| 152 | /** |
| 153 | * This comment will be preserved. |
| 154 | */ |
dgn | 2b5c5e82 | 2017-05-12 20:33:39 | [diff] [blame] | 155 | int ONE = 0; |
| 156 | int TWO = 1; |
| 157 | int THREE = 2; |
estevenson | c0b135ff | 2016-11-17 16:03:51 | [diff] [blame] | 158 | } |
| 159 | ``` |
| 160 | |
Nate Fischer | bce81715 | 2021-07-14 02:14:15 | [diff] [blame] | 161 | ## Troubleshooting |
| 162 | |
| 163 | ### Symbol not found/could not resolve IntDef |
| 164 | |
| 165 | You may see an error like this when compiling: |
| 166 | |
| 167 | ```shell |
| 168 | $ autoninja -C out/Default base/foo_java |
| 169 | util.build_utils.CalledProcessError: Command failed: ... |
| 170 | org/chromium/chrome/FooBar.java:13: error: symbol not found androidx.annotation.IntDef |
Andrew Grieve | 048ea39 | 2022-02-14 17:58:54 | [diff] [blame] | 171 | Hint: Add "//third_party/androidx:androidx_annotation_annotation_java" to deps of //base/foo_java |
Nate Fischer | bce81715 | 2021-07-14 02:14:15 | [diff] [blame] | 172 | import androidx.annotation.IntDef; |
| 173 | ^ |
| 174 | org/chromium/chrome/FooBar.java:18: error: could not resolve IntDef |
| 175 | @IntDef({ |
| 176 | ^ |
| 177 | ``` |
| 178 | |
| 179 | The fix is to add |
| 180 | `"//third_party/androidx:androidx_annotation_annotation_java"` to the `deps` of |
| 181 | the `android_library`. Note: **do not** add this to the `java_cpp_enum` target |
| 182 | by mistake, otherwise you'll see a new error: |
| 183 | |
| 184 | ```shell |
| 185 | $ autoninja -C out/Default base/foo_java |
| 186 | [0/1] Regenerating ninja files |
| 187 | ERROR at //base/BUILD.gn:194:12: Assignment had no effect. |
| 188 | deps = [ "//third_party/androidx:androidx_annotation_annotation_java" ] |
| 189 | ^-------------------------------------------------------------- |
| 190 | You set the variable "deps" here and it was unused before it went |
| 191 | out of scope. |
| 192 | ... |
| 193 | ``` |
| 194 | |
Nate Fischer | ac07b262 | 2020-10-01 20:20:14 | [diff] [blame] | 195 | ## See also |
| 196 | * [Accessing C++ Switches In Java](android_accessing_cpp_switches_in_java.md) |
| 197 | * [Accessing C++ Features In Java](android_accessing_cpp_features_in_java.md) |
| 198 | |
estevenson | c0b135ff | 2016-11-17 16:03:51 | [diff] [blame] | 199 | ## Code |
| 200 | * [Generator |
| 201 | code](https://cs.chromium.org/chromium/src/build/android/gyp/java_cpp_enum.py?dr=C&sq=package:chromium) |
| 202 | and |
| 203 | [Tests](https://cs.chromium.org/chromium/src/build/android/gyp/java_cpp_enum_tests.py?dr=C&q=java_cpp_enum_tests&sq=package:chromium&l=1) |
| 204 | * [GN |
| 205 | template](https://cs.chromium.org/chromium/src/build/config/android/rules.gni?q=java_cpp_enum.py&sq=package:chromium&dr=C&l=458) |