diff options
| author | Joel Klinghed <the_jk@spawned.biz> | 2022-11-05 11:14:20 +0100 |
|---|---|---|
| committer | Joel Klinghed <the_jk@spawned.biz> | 2022-11-05 11:14:20 +0100 |
| commit | 7daade1c4d3756e67fe14c87d5212f0c9f77205c (patch) | |
| tree | 24030f9b373b3d110a641e3f3e68a43ab47d6436 /bin | |
| parent | b3467614c5917a1c24afa30ae9101984f09a0b0b (diff) | |
Add support for kotlin by adding flycheck-android-kotlin checker
Diffstat (limited to 'bin')
| -rw-r--r-- | bin/flycheck-android-java.py | 2 | ||||
| -rw-r--r-- | bin/flycheck-android-kotlin.py | 296 |
2 files changed, 297 insertions, 1 deletions
diff --git a/bin/flycheck-android-java.py b/bin/flycheck-android-java.py index 4194049..37c1b68 100644 --- a/bin/flycheck-android-java.py +++ b/bin/flycheck-android-java.py @@ -135,7 +135,7 @@ def figure_out_java_compilation(sessiondir, sourcefile, tempfile, checkstyle, if exc.errno != errno.EEXIST or not os.path.isdir(outdir): outdir = None pass - cachefile = os.path.join(sessiondir, 'gradle_output') + cachefile = os.path.join(sessiondir, 'gradle_java_output') try: if not force and os.path.getmtime(cachefile) >= mtime: with open(cachefile, 'r') as f: diff --git a/bin/flycheck-android-kotlin.py b/bin/flycheck-android-kotlin.py new file mode 100644 index 0000000..ad1d88f --- /dev/null +++ b/bin/flycheck-android-kotlin.py @@ -0,0 +1,296 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import argparse +import errno +import os +import subprocess +import sys +import tempfile + +def get_gradle_command_and_project_mtime(dir): + """Find top-project gradlew or fallback to using gradle on sub-project dir. +While iterating the paths, also check mtime on build.gradle files""" + topdir = dir + build_gradle = None + gradlew = None + mtime = -1 + while True: + test = os.path.join(topdir, 'build.gradle') + if os.path.exists(test): + if not build_gradle: + build_gradle = test + + try: + m = os.path.getmtime(test) + if m > mtime: + mtime = m + except OSError: + pass + + test = os.path.join(topdir, 'gradlew') + if os.path.exists(test): + gradlew = test + break + + test = os.path.dirname(topdir) + if test == topdir: + break + topdir = test + + if gradlew: + cmd = [gradlew, '-p', topdir] + elif build_gradle: + topdir = os.path.dirname(build_gradle) + cmd = ['gradle', '-p', topdir] + else: + topdir = '' + cmd = ['gradle'] + return (cmd, mtime, topdir) + +def filter_source_files(files): + """Remove any item not looking like a kotlin or java source file.""" + return [f for f in files if f.endswith('.kt') or f.endswith('.java')] + +def filter_java_source_files(files): + """Remove any item not looking like a java source file.""" + return [f for f in files if f.endswith('.java')] + +def cleanup_output(tmp, real): + """Remove any file in tmp that is older than the one in real""" + offset = len(tmp) + if tmp[-1] != '/': + offset = offset + 1 + for (dirpath, _, files) in os.walk(tmp): + realpath = os.path.join(real, dirpath[offset:]) + for filename in files: + try: + mtime = os.path.getmtime(os.path.join(realpath, filename)) + tmp = os.path.join(dirpath, filename) + if os.path.getmtime(tmp) <= mtime: + os.remove(tmp) + except OSError: + pass + +def run_kotlinc(cp, files, output, args, sourcefile, outdir): + """Execute kotlinc with the given options.""" + command = ['kotlinc'] + if outdir: + command.extend(['-d', outdir]) + tmp = [outdir] + if output: + tmp.append(output) + if cp: + tmp.extend(cp) + if tmp: + command.extend(['-cp', ':'.join(tmp)]) + + if os.fork() == 0: + cleanup_output(outdir, output) + sys.exit(0) + else: + if cp: + command.extend(['-cp', ':'.join(cp)]) + if output: + command.extend(['-d', output]) + command.extend(args) + command.append(sourcefile) + print(sourcefile) + if outdir: + with tempfile.NamedTemporaryFile(mode='w') as f: + command.append('@' + f.name) + for source in filter_java_source_files(files): + f.write(source) + f.write('\n') + f.flush() + return subprocess.call(command) + else: + with tempfile.NamedTemporaryFile(mode='w') as f: + command.append('@' + f.name) + for source in filter_source_files(files): + f.write(source) + f.write('\n') + f.flush() + return subprocess.call(command) + +def file_in_list(needle, files): + """Find needle in files and if so, return the list without needle. +Otherwise return None.""" + if os.path.exists(needle): + for i in range(0, len(files) - 1): + if os.path.exists(files[i]) and os.path.samefile(needle, files[i]): + return files[0:i] + files[i + 1:] + return None + +def figure_out_kotlin_compilation(sessiondir, sourcefile, tempfile, detekt, + variant, run_gen, force=False): + """Get options for Kotlin compilation from gradle project and run kotlinc.""" + (cmd, mtime, projectdir) = get_gradle_command_and_project_mtime( + os.path.dirname(sourcefile)) + output = None + cached = False + if sessiondir != None: + outdir = os.path.join(sessiondir, 'kotlin_output') + try: + os.mkdir(outdir) + except OSError as exc: + if exc.errno != errno.EEXIST or not os.path.isdir(outdir): + outdir = None + pass + cachefile = os.path.join(sessiondir, 'gradle_kotlin_output') + try: + if not force and os.path.getmtime(cachefile) >= mtime: + with open(cachefile, 'r') as f: + output = f.read() + cached = True + except (OSError, IOError): + pass + else: + outdir = None + + if not output: + flycheck_cmd = cmd + ['-q', 'flycheckAndroidKotlin'] + output = subprocess.check_output(flycheck_cmd, universal_newlines=True) + if sessiondir != None: + try: + with open(cachefile, 'w') as f: + f.write(output) + except IOError: + pass + + output = output.split('\n') + + data = None + data_type = None + variants = [] + gen = [] + ret = 0 + compiled = False + for line in output: + if line == '***' or line == '!!!': + if data_type == '*': + variants.append({'data': data, 'gen': len(gen)}) + elif data_type == '!' and variants: + gen.append(data) + data_type = line[0] + if data_type == '*': + data = {} + else: + data = [] + elif data_type == '*': + if line.startswith('variant='): + data['variant'] = line[8:] + if line.startswith('args='): + data['args'] = line[6:-1].split(', ') + elif line.startswith('cp='): + data['cp'] = line[3:].split(':') + elif line.startswith('files='): + data['files'] = line[6:].split(':') + elif line.startswith('output='): + data['output'] = line[7:] + elif data_type == '!': + data.append(line) + + if data_type == '*': + variants.append({'data': data, 'gen': len(gen)}) + elif data_type == '!' and variants: + gen.append(data) + + fallback = None + + if variant: + for v in variants: + data = v['data'] + if 'variant' in data and data['variant'] == variant: + if not fallback: + fallback = data + files = file_in_list(sourcefile, data['files']) + if files: + if run_gen and v['gen'] < len(gen): + subprocess.call(cmd + ['-q'] + gen[v['gen']]) + + ret = run_kotlinc(data['cp'], files, data['output'], + data['args'], tempfile, outdir) + compiled = True + break + + if not compiled and variants: + data = variants[0]['data'] + first = data['variant'] if 'variant' in data else None + for v in variants: + data = v['data'] + if first and 'variant' in data and data['variant'] == first: + if not fallback: + fallback = data + files = file_in_list(sourcefile, data['files']) + if files: + if run_gen and v['gen'] < len(gen): + subprocess.call(cmd + ['-q'] + gen[v['gen']]) + + ret = run_kotlinc(data['cp'], files, data['output'], + data['args'], tempfile, outdir) + compiled = True + break + + if not compiled: + # Probably need to rerun gradle to find group for sourcefile + if cached and os.path.exists(sourcefile): + return figure_out_kotlin_compilation(sessiondir, sourcefile, + tempfile, detekt, variant, + run_gen, force=True) + # OK, perhaps file doesn't exist yet or not yet added to gradle, + # whatever, assume the first group is good enough + if fallback: + ret = run_kotlin(fallback['cp'], fallback['files'], + fallback['output'], fallback['args'], + tempfile, outdir) + compiled = True + + if not ret and detekt: + cmd = [detekt['cli']] + + if 'config' in detekt: + if detekt['config']: + cmd.extend(['-c', detekt['config']]) + else: + cmd.append('--build-upon-default-config') + + cmd.append('-i') + cmd.append(tempfile) + if len(projectdir): + cwd = projectdir + else: + cwd = None + ret = subprocess.call(cmd, cwd=cwd) + + if not ret and not compiled: + print("Source file not in project and project seems empty!") + ret = -1 + + return ret + +def main(argv): + parser = argparse.ArgumentParser() + parser.add_argument('--detekt-cli') + parser.add_argument('--detekt-config') + parser.add_argument('--variant') + parser.add_argument('--skip-gen', dest='gen', action='store_const', + const=False, default=True) + parser.add_argument('sessiondir', nargs='?') + parser.add_argument('sourcefile') + parser.add_argument('tempfile', nargs='?') + args = parser.parse_args(argv[1:]) + + sourcefile = args.sourcefile + tempfile = args.tempfile or sourcefile + + detekt = None + if args.detekt_cli: + detekt = {'cli': args.detekt_cli, + 'config': args.detekt_config} + + sourcefile = os.path.abspath(sourcefile) + return figure_out_kotlin_compilation(args.sessiondir, sourcefile, tempfile, + detekt, args.variant, args.gen) + +sys.exit(main(sys.argv)) |
