├── .bazelci └── presubmit.yml ├── .gitignore ├── AUTHORS ├── CODEOWNERS ├── CONTRIBUTING.md ├── CONTRIBUTORS ├── LICENSE.txt ├── README.md ├── WORKSPACE ├── appengine ├── BUILD ├── appengine.bzl ├── cloud_sdk.BUILD ├── java │ ├── BUILD │ ├── appengine_deploy.sh.template │ ├── appengine_runner.sh.template │ └── sdk.BUILD ├── java_appengine.bzl ├── py │ ├── BUILD │ ├── appengine_deploy.sh.template │ ├── appengine_runner.sh.template │ └── sdk.BUILD ├── py_appengine.bzl ├── sdk.bzl └── variables.bzl ├── examples ├── java │ ├── BUILD │ ├── src │ │ ├── App.java │ │ └── BUILD │ └── webapp │ │ ├── BUILD │ │ └── WEB-INF │ │ └── appengine-web.xml └── py │ └── hello_world │ ├── BUILD │ ├── app.yaml │ └── main.py └── test └── java ├── BUILD ├── WEB-INF ├── appengine-web.xml ├── logging.properties └── web.xml ├── check_war.sh └── data ├── BUILD └── welcome.jsp /.bazelci/presubmit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | platforms: 3 | ubuntu1604: 4 | build_targets: 5 | - "..." 6 | test_targets: 7 | - "..." 8 | ubuntu1804: 9 | build_targets: 10 | - "..." 11 | test_targets: 12 | - "..." 13 | macos: 14 | build_targets: 15 | - "..." 16 | test_targets: 17 | - "..." 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bazel-* 2 | .ijwb/ 3 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This the official list of Bazel authors for copyright purposes. 2 | # This file is distinct from the CONTRIBUTORS files. 3 | # See the latter for an explanation. 4 | 5 | # Names should be added to this file as: 6 | # Name or Organization 7 | # The email address is not required for organizations. 8 | 9 | Google Inc. 10 | Alex Humesky 11 | Damien Martin-Guillerez 12 | David Chen 13 | Erik Kuefler 14 | Kristina Chodorow 15 | Lukacs Berki 16 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @pmbethe09 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Want to contribute? Great! First, read this page (including the small print at the end). 2 | 3 | ### Before you contribute 4 | **Before we can use your code, you must sign the 5 | [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1) 6 | (CLA)**, which you can do online. 7 | 8 | The CLA is necessary mainly because you own the copyright to your changes, 9 | even after your contribution becomes part of our codebase, so we need your 10 | permission to use and distribute your code. We also need to be sure of 11 | various other things — for instance that you'll tell us if you know that 12 | your code infringes on other people's patents. You don't have to sign 13 | the CLA until after you've submitted your code for review and a member has 14 | approved it, but you must do it before we can put your code into our codebase. 15 | 16 | Before you start working on a larger contribution, you should get in touch 17 | with us first. Use the issue tracker to explain your idea so we can help and 18 | possibly guide you. 19 | 20 | ### Code reviews and other contributions. 21 | **All submissions, including submissions by project members, require review.** 22 | Please follow the instructions in [the contributors documentation](http://bazel.io/contributing.html). 23 | 24 | ### The small print 25 | Contributions made by corporations are covered by a different agreement than 26 | the one above, the 27 | [Software Grant and Corporate Contributor License Agreement](https://cla.developers.google.com/about/google-corporate). 28 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # People who have agreed to one of the CLAs and can contribute patches. 2 | # The AUTHORS file lists the copyright holders; this file 3 | # lists people. For example, Google employees are listed here 4 | # but not in AUTHORS, because Google holds the copyright. 5 | # 6 | # https://developers.google.com/open-source/cla/individual 7 | # https://developers.google.com/open-source/cla/corporate 8 | # 9 | # Names should be added to this file as: 10 | # Name 11 | 12 | Alex Humesky 13 | Damien Martin-Guillerez 14 | David Chen 15 | Doug Roeper 16 | Erik Kuefler 17 | James O'Kane 18 | Julio Merino 19 | Kristina Chodorow 20 | Lukacs Berki 21 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 🚨 rules_appengine is no longer maintained. 2 | 3 | 4 | [![Build status](https://badge.buildkite.com/98ffeea8dc7631a8a7d7af13cbe7570a7209a98ef18fe3cbc5.svg)](https://buildkite.com/bazel/appengine-rules-appengine-postsubmit) 5 | 6 | 7 | # App Engine Rules for Bazel 8 | 9 |
10 |

Rules

11 | 17 |
18 | 19 | ## Overview 20 | 21 | These build rules are used for building 22 | [Java App Engine](https://cloud.google.com/appengine/docs/java/) application or 23 | [Python App Engine](https://cloud.google.com/appengine/docs/python/) application 24 | with Bazel. It does not aim at general web application 25 | support but can be easily modified to handle a standard web application. 26 | 27 | 28 | ## Setup 29 | 30 | To be able to use the rules, you must make the App Engine SDK available to 31 | Bazel. The easiest way to do so is by adding the following to your 32 | `WORKSPACE` file: 33 | 34 | Note: The `${LANG}_appengine_repository()` lines are only needed for the languages you plan to use. 35 | 36 | ```python 37 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 38 | http_archive( 39 | name = "bazel_skylib", 40 | urls = [ 41 | "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz", 42 | "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz", 43 | ], 44 | sha256 = "1c531376ac7e5a180e0237938a2536de0c54d93f5c278634818e0efc952dd56c", 45 | ) 46 | load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") 47 | bazel_skylib_workspace() 48 | 49 | git_repository( 50 | name = "io_bazel_rules_appengine", 51 | remote = "https://github.com/bazelbuild/rules_appengine.git", 52 | # Check https://github.com/bazelbuild/rules_appengine/releases for the latest version. 53 | tag = "0.0.8", 54 | ) 55 | # Java 56 | load( 57 | "@io_bazel_rules_appengine//appengine:java_appengine.bzl", 58 | "java_appengine_repositories", 59 | ) 60 | 61 | java_appengine_repositories() 62 | 63 | # Python 64 | load( 65 | "@io_bazel_rules_appengine//appengine:sdk.bzl", 66 | "appengine_repositories", 67 | ) 68 | 69 | appengine_repositories() 70 | 71 | load( 72 | "@io_bazel_rules_appengine//appengine:py_appengine.bzl", 73 | "py_appengine_repositories", 74 | ) 75 | 76 | py_appengine_repositories() 77 | ``` 78 | 79 | The App Engine rules download the App Engine SDK, which is a few hundred 80 | megabytes in size. To avoid downloading this multiple times for multiple 81 | projects or inadvertently re-downloading it, you might want to add the 82 | following lines to your `$HOME/.bazelrc` file: 83 | 84 | ``` 85 | build --experimental_repository_cache=/home/user/.bazel/cache 86 | fetch --experimental_repository_cache=/home/user/.bazel/cache 87 | ``` 88 | 89 | ### Requesting a specific App Engine SDK 90 | 91 | All ${LANG}_appengine_repository macros accept optional arguments `version` 92 | and `sha256`. 93 | 94 | ```python 95 | py_appengine_repositories( 96 | version = '1.9.67', 97 | sha256 = 'f9f45150643424cb164185d9134b86511c2bec3001499247ef9027f1605ef8a3', 98 | ) 99 | ``` 100 | 101 | ### Using a predownloaded SDK version 102 | 103 | You can, optionally, specify the environment variable 104 | `${LANG}_APPENGINE_SDK_PATH` to use an SDK that is unzipped on your filesystem 105 | (instead of downloading a new one). 106 | 107 | ``` 108 | PY_APPENGINE_SDK_PATH=/path/to/google_appengine bazel build //whatever 109 | JAVA_APPENGINE_SDK_PATH=/path/to/appengine-java-sdk-1.9.50 bazel build //whatever 110 | ``` 111 | 112 | 113 | ## Basic Java Example 114 | 115 | Suppose you have the following directory structure for a simple App Engine 116 | application: 117 | 118 | ``` 119 | [workspace]/ 120 | WORKSPACE 121 | hello_app/ 122 | BUILD 123 | java/my/webapp/ 124 | TestServlet.java 125 | webapp/ 126 | index.html 127 | webapp/WEB-INF 128 | web.xml 129 | appengine-web.xml 130 | ``` 131 | 132 | ### BUILD definition 133 | 134 | Then, to build your webapp, your `hello_app/BUILD` can look like: 135 | 136 | ```python 137 | load("@io_bazel_rules_appengine//appengine:java_appengine.bzl", "appengine_war") 138 | 139 | java_library( 140 | name = "mylib", 141 | srcs = ["java/my/webapp/TestServlet.java"], 142 | deps = [ 143 | "//external:appengine/java/api", 144 | "@io_bazel_rules_appengine//appengine:javax.servlet.api", 145 | ], 146 | ) 147 | 148 | appengine_war( 149 | name = "myapp", 150 | jars = [":mylib"], 151 | data = glob(["webapp/**"]), 152 | data_path = "/webapp", 153 | ) 154 | ``` 155 | 156 | For simplicity, you can use the `java_war` rule to build an app from source. 157 | Your `hello_app/BUILD` file would then look like: 158 | 159 | ```python 160 | load("@io_bazel_rules_appengine//appengine:java_appengine.bzl", "java_war") 161 | 162 | java_war( 163 | name = "myapp", 164 | srcs = ["java/my/webapp/TestServlet.java"], 165 | data = glob(["webapp/**"]), 166 | data_path = "/webapp", 167 | deps = [ 168 | "//external:appengine/java/api", 169 | "@io_bazel_rules_appengine//appengine:javax.servlet.api", 170 | ], 171 | ) 172 | ``` 173 | 174 | You can then build the application with `bazel build //hello_app:myapp`. 175 | 176 | ### Run on a local server 177 | 178 | You can run it in a development server with `bazel run //hello_app:myapp`. 179 | This will bind a test server on port 8080. If you wish to select another port, 180 | use the `--port` option: 181 | 182 | ``` 183 | bazel run //hello_app:myapp -- --port=12345 184 | ``` 185 | 186 | You can see other options with `-- --help` (the `--` tells Bazel to pass the 187 | rest of the arguments to the executable). 188 | 189 | ### Deploy to Google App Engine 190 | 191 | Another target `//hello_app:myapp.deploy` allows you to deploy your 192 | application to App Engine. It takes an optional argument: the 193 | `APP_ID`. If not specified, it uses the default `APP_ID` provided in 194 | the application. This target needs to open a browser to authenticate 195 | with App Engine, then have you copy-paste a "key" from the browser in 196 | the terminal. Since Bazel closes standard input, you can only input 197 | this by building the target and then running: 198 | 199 | ``` 200 | $ bazel-bin/hello_app/myapp.deploy APP_ID 201 | ``` 202 | 203 | After the first launch, subsequent launch will be registered to 204 | App Engine so you can just do a normal `bazel run 205 | //hello_app:myapp.deploy -- APP_ID` to deploy next versions of 206 | your application. 207 | 208 | ## Java specific details 209 | 210 | *Note:* App Engine uses Java 8 (or Java 7, but this runtime is deprecated). If you 211 | are using a more recent version of Java, you will get the following error message when 212 | you try to deploy: 213 | 214 | ``` 215 | java.lang.IllegalArgumentException: Class file is Java 9 but max supported is Java 8 216 | ``` 217 | 218 | To build with Java 8, use the toolchain bundled with these App Engine rules: 219 | 220 | ``` 221 | $ bazel build --extra_toolchains=@io_bazel_rules_appengine//appengine/jdk:jdk8_definition //my-project 222 | ``` 223 | 224 | To avoid having to specify this toolchain during every build, you can add this 225 | to your project's `.bazelrc`. Create a `.bazelrc` file in the root directory of 226 | your project and add the line: 227 | 228 | ``` 229 | build --extra_toolchains=@io_bazel_rules_appengine//appengine/jdk:jdk8_definition 230 | ``` 231 | 232 | In case you don't want to install JDK 8 on the machine running Bazel, you can 233 | import remote JDK8 repositories by adding the following lines to your WORKSPACE 234 | file: 235 | 236 | ```python 237 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 238 | 239 | http_archive( 240 | name = "rules_java", 241 | sha256 = "7c4bbe11e41c61212a5cf16d9aafaddade3f5b1b6c8bf94270d78215fafd4007", 242 | strip_prefix = "rules_java-c13e3ead84afb95f81fbddfade2749d8ba7cb77f", 243 | url = "https://github.com/bazelbuild/rules_java/archive/c13e3ead84afb95f81fbddfade2749d8ba7cb77f.tar.gz", # 2021-01-25 244 | ) 245 | 246 | load("@rules_java//java:repositories.bzl", "remote_jdk8_repos") 247 | 248 | remote_jdk8_repos() 249 | ``` 250 | 251 | 252 | ## appengine_war 253 | 254 | ```python 255 | appengine_war(name, jars, data, data_path) 256 | ``` 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 275 | 276 | 277 | 278 | 290 | 291 | 292 | 293 | 301 | 302 | 303 | 304 | 315 | 316 | 317 |
Attributes
name 272 | Name, required 273 |

A unique name for this rule.

274 |
jars 279 | List of labels, required 280 |

281 | List of JAR files that will be uncompressed as the code for the 282 | Web Application. 283 |

284 |

285 | If it is a `java_library` or a `java_import`, the 286 | JAR from the runtime classpath will be added in the `lib` directory 287 | of the Web Application. 288 |

289 |
data 294 | List of files, optional 295 |

List of files used by the Web Application at runtime.

296 |

297 | This attribute can be used to specify the list of resources to 298 | be included into the WAR file. 299 |

300 |
data_path 305 | String, optional 306 |

Root path of the data.

307 |

308 | The directory structure from the data is preserved inside the 309 | WebApplication but a prefix path determined by `data_path` 310 | is removed from the the directory structure. This path can 311 | be absolute from the workspace root if starting with a `/` or 312 | relative to the rule's directory. It is set to `.` by default. 313 |

314 |
318 | 319 | 320 | ## java_war 321 | 322 | ```python 323 | java_war(name, data, data_path, **kwargs) 324 | ``` 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 343 | 344 | 345 | 346 | 351 | 352 | 353 | 354 | 359 | 360 | 361 | 362 | 370 | 371 | 372 |
Attributes
name 340 | Name, required 341 |

A unique name for this rule.

342 |
data 347 | List of labels, optional 348 |

List of files used by the Web Application at runtime.

349 |

Passed to the appengine_war rule.

350 |
data_path 355 | String, optional 356 |

Root path of the data.

357 |

Passed to the appengine_war rule.

358 |
**kwargs 363 | see java_library 364 |

365 | The other arguments of this rule will be passed to build a `java_library` 366 | that will be passed in the `jar` arguments of a 367 | appengine_war rule. 368 |

369 |
373 | 374 | ## Python specific details 375 | 376 | 377 | ## py_appengine_binary 378 | ```python 379 | py_appengine_binary(name, srcs, configs, deps=[], data=[], overwrite_appengine_config=True) 380 | ``` 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 398 | 399 | 400 | 401 | 405 | 406 | 407 | 408 | 412 | 413 | 414 | 415 | 419 | 420 | 421 | 422 | 426 | 427 | 428 | 429 | 434 | 435 | 436 |
Attributes
name 395 | Name, required 396 |

A unique name for this rule.

397 |
configs 402 | List of labels, required 403 |

the path to your app.yaml/index.yaml/cron.yaml files

404 |
srcs 409 | List of labels, optional 410 |

The list of source files that are processed to create the target.

411 |
deps 416 | List of labels, optional 417 |

The list of libraries to link into this library.

418 |
data 423 | List of labels, optional 424 |

List of files used by the Web Application at runtime.

425 |
overwrite_appengine_config 430 | Boolean, optional 431 |

If true, patch the user's appengine_config into the base one. If false, use 432 | the user specified config directly. Set to False to behave pre 0.0.8.

433 |
437 | 438 | 439 | ## py_appengine_test 440 | ```python 441 | py_appengine_test(name, srcs, deps=[], data=[], libraries={}) 442 | ``` 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 460 | 461 | 462 | 463 | 467 | 468 | 469 | 470 | 474 | 475 | 476 | 477 | 481 | 482 | 483 | 484 | 488 | 489 | 490 |
Attributes
name 457 | Name, required 458 |

A unique name for this rule.

459 |
srcs 464 | List of labels, required 465 |

The list of source files that are processed to create the target.

466 |
deps 471 | List of labels, optional 472 |

The list of libraries to link into this library.

473 |
data 478 | List of labels, optional 479 |

List of files used by the Web Application at runtime.

480 |
libraries 485 | dict, optional 486 |

dictionary of name and the corresponding version for third-party libraries required from sdk.

487 |
491 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | workspace(name = "io_bazel_rules_appengine") 2 | 3 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 4 | 5 | http_archive( 6 | name = "bazel_skylib", 7 | urls = [ 8 | "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz", 9 | "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.0.3/bazel-skylib-1.0.3.tar.gz", 10 | ], 11 | sha256 = "1c531376ac7e5a180e0237938a2536de0c54d93f5c278634818e0efc952dd56c", 12 | ) 13 | load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") 14 | bazel_skylib_workspace() 15 | 16 | http_archive( 17 | name = "rules_java", 18 | sha256 = "7c4bbe11e41c61212a5cf16d9aafaddade3f5b1b6c8bf94270d78215fafd4007", 19 | strip_prefix = "rules_java-c13e3ead84afb95f81fbddfade2749d8ba7cb77f", 20 | url = "https://github.com/bazelbuild/rules_java/archive/c13e3ead84afb95f81fbddfade2749d8ba7cb77f.tar.gz", # 2021-01-25 21 | ) 22 | 23 | load("@rules_java//java:repositories.bzl", "remote_jdk8_repos") 24 | 25 | remote_jdk8_repos() 26 | 27 | load("//appengine:sdk.bzl", "appengine_repositories") 28 | load("//appengine:java_appengine.bzl", "java_appengine_repositories") 29 | load("//appengine:py_appengine.bzl", "py_appengine_repositories") 30 | 31 | appengine_repositories() 32 | 33 | java_appengine_repositories() 34 | 35 | py_appengine_repositories() 36 | -------------------------------------------------------------------------------- /appengine/BUILD: -------------------------------------------------------------------------------- 1 | # A target to ensure the servlet-api is not linked in the webapp. 2 | java_library( 3 | name = "javax.servlet.api", 4 | neverlink = 1, 5 | visibility = ["//visibility:public"], 6 | exports = ["@javax_servlet_api//jar"], 7 | ) 8 | 9 | filegroup( 10 | name = "srcs", 11 | srcs = glob(["**"]), 12 | visibility = ["//tools:__pkg__"], 13 | ) 14 | 15 | alias( 16 | name = "jdk8", 17 | actual = "@rules_appengine_toolchain//:jdk8", 18 | visibility = ["//visibility:public"], 19 | ) 20 | -------------------------------------------------------------------------------- /appengine/appengine.bzl: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The Bazel Authors. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """ 15 | Use of this file is deprecated. Use the functions in java_appengine.bzl directly. 16 | """ 17 | 18 | load( 19 | ":java_appengine.bzl", 20 | _appengine_war = "appengine_war", 21 | _appengine_war_base = "appengine_war_base", 22 | _java_appengine_repositories = "java_appengine_repositories", 23 | _java_war = "java_war", 24 | ) 25 | 26 | appengine_war = _appengine_war 27 | 28 | appengine_war_base = _appengine_war_base 29 | 30 | java_appengine_repositories = _java_appengine_repositories 31 | 32 | java_war = _java_war 33 | -------------------------------------------------------------------------------- /appengine/cloud_sdk.BUILD: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//visibility:public"]) 2 | 3 | filegroup( 4 | name = "gcloud", 5 | srcs = ["bin/gcloud"], 6 | ) 7 | -------------------------------------------------------------------------------- /appengine/java/BUILD: -------------------------------------------------------------------------------- 1 | filegroup( 2 | name = "runner_template", 3 | srcs = ["appengine_runner.sh.template"], 4 | visibility = ["//visibility:public"], 5 | ) 6 | 7 | filegroup( 8 | name = "deploy_template", 9 | srcs = ["appengine_deploy.sh.template"], 10 | visibility = ["//visibility:public"], 11 | ) 12 | 13 | filegroup( 14 | name = "sdk_build_file", 15 | srcs = ["sdk.BUILD"], 16 | visibility = ["//visibility:public"], 17 | ) 18 | -------------------------------------------------------------------------------- /appengine/java/appengine_deploy.sh.template: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2015 The Bazel Authors. All rights reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | case "$0" in 17 | /*) self="$0" ;; 18 | *) self="$PWD/$0";; 19 | esac 20 | 21 | if [[ -z "$JAVA_RUNFILES" ]]; then 22 | if [[ -e "${self}.runfiles/%{workspace_name}" ]]; then 23 | JAVA_RUNFILES="${self}.runfiles/%{workspace_name}" 24 | fi 25 | fi 26 | 27 | root_path=$(pwd) 28 | tmp_dir=$(mktemp -d ${TMPDIR:-/tmp}/war.XXXXXXXX) 29 | trap "{ cd ${root_path}; rm -rf ${tmp_dir}; }" EXIT 30 | cd ${tmp_dir} 31 | 32 | ${JAVA_RUNFILES}/%{zipper} x ${JAVA_RUNFILES}/%{war} 33 | cd ${root_dir} 34 | 35 | APP_ENGINE_ROOT=${JAVA_RUNFILES}/%{appengine_sdk} 36 | if [ -n "${1-}" ]; then 37 | ${APP_ENGINE_ROOT}/bin/appcfg.sh -A "$1" update ${tmp_dir} 38 | retCode=$? 39 | else 40 | ${APP_ENGINE_ROOT}/bin/appcfg.sh update ${tmp_dir} 41 | retCode=$? 42 | fi 43 | 44 | rm -rf ${tmp_dir} 45 | trap - EXIT 46 | 47 | exit $retCode 48 | -------------------------------------------------------------------------------- /appengine/java/appengine_runner.sh.template: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2015 The Bazel Authors. All rights reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | case "$0" in 17 | /*) self="$0" ;; 18 | *) self="$PWD/$0";; 19 | esac 20 | 21 | if [[ -z "$JAVA_RUNFILES" ]]; then 22 | if [[ -e "${self}.runfiles/%{workspace_name}" ]]; then 23 | JAVA_RUNFILES="${self}.runfiles/%{workspace_name}" 24 | fi 25 | fi 26 | 27 | jvm_bin="${JAVA_RUNFILES}/%{java}" 28 | if [[ ! -x ${jvm_bin} ]]; then 29 | jvm_bin=$(which java) 30 | fi 31 | 32 | APP_ENGINE_ROOT="${JAVA_RUNFILES}/%{appengine_sdk}" 33 | main_class="com.google.appengine.tools.development.DevAppServerMain" 34 | classpath="%{classpath}" 35 | 36 | # If we are not on the data path, we'll get a warning: 37 | # WARNING: Your working directory, ($PWD) is not equal to your 38 | # web application root (%{data_path}) 39 | # You will not be able to access files from your working directory on the 40 | # production server. 41 | cd "%{data_path}" 42 | 43 | mkdir -p WEB-INF/lib 44 | for i in $(echo $classpath | tr ":" "\n"); do 45 | jar="WEB-INF/lib/$(basename "$i")" 46 | rm -f "$jar" 47 | ln -s "$i" "$jar" 48 | done 49 | 50 | JVM_FLAGS_CMDLINE=() 51 | 52 | # Processes an argument for the wrapper. Returns 0 if the given argument 53 | # was recognized as an argument for this wrapper, and 1 if it was not. 54 | function process_wrapper_argument() { 55 | case "$1" in 56 | --jvm_flag=*) JVM_FLAGS_CMDLINE+=( "${1#--jvm_flag=}" ) ;; 57 | --jvm_flags=*) JVM_FLAGS_CMDLINE+=( ${1#--jvm_flags=} ) ;; 58 | *) 59 | return 1 ;; 60 | esac 61 | return 0 62 | } 63 | 64 | # Parse arguments sequentially until the first unrecognized arg is encountered. 65 | # Scan the remaining args for --wrapper_script_flag=X options and process them. 66 | ARGS=() 67 | for ARG in "$@"; do 68 | if [[ "$ARG" == --wrapper_script_flag=* ]]; then 69 | process_wrapper_argument "${ARG#--wrapper_script_flag=}" \ 70 | || die "invalid wrapper argument '%s'" "$ARG" 71 | elif [[ "${#ARGS}" -gt 0 ]] || ! process_wrapper_argument "$ARG"; then 72 | ARGS+=( "$ARG" ) 73 | fi 74 | done 75 | 76 | ARGS=( 77 | ${JVM_FLAGS} 78 | "${JVM_FLAGS_CMDLINE[@]}" 79 | "-Dappengine.sdk.root=${APP_ENGINE_ROOT}" 80 | %{local_jvm_flags} 81 | ${main_class} 82 | "--disable_update_check" 83 | "${ARGS[@]}" 84 | . 85 | ) 86 | 87 | # Linux per-arg limit MAX_ARG_STRLEN == 128k! 88 | if (("${#classpath}" > 120000)); then 89 | set +o posix # Enable process substitution. 90 | exec "${jvm_bin}" -classpath @<(echo "${classpath}") "${ARGS[@]}" 91 | else 92 | exec "${jvm_bin}" -classpath "${classpath}" "${ARGS[@]}" 93 | fi 94 | -------------------------------------------------------------------------------- /appengine/java/sdk.BUILD: -------------------------------------------------------------------------------- 1 | # BUILD file to use the Java AppEngine SDK with a remote repository. 2 | java_import( 3 | name = "jars", 4 | jars = glob(["lib/**/*.jar"]), 5 | visibility = ["//visibility:public"], 6 | ) 7 | 8 | java_import( 9 | name = "user", 10 | jars = glob(["lib/user/*.jar"]), 11 | visibility = ["//visibility:public"], 12 | ) 13 | 14 | java_import( 15 | name = "api", 16 | jars = [ 17 | "lib/appengine-tools-api.jar", 18 | "lib/impl/appengine-api.jar", 19 | ], 20 | neverlink = 1, 21 | visibility = ["//visibility:public"], 22 | ) 23 | 24 | filegroup( 25 | name = "sdk", 26 | srcs = glob(["**"]), 27 | visibility = ["//visibility:public"], 28 | ) 29 | -------------------------------------------------------------------------------- /appengine/java_appengine.bzl: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The Bazel Authors. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Java AppEngine support for Bazel. 15 | 16 | For now, it only support bundling a WebApp and running locally. 17 | 18 | To create a WebApp for Google AppEngine, add the rules: 19 | appengine_war( 20 | name = "MyWebApp", 21 | # Jars to use for the classpath in the webapp. 22 | jars = ["//java/com/google/examples/mywebapp:java"], 23 | # data to put in the webapp, the directory structure of the data set 24 | # will be maintained. 25 | data = ["//java/com/google/examples/mywebapp:data"], 26 | # Data's root path, it will be considered as the root of the data files. 27 | # If unspecified, the path to the current package will be used. The path is 28 | # relative to current package or, relative to the workspace root if starting 29 | # with a leading slash. 30 | data_path = "/java/com/google/examples/mywebapp", 31 | # Optional JVM arguments, such as -Dsome_varaible=somevalue. 32 | local_jvm_flags = ["-Dparam1=value1", "-Dparam2=value2"], 33 | ) 34 | 35 | To test locally: 36 | bazel run :MyWebApp 37 | 38 | To deploy on Google app engine: 39 | bazel run :MyWebApp.deploy 40 | 41 | You can also make directly a single target for it with: 42 | 43 | java_war( 44 | name = "MyWebApp", 45 | srcs = glob(["**/*.java"]), 46 | resources = ["..."], 47 | data = ["..."], 48 | data_path = "...", 49 | ) 50 | 51 | Resources will be put in the classpath whereas data will be bundled at the root 52 | of the war file. This is strictly equivalent to (it is actually a convenience 53 | macros that translate to that): 54 | 55 | java_library( 56 | name = "libMyWebApp", 57 | srcs = glob(["**/*.java"]), 58 | resources = ["..."], 59 | ) 60 | 61 | appengine_war( 62 | name = "MyWebApp", 63 | jars = [":libMyWebApp"], 64 | data = ["..."], 65 | data_path = "...", 66 | ) 67 | 68 | Finally, the appengine macro also create a .deploy target that will try to use the 69 | AppEngine SDK to upload your application to AppEngine. It takes an optional argument: the 70 | APP_ID. If not specified, it uses the default APP_ID provided in the application 71 | web.xml. 72 | """ 73 | 74 | load("@bazel_skylib//lib:versions.bzl", "versions") 75 | load("@bazel_tools//tools/build_defs/repo:jvm.bzl", "jvm_maven_import_external") 76 | load(":variables.bzl", "JAVA_SDK_SHA256", "JAVA_SDK_VERSION") 77 | load(":sdk.bzl", "find_locally_or_download") 78 | 79 | def _add_file(in_file, output, path = None): 80 | output_path = output 81 | input_path = in_file.path 82 | 83 | if path and in_file.short_path.startswith(path): 84 | output_path += in_file.short_path[len(path):] 85 | 86 | if in_file.basename.endswith(".jar") and in_file.owner.package: 87 | filename = "%s/%s" % (in_file.owner.package, in_file.basename) 88 | filename = filename.replace("/", "_").replace("=", "_") 89 | output_path = "%s/%s" % (output_path, filename) 90 | 91 | return [ 92 | "mkdir -p $(dirname %s)" % output_path, 93 | "test -L %s || ln -s $(pwd)/%s %s" % (output_path, input_path, output_path), 94 | ] 95 | 96 | def _make_war(zipper, input_dir, output): 97 | return [ 98 | "(root=$(pwd);" + 99 | ("cd %s &&" % input_dir) + 100 | ("find . ! -type d > $root/file_list &&") + 101 | ("${root}/%s Cc ${root}/%s @${root}/file_list)" % (zipper.path, output.path)), 102 | ] 103 | 104 | def _common_substring(str1, str2): 105 | i = 0 106 | res = "" 107 | for c in str1.elems(): 108 | if str2[i] != c: 109 | return res 110 | res += c 111 | i += 1 112 | return res 113 | 114 | def _short_path_dirname(path): 115 | sp = path.short_path 116 | return sp[0:len(sp) - len(path.basename) - 1] 117 | 118 | def _collect_transitive_runtime_deps_for(deps): 119 | transitive_runtime_deps = [] 120 | for dep in deps: 121 | if JavaInfo in dep: 122 | transitive_runtime_deps += [dep[JavaInfo].transitive_runtime_deps] 123 | elif hasattr(dep, "files"): # a jar file 124 | transitive_runtime_deps += [dep.files] 125 | 126 | return depset(transitive = transitive_runtime_deps) 127 | 128 | def _war_impl(ctxt): 129 | """Implementation of the rule that creates 130 | - the war 131 | - the script to deploy 132 | """ 133 | 134 | zipper = ctxt.file._zipper 135 | _java_runtime = ctxt.attr._java[java_common.JavaRuntimeInfo] 136 | 137 | data_path = ctxt.attr.data_path 138 | if not data_path: 139 | data_path = _short_path_dirname(ctxt.outputs.war) 140 | elif data_path[0] == "/": 141 | data_path = data_path[1:] 142 | else: # relative path 143 | data_path = _short_path_dirname(ctxt.outputs.war) + "/" + data_path 144 | 145 | war = ctxt.outputs.war 146 | build_output = war.path + ".build_output" 147 | cmd = [ 148 | "set -e;rm -rf " + build_output, 149 | "mkdir -p " + build_output, 150 | ] 151 | 152 | inputs = [zipper] 153 | cmd += ["mkdir -p %s/WEB-INF/lib" % build_output] 154 | 155 | transitive_deps = _collect_transitive_runtime_deps_for(ctxt.attr.jars) 156 | 157 | for dep in transitive_deps.to_list(): 158 | cmd += _add_file(dep, build_output + "/WEB-INF/lib") 159 | inputs.append(dep) 160 | 161 | for jar in ctxt.files._appengine_deps: 162 | cmd += _add_file(jar, build_output + "/WEB-INF/lib") 163 | inputs.append(jar) 164 | 165 | inputs += ctxt.files.data 166 | for res in ctxt.files.data: 167 | # Add the data file 168 | cmd += _add_file(res, build_output, path = data_path) 169 | 170 | cmd += _make_war(zipper, build_output, war) 171 | 172 | ctxt.actions.run_shell( 173 | inputs = inputs, 174 | outputs = [war], 175 | mnemonic = "WAR", 176 | command = "\n".join(cmd), 177 | use_default_shell_env = True, 178 | ) 179 | 180 | executable = ctxt.outputs.executable 181 | appengine_sdk = None 182 | for f in ctxt.files._appengine_sdk: 183 | if not appengine_sdk: 184 | appengine_sdk = f.short_path 185 | elif not f.path.startswith(appengine_sdk): 186 | appengine_sdk = _common_substring(appengine_sdk, f.short_path) 187 | if not appengine_sdk: 188 | fail( 189 | "could not find appengine files", 190 | attr = str(ctxt.attr._appengine_sdk.label), 191 | ) 192 | 193 | classpath = ["${JAVA_RUNFILES}/%s" % jar.short_path for jar in transitive_deps.to_list()] 194 | classpath += [ 195 | "${JAVA_RUNFILES}/%s" % jar.short_path 196 | for jar in ctxt.files._appengine_deps 197 | ] 198 | 199 | substitutions = { 200 | "%{workspace_name}": ctxt.workspace_name, 201 | "%{zipper}": ctxt.file._zipper.short_path, 202 | "%{war}": ctxt.outputs.war.short_path, 203 | "%{java}": _java_runtime.java_executable_runfiles_path, 204 | "%{appengine_sdk}": appengine_sdk, 205 | "%{classpath}": (":".join(classpath)), 206 | "%{data_path}": data_path, 207 | "%{local_jvm_flags}": (" ".join(ctxt.attr.local_jvm_flags)), 208 | } 209 | 210 | ctxt.actions.expand_template( 211 | output = executable, 212 | template = ctxt.file._runner_template, 213 | substitutions = substitutions, 214 | is_executable = True, 215 | ) 216 | ctxt.actions.expand_template( 217 | output = ctxt.outputs.deploy_sh, 218 | template = ctxt.file._deploy_template, 219 | substitutions = substitutions, 220 | is_executable = True, 221 | ) 222 | 223 | runfiles = ctxt.runfiles( 224 | files = [war, executable] + 225 | inputs + 226 | ctxt.files._appengine_sdk + 227 | [ctxt.file._zipper], 228 | transitive_files = depset( 229 | transitive = [transitive_deps, _java_runtime.files] 230 | ), 231 | ) 232 | return struct(runfiles = runfiles) 233 | 234 | appengine_war_base = rule( 235 | _war_impl, 236 | attrs = { 237 | "_java": attr.label( 238 | default = Label("@bazel_tools//tools/jdk:current_java_runtime"), 239 | ), 240 | "_zipper": attr.label( 241 | default = Label("@bazel_tools//tools/zip:zipper"), 242 | allow_single_file = True, 243 | ), 244 | "_runner_template": attr.label( 245 | default = Label("//appengine/java:runner_template"), 246 | allow_single_file = True, 247 | ), 248 | "_deploy_template": attr.label( 249 | default = Label("//appengine/java:deploy_template"), 250 | allow_single_file = True, 251 | ), 252 | "_appengine_sdk": attr.label( 253 | default = Label("@com_google_appengine_java//:sdk"), 254 | ), 255 | "_appengine_deps": attr.label_list( 256 | default = [Label("@com_google_appengine_java//:api")], 257 | ), 258 | "jars": attr.label_list( 259 | allow_files = [".jar"], 260 | mandatory = True, 261 | ), 262 | "data": attr.label_list(allow_files = True), 263 | "data_path": attr.string(), 264 | "local_jvm_flags": attr.string_list(), 265 | }, 266 | executable = True, 267 | outputs = { 268 | "war": "%{name}.war", 269 | "deploy_sh": "%{name}_deploy.sh", 270 | }, 271 | ) 272 | 273 | def java_war(name, data = [], data_path = None, local_jvm_flags = [], **kwargs): 274 | """Convenience macro to call appengine_war with Java sources rather than jar. 275 | """ 276 | native.java_library(name = "lib%s" % name, **kwargs) 277 | appengine_war( 278 | name = name, 279 | jars = ["lib%s" % name], 280 | data = data, 281 | data_path = data_path, 282 | local_jvm_flags = local_jvm_flags, 283 | ) 284 | 285 | def appengine_war(name, jars, data, data_path, local_jvm_flags = [], testonly = 0): 286 | """Convenience macro that builds the war and offers an executable 287 | target to deploy on Google app engine. 288 | """ 289 | appengine_war_base( 290 | name = name, 291 | jars = jars, 292 | data = data, 293 | data_path = data_path, 294 | local_jvm_flags = local_jvm_flags, 295 | testonly = testonly, 296 | ) 297 | 298 | # Create the executable rule to deploy 299 | native.sh_binary( 300 | name = "%s.deploy" % name, 301 | srcs = ["%s_deploy.sh" % name], 302 | data = [name], 303 | testonly = testonly, 304 | ) 305 | 306 | def java_appengine_repositories( 307 | version = JAVA_SDK_VERSION, 308 | sha256 = JAVA_SDK_SHA256): 309 | find_locally_or_download( 310 | name = "com_google_appengine_java", 311 | lang = "java", 312 | sha256 = sha256, 313 | version = version, 314 | filename_pattern = "appengine-java-sdk-{version}.zip", 315 | strip_prefix_pattern = "appengine-java-sdk-{version}", 316 | ) 317 | 318 | jvm_maven_import_external( 319 | name = "javax_servlet_api", 320 | artifact = "javax.servlet:javax.servlet-api:3.1.0", 321 | artifact_sha256 = "af456b2dd41c4e82cf54f3e743bc678973d9fe35bd4d3071fa05c7e5333b8482", 322 | server_urls = ["https://repo1.maven.org/maven2/"], 323 | licenses = ["reciprocal"], # CDDL License 324 | ) 325 | 326 | if not versions.get() or versions.is_at_least("6.0.0", versions.get()): # development or version >= 6.0.0 327 | build_file_content = """ 328 | load( 329 | "@bazel_tools//tools/jdk:default_java_toolchain.bzl", 330 | "default_java_toolchain", 331 | "DEFAULT_TOOLCHAIN_CONFIGURATION", 332 | "DEFAULT_JAVACOPTS" 333 | ) 334 | 335 | default_java_toolchain( 336 | name = "jdk8", 337 | configuration = DEFAULT_TOOLCHAIN_CONFIGURATION, 338 | jvm_opts = DEFAULT_JAVACOPTS + [ 339 | "-XX:+TieredCompilation", 340 | "-XX:TieredStopAtLevel=1", 341 | ], 342 | source_version = "8", 343 | target_version = "8", 344 | visibility = ["//visibility:public"], 345 | ) 346 | """ 347 | # Bazel < 6.0.0 348 | 349 | else: 350 | build_file_content = """ 351 | load( 352 | "@bazel_tools//tools/jdk:default_java_toolchain.bzl", 353 | "default_java_toolchain", 354 | "JDK8_JVM_OPTS", 355 | ) 356 | 357 | default_java_toolchain( 358 | name = "jdk8", 359 | tools = ["@bazel_tools//tools/jdk:javac_jar"], 360 | jvm_opts = JDK8_JVM_OPTS + [ 361 | "-XX:+TieredCompilation", 362 | "-XX:TieredStopAtLevel=1", 363 | ], 364 | source_version = "8", 365 | target_version = "8", 366 | visibility = ["//visibility:public"], 367 | ) 368 | """ 369 | native.new_local_repository( 370 | name = "rules_appengine_toolchain", 371 | path = ".", 372 | build_file_content = build_file_content, 373 | ) 374 | -------------------------------------------------------------------------------- /appengine/py/BUILD: -------------------------------------------------------------------------------- 1 | filegroup( 2 | name = "runner_template", 3 | srcs = ["appengine_runner.sh.template"], 4 | visibility = ["//visibility:public"], 5 | ) 6 | 7 | filegroup( 8 | name = "deploy_template", 9 | srcs = ["appengine_deploy.sh.template"], 10 | visibility = ["//visibility:public"], 11 | ) 12 | 13 | filegroup( 14 | name = "sdk_build_file", 15 | srcs = ["sdk.BUILD"], 16 | visibility = ["//visibility:public"], 17 | ) 18 | -------------------------------------------------------------------------------- /appengine/py/appengine_deploy.sh.template: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2017 The Bazel Authors. All rights reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | case "$0" in 17 | /*) self="$0" ;; 18 | *) self="$PWD/$0";; 19 | esac 20 | if [[ -e "$self.runfiles/%{workspace_name}" ]]; then 21 | RUNFILES="$self.runfiles/%{workspace_name}" 22 | cd "$RUNFILES" 23 | fi 24 | 25 | ROOT=$PWD 26 | 27 | # remove double slash from TMPDIR trailing slash 28 | tmp_dir=$(echo ${TMPDIR:-/tmp}/war.XXXXXXXX | tr -s /) 29 | tmp_dir="$(mktemp -d "${tmp_dir}")" 30 | 31 | cp -R $ROOT $tmp_dir 32 | trap "{ cd "$ROOT"; rm -rf "$tmp_dir"; }" EXIT 33 | 34 | cd "${tmp_dir}/%{workspace_name}" 35 | rm -Rf "external/com_google_appengine_py" 36 | 37 | $ROOT/%{gcloud_path} app deploy "${@:1}" %{configs} 38 | 39 | exit 40 | -------------------------------------------------------------------------------- /appengine/py/appengine_runner.sh.template: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2017 The Bazel Authors. All rights reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | case "$0" in 17 | /*) self="$0" ;; 18 | *) self="$PWD/$0";; 19 | esac 20 | if [[ -e "$self.runfiles/%{workspace_name}" ]]; then 21 | RUNFILES="$self.runfiles/%{workspace_name}" 22 | cd $RUNFILES 23 | fi 24 | 25 | %{devappserver} --skip_sdk_update_check 1 "$@" app.yaml 26 | -------------------------------------------------------------------------------- /appengine/py/sdk.BUILD: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The Bazel Authors. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # BUILD file to use the Python AppEngine SDK with a remote repository. 16 | package(default_visibility = ["//visibility:public"]) 17 | 18 | py_library( 19 | name = "appengine", 20 | srcs = glob(["**/*.py"]), 21 | data = glob( 22 | ["**/*"], 23 | exclude = ["**/*.py"], 24 | ), 25 | ) 26 | 27 | py_binary( 28 | name = "dev_appserver", 29 | srcs = ["dev_appserver.py"], 30 | deps = [":appengine"], 31 | ) 32 | 33 | py_binary( 34 | name = "appcfg", 35 | srcs = ["appcfg.py"], 36 | deps = [":appengine"], 37 | ) 38 | 39 | py_library( 40 | name = "endpoints-1.0", 41 | srcs = glob(["lib/endpoints-1.0/**/*.py"]), 42 | imports = ["lib/endpoints-1.0"], 43 | deps = [":protorpc-1.0"], 44 | ) 45 | 46 | py_library( 47 | name = "fancy_urllib", 48 | srcs = glob(["lib/fancy_urllib/**/*.py"]), 49 | imports = ["lib/fancy_urllib"], 50 | ) 51 | 52 | py_library( 53 | name = "jinja2-2.6", 54 | srcs = glob(["lib/jinja2-2.6/jinja2/**/*.py"]), 55 | imports = ["lib/jinja2-2.6"], 56 | ) 57 | 58 | py_library( 59 | name = "markupsafe-0.15", 60 | srcs = glob(["lib/markupsafe-0.15/**/*.py"]), 61 | imports = ["lib/markupsafe-0.15"], 62 | ) 63 | 64 | py_library( 65 | name = "markupsafe-0.23", 66 | srcs = glob(["lib/markupsafe-0.23/**/*.py"]), 67 | imports = ["lib/markupsafe-0.23"], 68 | ) 69 | 70 | py_library( 71 | name = "protorpc-1.0", 72 | srcs = glob(["lib/protorpc-1.0/**/*.py"]), 73 | imports = ["lib/protorpc-1.0"], 74 | ) 75 | 76 | py_library( 77 | name = "PyAMF-0.6.1", 78 | srcs = glob(["lib/PyAMF-0.6.1/**/*.py"]), 79 | imports = ["lib/PyAMF-0.6.1"], 80 | ) 81 | 82 | py_library( 83 | name = "PyAMF-0.7.2", 84 | srcs = glob(["lib/PyAMF-0.7.2/**/*.py"]), 85 | imports = ["lib/PyAMF-0.7.2"], 86 | ) 87 | 88 | py_library( 89 | name = "webapp2-2.5.2", 90 | srcs = glob(["lib/webapp2-2.5.2/**/*.py"]), 91 | imports = ["lib/webapp2-2.5.2"], 92 | deps = [":webob-1.2.3"], 93 | ) 94 | 95 | py_library( 96 | name = "webob-1.2.3", 97 | srcs = glob(["lib/webob-1.2.3/**/*.py"]), 98 | imports = ["lib/webob-1.2.3"], 99 | ) 100 | 101 | py_library( 102 | name = "werkzeug-0.11.10", 103 | srcs = glob(["lib/werkzeug-0.11.10/**/*.py"]), 104 | imports = ["lib/werkzeug-0.11.10"], 105 | ) 106 | 107 | py_library( 108 | name = "yaml-3.10", 109 | srcs = glob(["lib/yaml-3.10/**/*.py"]), 110 | imports = ["lib/yaml-3.10"], 111 | ) 112 | 113 | py_library( 114 | name = "endpoints-latest", 115 | deps = [":endpoints-1.0"], 116 | ) 117 | 118 | py_library( 119 | name = "fancy_urllib-latest", 120 | deps = [":fancy_urllib"], 121 | ) 122 | 123 | py_library( 124 | name = "jinja2-latest", 125 | deps = [":jinja2-2.6"], 126 | ) 127 | 128 | py_library( 129 | name = "markupsafe-latest", 130 | deps = [":markupsafe-0.23"], 131 | ) 132 | 133 | py_library( 134 | name = "protorpc-latest", 135 | deps = [":protorpc-1.0"], 136 | ) 137 | 138 | py_library( 139 | name = "PyAMF-latest", 140 | deps = [":PyAMF-0.7.2"], 141 | ) 142 | 143 | py_library( 144 | name = "webapp2-latest", 145 | deps = [":webapp2-2.5.2"], 146 | ) 147 | 148 | py_library( 149 | name = "webob-latest", 150 | deps = [":webob-1.2.3"], 151 | ) 152 | 153 | py_library( 154 | name = "werkzeug-latest", 155 | deps = [":werkzeug-0.11.10"], 156 | ) 157 | 158 | py_library( 159 | name = "yaml-latest", 160 | deps = [":yaml-3.10"], 161 | ) 162 | -------------------------------------------------------------------------------- /appengine/py_appengine.bzl: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The Bazel Authors. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Python AppEngine support for Bazel. 15 | 16 | To create a Python WebApp for Google AppEngine, add the rules: 17 | py_appengine_binary( 18 | name = "mywebapp", 19 | # data to put in the webapp, the directory structure of the data set 20 | # will be maintained. 21 | data = ["//mywebapp:data"], 22 | configs = ["//mywebapp:app.yaml", //mywebapp:appengine_config.py], 23 | srcs = ["main.py"], 24 | ) 25 | 26 | #optional test 27 | py_appengine_test( 28 | name = "mywebapp_test", 29 | srcs = ["main_test.py"], 30 | deps = [":main"], 31 | libraries = {"webapp2": "latest"}, 32 | ) 33 | 34 | To run locally: 35 | bazel run :mywebapp 36 | 37 | To deploy on Google app engine: 38 | bazel run :mywebapp.deploy -- my-project-id [module.yaml files ...] 39 | 40 | Finally, the appengine macro also create a .deploy target that will try to use 41 | the AppEngine SDK to upload your application to AppEngine. It requires the 42 | project ID as the first argument and takes 0 or more module YAML files. If no 43 | YAML files are specified, only "app.yaml", the main module, will be deployed. 44 | """ 45 | 46 | load(":variables.bzl", "PY_SDK_SHA256", "PY_SDK_VERSION") 47 | load(":sdk.bzl", "find_locally_or_download") 48 | 49 | def py_appengine_repositories(version = PY_SDK_VERSION, sha256 = PY_SDK_SHA256): 50 | find_locally_or_download( 51 | name = "com_google_appengine_py", 52 | lang = "py", 53 | sha256 = sha256, 54 | version = version, 55 | filename_pattern = "google_appengine_{version}.zip", 56 | strip_prefix_pattern = "google_appengine", 57 | ) 58 | 59 | def py_appengine_test(name, srcs, deps = [], data = [], libraries = {}, size = None): 60 | """A variant of py_test that sets up an App Engine environment.""" 61 | extra_deps = ["@com_google_appengine_py//:appengine"] 62 | for l in libraries: 63 | extra_deps.append("@com_google_appengine_py//:{0}-{1}".format( 64 | l, 65 | libraries[l], 66 | )) 67 | native.py_test( 68 | name = name, 69 | deps = deps + extra_deps, 70 | srcs = srcs, 71 | data = data, 72 | size = size, 73 | ) 74 | 75 | def _py_appengine_binary_base_impl(ctx): 76 | """Implementation of the rule that creates 77 | 78 | - the script to run locally 79 | - the script to deploy 80 | """ 81 | 82 | # TODO(maximermilov): Add support for custom import paths. 83 | config = ctx.actions.declare_file("appengine_config.py") 84 | config_content = """ 85 | import os 86 | import sys 87 | 88 | module_space = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'external') 89 | 90 | repo_dirs = [os.path.join(module_space, d) for d in os.listdir(module_space)] 91 | sys.path.extend([d for d in repo_dirs if os.path.isdir(d)]) 92 | """ 93 | symlinks = { 94 | "appengine_config.py": config, 95 | } 96 | 97 | appengine_config = None 98 | configs = "" 99 | 100 | for c in ctx.attr.configs: 101 | files = c.files.to_list() 102 | for f in files: 103 | if f.basename == "appengine_config.py": 104 | appengine_config = f 105 | elif f.extension == "yaml": 106 | # Symlink YAML config files to the top-level directory. 107 | symlinks[f.basename] = f 108 | configs += " \"" + f.basename + "\"" 109 | else: 110 | # Fail if any .py files were provided that were not appengine_configs. 111 | fail("Invalid config file provided: " + f.short_path) 112 | 113 | if ctx.attr.overwrite_appengine_config and appengine_config: 114 | ctx.actions.run_shell( 115 | inputs = [appengine_config], 116 | outputs = [config], 117 | command = "cp %s %s" % (appengine_config.path, config.path), 118 | ) 119 | else: 120 | if appengine_config: 121 | # Symlink the user-provided appengine_config file(s) to avoid name 122 | # collisions and add import(s) from the custom appengine_config being 123 | # created. 124 | new_path = f.short_path.replace( 125 | "appengine_config", 126 | "real_appengine_config", 127 | ) 128 | symlinks[new_path] = f 129 | 130 | import_path = new_path.rsplit(".", 1)[0].replace("/", ".") 131 | config_content += "\nimport {}\n".format(import_path) 132 | 133 | ctx.actions.write( 134 | output = config, 135 | content = config_content, 136 | ) 137 | 138 | runfiles = ctx.runfiles( 139 | transitive_files = ctx.attr.devappserver.data_runfiles.files, 140 | symlinks = symlinks, 141 | ).merge(ctx.attr.binary.data_runfiles).merge(ctx.attr.appcfg.data_runfiles) 142 | 143 | substitutions = { 144 | "%{appcfg}": ctx.attr.appcfg.files_to_run.executable.short_path, 145 | "%{devappserver}": ctx.attr.devappserver.files_to_run.executable.short_path, 146 | "%{gcloud_path}": ctx.attr.gcloud.files_to_run.executable.short_path, 147 | "%{workspace_name}": ctx.workspace_name, 148 | "%{configs}": configs, 149 | } 150 | 151 | ctx.actions.expand_template( 152 | output = ctx.outputs.executable, 153 | template = ctx.file._runner_template, 154 | substitutions = substitutions, 155 | is_executable = True, 156 | ) 157 | 158 | ctx.actions.expand_template( 159 | output = ctx.outputs.deploy_sh, 160 | template = ctx.file._deploy_template, 161 | substitutions = substitutions, 162 | is_executable = True, 163 | ) 164 | 165 | return [DefaultInfo(runfiles = runfiles), ctx.attr.binary[PyInfo]] 166 | 167 | py_appengine_binary_base = rule( 168 | _py_appengine_binary_base_impl, 169 | attrs = { 170 | "binary": attr.label(), 171 | "devappserver": attr.label( 172 | default = Label("@com_google_appengine_py//:dev_appserver"), 173 | ), 174 | "appcfg": attr.label(default = Label("@com_google_appengine_py//:appcfg")), 175 | "gcloud": attr.label(default = Label("@com_google_cloud_sdk//:gcloud")), 176 | "configs": attr.label_list(allow_files = [".yaml", ".py"]), 177 | "overwrite_appengine_config": attr.bool( 178 | doc = """"If true, patch the user's appengine_config into the base one. If false, use 179 | the user specified config directly.""", 180 | default = True, 181 | ), 182 | "_deploy_template": attr.label( 183 | default = Label("//appengine/py:deploy_template"), 184 | allow_single_file = True, 185 | ), 186 | "_runner_template": attr.label( 187 | default = Label("//appengine/py:runner_template"), 188 | allow_single_file = True, 189 | ), 190 | }, 191 | executable = True, 192 | outputs = { 193 | "deploy_sh": "%{name}_deploy.sh", 194 | }, 195 | ) 196 | 197 | def py_appengine_binary(name, srcs, configs, deps = [], data = [], overwrite_appengine_config = True): 198 | """Convenience macro that builds the app and offers an executable 199 | 200 | target to deploy on Google app engine. 201 | """ 202 | if not srcs: 203 | fail("srcs should not be empty.") 204 | 205 | # uses py_binary because it generates __init__.py files 206 | native.py_binary( 207 | name = "_py_appengine_" + name, 208 | srcs = srcs, 209 | deps = deps, 210 | data = data, 211 | main = srcs[0], # no entry point, use arbitrary source file 212 | ) 213 | py_appengine_binary_base( 214 | name = name, 215 | binary = ":_py_appengine_" + name, 216 | configs = configs, 217 | overwrite_appengine_config = overwrite_appengine_config, 218 | ) 219 | native.sh_binary( 220 | name = "%s.deploy" % name, 221 | srcs = ["%s_deploy.sh" % name], 222 | data = [ 223 | name, 224 | "@com_google_cloud_sdk//:gcloud", 225 | ], 226 | ) 227 | 228 | def py_appengine_library(**kwargs): 229 | """Wrapper for py_library 230 | 231 | """ 232 | native.py_library(**kwargs) 233 | -------------------------------------------------------------------------------- /appengine/sdk.bzl: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The Bazel Authors. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Language agnostic utility functions for use in the other .bzl files. 15 | 16 | """ 17 | 18 | load( 19 | ":variables.bzl", 20 | "APPENGINE_VERSION", 21 | "CLOUD_SDK_PLATFORM_ARCHIVE", 22 | "CLOUD_SDK_PLATFORM_SHA256", 23 | "SDK_URL_PREFIX", 24 | ) 25 | 26 | def _find_locally_or_download_impl(repository_ctx): 27 | lang = repository_ctx.attr.lang 28 | env_var = lang.upper() + "_APPENGINE_SDK_PATH" 29 | if env_var in repository_ctx.os.environ: 30 | path = repository_ctx.os.environ[env_var] 31 | if path == "": 32 | fail(env_var + " set, but empty") 33 | repository_ctx.symlink(path, ".") 34 | else: 35 | substitutions = { 36 | "version": repository_ctx.attr.version, 37 | } 38 | repository_ctx.download_and_extract( 39 | url = "{}/{}".format( 40 | SDK_URL_PREFIX, 41 | repository_ctx.attr.filename_pattern.format(**substitutions), 42 | ), 43 | output = ".", 44 | sha256 = repository_ctx.attr.sha256, 45 | stripPrefix = repository_ctx.attr.strip_prefix_pattern.format( 46 | **substitutions 47 | ), 48 | ) 49 | repository_ctx.template( 50 | "BUILD", 51 | Label("//appengine:{}/sdk.BUILD".format(lang.lower())), 52 | ) 53 | 54 | find_locally_or_download = repository_rule( 55 | attrs = { 56 | "lang": attr.string( 57 | mandatory = True, 58 | doc = "The language of the SDK to download.", 59 | values = ["java", "py"], 60 | ), 61 | "sha256": attr.string( 62 | mandatory = True, 63 | doc = "The sha256sum of the sdk zip file.", 64 | ), 65 | "version": attr.string( 66 | mandatory = True, 67 | doc = "The SDK version to download. Usually of the form %s." % 68 | APPENGINE_VERSION, 69 | ), 70 | "strip_prefix_pattern": attr.string( 71 | default = "google_appengine", 72 | mandatory = True, 73 | doc = 74 | "When the zip file is extracted, remove this prefix from all paths. If it includes '{version}', it will be replaced with the version.", 75 | ), 76 | "filename_pattern": attr.string( 77 | default = "google_appengine_{version}.zip", 78 | mandatory = True, 79 | doc = 80 | "The filename of the SDK zip file to download. If it includes '{version}', it will be replaced with the version.", 81 | ), 82 | }, 83 | local = False, 84 | implementation = _find_locally_or_download_impl, 85 | ) 86 | 87 | def _appengine_download_cloud_sdk(repository_ctx): 88 | repository_ctx.download_and_extract( 89 | url = CLOUD_SDK_PLATFORM_ARCHIVE, 90 | output = ".", 91 | sha256 = CLOUD_SDK_PLATFORM_SHA256, 92 | stripPrefix = "google-cloud-sdk", 93 | ) 94 | repository_ctx.template("BUILD", Label("//appengine:cloud_sdk.BUILD")) 95 | 96 | appengine_download_cloud_sdk = repository_rule( 97 | local = False, 98 | implementation = _appengine_download_cloud_sdk, 99 | ) 100 | 101 | def appengine_repositories(): 102 | appengine_download_cloud_sdk(name = "com_google_cloud_sdk") 103 | -------------------------------------------------------------------------------- /appengine/variables.bzl: -------------------------------------------------------------------------------- 1 | """This file is a central location for configuring new SDK versions. 2 | 3 | """ 4 | 5 | # Not all languages are released for every SDK version. Whenever possible, set 6 | # ${LANG}_SDK_VERSION = APPENGINE_VERSION. 7 | APPENGINE_VERSION = "1.9.73" 8 | 9 | SDK_URL_PREFIX = "https://storage.googleapis.com/appengine-sdks/featured" 10 | 11 | JAVA_SDK_SHA256 = "2c855a7f2a97dd43287569648b9175c4ab6dce61f1720ef02dc9308a74b33f00" 12 | 13 | JAVA_SDK_VERSION = APPENGINE_VERSION 14 | 15 | PY_SDK_SHA256 = "68a6550118e557821dd2305fa75fec4750aa6d582be2542ac8ca40b782be6948" 16 | 17 | PY_SDK_VERSION = APPENGINE_VERSION 18 | 19 | # TODO: Support other platforms besides linux 20 | _CLOUD_SDK_BASE_URL = "https://dl.google.com/dl/cloudsdk/channels/rapid/downloads" 21 | CLOUD_SDK_PLATFORM_ARCHIVE = "{}/google-cloud-sdk-214.0.0-linux-x86_64.tar.gz".format( 22 | _CLOUD_SDK_BASE_URL, 23 | ) 24 | CLOUD_SDK_PLATFORM_SHA256 = "7fc406095fbb4267f4d3db3d1648a6a97b47eef7b48cee30335e29e76ef85aeb" 25 | -------------------------------------------------------------------------------- /examples/java/BUILD: -------------------------------------------------------------------------------- 1 | load("//appengine:java_appengine.bzl", "appengine_war") 2 | 3 | appengine_war( 4 | name = "examples", 5 | data = ["//examples/java/webapp"], 6 | data_path = "/examples/java/webapp", 7 | jars = ["//examples/java/src:src_deploy.jar"], 8 | ) 9 | -------------------------------------------------------------------------------- /examples/java/src/App.java: -------------------------------------------------------------------------------- 1 | import com.google.appengine.api.users.UserService; 2 | import com.google.appengine.api.users.UserServiceFactory; 3 | import java.io.IOException; 4 | import javax.servlet.annotation.WebServlet; 5 | import javax.servlet.http.HttpServlet; 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | @WebServlet(name = "examples", value = "/") 10 | public class App extends HttpServlet { 11 | @Override 12 | public void doGet(HttpServletRequest req, HttpServletResponse resp) 13 | throws IOException { 14 | UserService userService = UserServiceFactory.getUserService(); 15 | System.out.println(userService.getCurrentUser()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/java/src/BUILD: -------------------------------------------------------------------------------- 1 | java_binary( 2 | name = "src", 3 | srcs = ["App.java"], 4 | visibility = ["//examples/java:__pkg__"], 5 | deps = [ 6 | "//appengine:javax.servlet.api", 7 | "@com_google_appengine_java//:api", 8 | ], 9 | ) 10 | -------------------------------------------------------------------------------- /examples/java/webapp/BUILD: -------------------------------------------------------------------------------- 1 | filegroup( 2 | name = "webapp", 3 | srcs = glob(["**"]), 4 | visibility = ["//examples/java:__pkg__"], 5 | ) 6 | -------------------------------------------------------------------------------- /examples/java/webapp/WEB-INF/appengine-web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | example-app 4 | 1 5 | true 6 | /static 7 | java8 8 | true 9 | 10 | -------------------------------------------------------------------------------- /examples/py/hello_world/BUILD: -------------------------------------------------------------------------------- 1 | """ 2 | Example taken from the official App Engine examples: 3 | https://github.com/GoogleCloudPlatform/python-docs-samples/tree/master/appengine/standard/hello_world 4 | """ 5 | 6 | load("@io_bazel_rules_appengine//appengine:py_appengine.bzl", "py_appengine_binary") 7 | 8 | 9 | py_appengine_binary( 10 | name = "main", 11 | srcs = ["main.py"], 12 | configs = ["app.yaml"], 13 | ) 14 | -------------------------------------------------------------------------------- /examples/py/hello_world/app.yaml: -------------------------------------------------------------------------------- 1 | runtime: python27 2 | api_version: 1 3 | threadsafe: true 4 | 5 | handlers: 6 | - url: /.* 7 | script: main.app 8 | -------------------------------------------------------------------------------- /examples/py/hello_world/main.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import webapp2 16 | 17 | 18 | class MainPage(webapp2.RequestHandler): 19 | def get(self): 20 | self.response.headers['Content-Type'] = 'text/plain' 21 | self.response.write('Hello, World!') 22 | 23 | 24 | app = webapp2.WSGIApplication([ 25 | ('/', MainPage), 26 | ], debug=True) 27 | -------------------------------------------------------------------------------- /test/java/BUILD: -------------------------------------------------------------------------------- 1 | load("//appengine:java_appengine.bzl", "appengine_war") 2 | 3 | appengine_war( 4 | name = "test-war", 5 | testonly = 1, 6 | data = [ 7 | ":web-inf", 8 | "//test/java/data:gen-data", 9 | "//test/java/data:test-data", 10 | ], 11 | data_path = "/test/java", 12 | jars = [":app_deploy.jar"], 13 | ) 14 | 15 | java_binary( 16 | name = "app", 17 | main_class = "dummy", 18 | ) 19 | 20 | sh_test( 21 | name = "check_war", 22 | srcs = ["check_war.sh"], 23 | args = ["$(JAVABASE)"], 24 | data = [ 25 | ":test-war", 26 | "@bazel_tools//tools/jdk:current_java_runtime", 27 | ], 28 | toolchains = [ 29 | "@bazel_tools//tools/jdk:current_java_runtime", 30 | ], 31 | ) 32 | 33 | filegroup( 34 | name = "web-inf", 35 | srcs = glob(["WEB-INF/*"]), 36 | ) 37 | -------------------------------------------------------------------------------- /test/java/WEB-INF/appengine-web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | test-war 4 | 1 5 | true 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/java/WEB-INF/logging.properties: -------------------------------------------------------------------------------- 1 | # A default java.util.logging configuration. 2 | # (All App Engine logging is through java.util.logging by default). 3 | # 4 | # To use this configuration, copy it into your application's WEB-INF 5 | # folder and add the following to your appengine-web.xml: 6 | # 7 | # 8 | # 9 | # 10 | # 11 | 12 | # Set the default logging level for all loggers to WARNING 13 | .level = WARNING 14 | -------------------------------------------------------------------------------- /test/java/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | welcome.jsp 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/java/check_war.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | RUNFILES="$TEST_SRCDIR" 6 | if [ -d "${TEST_SRCDIR}/io_bazel_rules_appengine" ]; then 7 | RUNFILES="${TEST_SRCDIR}/io_bazel_rules_appengine" 8 | fi 9 | TEST_WAR="${RUNFILES}/test/java/test-war.war" 10 | JAR="$1/bin/jar" 11 | 12 | function assert_war_contains() { 13 | local needle="$1" 14 | "${JAR}" -tf "$TEST_WAR" | grep -sq "$needle" && return 0 15 | echo "Contents of $TEST_WAR:" 16 | "${JAR}" -tf "$TEST_WAR" 17 | echo "Expected '$needle' in $TEST_WAR" 18 | return 1 19 | } 20 | 21 | assert_war_contains "./WEB-INF/lib/test_java_app_deploy.jar" 22 | assert_war_contains "./WEB-INF/lib/appengine-api.jar" 23 | assert_war_contains "./WEB-INF/appengine-web.xml" 24 | assert_war_contains "./WEB-INF/web.xml" 25 | assert_war_contains "./data/welcome.jsp" 26 | assert_war_contains "./data/gen-data.out" 27 | -------------------------------------------------------------------------------- /test/java/data/BUILD: -------------------------------------------------------------------------------- 1 | package( 2 | default_testonly = 1, 3 | default_visibility = ["//test/java:__pkg__"], 4 | ) 5 | 6 | filegroup( 7 | name = "test-data", 8 | srcs = glob(["*"]), 9 | ) 10 | 11 | genrule( 12 | name = "gen-data", 13 | outs = ["gen-data.out"], 14 | cmd = "echo hi > $@", 15 | ) 16 | -------------------------------------------------------------------------------- /test/java/data/welcome.jsp: -------------------------------------------------------------------------------- 1 | <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 | --------------------------------------------------------------------------------