#!/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()