• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2
3#
4# Copyright 2022, The Android Open Source Project
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#     http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18"""Script to format or check Kotlin files."""
19
20import argparse
21import os
22import subprocess
23import sys
24
25
26def main():
27  parser = argparse.ArgumentParser(
28      'Format Kotlin files or check that they are correctly formatted.')
29  parser.add_argument(
30      '--check',
31      '-c',
32      action='store_true',
33      default=False,
34      help='Perform a format check instead of formatting.')
35  parser.add_argument(
36      '--includes_file',
37      '-i',
38      default='',
39      help='The file containing the Kotlin files and directories that should be included/excluded, generated using generate_includes_file.py.'
40  )
41  parser.add_argument(
42      '--jar',
43      default='',
44      help='The path to the ktfmt jar.'
45  )
46  parser.add_argument(
47      'files',
48      nargs='*',
49      help='The files to format or check. If --include_file is specified, only the files at their intersection will be formatted/checked.'
50  )
51  args = parser.parse_args()
52
53  ktfmt_args = ['--kotlinlang-style']
54
55  check = args.check
56  if check:
57    ktfmt_args += ['--set-exit-if-changed', '--dry-run']
58
59  kt_files = []
60  for file in args.files:
61    if os.path.isdir(file):
62      for root, dirs, files in os.walk(file):
63        for f in files:
64          if is_kotlin_file(f):
65            kt_files += [os.path.join(root, f)]
66
67    if is_kotlin_file(file):
68      kt_files += [file]
69
70  # Only format/check files from the includes list.
71  includes_file = args.includes_file
72  if kt_files and includes_file:
73    f = open(includes_file, 'r')
74
75    lines = f.read().splitlines()
76    included = [line[1:] for line in lines if line.startswith('+')]
77    included_files = set()
78    included_dirs = []
79    for line in included:
80      if is_kotlin_file(line):
81        included_files.add(line)
82      else:
83        included_dirs += [line]
84
85    excluded_files = [line[1:] for line in lines if line.startswith('-')]
86
87    kt_files = [
88        kt_file for kt_file in kt_files if kt_file not in excluded_files and
89        (kt_file in included_files or is_included(kt_file, included_dirs))
90    ]
91
92  # No need to start ktfmt if there are no files to check/format.
93  if not kt_files:
94    sys.exit(0)
95
96  ktfmt_args += kt_files
97
98  dir = os.path.normpath(os.path.dirname(__file__))
99  ktfmt_jar = args.jar if args.jar else os.path.join(dir, 'ktfmt.jar')
100
101  ktlint_env = os.environ.copy()
102  ktlint_env['JAVA_CMD'] = 'java'
103  try:
104    process = subprocess.Popen(
105        ['java', '-jar', ktfmt_jar] + ktfmt_args,
106        stdout=subprocess.PIPE,
107        env=ktlint_env)
108    stdout, _ = process.communicate()
109    code = process.returncode
110    if check and code != 0:
111      print(
112          '**********************************************************************'
113      )
114      print(
115          'Some Kotlin files are not properly formatted. Run the command below to format them.\n'
116          'Note: If you are using the Android Studio ktfmt plugin, make sure to select the '
117          'Kotlinlang style in \'Editor > ktfmt Settings\'.\n')
118      script_path = os.path.normpath(__file__)
119      incorrect_files = [
120          os.path.abspath(file) for file in stdout.decode('utf-8').splitlines()
121      ]
122      print('$ ' + script_path + ' ' + ' '.join(incorrect_files) + '\n')
123      print(
124          '**********************************************************************'
125      )
126      sys.exit(code)
127    else:
128      sys.exit(0)
129  except OSError as e:
130    print('Error running ktfmt')
131    sys.exit(1)
132
133
134def is_kotlin_file(name):
135  return name.endswith('.kt') or name.endswith('.kts')
136
137
138def is_included(file, dirs):
139  for dir in dirs:
140    if file.startswith(dir):
141      return True
142
143
144if __name__ == '__main__':
145  main()
146