Skip to main content
Glama
compile_kotlin.py20.7 kB
# Copyright (c) Meta Platforms, Inc. and affiliates. # # This source code is licensed under both the MIT license found in the # LICENSE-MIT file in the root directory of this source tree and the Apache # License, Version 2.0 found in the LICENSE-APACHE file in the root directory # of this source tree. import argparse import os import pathlib import shutil import zipfile from tempfile import TemporaryDirectory from typing import List import utils _JAVA_OR_KOTLIN_FILE_EXTENSION = [".java", ".kt"] _PLUGIN = "-P" _X_PLUGIN_ARG = "-Xplugin" _KAPT3_PLUGIN = "plugin:org.jetbrains.kotlin.kapt3:" _APT_MODE_COMPILE = _KAPT3_PLUGIN + "aptMode=compile" _AP_CLASSPATH_ARG = _KAPT3_PLUGIN + "apclasspath" _AP_PROCESSORS_ARG = _KAPT3_PLUGIN + "processors" _SOURCES_ARG = _KAPT3_PLUGIN + "sources" _CLASSES_ARG = _KAPT3_PLUGIN + "classes" _STUBS_ARG = _KAPT3_PLUGIN + "stubs" _KAPT_GENERATED_ARG = "kapt.kotlin.generated" _LIGHT_ANALYSIS = _KAPT3_PLUGIN + "useLightAnalysis" _CORRECT_ERROR_TYPES = _KAPT3_PLUGIN + "correctErrorTypes" _AP_OPTIONS = _KAPT3_PLUGIN + "apoptions" _JAVAC_ARG = _KAPT3_PLUGIN + "javacArguments" _KSP_PLUGIN = "plugin:com.google.devtools.ksp.symbol-processing:" _KSP_AP_CLASSPATH_ARG = _KSP_PLUGIN + "apclasspath" _KSP_PROJECT_BASE_DIR_ARG = _KSP_PLUGIN + "projectBaseDir" _ksp_classes_and_resources_output_ARG = _KSP_PLUGIN + "classOutputDir" _KSP_KOTLIN_OUTPUT_ARG = _KSP_PLUGIN + "kotlinOutputDir" _KSP_JAVA_OUTPUT_ARG = _KSP_PLUGIN + "javaOutputDir" _KSP_RESOURCE_OUTPUT_ARG = _KSP_PLUGIN + "resourceOutputDir" _KSP_CACHES_DIR_ARG = _KSP_PLUGIN + "cachesDir" _KSP_OUTPUT_ARG = _KSP_PLUGIN + "kspOutputDir" _KSP_INCREMENTAL_ARG = _KSP_PLUGIN + "incremental" _KSP_WITH_COMPILATION_ARG = _KSP_PLUGIN + "withCompilation" def _parse_args(): parser = argparse.ArgumentParser(description="Tool to compile kotlin source files.") parser.add_argument( "--kotlinc_cmd_file", type=pathlib.Path, required=False, metavar="kotlinc_cmd_file", help="path to file with the command that should be run for compilation", ) parser.add_argument( "--kotlinc_output", type=pathlib.Path, required=False, metavar="kotlinc_output", help="path to .class files produced by running kotlinc", ) parser.add_argument( "--zipped_sources_file", type=pathlib.Path, required=False, metavar="zipped_sources_file", help="path to file with stored zipped source files that need to be passed to kotlin compiler", ) parser.add_argument( "--kapt_annotation_processing_jar", type=str, required=False, metavar="kapt_annotation_processing_jar", help="annotation processing jar used by KAPT", ) parser.add_argument( "--kapt_annotation_processors", type=str, required=False, metavar="kapt_annotation_processors", help="comma separated list of annotation processors to be run by KAPT", ) parser.add_argument( "--kapt_annotation_processor_params", type=str, required=False, metavar="kapt_annotation_processor_params", help="comma separated list of annotation processor params to be passed to KAPT", ) parser.add_argument( "--kapt_classpath_file", type=pathlib.Path, required=False, metavar="kapt_classpath_file", help="path to file with the classpath that should be used for KAPT", ) parser.add_argument( "--kapt_sources_output", type=pathlib.Path, required=False, metavar="kapt_sources_output", help="path where kapt-generated sources should be written", ) parser.add_argument( "--kapt_generated_sources_output", type=pathlib.Path, required=False, metavar="kapt_generated_sources_output", help="path generated sources should be zipped", ) parser.add_argument( "--kapt_classes_output", type=pathlib.Path, required=False, metavar="kapt_classes_output", help="path where kapt-generated classes should be written", ) parser.add_argument( "--kapt_stubs", type=pathlib.Path, required=False, metavar="kapt_stubs", help="path where kapt-generated stubs should be written", ) parser.add_argument( "--kapt_base64_encoder", type=str, required=False, metavar="kapt_base64_encoder", help="tool for doing base64 encoding for KAPT", ) parser.add_argument( "--kapt_generated_kotlin_output", type=pathlib.Path, required=False, metavar="kapt_generated_kotlin_output", help="path where kapt-generated kotlin output should be written", ) parser.add_argument( "--kapt_jvm_target", type=str, required=False, metavar="kapt_jvm_target", help="JVM target to use for KAPT call", ) parser.add_argument( "--ksp_processor_jars", type=str, required=False, metavar="ksp_processor_jars", help="comma separated list of jars containing KSP annotation processors", ) parser.add_argument( "--ksp_classpath", type=str, required=False, metavar="ksp_classpath", help="classpath to be passed to KSP (so that it can find the resources it needs)", ) parser.add_argument( "--ksp_classes_and_resources_output", type=pathlib.Path, required=False, metavar="ksp_classes_and_resources_output", help="path where ksp-generated classes and resources should be written", ) parser.add_argument( "--ksp_sources_output", type=pathlib.Path, required=False, metavar="ksp_sources_output", help="path where ksp-generated Java and Kotlin sources should be written", ) parser.add_argument( "--ksp_zipped_sources_output", type=pathlib.Path, required=False, metavar="ksp_zipped_sources_output", help="path where zipped ksp-generated Java and Kotlin sources should be written", ) parser.add_argument( "--ksp_output", type=pathlib.Path, required=False, metavar="ksp_output", help="root of KSP output dirs", ) parser.add_argument( "--ksp_project_base_dir", type=pathlib.Path, required=False, metavar="ksp_project_base_dir", help="project dir for this KSP invocation", ) parser.add_argument( "--ksp_generated_classes_and_resources", type=pathlib.Path, required=False, metavar="ksp_generated_classes_and_resources", help="classes and resources that were generated by a previous invocation of KSP", ) parser.add_argument( "--kotlin_compiler_plugin_dir", type=pathlib.Path, required=False, metavar="kotlin_compiler_plugin_dir", help="Directory for KAPT compiler plugins to use", ) parser.add_argument( "--zip_scrubber", required=True, help="tool for scrubbing timestamps from zip files to produce deterministic output", ) return parser.parse_args() def _run_kotlinc( kotlinc_output: pathlib.Path, kotlinc_cmd_file: pathlib.Path, zipped_sources_file: pathlib.Path, ksp_cmd: List[str], kapt_cmd: List[str], temp_dir: TemporaryDirectory, ) -> pathlib.Path: kotlinc_cmd = [] cmd_file = kotlinc_cmd_file if zipped_sources_file: cmd_file = utils.extract_source_files( zipped_sources_file, kotlinc_cmd_file, _JAVA_OR_KOTLIN_FILE_EXTENSION, temp_dir, ) if utils.sources_are_present(cmd_file, [".kt"]): with open(cmd_file, "r") as file: kotlinc_cmd += [line.strip() for line in file.readlines()] kotlinc_cmd += ksp_cmd kotlinc_cmd += kapt_cmd if kotlinc_output: kotlinc_cmd += ["-d", kotlinc_output] utils.execute_command(kotlinc_cmd) else: os.mkdir(kotlinc_output) return kotlinc_output def _encode_kapt_ap_options( kapt_annotation_processor_params: str, kapt_base64_encoder_cmd: List[str], kapt_generated_kotlin_output: pathlib.Path, temp_dir: TemporaryDirectory, ) -> str: ap_options_file = os.path.join(temp_dir, "ap_options.txt") with open(ap_options_file, "w") as file: file.write( "{}={}".format(_KAPT_GENERATED_ARG, str(kapt_generated_kotlin_output)) ) if kapt_annotation_processor_params: for param in kapt_annotation_processor_params.split(";"): file.write("\n") file.write(param) encoded_ap_options_file = os.path.join(temp_dir, "encoded_ap_options.txt") return _encode_options( kapt_base64_encoder_cmd, ap_options_file, encoded_ap_options_file ) def _encode_javac_arguments( jvm_target: str, kapt_base64_encoder_cmd: List[str], temp_dir: TemporaryDirectory, ) -> str: javac_arguments_file = os.path.join(temp_dir, "javac_arguments.txt") with open(javac_arguments_file, "w") as file: file.write("-source={}\n-target={}".format(jvm_target, jvm_target)) encoded_javac_arguments_file = os.path.join(temp_dir, "encoded_javac_arguments.txt") return _encode_options( kapt_base64_encoder_cmd, javac_arguments_file, encoded_javac_arguments_file ) def _encode_options( kapt_base64_encoder_cmd: List[str], options_file: pathlib.Path, encoded_options_file: pathlib.Path, ) -> str: cmd = kapt_base64_encoder_cmd + [options_file, encoded_options_file] utils.execute_command(cmd) with open(encoded_options_file, "r") as file: return file.read().strip() def _get_kapt_cmd( kapt_annotation_processing_jar: pathlib.Path, kapt_annotation_processors: str, kapt_annotation_processor_params: str, kapt_classpath_file: pathlib.Path, kapt_sources_output: pathlib.Path, kapt_classes_output: pathlib.Path, kapt_stubs: pathlib.Path, kapt_base64_encoder: pathlib.Path, kapt_generated_kotlin_output: pathlib.Path, kapt_jvm_target: str, temp_dir: TemporaryDirectory, ) -> List[str]: if not kapt_annotation_processors: return [] kapt_plugin_options = [_APT_MODE_COMPILE] kapt_plugin_options += [ "=".join([_AP_PROCESSORS_ARG, ap]) for ap in kapt_annotation_processors.split(",") ] with open(kapt_classpath_file, "r") as file: kapt_plugin_options += [ "=".join([_AP_CLASSPATH_ARG, line.strip()]) for line in file.readlines() ] kapt_base64_encoder_cmd = utils.shlex_split(kapt_base64_encoder) kapt_plugin_options += [ "=".join([_SOURCES_ARG, str(kapt_sources_output)]), "=".join([_CLASSES_ARG, str(kapt_classes_output)]), "=".join([_STUBS_ARG, str(kapt_stubs)]), "=".join([_LIGHT_ANALYSIS, "true"]), "=".join([_CORRECT_ERROR_TYPES, "true"]), "=".join( [ _AP_OPTIONS, _encode_kapt_ap_options( kapt_annotation_processor_params, kapt_base64_encoder_cmd, kapt_generated_kotlin_output, temp_dir, ), ] ), "=".join( [ _JAVAC_ARG, _encode_javac_arguments( kapt_jvm_target, kapt_base64_encoder_cmd, temp_dir ), ] ), ] return [ "=".join([_X_PLUGIN_ARG, str(kapt_annotation_processing_jar)]), _PLUGIN, ",".join(kapt_plugin_options), ] def _get_ksp_cmd( ksp_processors_jars: str, ksp_classpath: str, ksp_project_base_dir: pathlib.Path, base_ksp_output_dir: pathlib.Path, ksp_classes_and_resources_output: pathlib.Path, ksp_sources_output: pathlib.Path, ) -> List[str]: if not ksp_processors_jars: return [] ksp_plugin_options = [ "{}={}".format( _KSP_AP_CLASSPATH_ARG, os.pathsep.join(ksp_processors_jars.split(",")) ), "{}={}".format(_KSP_PLUGIN + "apoption=cp", ksp_classpath), "{}={}".format(_KSP_PROJECT_BASE_DIR_ARG, ksp_project_base_dir.resolve()), "{}={}".format( _ksp_classes_and_resources_output_ARG, ksp_classes_and_resources_output.resolve(), ), "{}={}".format(_KSP_KOTLIN_OUTPUT_ARG, ksp_sources_output.resolve()), "{}={}".format(_KSP_JAVA_OUTPUT_ARG, ksp_sources_output.resolve()), "{}={}".format( _KSP_RESOURCE_OUTPUT_ARG, ksp_classes_and_resources_output.resolve() ), "{}={}".format(_KSP_OUTPUT_ARG, base_ksp_output_dir.resolve()), "{}={}".format(_KSP_CACHES_DIR_ARG, base_ksp_output_dir.resolve()), "{}={}".format(_KSP_INCREMENTAL_ARG, False), "{}={}".format(_KSP_WITH_COMPILATION_ARG, False), ] return [ _PLUGIN, ",".join(ksp_plugin_options), ] def _zip_recursive(archive_path: pathlib.Path, source_path: pathlib.Path): # Same as 'zip -r archive_path source_path' with zipfile.ZipFile( archive_path, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=6 ) as z: z.write(source_path) for f in source_path.glob("**/*"): z.write(f) def main(): args = _parse_args() output_path = args.kotlinc_output kotlinc_cmd_file = args.kotlinc_cmd_file zipped_sources_file = args.zipped_sources_file kapt_annotation_processing_jar = args.kapt_annotation_processing_jar kapt_annotation_processors = args.kapt_annotation_processors kapt_annotation_processor_params = args.kapt_annotation_processor_params kapt_classpath_file = args.kapt_classpath_file kapt_sources_output = args.kapt_sources_output kapt_classes_output = args.kapt_classes_output kapt_generated_sources_output = args.kapt_generated_sources_output kapt_stubs = args.kapt_stubs kapt_base64_encoder = args.kapt_base64_encoder kapt_generated_kotlin_output = args.kapt_generated_kotlin_output kapt_jvm_target = args.kapt_jvm_target kotlin_compiler_plugin_dir = args.kotlin_compiler_plugin_dir ksp_processor_jars = args.ksp_processor_jars ksp_classpath = args.ksp_classpath ksp_output = args.ksp_output ksp_project_base_dir = args.ksp_project_base_dir ksp_classes_and_resources_output = args.ksp_classes_and_resources_output ksp_sources_output = args.ksp_sources_output ksp_zipped_sources_output = args.ksp_zipped_sources_output ksp_generated_classes_and_resources = args.ksp_generated_classes_and_resources zip_scrubber = args.zip_scrubber utils.log_message("output: {}".format(output_path)) utils.log_message("kotlinc_cmd_file: {}".format(kotlinc_cmd_file)) if zipped_sources_file: utils.log_message("zipped_sources_file: {}".format(zipped_sources_file)) utils.log_message( "kapt_annotation_processing_jar: {}".format(kapt_annotation_processing_jar) ) utils.log_message( "kapt_annotation_processors: {}".format(kapt_annotation_processors) ) utils.log_message( "kapt_annotation_processor_params: {}".format(kapt_annotation_processor_params) ) utils.log_message("kapt_classpath_file: {}".format(kapt_classpath_file)) utils.log_message("kapt_sources_output: {}".format(kapt_sources_output)) utils.log_message( "kapt_generated_sources_output: {}".format(kapt_generated_sources_output) ) utils.log_message("kapt_classes_output: {}".format(kapt_classes_output)) utils.log_message("kapt_stubs: {}".format(kapt_stubs)) utils.log_message("kapt_base64_encoder: {}".format(kapt_base64_encoder)) utils.log_message( "kapt_generated_kotlin_output: {}".format(kapt_generated_kotlin_output) ) utils.log_message("kapt_jvm_target: {}".format(kapt_jvm_target)) utils.log_message( "kotlin_compiler_plugin_dir: {}".format(kotlin_compiler_plugin_dir) ) utils.log_message("ksp_processor_jars: {}".format(ksp_processor_jars)) utils.log_message("ksp_classpath: {}".format(ksp_classpath)) utils.log_message("ksp_output: {}".format(ksp_output)) utils.log_message("ksp_project_base_dir: {}".format(ksp_project_base_dir)) utils.log_message( "ksp_classes_and_resources_output: {}".format(ksp_classes_and_resources_output) ) utils.log_message("ksp_sources_output: {}".format(ksp_sources_output)) utils.log_message("ksp_zipped_sources_output: {}".format(ksp_zipped_sources_output)) utils.log_message( "ksp_generated_classes_and_resources: {}".format( ksp_generated_classes_and_resources ) ) utils.log_message("zip_scrubber: {}".format(zip_scrubber)) if ( kapt_annotation_processing_jar or kapt_annotation_processors or kapt_classpath_file or kapt_sources_output or kapt_classes_output or kapt_generated_sources_output or kapt_stubs or kapt_base64_encoder or kapt_generated_kotlin_output or kapt_jvm_target ): assert ( kapt_annotation_processing_jar and kapt_annotation_processors and kapt_classpath_file and kapt_sources_output and kapt_classes_output and kapt_generated_sources_output and kapt_stubs and kapt_base64_encoder and kapt_generated_kotlin_output ) if ( ksp_processor_jars or ksp_classpath or ksp_output or ksp_project_base_dir or ksp_classes_and_resources_output or ksp_sources_output or ksp_zipped_sources_output ): assert ( ksp_processor_jars and ksp_classpath and ksp_output and ksp_project_base_dir and ksp_classes_and_resources_output and ksp_sources_output and ksp_zipped_sources_output ) with TemporaryDirectory() as temp_dir: ksp_cmd = _get_ksp_cmd( ksp_processor_jars, ksp_classpath, ksp_project_base_dir, ksp_output, ksp_classes_and_resources_output, ksp_sources_output, ) kapt_cmd = _get_kapt_cmd( kapt_annotation_processing_jar, kapt_annotation_processors, kapt_annotation_processor_params, kapt_classpath_file, kapt_sources_output, kapt_classes_output, kapt_stubs, kapt_base64_encoder, kapt_generated_kotlin_output, kapt_jvm_target, temp_dir, ) _run_kotlinc( output_path, kotlinc_cmd_file, zipped_sources_file, ksp_cmd, kapt_cmd, temp_dir, ) if ksp_sources_output: if not ksp_sources_output.exists(): ksp_sources_output.mkdir() _zip_recursive(ksp_zipped_sources_output, ksp_sources_output) utils.execute_command( utils.shlex_split(zip_scrubber) + [ksp_zipped_sources_output] ) if ksp_classes_and_resources_output: if not ksp_classes_and_resources_output.exists(): ksp_classes_and_resources_output.mkdir() if kapt_sources_output: if not os.path.exists(kapt_sources_output): os.mkdir(kapt_sources_output) kapt_generated_sources_output.touch() else: _zip_recursive(kapt_generated_sources_output, kapt_sources_output) utils.execute_command( utils.shlex_split(zip_scrubber) + [kapt_generated_sources_output] ) if kapt_classes_output: if not os.path.exists(kapt_classes_output): os.mkdir(kapt_classes_output) else: shutil.copytree(kapt_classes_output, output_path, dirs_exist_ok=True) if kapt_stubs and not os.path.exists(kapt_stubs): os.mkdir(kapt_stubs) if kapt_generated_kotlin_output and not os.path.exists( kapt_generated_kotlin_output ): os.mkdir(kapt_generated_kotlin_output) if kotlin_compiler_plugin_dir and not kotlin_compiler_plugin_dir.exists(): kotlin_compiler_plugin_dir.mkdir() if ksp_generated_classes_and_resources: shutil.copytree( ksp_generated_classes_and_resources, output_path, dirs_exist_ok=True ) if __name__ == "__main__": main()

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server