aboutsummaryrefslogtreecommitdiff
path: root/ktfmt.py
blob: 044379584c16e617bd5245e257e8d06e99633ba1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/usr/bin/env python3

#
# Copyright 2022, The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Script to format or check Kotlin files."""

import argparse
import os
import subprocess
import sys


def main():
  parser = argparse.ArgumentParser(
      'Format Kotlin files or check that they are correctly formatted.')
  parser.add_argument(
      '--check',
      '-c',
      action='store_true',
      default=False,
      help='Perform a format check instead of formatting.')
  parser.add_argument(
      '--includes_file',
      '-i',
      default='',
      help='The file containing the Kotlin files and directories that should be included/excluded, generated using generate_includes_file.py.'
  )
  parser.add_argument(
      '--jar',
      default='',
      help='The path to the ktfmt jar.'
  )
  parser.add_argument(
      'files',
      nargs='*',
      help='The files to format or check. If --include_file is specified, only the files at their intersection will be formatted/checked.'
  )
  args = parser.parse_args()

  ktfmt_args = ['--kotlinlang-style']

  check = args.check
  if check:
    ktfmt_args += ['--set-exit-if-changed', '--dry-run']

  kt_files = []
  for file in args.files:
    if os.path.isdir(file):
      for root, dirs, files in os.walk(file):
        for f in files:
          if is_kotlin_file(f):
            kt_files += [os.path.join(root, f)]

    if is_kotlin_file(file):
      kt_files += [file]

  # Only format/check files from the includes list.
  includes_file = args.includes_file
  if kt_files and includes_file:
    f = open(includes_file, 'r')

    lines = f.read().splitlines()
    included = [line[1:] for line in lines if line.startswith('+')]
    included_files = set()
    included_dirs = []
    for line in included:
      if is_kotlin_file(line):
        included_files.add(line)
      else:
        included_dirs += [line]

    excluded_files = [line[1:] for line in lines if line.startswith('-')]

    kt_files = [
        kt_file for kt_file in kt_files if kt_file not in excluded_files and
        (kt_file in included_files or is_included(kt_file, included_dirs))
    ]

  # No need to start ktfmt if there are no files to check/format.
  if not kt_files:
    sys.exit(0)

  ktfmt_args += kt_files

  dir = os.path.normpath(os.path.dirname(__file__))
  ktfmt_jar = args.jar if args.jar else os.path.join(dir, 'ktfmt.jar')

  ktlint_env = os.environ.copy()
  ktlint_env['JAVA_CMD'] = 'java'
  try:
    process = subprocess.Popen(
        ['java', '-jar', ktfmt_jar] + ktfmt_args,
        stdout=subprocess.PIPE,
        env=ktlint_env)
    stdout, _ = process.communicate()
    code = process.returncode
    if check and code != 0:
      print(
          '**********************************************************************'
      )
      print(
          'Some Kotlin files are not properly formatted. Run the command below to format them.\n'
          'Note: If you are using the Android Studio ktfmt plugin, make sure to select the '
          'Kotlinlang style in \'Editor > ktfmt Settings\'.\n')
      script_path = os.path.normpath(__file__)
      incorrect_files = [
          os.path.abspath(file) for file in stdout.decode('utf-8').splitlines()
      ]
      print('$ ' + script_path + ' ' + ' '.join(incorrect_files) + '\n')
      print(
          '**********************************************************************'
      )
      sys.exit(code)
    else:
      sys.exit(0)
  except OSError as e:
    print('Error running ktfmt')
    sys.exit(1)


def is_kotlin_file(name):
  return name.endswith('.kt') or name.endswith('.kts')


def is_included(file, dirs):
  for dir in dirs:
    if file.startswith(dir):
      return True


if __name__ == '__main__':
  main()