├── .circleci └── config.yml ├── .github └── renovate.json ├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── check-google-java-format.py ├── fixup-google-java-format.py ├── run-google-java-format.el └── run-google-java-format.py /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | jobs: 3 | build: 4 | 5 | docker: 6 | - image: cimg/openjdk:11.0-browsers 7 | 8 | steps: 9 | 10 | - restore_cache: 11 | keys: 12 | - source-v1-{{ .Branch }}-{{ .Revision }} 13 | - source-v1-{{ .Branch }}- 14 | - source-v1- 15 | - checkout 16 | - save_cache: 17 | key: source-v1-{{ .Branch }}-{{ .Revision }} 18 | paths: 19 | - ".git" 20 | 21 | - run: echo "TODO add some tests" 22 | 23 | workflows: 24 | version: 2 25 | build: 26 | jobs: 27 | - build 28 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base", 5 | ":automergeAll", 6 | ":automergeRequireAllStatusChecks", 7 | "schedule:nonOfficeHours", 8 | ":disableDependencyDashboard" 9 | ], 10 | "timezone": "America/Los_Angeles" 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # These files are downloaded to this directory from elsewhere. 2 | google-java-format-*-all-deps.jar 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | install: true 2 | 3 | # TODO: add some tests 4 | script: true 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 plume-lib 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PYTHON_FILES=$(wildcard *.py) $(wildcard *.pm) 2 | python-style: 3 | ruff format ${PYTHON_FILES} 4 | ruff check ${PYTHON_FILES} --fix 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # run-google-java-format 2 | 3 | This repository contains the `run-google-java-format.py` and 4 | `check-google-java-format.py` scripts. 5 | They automatically download and run 6 | [google-java-format](https://github.com/google/google-java-format), 7 | they slightly improve its output, and they add checking functionality. 8 | 9 | The [google-java-format](https://github.com/google/google-java-format), or GJF, 10 | program reformats Java source code. It is a good formatter, but 11 | this project (run-google-java-format) improves it: 12 | * This project creates better formatting for [type annotations](https://github.com/google/google-java-format/issues/5). 13 | * This project creates better formatting for annotations in comments. 14 | * This project can check whether a file is properly formatted, which is desirable in a pre-commit hook. 15 | (Update: Version 1.5 of GJF can do this, but it is not cognizant of the better formatting that this project does.) 16 | * This project has a handy script to reformat code; by contrast, GJF requires a long, hard-to-remember command line. 17 | * You can run this project in a way that automatically uses the latest version of GJF, if you wish, without having to remember to update your GJF installation periodically. (You can also control which version of GJF this project uses; see "Customization" below.) 18 | 19 | The `run-google-java-format.py` and `check-google-java-format.py` scripts provide these enhancements over plain google-java-format. 20 | 21 | 22 | ## run-google-java-format.py 23 | 24 | This script reformats each file supplied on the command line according to 25 | the Google Java style, but with improvements to the formatting of 26 | annotations in comments. 27 | If called with no arguments, it reads from and writes to standard output. 28 | 29 | 30 | ## check-google-java-format.py 31 | 32 | Given `.java` file names on the command line, reports any that would be 33 | reformatted by the `run-google-java-format.py` program, and returns 34 | non-zero status if there were any. 35 | If called with no arguments, it reads from standard output. 36 | You could invoke this program, for example, in a [git pre-commit hook](#git-pre-commit-hook). 37 | 38 | 39 | ## Installing 40 | 41 | There are two ways to install and use these scripts (see below for integration with [Make](#makefile), [Ant](#ant-buildxml), [Gradle](#gradle-buildgradle), and [Git pre-commit hooks](#git-pre-commit-hook) that you can copy-and-paste into your build file): 42 | * Clone the repository (run `git clone 43 | https://github.com/plume-lib/run-google-java-format.git`) and run the 44 | scripts from there. 45 | * Download the 46 | [run-google-java-format.py](https://raw.githubusercontent.com/plume-lib/run-google-java-format/master/run-google-java-format.py) 47 | or 48 | [check-google-java-format.py](https://raw.githubusercontent.com/plume-lib/run-google-java-format/master/check-google-java-format.py) 49 | file and run it. The file will automatically download any additional 50 | needed files. 51 | 52 | 53 | ## Customization 54 | 55 | To control which version of google-java-format to use, 56 | set environment variable `GJF_VERSION`. For example: 57 | 58 | ```export GJF_VERSION=1.7``` 59 | 60 | 61 | ## Integrating with a build system 62 | 63 | Add the following targets to your build system. 64 | 65 | Integration with other build systems is similar. (Feel free to contribute 66 | concrete examples for build systems that are not listed here.) 67 | 68 | Some of these commands have only been tested on Unix; 69 | if you can create a version that also works on Windows, please contribute it. 70 | 71 | 72 | ### Makefile 73 | 74 | ``` 75 | JAVA_FILES_TO_FORMAT ?= $(shell find src -name '*.java' -print | grep -v '\.\#' | grep -v WeakHasherMap.java | grep -v WeakIdentityHashMap.java | grep -v MathMDE.java | sort) 76 | 77 | update-run-google-java-format: 78 | @[ -d .run-google-java-format ] && (cd .run-google-java-format && git pull -q) || git clone -q https://github.com/plume-lib/run-google-java-format.git .run-google-java-format 79 | 80 | # Requires Java 8 81 | reformat: 82 | ${MAKE} update-run-google-java-format 83 | @./.run-google-java-format/run-google-java-format.py ${JAVA_FILES_TO_FORMAT} 84 | 85 | # Requires Java 8 86 | check-format: 87 | ${MAKE} update-run-google-java-format 88 | @./.run-google-java-format/check-google-java-format.py ${JAVA_FILES_TO_FORMAT} || (echo "Try running: make reformat" && /bin/false) 89 | ``` 90 | 91 | 92 | ### Ant `build.xml` 93 | 94 | At the top of your Ant file, augment the `` block: 95 | 96 | ``` 97 | 99 | ``` 100 | 101 | Then, add this: 102 | 103 | ``` 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 127 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 139 | 141 | 142 | 143 | 144 | 145 | 146 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 156 | 159 | 160 | 161 | 162 | ${check.format.stdout} 163 | ${check.format.stderr} 164 | Fix syntax errors, then re-run: ant check-format 165 | Try running: ant reformat 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | ``` 175 | 176 | 177 | ### Gradle `build.gradle` 178 | 179 | Customize per your requirements, such as excluding generated `.java` files from formatting. 180 | 181 | ``` 182 | task getCodeFormatScripts { 183 | description "Obtain the run-google-java-format scripts" 184 | doLast { 185 | def rgjfDir = "$projectDir/.run-google-java-format" 186 | if (! new File(rgjfDir).exists()) { 187 | exec { 188 | commandLine 'git', 'clone', '--filter=tree:0', "https://github.com/plume-lib/run-google-java-format.git", rgjfDir 189 | } 190 | } else { 191 | // Ignore exit value so this does not halt the build when not connected to the Internet. 192 | exec { 193 | workingDir rgjfDir 194 | ignoreExitValue true 195 | commandLine 'git', 'pull', '-q' 196 | } 197 | } 198 | } 199 | } 200 | 201 | task pythonIsInstalled(type: Exec) { 202 | description "Check that the python3 executable is installed." 203 | executable = "python3" 204 | args "--version" 205 | } 206 | 207 | task checkFormat(type: Exec, dependsOn: [getCodeFormatScripts, pythonIsInstalled], group: 'Formatting') { 208 | description "Check whether the Java source code is properly formatted" 209 | def javaFiles = fileTree("$projectDir").matching{ include "**/*.java" } as List 210 | def pythonArgs = javaFiles.clone() 211 | pythonArgs.add(0, "$projectDir/.run-google-java-format/check-google-java-format.py") 212 | 213 | commandLine "python3" 214 | args pythonArgs 215 | ignoreExitValue true 216 | 217 | doLast { 218 | if (!executionResult.isPresent() || executionResult.get().getExitValue() != 0) { 219 | throw new GradleException("Found improper formatting, try running: ./gradlew reformat") 220 | } 221 | } 222 | } 223 | 224 | task reformat(type: Exec, dependsOn: [getCodeFormatScripts, pythonIsInstalled], group: 'Formatting') { 225 | description "Format the Java source code according to the Google Java Format style" 226 | def javaFiles = fileTree("$projectDir").matching{ include "**/*.java" } as List 227 | def pythonArgs = javaFiles.clone() 228 | pythonArgs.add(0, "$projectDir/.run-google-java-format/run-google-java-format.py") 229 | 230 | commandLine "python3" 231 | args pythonArgs 232 | } 233 | ``` 234 | 235 | 265 | 266 | 267 | ### Git pre-commit hook 268 | 269 | Here is an example of what you might put in a Git pre-commit hook. 270 | This only checks the files that are being comitted, which is much faster than checking all files. 271 | 272 | ``` 273 | CHANGED_JAVA_FILES=`git diff --staged --name-only --diff-filter=ACM | grep '\.java$' | grep -v '/ignored-directory/' ` || true 274 | if [ ! -z "$CHANGED_JAVA_FILES" ]; then 275 | # Choose one of these lines, depending on your build system; adjust the final echo statement too: 276 | ant -silent update-run-google-java-format 277 | make --silent update-run-google-java-format 278 | ## For debugging: 279 | # echo "CHANGED_JAVA_FILES: ${CHANGED_JAVA_FILES}" 280 | ./.run-google-java-format/check-google-java-format.py ${CHANGED_JAVA_FILES} || (echo "Try running: make reformat" && /bin/false) 281 | fi 282 | ``` 283 | 284 | You will also want to add `.run-google-java-format` to your 285 | `~/.gitignore-global` file or your project's `.gitignore` file. 286 | 287 | 288 | #### Finding trailing spaces 289 | 290 | google-java-format will complain about Java files with trailing spaces. 291 | Here is code for your Git pre-commit hook that finds all files that have trailing spaces. 292 | 293 | ``` 294 | CHANGED_FILES=`git diff --staged --name-only --diff-filter=ACM | grep -v '.class$' | grep -v '.gz$'` | grep -v '.jar$'` | grep -v '.png$' | grep -v '.xcf$'` || true 295 | if [ ! -z "$CHANGED_FILES" ]; then 296 | # echo "CHANGED_FILES: ${CHANGED_FILES}" 297 | FILES_WITH_TRAILING_SPACES=`grep -l -s '[[:blank:]]$' ${CHANGED_FILES} 2>&1` || true 298 | if [ ! -z "$FILES_WITH_TRAILING_SPACES" ]; then 299 | echo "Some files have trailing whitespace: ${FILES_WITH_TRAILING_SPACES}" && exit 1 300 | fi 301 | fi 302 | ``` 303 | 304 | 305 | ## Dealing with large changes when reformatting your codebase 306 | 307 | When you first apply standard formatting, that may be disruptive to people 308 | who have changes in their own branches/clones/forks. 309 | (But, once you settle on consistent formatting, you will enjoy a number of 310 | benefits. Applying standard formatting to your codebase makes the code easier 311 | to read. It eases code review by eliminating comments about code style. 312 | It also simplifies use of a version control system: it reduces the 313 | likelihood of merge conflicts due to formatting changes, and it ensures 314 | that commits and pull requests don't intermingle substantive changes with 315 | formatting changes.) 316 | 317 | Here is a way to deal with upstream reformatting. 318 | 319 | ### For the person doing the reformatting 320 | 321 | 1. Create a new branch and do your work there. 322 | 323 | ```git checkout -b reformat-gjf``` 324 | 325 | 2. Tag the commit before the whitespace change as "before reformatting". 326 | 327 | ```git tag -a before-reformatting -m "Code before running google-java-format"``` 328 | 329 | 3. Reformat by running a command such as 330 | `make reformat`, 331 | `ant reformat`, or 332 | `./gradlew reformat` (or whatever buildfile target you have set up). 333 | 4. Examine the diffs to look for poor reformatting: 334 | 335 | ```git diff -w -b | grep -v '^[-+]import' | grep -v '^[-+]$'``` 336 | 337 | (You may wish to use `grep -v` to exclude some additional lines, 338 | depending on your project.) 339 | 340 | Here are two examples of poor reformatting to look out for: 341 | 342 | * A single statement 343 | that is the body of an `if`/`for`/`while` statement. google-java-format 344 | will move this onto the previous line with the boolean expression. It's 345 | better to use curly braces `{}` on every `then` clause, `else` clause, 346 | and `for`/`while` body. To find the poor reformatting (regexps in Emacs 347 | syntax): 348 | 349 | * Search for occurrences of `^\+.*\) return `. 350 | * Search for occurrences of `^\+.*\(if\|while\|for\) (.*) [^{]`. 351 | * Search for hunks that have fewer `+` than `-` lines. 352 | 353 | Add curly braces to get the body back on its own line. 354 | 355 | * Formatted Javadoc. To preserve line breaks and horizontal formatting, 356 | you may wish to enclose parts of your Javadoc comments in `
...
` 357 | or use `
    ` to format lists. 358 | 359 | (You can work in the branch where you are doing reformatting. 360 | Alternately, you might want to change your source code in the master 361 | branch, move the `before-reformatting` tag, and then start over with 362 | formatting.) 363 | 364 | 5. Run tests 365 | 6. Commit changes: 366 | 367 | ```git commit -m "Reformat code using google-java-format"``` 368 | 369 | 7. Tag the commit that does the whitespace change as "after reformatting". 370 | 371 | ```git tag -a after-reformatting -m "Code after running google-java-format"``` 372 | 373 | 8. Push both the commits and the tags: 374 | 375 | ```git push --tags``` 376 | 377 | ### For a client to merge the massive upstream changes 378 | 379 | Assuming before-reformatting is the last commit before reformatting 380 | and after-reformatting is the reformatting commit: 381 | 382 | 1. Merge in the commit before the reformatting into your branch. 383 | 384 | ```git merge before-reformatting``` 385 | 386 | Or, if you have "myremote" configured as a remote, run these commands: 387 | 388 | ``` 389 | git fetch myremote after-reformatting:after-reformatting 390 | git fetch myremote before-reformatting:before-reformatting 391 | ``` 392 | 393 | 2. Resolve any conflicts, run tests, and commit your changes. 394 | 3. Merge in the reformatting commit, preferring all your own changes. 395 | 396 | ```git merge after-reformatting -s recursive -X ours``` 397 | 398 | 4. Reformat the code by running a command such as 399 | `make reformat`, 400 | `ant reformat`, or 401 | `./gradlew reformat` (or whatever buildfile target you have set up). 402 | 5. Commit any formatting changes. 403 | 6. Verify that this contains only changes you made (that is, the formatting 404 | changes were ignored): 405 | 406 | ```git diff after-reformatting...HEAD``` 407 | 408 | For a client of a client (such as a fork of a fork), the above instructions must be revised. 409 | 410 | 411 | ## Troubleshooting 412 | 413 | If you get an error in 414 | ```urllib.urlretrieve(gjf_url, gjf_jar_path)``` 415 | then there is a problem with your installation of Python. 416 | 417 | On MacOS Sierra, you can correct the problem by running these commands: 418 | 419 | ``` 420 | brew install openssl 421 | brew install python@3 --with-brewed-openssl 422 | brew link --overwrite python@3 423 | ``` 424 | -------------------------------------------------------------------------------- /check-google-java-format.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | This script checks whether the files supplied on the command line conform 4 | to the Google Java style (as enforced by the google-java-format program, 5 | but with improvements to the formatting of annotations in comments). 6 | If any files would be affected by running run-google-java-format.py, 7 | this script prints their names and returns a non-zero status. 8 | If called with no arguments, it reads from standard input. 9 | You could invoke this program, for example, in a git pre-commit hook. 10 | """ 11 | 12 | # TODO: Thanks to https://github.com/google/google-java-format/pull/106 13 | # this script can be eliminated, or its interface simplified. 14 | 15 | from __future__ import print_function 16 | import filecmp 17 | import os 18 | import os.path 19 | import shutil 20 | import stat 21 | import subprocess 22 | import sys 23 | import tempfile 24 | 25 | from shutil import copyfileobj 26 | 27 | try: 28 | from urllib import urlopen 29 | except ImportError: 30 | from urllib.request import urlopen 31 | 32 | debug = False 33 | # debug = True 34 | 35 | script_dir = os.path.dirname(os.path.abspath(__file__)) 36 | run_py = os.path.join(script_dir, "run-google-java-format.py") 37 | 38 | 39 | # For some reason, the "git ls-files" must be run from the root. 40 | # (I can run "git ls-files" from the command line in any directory.) 41 | def under_git(dir, filename): 42 | """Return true if filename in dir is under git control.""" 43 | if not shutil.which("git"): 44 | if debug: 45 | print("no git executable found") 46 | return False 47 | with subprocess.Popen( 48 | ["git", "ls-files", filename, "--error-unmatch"], 49 | cwd=dir, 50 | stdout=subprocess.DEVNULL, 51 | stderr=subprocess.STDOUT, 52 | ) as p: 53 | p.wait() 54 | if debug: 55 | print("p.returncode", p.returncode) 56 | return p.returncode == 0 57 | 58 | 59 | def urlretrieve(url, filename): 60 | """Like urllib.urlretrieve.""" 61 | with urlopen(url) as in_stream, open(filename, "wb") as out_file: 62 | copyfileobj(in_stream, out_file) 63 | 64 | 65 | # Don't replace local with remote if local is under version control. 66 | # It would be better to just test whether the remote is newer than local, 67 | # but raw GitHub URLs don't have the necessary last-modified information. 68 | if not under_git(script_dir, "run-google-java-format.py"): 69 | urlretrieve( 70 | "https://raw.githubusercontent.com/" 71 | + "plume-lib/run-google-java-format/master/run-google-java-format.py", 72 | run_py, 73 | ) 74 | os.chmod( 75 | run_py, os.stat(run_py).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH 76 | ) 77 | 78 | temp_dir = tempfile.mkdtemp(prefix="check-google-java-format-") 79 | 80 | 81 | def temporary_file_name(): 82 | """Return the name of a temporary file.""" 83 | return os.path.join(temp_dir, next(tempfile._get_candidate_names())) 84 | 85 | 86 | def cleanup(): 87 | """Clean up temporary files.""" 88 | shutil.rmtree(temp_dir) 89 | 90 | 91 | files = sys.argv[1:] 92 | if len(files) == 0: 93 | content = sys.stdin.read() 94 | fname = temporary_file_name() + ".java" 95 | with open(fname, "w") as outfile: 96 | print(content, file=outfile) 97 | files = [fname] 98 | 99 | temps = [] 100 | cmdlineargs = [f for f in files if f.startswith("-")] 101 | files = [f for f in files if not f.startswith("-")] 102 | for fname in files: 103 | ftemp = temporary_file_name() + "_" + os.path.basename(fname) 104 | shutil.copyfile(fname, ftemp) 105 | temps.append(ftemp) 106 | 107 | if debug: 108 | print("Running run-google-java-format.py") 109 | # Problem: if a file is syntactically illegal, this outputs the temporary file 110 | # name rather than the real file name. 111 | # Minor optimization: To save one process creation, could call directly in Python. 112 | result = subprocess.call([run_py] + cmdlineargs + temps) 113 | if result != 0: 114 | cleanup() 115 | sys.exit(result) 116 | 117 | exit_code = 0 118 | 119 | for i, file in enumerate(files): 120 | if not filecmp.cmp(file, temps[i]): 121 | # TODO: gives temporary file name if reading from stdin 122 | print("Improper formatting:", file) 123 | exit_code = 1 124 | 125 | cleanup() 126 | 127 | sys.exit(exit_code) 128 | -------------------------------------------------------------------------------- /fixup-google-java-format.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """The google-java-format program (https://github.com/google/google-java-format) 3 | reformats Java source code, but it creates poor formatting for annotations 4 | in comments. 5 | Run this script on files after running google-java-format, and it will perform 6 | small changes in place to improve formatting of annotations in comments. 7 | If called with no arguments, it reads from and writes to standard output. 8 | 9 | You typically will not run this program directly; it is run by 10 | run-google-java-format.py. 11 | """ 12 | 13 | from __future__ import print_function 14 | 15 | import os 16 | import os.path 17 | import re 18 | import sys 19 | 20 | 21 | def eprint(*args, **kwargs): 22 | "Print to standard error" 23 | print(*args, file=sys.stderr, **kwargs) 24 | 25 | 26 | # pylint: disable=line-too-long, multiple-statements 27 | 28 | # Keep this list in sync with FormatAnnotationsStep.java in spotless. 29 | # These are type annotations, which should NOT go on their own line. 30 | # A type annotation's @Target annotation contains "TYPE_USE". 31 | # This includes private type annotations used in Checker Framework tests. 32 | # To generate this list, in a file named `type-qualifiers.txt`: 33 | # grep --recursive --files-with-matches -e '^@Target\b.*TYPE_USE' $CHECKERFRAMEWORK/checker/src/test $CHECKERFRAMEWORK/checker-qual/src/main/java $CHECKERFRAMEWORK/framework/src/main/java $CHECKERFRAMEWORK/docs/examples/units-extension $CHECKERFRAMEWORK/framework/src/test/java $t/checker-framework-fork-t-rasmud-branch-nondet-checker/checker-qual/src/main/java/org/checkerframework/checker/determinism | grep -v '~' | sed 's/.*\///' | awk '{print $1} END {print "NotNull.java"; print "UbTop.java"; print "LbTop.java"; print "UB_TOP.java"; print "LB_TOP.java";}' | sed 's/\(.*\)\.java/ "\1",/' | LC_ALL=en_US.utf8 sort | uniq > type-qualifiers.txt 34 | typeAnnotations = set( 35 | [ 36 | "A", 37 | "ACCBottom", 38 | "Acceleration", 39 | "ACCTop", 40 | "AinferBottom", 41 | "AinferDefaultType", 42 | "AinferImplicitAnno", 43 | "AinferParent", 44 | "AinferSibling1", 45 | "AinferSibling2", 46 | "AinferSiblingWithFields", 47 | "AinferTop", 48 | "AlwaysSafe", 49 | "Angle", 50 | "AnnoWithStringArg", 51 | "Area", 52 | "ArrayLen", 53 | "ArrayLenRange", 54 | "ArrayWithoutPackage", 55 | "AwtAlphaCompositingRule", 56 | "AwtColorSpace", 57 | "AwtCursorType", 58 | "AwtFlowLayout", 59 | "B", 60 | "BinaryName", 61 | "BinaryNameOrPrimitiveType", 62 | "BinaryNameWithoutPackage", 63 | "BoolVal", 64 | "Bottom", 65 | "BottomQualifier", 66 | "BottomThis", 67 | "BottomVal", 68 | "C", 69 | "CalledMethods", 70 | "CalledMethodsBottom", 71 | "CalledMethodsPredicate", 72 | "CanonicalName", 73 | "CanonicalNameAndBinaryName", 74 | "CanonicalNameOrEmpty", 75 | "CanonicalNameOrPrimitiveType", 76 | "CCBottom", 77 | "CCTop", 78 | "cd", 79 | "ClassBound", 80 | "ClassGetName", 81 | "ClassGetSimpleName", 82 | "ClassVal", 83 | "ClassValBottom", 84 | "CompilerMessageKey", 85 | "CompilerMessageKeyBottom", 86 | "Critical", 87 | "Current", 88 | "degrees", 89 | "Det", 90 | "DisbarUseBottom", 91 | "DisbarUseTop", 92 | "DoesNotMatchRegex", 93 | "DotSeparatedIdentifiers", 94 | "DotSeparatedIdentifiersOrPrimitiveType", 95 | "DoubleVal", 96 | "Encrypted", 97 | "EnumVal", 98 | "Even", 99 | "FBCBottom", 100 | "FEBottom", 101 | "Fenum", 102 | "FenumBottom", 103 | "FenumTop", 104 | "FETop", 105 | "FieldDescriptor", 106 | "FieldDescriptorForPrimitive", 107 | "FieldDescriptorWithoutPackage", 108 | "FlowExp", 109 | "Force", 110 | "Format", 111 | "FormatBottom", 112 | "FqBinaryName", 113 | "Frequency", 114 | "FullyQualifiedName", 115 | "g", 116 | "GTENegativeOne", 117 | "GuardedBy", 118 | "GuardedByBottom", 119 | "GuardedByUnknown", 120 | "GuardSatisfied", 121 | "h", 122 | "H1Bot", 123 | "H1Invalid", 124 | "H1Poly", 125 | "H1S1", 126 | "H1S2", 127 | "H1Top", 128 | "H2Bot", 129 | "H2Poly", 130 | "H2S1", 131 | "H2S2", 132 | "H2Top", 133 | "Hz", 134 | "I18nFormat", 135 | "I18nFormatBottom", 136 | "I18nFormatFor", 137 | "I18nInvalidFormat", 138 | "I18nUnknownFormat", 139 | "Identifier", 140 | "IdentifierOrPrimitiveType", 141 | "IndexFor", 142 | "IndexOrHigh", 143 | "IndexOrLow", 144 | "Initialized", 145 | "InitializedFields", 146 | "InitializedFieldsBottom", 147 | "InternalForm", 148 | "Interned", 149 | "InternedDistinct", 150 | "IntRange", 151 | "IntVal", 152 | "InvalidFormat", 153 | "K", 154 | "KeyFor", 155 | "KeyForBottom", 156 | "kg", 157 | "kHz", 158 | "km", 159 | "km2", 160 | "km3", 161 | "kmPERh", 162 | "kN", 163 | "LbTop", 164 | "LB_TOP", 165 | "LeakedToResult", 166 | "Length", 167 | "LengthOf", 168 | "LessThan", 169 | "LessThanBottom", 170 | "LessThanUnknown", 171 | "LocalizableKey", 172 | "LocalizableKeyBottom", 173 | "Localized", 174 | "LowerBoundBottom", 175 | "LowerBoundUnknown", 176 | "LTEqLengthOf", 177 | "LTLengthOf", 178 | "LTOMLengthOf", 179 | "LubglbA", 180 | "LubglbB", 181 | "LubglbC", 182 | "LubglbD", 183 | "LubglbE", 184 | "LubglbF", 185 | "Luminance", 186 | "m", 187 | "m2", 188 | "m3", 189 | "Mass", 190 | "MatchesRegex", 191 | "MaybeAliased", 192 | "MaybePresent", 193 | "MethodDescriptor", 194 | "MethodVal", 195 | "MethodValBottom", 196 | "min", 197 | "MinLen", 198 | "mm", 199 | "mm2", 200 | "mm3", 201 | "mol", 202 | "MonotonicNonNull", 203 | "MonotonicOdd", 204 | "mPERs", 205 | "mPERs2", 206 | "MustCall", 207 | "MustCallAlias", 208 | "MustCallUnknown", 209 | "N", 210 | "NegativeIndexFor", 211 | "NewObject", 212 | "NonDet", 213 | "NonEmpty", 214 | "NonLeaked", 215 | "NonNegative", 216 | "NonNull", 217 | "NotCalledMethods", 218 | "NotNull", 219 | "NotQualifier", 220 | "NTDBottom", 221 | "NTDMiddle", 222 | "NTDSide", 223 | "NTDTop", 224 | "Nullable", 225 | "Odd", 226 | "OptionalBottom", 227 | "OrderNonDet", 228 | "PatternA", 229 | "PatternAB", 230 | "PatternAC", 231 | "PatternB", 232 | "PatternBC", 233 | "PatternBottomFull", 234 | "PatternC", 235 | "PatternUnknown", 236 | "PolyDet", 237 | "PolyEncrypted", 238 | "PolyFenum", 239 | "PolyIndex", 240 | "PolyInitializedFields", 241 | "PolyInterned", 242 | "PolyKeyFor", 243 | "PolyLength", 244 | "PolyLowerBound", 245 | "PolyLubglb", 246 | "PolyMustCall", 247 | "PolyNonEmpty", 248 | "PolyNull", 249 | "PolyPresent", 250 | "PolyRegex", 251 | "PolySameLen", 252 | "PolySignature", 253 | "PolySigned", 254 | "PolyTainted", 255 | "PolyTestAccumulation", 256 | "PolyTestReflect", 257 | "PolyTypeDeclDefault", 258 | "PolyUI", 259 | "PolyUnit", 260 | "PolyUpperBound", 261 | "PolyValue", 262 | "PolyVariableNameDefault", 263 | "Positive", 264 | "Present", 265 | "PrimitiveType", 266 | "PropertyKey", 267 | "PropertyKeyBottom", 268 | "PurityUnqualified", 269 | "Qualifier", 270 | "radians", 271 | "Regex", 272 | "RegexBottom", 273 | "ReportUnqualified", 274 | "s", 275 | "SameLen", 276 | "SameLenBottom", 277 | "SameLenUnknown", 278 | "SearchIndexBottom", 279 | "SearchIndexFor", 280 | "SearchIndexUnknown", 281 | "SignatureBottom", 282 | "Signed", 283 | "SignednessBottom", 284 | "SignednessGlb", 285 | "SignedPositive", 286 | "Speed", 287 | "StringVal", 288 | "SubQual", 289 | "Substance", 290 | "SubstringIndexBottom", 291 | "SubstringIndexFor", 292 | "SubstringIndexUnknown", 293 | "SuperQual", 294 | "SwingBoxOrientation", 295 | "SwingCompassDirection", 296 | "SwingElementOrientation", 297 | "SwingHorizontalOrientation", 298 | "SwingSplitPaneOrientation", 299 | "SwingTextOrientation", 300 | "SwingTitleJustification", 301 | "SwingTitlePosition", 302 | "SwingVerticalOrientation", 303 | "t", 304 | "Tainted", 305 | "Temperature", 306 | "TestAccumulation", 307 | "TestAccumulationBottom", 308 | "TestAccumulationPredicate", 309 | "TestReflectBottom", 310 | "TestReflectSibling1", 311 | "TestReflectSibling2", 312 | "TestReflectTop", 313 | "This", 314 | "Time", 315 | "TypeDeclDefaultBottom", 316 | "TypeDeclDefaultMiddle", 317 | "TypeDeclDefaultTop", 318 | "UbTop", 319 | "UB_TOP", 320 | "UI", 321 | "UnderInitialization", 322 | "Unique", 323 | "UnitsBottom", 324 | "UnknownClass", 325 | "UnknownCompilerMessageKey", 326 | "UnknownFormat", 327 | "UnknownInitialization", 328 | "UnknownInterned", 329 | "UnknownKeyFor", 330 | "UnknownLocalizableKey", 331 | "UnknownLocalized", 332 | "UnknownMethod", 333 | "UnknownNonEmpty", 334 | "UnknownPropertyKey", 335 | "UnknownRegex", 336 | "UnknownSignedness", 337 | "UnknownThis", 338 | "UnknownUnits", 339 | "UnknownVal", 340 | "Unsigned", 341 | "Untainted", 342 | "UpperBoundBottom", 343 | "UpperBoundLiteral", 344 | "UpperBoundUnknown", 345 | "ValueTypeAnno", 346 | "VariableNameDefaultBottom", 347 | "VariableNameDefaultMiddle", 348 | "VariableNameDefaultTop", 349 | "Volume", 350 | "WholeProgramInferenceBottom", 351 | ] 352 | ) 353 | 354 | # File .type-annotations can add to the typeAnnotations variable. 355 | if os.path.isfile(".type-annotations"): 356 | with open(".type-annotations") as ta: 357 | exec(ta.read()) 358 | 359 | debug = False 360 | # debug = True 361 | 362 | 363 | def debug_print(*args, **kwargs): 364 | """Print, only if the debug variable is set.""" 365 | if debug: 366 | print(*args, **kwargs) 367 | 368 | 369 | # Two annotations in a row, or an annotation abutting array brackets "[]". 370 | # Space is inserted between. 371 | abuttingannoRegex = re.compile(r"(/\*@[A-Za-z0-9_]+\*/)(\[\]|/\*@[A-Za-z0-9_]+\*/)") 372 | # Voodoo annotation with extra space after 373 | voodootrailingspaceRegex = re.compile(r"(/\*>>> ?@.*\bthis\*/) (\))") 374 | 375 | # Matches the argument to an annotation. 376 | # 3 cases: 377 | # () 378 | # (".*") 379 | # (.*) 380 | # The regex tries to safely permit "()" within a string in an annotation, such as 381 | # @GuardedBy("c1.getFieldPure2()") 382 | annoargRegex = r'(?: *(?:\( *\)|\( *"[^"]*" *\)|\([^")][^)]*\)))?' 383 | # Matches an annotation 384 | annoRegex = r"@[A-Za-z0-9_.]+" + annoargRegex 385 | 386 | # Matches, at the end of its line (in capturing group 2): 387 | # * An annotation 388 | # This is a bit dangerous! It might be within a comment. 389 | # The script tries to heuristically detect this. 390 | # * An annotation in comments 391 | # * a comment like /*offset = */ that should appear right 392 | # before the argument it documents. 393 | # (Supported in main google-java-format as of April 26, 2017, but not yet in a release: 394 | # https://github.com/google/google-java-format/commit/ca0c4d90cdbb46b3a2bf9c2b83d0bd558cccc41e ) 395 | # The annotation will be moved to the beginning of the following line, 396 | # if it appears in typeAnnotations. 397 | trailingannoRegex = re.compile( 398 | r"^(.*?)[ \t]*(" 399 | + annoRegex 400 | + r"|/\*" 401 | + annoRegex 402 | + r"\*/|/\* *[A-Za-z0-9_]+ *= *\*/)$" 403 | ) 404 | 405 | whitespaceRegex = re.compile(r"^([ \t]*).*$") 406 | 407 | emptylineRegex = re.compile(r"^[ \t]*$") 408 | 409 | # Heuristic: matches if the line might be within a //, /*, or Javadoc comment. 410 | withinCommentRegex = re.compile(r"//|/\*(?!.*\/*/)|^[ \t]*\*[ \t]") 411 | 412 | startsWithCommentRegex = re.compile(r"^[ \t]*(//|/\*$|/\*[^@]|\*|void\b)") 413 | 414 | 415 | def insert_after_whitespace(insertion, s): 416 | """Return s, with insertion inserted after its leading whitespace.""" 417 | m = re.match(whitespaceRegex, s) 418 | return s[0 : m.end(1)] + insertion + s[m.end(1) :] 419 | 420 | 421 | def fixup_loop(infile, outfile): 422 | """Fix up formatting while reading from infile and writing to outfile.""" 423 | prev = "" # previous line, which might end with a type annotation. 424 | for line in infile: 425 | # Handle trailing space after a voodoo comment 426 | line = voodootrailingspaceRegex.sub(r"\1\2", line) 427 | # Handle abutting annotations in comments 428 | m = re.search(abuttingannoRegex, line) 429 | while m: 430 | debug_print("found abutting", line) 431 | line = line[0 : m.end(1)] + " " + line[m.start(2) :] 432 | m = re.search(abuttingannoRegex, line) 433 | # Don't move an annotation to the start of a comment line 434 | if re.search(startsWithCommentRegex, line): 435 | m = None 436 | debug_print("Don't prepend to comment", prev, line) 437 | else: 438 | # Handle annotations at end of line that should be at beginning of 439 | # next line. 440 | m = re.search(trailingannoRegex, prev) 441 | debug_print("trailing? (pre-loop)", m, prev, line) 442 | while m: 443 | debug_print("found trailing", prev, line) 444 | anno = m.group(2) 445 | if base_annotation(anno) not in typeAnnotations: 446 | break 447 | debug_print("prev was:", prev) 448 | candidate_prev = prev[0 : m.end(1)] + prev[m.end(2) :] 449 | debug_print("candidate_prev is :", candidate_prev) 450 | if re.search(withinCommentRegex, candidate_prev): 451 | debug_print("withinCommentRegex prohibits action") 452 | break 453 | prev = candidate_prev 454 | debug_print("prev is:", prev) 455 | if re.search(emptylineRegex, prev): 456 | prev = "" 457 | debug_print("prev is empty") 458 | debug_print("line was:", line) 459 | line = insert_after_whitespace(anno + " ", line) 460 | debug_print("line is :", line) 461 | m = re.search(trailingannoRegex, prev) 462 | debug_print("trailing? (post-loop-body)", m, prev, line) 463 | if re.search(r" try \($", prev): 464 | candidate_line = prev.rstrip() + line.lstrip() 465 | if len(candidate_line) < 100: 466 | line = candidate_line 467 | prev = "" 468 | debug_print("joined, now line is:", line) 469 | else: 470 | debug_print("no try match for", prev, line) 471 | outfile.write(prev) 472 | prev = line 473 | outfile.write(prev) 474 | 475 | 476 | def base_annotation(annotation): 477 | """Remove leading and trailing comment characters, spaces, arguments, and at sign. 478 | Example: base_annotation('/*@RequiresNonNull("FileIO.data_trace_state")*/' => 'RequiresNonNull'""" 479 | debug_print("base_annotation <=", annotation) 480 | 481 | # Remove comments 482 | if annotation.startswith("/*"): 483 | annotation = annotation[2:] 484 | if annotation.endswith("*/"): 485 | annotation = annotation[:-2] 486 | 487 | # Remove arguments 488 | idx = annotation.find("(") 489 | if idx != -1: 490 | annotation = annotation[0:idx] 491 | 492 | # Remove package names 493 | idx = annotation.rfind(".") 494 | if idx != -1: 495 | annotation = annotation[idx + 1 :] 496 | 497 | annotation = annotation.strip() 498 | if annotation.startswith("@"): 499 | annotation = annotation[1:] 500 | debug_print("base_annotation =>", annotation) 501 | return annotation 502 | 503 | 504 | if len(sys.argv) == 1: 505 | fixup_loop(sys.stdin, sys.stdout) 506 | else: 507 | for fname in sys.argv[1:]: 508 | outfname = fname + ".out" 509 | with open(fname, "r") as infile: 510 | with open(outfname, "w") as outfile: 511 | fixup_loop(infile, outfile) 512 | os.rename(outfname, fname) 513 | -------------------------------------------------------------------------------- /run-google-java-format.el: -------------------------------------------------------------------------------- 1 | ;; Emacs Lisp code to automatically format your code when you save it. 2 | 3 | (defun update-java-mode-hook-for-gjf () 4 | (add-hook 'after-save-hook 'run-google-java-format nil 'local)) 5 | (add-hook 'java-mode-hook 'update-java-mode-hook-for-gjf) 6 | 7 | (defun run-google-java-format () 8 | "Run external program run-google-java-format.py on the file, 9 | if it matches a hard-coded list of directories." 10 | (interactive) 11 | (let ((cmd 12 | (cond 13 | ((or (and (string-match-p "/\\(randoop\\)" (buffer-file-name)) 14 | (not (string-match-p "CloneVisitor\\.java$" (buffer-file-name)))) 15 | (and (string-match-p "/daikon" (buffer-file-name)) 16 | (not (string-match-p "\\.jpp$" (buffer-file-name)))) 17 | (and (string-match-p "/toradocu" (buffer-file-name)) 18 | (not (string-match-p "/src/test/resources/" (buffer-file-name)))) 19 | (and (string-match-p "/plume-lib" (buffer-file-name)) 20 | (not (string-match-p "WeakHasherMap.java$\\|WeakIdentityHashMap.java$" 21 | (buffer-file-name)))) 22 | (string-match-p "/org/plumelib/" (buffer-file-name))) 23 | ;; normal formatting 24 | "run-google-java-format.py ") 25 | ((and (string-match-p "/checker-framework" (buffer-file-name)) 26 | (not (string-match-p "/checker-framework-inference" (buffer-file-name))) 27 | (not (string-match-p "/checker/jdk/" (buffer-file-name))) 28 | (not (string-match-p "\\.astub$" (buffer-file-name))) 29 | ) 30 | ;; non-standard cammand-line arguments 31 | "run-google-java-format.py -a ") 32 | (t 33 | ;; for all other projects, don't automatically reformat 34 | nil)))) 35 | (if cmd 36 | (progn 37 | ;; I would like to avoid the "(Shell command succeeded with no output)" 38 | ;; message. 39 | (shell-command (concat cmd (buffer-file-name)) "*run-google-java-format*") 40 | ;; If you use bdiff, use this version instead 41 | ;; (bdiff-revert-buffer-maybe) 42 | (revert-buffer nil t))))) 43 | -------------------------------------------------------------------------------- /run-google-java-format.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | This script reformats each file supplied on the command line according to 4 | the Google Java style (by calling out to the google-java-format program, 5 | https://github.com/google/google-java-format), but with improvements to 6 | the formatting of type annotations and annotations in comments. 7 | """ 8 | 9 | from __future__ import print_function 10 | import os 11 | import re 12 | import stat 13 | import shutil 14 | import subprocess 15 | import sys 16 | import tempfile 17 | 18 | from shutil import copyfileobj 19 | 20 | try: 21 | from urllib import urlopen 22 | except ImportError: 23 | from urllib.request import urlopen 24 | 25 | debug = False 26 | # debug = True 27 | 28 | script_dir = os.path.dirname(os.path.abspath(__file__)) 29 | # Rather than calling out to the shell, it would be better to 30 | # call directly in Python. 31 | fixup_py = os.path.join(script_dir, "fixup-google-java-format.py") 32 | 33 | # java_version_string is either 1.8 or nothing. 34 | # For JDK 8, `java -version` has the form: openjdk version "1.8.0_292" 35 | # For JDK 11, `java -version` has the form: openjdk 11.0.11 2021-04-20 36 | # For JDK 17, `java -version` has the form: java 17 2021-09-14 LTS 37 | java_version_string = subprocess.check_output( 38 | ["java", "-version"], stderr=subprocess.STDOUT 39 | ).decode("utf-8") 40 | if debug: 41 | print("java_version_string =", java_version_string) 42 | java_version = re.search(r'"(\d+(\.\d+)?).*"', java_version_string).groups()[0] 43 | 44 | ## To use an officially released version. 45 | ## (Releases appear at https://github.com/google/google-java-format/releases/ , 46 | ## but I keep this in sync with Spotless.) 47 | # Because formatting is inconsistent between versions of GJF, you should 48 | # enable formatting only on versions of Java that support your version of GJF. 49 | # For example, don't format under Java 8/11 if you also use a later version of Java. 50 | # Version 1.3 and earlier do not wrap line comments. 51 | # Version 1.8 and later require JDK 11. 52 | # Version 1.10.0 and later can run under JDK 16. 53 | # Version 1.25.0 and later require JDK 17. 54 | ## To set this variable: 55 | ## See https://github.com/diffplug/spotless/blob/main/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java#L75 56 | ## or search for "Bump default google" in https://github.com/diffplug/spotless/blob/main/plugin-gradle/CHANGES.md 57 | if java_version == "1.8": 58 | gjf_version_default = "1.7" 59 | elif java_version == "11": 60 | gjf_version_default = "1.24.0" 61 | else: 62 | gjf_version_default = "1.26.0" 63 | gjf_version = os.getenv("GJF_VERSION", gjf_version_default) 64 | gjf_download_prefix = ( 65 | "v" if re.match(r"^1\.[1-9][0-9]", gjf_version) else "google-java-format-" 66 | ) 67 | gjf_snapshot = os.getenv("GJF_SNAPSHOT", "") 68 | gjf_url_base = os.getenv( 69 | "GJF_URL_BASE", 70 | "https://github.com/google/google-java-format/releases/download/" 71 | + gjf_download_prefix 72 | + gjf_version 73 | + "/", 74 | ) 75 | ## To use a non-official version by default, because an official version is 76 | ## unusably buggy (like 1.1) or no new release has been made in a long time. 77 | ## Never change the file at a URL; make it unique by adding a date. 78 | # gjf_version = "1.5" 79 | # gjf_snapshot = "-SNAPSHOT-20171012" 80 | # gjf_url_base = "http://types.cs.washington.edu/" 81 | # gjf_url_base = "http://homes.cs.washington.edu/~mernst/tmp2/" 82 | 83 | gjf_jar_name = "google-java-format-" + gjf_version + gjf_snapshot + "-all-deps.jar" 84 | gjf_url = gjf_url_base + gjf_jar_name 85 | 86 | 87 | def urlretrieve(url, filename): 88 | """Like urllib.urlretrieve.""" 89 | with urlopen(url) as in_stream, open(filename, "wb") as out_file: 90 | copyfileobj(in_stream, out_file) 91 | 92 | 93 | # Set gjf_jar_path, or retrieve it if it doesn't appear locally. Does not update 94 | # from remove path if remote is newer, so never change files on the server. 95 | if os.path.isfile(os.path.join(script_dir, gjf_jar_name)): 96 | gjf_jar_path = os.path.join(script_dir, gjf_jar_name) 97 | elif os.path.isfile(os.path.join(os.path.dirname(script_dir), "lib", gjf_jar_name)): 98 | gjf_jar_path = os.path.join(os.path.dirname(script_dir), "lib", gjf_jar_name) 99 | else: 100 | gjf_jar_path = os.path.join(script_dir, gjf_jar_name) 101 | # print("retrieving " + gjf_url + " to " + gjf_jar_path) 102 | try: 103 | # Download to a temporary file, then rename atomically. 104 | # This avoids race conditions with other run-google-java-format processes. 105 | # "delete=False" because the file will be renamed. 106 | with tempfile.NamedTemporaryFile(dir=script_dir, delete=False) as f: 107 | urlretrieve(gjf_url, f.name) 108 | os.rename(f.name, gjf_jar_path) 109 | except Exception as e: 110 | raise Exception( 111 | "Problem while retrieving " + gjf_url + " to " + gjf_jar_path 112 | ) from e 113 | 114 | 115 | # For some reason, the "git ls-files" must be run from the root. 116 | # (I can run "git ls-files" from the command line in any directory.) 117 | def under_git(dir, filename): 118 | """Return true if filename in dir is under git control.""" 119 | if not shutil.which("git"): 120 | if debug: 121 | print("no git executable found") 122 | return False 123 | with subprocess.Popen( 124 | ["git", "ls-files", filename, "--error-unmatch"], 125 | cwd=dir, 126 | stdout=subprocess.DEVNULL, 127 | stderr=subprocess.STDOUT, 128 | ) as p: 129 | p.wait() 130 | if debug: 131 | print("p.returncode", p.returncode) 132 | return p.returncode == 0 133 | 134 | 135 | # Don't replace local with remote if local is under version control. 136 | # It would be better to just test whether the remote is newer than local, 137 | # but raw GitHub URLs don't have the necessary last-modified information. 138 | if not under_git(script_dir, "fixup-google-java-format.py"): 139 | url = ( 140 | "https://raw.githubusercontent.com/" 141 | + "plume-lib/run-google-java-format/master/fixup-google-java-format.py" 142 | ) 143 | try: 144 | urlretrieve(url, fixup_py) 145 | except Exception: 146 | if os.path.exists(fixup_py): 147 | print( 148 | "Couldn't retrieve fixup-google-java-format.py from " 149 | + url 150 | + "; using cached version" 151 | ) 152 | else: 153 | print("Couldn't retrieve fixup-google-java-format.py from " + url) 154 | sys.exit(1) 155 | os.chmod( 156 | fixup_py, os.stat(fixup_py).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH 157 | ) 158 | 159 | if debug: 160 | print("script_dir:", script_dir) 161 | print("fixup_py: ", fixup_py) 162 | print("gjf_jar_path: ", gjf_jar_path) 163 | 164 | files = sys.argv[1:] 165 | if len(files) == 0: 166 | print("run-google-java-format.py expects 1 or more filenames as arguments") 167 | sys.exit(1) 168 | 169 | if java_version == "1.8": 170 | jdk_opens = [] 171 | else: 172 | # From https://github.com/google/google-java-format/releases/ 173 | # This is no longer required as of GJF version 1.15.0, but users might 174 | # supply a version number lower than that. 175 | jdk_opens = [ 176 | "--add-exports", 177 | "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", 178 | "--add-exports", 179 | "jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", 180 | "--add-exports", 181 | "jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", 182 | "--add-exports", 183 | "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", 184 | "--add-exports", 185 | "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", 186 | ] 187 | 188 | result = subprocess.call( 189 | ["java"] + jdk_opens + ["-jar", gjf_jar_path, "--replace"] + files 190 | ) 191 | 192 | ## This if statement used to be commented out, because google-java-format 193 | ## crashed a lot. It seems more stable now. 194 | # Don't stop if there was an error, because google-java-format won't munge 195 | # files and we still want to run fixup-google-java-format.py. 196 | if result != 0: 197 | print("Error when running google-java-format") 198 | sys.exit(result) 199 | 200 | # Remove command-line arguments 201 | files = [f for f in files if not f.startswith("-")] 202 | # Exit if no files were supplied (maybe "--help" was supplied) 203 | if not files: 204 | sys.exit(0) 205 | 206 | if debug: 207 | print("Running fixup-google-java-format.py") 208 | result = subprocess.call([fixup_py] + files) 209 | if result != 0: 210 | print("Error when running fixup-google-java-format.py") 211 | sys.exit(result) 212 | --------------------------------------------------------------------------------