Googler | 7b30d75 | 2017-07-17 16:35:17 +0200 | [diff] [blame] | 1 | --- |
| 2 | layout: documentation |
| 3 | title: Migrating from Maven to Bazel |
| 4 | --- |
| 5 | |
| 6 | # Migrating from Maven to Bazel |
| 7 | |
| 8 | When migrating from any build tool to Bazel, it’s best to have both build |
| 9 | tools running in parallel until you have fully migrated your development team, |
| 10 | CI system, and any other relevant systems. You can run Maven and Bazel in the |
| 11 | same repository. |
| 12 | |
| 13 | ## Table of contents |
| 14 | |
| 15 | |
| 16 | * [Before you begin](#before-you-begin) |
| 17 | * [Differences between Maven and Bazel](#differences-between-maven-and-bazel) |
| 18 | * [Migrate from Maven to Bazel:](#migrate-from-maven-to-bazel) |
| 19 | * [1. Create the WORKSPACE file](#1-workspace) |
| 20 | * [Guava project example](#guava-1) |
| 21 | * [2. Create one BUILD file](#2-build) |
| 22 | * [Guava project example](#guava-2) |
Googler | ef1424c | 2017-08-30 00:03:42 +0200 | [diff] [blame] | 23 | * [3. Create more BUILD files (Optional)](#3-build) |
Googler | 7b30d75 | 2017-07-17 16:35:17 +0200 | [diff] [blame] | 24 | * [4. Build using Bazel](#4-build) |
| 25 | |
| 26 | ## Before you begin |
| 27 | |
| 28 | * [Install Bazel](install.md) if it’s not yet installed. |
| 29 | * If you’re new to Bazel, go through the tutorial |
| 30 | [Introduction to Bazel: Build Java](tutorial/java.md) before you start |
| 31 | migrating. The tutorial explains Bazel’s concepts, structure, and label |
| 32 | syntax. |
| 33 | |
| 34 | ## Differences between Maven and Bazel |
| 35 | |
| 36 | * Maven uses top-level `pom.xml` file(s). Bazel supports multiple build |
| 37 | files and multiple targets per BUILD file, allowing for builds that |
| 38 | are more incremental than Maven's. |
| 39 | * Maven takes charge of steps for the deployment process. Bazel does |
| 40 | not automate deployment. |
| 41 | * Bazel enables you to express dependencies between languages. |
| 42 | * As you add new sections to the project, with Bazel you may need to add new |
| 43 | BUILD files. Best practice is to add a BUILD file to each new Java package. |
| 44 | |
| 45 | ## Migrate from Maven to Bazel |
| 46 | |
| 47 | The steps below describe how to migrate your project to Bazel: |
| 48 | |
| 49 | 1. [Create the WORKSPACE file](#1-workspace) |
| 50 | 2. [Create one BUILD file](#2-build) |
| 51 | 3. [Create more BUILD files](#3-build) |
| 52 | 4. [Build using Bazel](#4-build) |
| 53 | |
| 54 | Examples below come from a migration of the |
| 55 | [Guava project](https://github.com/google/guava) from Maven to Bazel. The Guava |
| 56 | project used is release 22.0. The examples using Guava do not walk through |
| 57 | each step in the migration, but they do show the files and contents that are |
| 58 | generated or added manually for the migration. |
| 59 | |
| 60 | ### <a name="1-workspace"></a>1. Create the WORKSPACE file |
| 61 | |
| 62 | Create a file named `WORKSPACE` at the root of your project. If your project |
| 63 | has no external dependencies, the workspace file can be empty. |
| 64 | |
| 65 | If your project depends on files or packages that are not in one of the |
| 66 | project’s directories, specify these external dependencies in the workspace |
| 67 | file. To automate the listing of external dependencies for the workspace file, |
| 68 | use the tool `generate_workspace`. For instructions about using this tool, see |
| 69 | [Generate a WORKSPACE file for a Java project](generate-workspace.md). |
| 70 | |
| 71 | #### <a name="guava-1"></a>Guava project example: external dependencies |
| 72 | |
| 73 | Below are the results of using the tool `generate_workspace` to list the |
| 74 | [Guava project's](https://github.com/google/guava) external dependencies. |
| 75 | |
| 76 | 1. The new `WORKSPACE` file contains: |
| 77 | |
| 78 | ```bash |
| 79 | load("//:generate_workspace.bzl", "generated_maven_jars") |
| 80 | generated_maven_jars() |
| 81 | ``` |
| 82 | |
| 83 | 2. The new `BUILD` file in the directory `third_party` enables access |
| 84 | to external libraries. This BUILD file contains: |
| 85 | |
| 86 | ```bash |
| 87 | load("//:generate_workspace.bzl", "generated_java_libraries") |
| 88 | generated_java_libraries() |
| 89 | ``` |
| 90 | |
| 91 | 3. The generated `generate_workspace.bzl` file contains: |
| 92 | |
| 93 | ```bash |
| 94 | # The following dependencies were calculated from: |
| 95 | # |
| 96 | # generate_workspace --maven_project=/usr/local/.../guava |
| 97 | |
| 98 | |
| 99 | def generated_maven_jars(): |
| 100 | # pom.xml got requested version |
| 101 | # com.google.guava:guava-parent:pom:23.0-SNAPSHOT |
| 102 | native.maven_jar( |
| 103 | name = "com_google_code_findbugs_jsr305", |
| 104 | artifact = "com.google.code.findbugs:jsr305:1.3.9", |
| 105 | sha1 = "40719ea6961c0cb6afaeb6a921eaa1f6afd4cfdf", |
| 106 | ) |
| 107 | |
| 108 | |
| 109 | # pom.xml got requested version |
| 110 | # com.google.guava:guava-parent:pom:23.0-SNAPSHOT |
| 111 | native.maven_jar( |
| 112 | name = "com_google_errorprone_error_prone_annotations", |
| 113 | artifact = "com.google.errorprone:error_prone_annotations:2.0.18", |
| 114 | sha1 = "5f65affce1684999e2f4024983835efc3504012e", |
| 115 | ) |
| 116 | |
| 117 | |
| 118 | # pom.xml got requested version |
| 119 | # com.google.guava:guava-parent:pom:23.0-SNAPSHOT |
| 120 | native.maven_jar( |
| 121 | name = "com_google_j2objc_j2objc_annotations", |
| 122 | artifact = "com.google.j2objc:j2objc-annotations:1.1", |
| 123 | sha1 = "ed28ded51a8b1c6b112568def5f4b455e6809019", |
| 124 | ) |
| 125 | |
| 126 | |
| 127 | |
| 128 | |
| 129 | def generated_java_libraries(): |
| 130 | native.java_library( |
| 131 | name = "com_google_code_findbugs_jsr305", |
| 132 | visibility = ["//visibility:public"], |
| 133 | exports = ["@com_google_code_findbugs_jsr305//jar"], |
| 134 | ) |
| 135 | |
| 136 | |
| 137 | native.java_library( |
| 138 | name = "com_google_errorprone_error_prone_annotations", |
| 139 | visibility = ["//visibility:public"], |
| 140 | exports = ["@com_google_errorprone_error_prone_annotations//jar"], |
| 141 | ) |
| 142 | |
| 143 | |
| 144 | native.java_library( |
| 145 | name = "com_google_j2objc_j2objc_annotations", |
| 146 | visibility = ["//visibility:public"], |
| 147 | exports = ["@com_google_j2objc_j2objc_annotations//jar"], |
| 148 | ) |
| 149 | ``` |
| 150 | |
| 151 | ### <a name="2-build"></a>2. Create one BUILD file |
| 152 | |
| 153 | Now that you have your workspace defined and external dependencies (if |
| 154 | applicable) listed, you need to create BUILD files to describe how your project |
| 155 | should be built. Unlike Maven with its one `pom.xml` file, Bazel can use many |
| 156 | BUILD files to build a project. These files specify multiple build targets, |
| 157 | which allow Bazel to produce incremental builds. |
| 158 | |
| 159 | Add BUILD files in stages. Start with adding one BUILD file |
| 160 | at the root of your project and using it to do an initial build using Bazel. |
| 161 | Then, you refine your build by adding more BUILD files with more granular |
| 162 | targets. |
| 163 | |
| 164 | 1. In the same directory as your `WORKSPACE` file, create a text file and |
| 165 | name it `BUILD`. |
| 166 | |
| 167 | 2. In this BUILD file, use the appropriate rule to create one target to |
| 168 | build your project. Here are some tips: |
| 169 | * Use the appropriate rule: |
| 170 | * To build projects with a single Maven module, use the |
| 171 | `java_library` rule as follows: |
| 172 | |
| 173 | ```bash |
| 174 | java_library( |
| 175 | name = "everything", |
| 176 | srcs = glob(["src/main/java/**/*.java"]), |
| 177 | resources = glob(["src/main/resources/**"]), |
| 178 | deps = ["//:all-external-targets"], |
| 179 | ) |
| 180 | ``` |
| 181 | * To build projects with multiple Maven modules, use the |
| 182 | `java_library` rule as follows: |
| 183 | |
| 184 | ```bash |
| 185 | java_library( |
| 186 | name = "everything", |
| 187 | srcs = glob([ |
| 188 | "Module1/src/main/java/**/*.java", |
| 189 | "Module2/src/main/java/**/*.java", |
| 190 | ... |
| 191 | ]), |
| 192 | resources = glob([ |
| 193 | "Module1/src/main/resources/**", |
| 194 | "Module2/src/main/resources/**", |
| 195 | ... |
| 196 | ]), |
| 197 | deps = ["//:all-external-targets"], |
| 198 | ) |
| 199 | ``` |
| 200 | * To build binaries, use the `java_binary` rule: |
| 201 | |
| 202 | ```bash |
| 203 | java_binary( |
| 204 | name = "everything", |
| 205 | srcs = glob(["src/main/java/**/*.java"]), |
| 206 | resources = glob(["src/main/resources/**"]), |
| 207 | deps = ["//:all-external-targets"], |
| 208 | main_class = "com.example.Main" |
| 209 | ) |
| 210 | ``` |
| 211 | * Specify the attributes: |
| 212 | * `name`: Give the target a meaningful name. In the examples above |
| 213 | we call the target “everything.” |
| 214 | * `srcs`: Use globbing to list all .java files in your project. |
| 215 | * `resources`: Use globbing to list all resources in your project. |
| 216 | * `deps`: You need to determine which external dependencies your |
| 217 | project needs. For example, if you generated a list of external |
| 218 | dependencies using the tool `generate_workspace`, the dependencies |
Googler | d40b74a | 2017-08-10 22:12:28 +0200 | [diff] [blame] | 219 | for `java_library` are the libraries listed in the |
Googler | 7b30d75 | 2017-07-17 16:35:17 +0200 | [diff] [blame] | 220 | `generated_java_libraries` macro. |
| 221 | * Take a look at the |
| 222 | [example below of this top-level BUILD file](#guava-example-2) from |
| 223 | the migration of the Guava project. |
| 224 | |
| 225 | 3. Now that you have a BUILD file at the root of your project, build |
| 226 | your project to ensure that it works. On the command line, from your |
| 227 | workspace directory, use `bazel build //:everything` to build your |
| 228 | project with Bazel. |
| 229 | |
| 230 | The project has now been successfully built with Bazel. You will need |
| 231 | to add more BUILD files to allow incremental builds of the project. |
| 232 | |
| 233 | #### <a name="guava-2"></a>Guava project example: start with one BUILD file |
| 234 | |
| 235 | When migrating the Guava project to Bazel, initially one BUILD file is used |
| 236 | to build the enitre project. Here are the contents of this initial `BUILD` |
| 237 | file in the workspace directory: |
| 238 | |
| 239 | ```bash |
| 240 | java_library( |
| 241 | name = "everything", |
| 242 | srcs = glob(["guava/src/**/*.java"]), |
| 243 | deps = [ |
| 244 | "//third_party:com_google_code_findbugs_jsr305", |
| 245 | "//third_party:com_google_errorprone_error_prone_annotations", |
| 246 | "//third_party:com_google_j2objc_j2objc_annotations" |
| 247 | ], |
| 248 | ) |
| 249 | ``` |
| 250 | |
Googler | ef1424c | 2017-08-30 00:03:42 +0200 | [diff] [blame] | 251 | ### <a name="3-build"></a>3. Create more BUILD files (Optional) |
Googler | 7b30d75 | 2017-07-17 16:35:17 +0200 | [diff] [blame] | 252 | |
Googler | ef1424c | 2017-08-30 00:03:42 +0200 | [diff] [blame] | 253 | Bazel does work with just one BUILD file, as you saw after completing your first |
| 254 | build. You should still consider breaking the build into smaller chunks by |
| 255 | adding more BUILD files with granular targets. |
Googler | 7b30d75 | 2017-07-17 16:35:17 +0200 | [diff] [blame] | 256 | |
Googler | ef1424c | 2017-08-30 00:03:42 +0200 | [diff] [blame] | 257 | Multiple BUILD files with multiple targets will give the build increased |
| 258 | granularity, allowing: |
| 259 | |
| 260 | * increased incremental builds of the project, |
| 261 | * increased parallel execution of the build, |
| 262 | * better maintainability of the build for future users, and |
| 263 | * control over visibility of targets between packages, which can prevent |
| 264 | issues such as libraries containing implementation details leaking into |
| 265 | public APIs. |
Googler | 7b30d75 | 2017-07-17 16:35:17 +0200 | [diff] [blame] | 266 | |
| 267 | Tips for adding more BUILD files: |
| 268 | |
Akira Baruah | 4cf32b0 | 2017-11-03 19:40:20 +0100 | [diff] [blame] | 269 | * You can start by adding a BUILD file to each Java package. Start with |
Googler | 7b30d75 | 2017-07-17 16:35:17 +0200 | [diff] [blame] | 270 | Java packages that have the fewest dependencies and work you way up |
| 271 | to packages with the most dependencies. |
| 272 | * As you add BUILD files and specify targets, add these new targets to the |
| 273 | `deps` sections of targets that depend on them. Note that the `glob()` |
| 274 | function does not cross package boundaries, so as the number |
| 275 | of packages grows the files matched by `glob()` will shrink. |
| 276 | * Any time you add a BUILD file to a `main` directory, ensure that you add |
| 277 | a BUILD file to the corresponding `test` directory. |
| 278 | * Take care to limit visibility properly between packages. |
| 279 | * To simplify troubleshooting errors in your setup of BUILD files, ensure |
| 280 | that the project continues to build with Bazel as you add each build |
| 281 | file. Run `bazel build //...` to ensure all of your targets still build. |
| 282 | |
Googler | 7b30d75 | 2017-07-17 16:35:17 +0200 | [diff] [blame] | 283 | ### <a name="4-build"></a>4. Build using Bazel |
| 284 | |
| 285 | You’ve been building using Bazel as you add BUILD files to validate the setup |
| 286 | of the build. |
| 287 | |
| 288 | When you have BUILD files at the desired granularity, you can use Bazel |
| 289 | to produce all of your builds. |