diff --git a/java/config/config.go b/java/config/config.go index 02dfbd54f..7f1ccd510 100644 --- a/java/config/config.go +++ b/java/config/config.go @@ -113,7 +113,7 @@ func init() { pctx.SourcePathVariable("JavaKytheExtractorJar", "prebuilts/build-tools/common/framework/javac_extractor.jar") pctx.SourcePathVariable("Ziptime", "prebuilts/build-tools/${hostPrebuiltTag}/bin/ziptime") - pctx.SourcePathVariable("GenKotlinBuildFileCmd", "build/soong/scripts/gen-kotlin-build-file.sh") + pctx.HostBinToolVariable("GenKotlinBuildFileCmd", "gen-kotlin-build-file.py") pctx.SourcePathVariable("JarArgsCmd", "build/soong/scripts/jar-args.sh") pctx.SourcePathVariable("PackageCheckCmd", "build/soong/scripts/package-check.sh") diff --git a/java/kotlin.go b/java/kotlin.go index 673970b9c..e3356be78 100644 --- a/java/kotlin.go +++ b/java/kotlin.go @@ -31,7 +31,9 @@ var kotlinc = pctx.AndroidRemoteStaticRule("kotlinc", android.RemoteRuleSupports Command: `rm -rf "$classesDir" "$srcJarDir" "$kotlinBuildFile" "$emptyDir" && ` + `mkdir -p "$classesDir" "$srcJarDir" "$emptyDir" && ` + `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` + - `${config.GenKotlinBuildFileCmd} $classpath "$name" $classesDir $out.rsp $srcJarDir/list > $kotlinBuildFile &&` + + `${config.GenKotlinBuildFileCmd} --classpath "$classpath" --name "$name"` + + ` --out_dir "$classesDir" --srcs "$out.rsp" --srcs "$srcJarDir/list"` + + ` --out "$kotlinBuildFile" && ` + `${config.KotlincCmd} ${config.JavacHeapFlags} $kotlincFlags ` + `-jvm-target $kotlinJvmTarget -Xbuild-file=$kotlinBuildFile -kotlin-home $emptyDir && ` + `${config.SoongZipCmd} -jar -o $out -C $classesDir -D $classesDir && ` + @@ -74,7 +76,7 @@ func kotlinCompile(ctx android.ModuleContext, outputFile android.WritablePath, Inputs: srcFiles, Implicits: deps, Args: map[string]string{ - "classpath": flags.kotlincClasspath.FormJavaClassPath("-classpath"), + "classpath": flags.kotlincClasspath.FormJavaClassPath(""), "kotlincFlags": flags.kotlincFlags, "srcJars": strings.Join(srcJars.Strings(), " "), "classesDir": android.PathForModuleOut(ctx, "kotlinc", "classes").String(), @@ -93,7 +95,9 @@ var kapt = pctx.AndroidRemoteStaticRule("kapt", android.RemoteRuleSupports{Goma: Command: `rm -rf "$srcJarDir" "$kotlinBuildFile" "$kaptDir" && ` + `mkdir -p "$srcJarDir" "$kaptDir/sources" "$kaptDir/classes" && ` + `${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` + - `${config.GenKotlinBuildFileCmd} $classpath "$name" "" $out.rsp $srcJarDir/list > $kotlinBuildFile &&` + + `${config.GenKotlinBuildFileCmd} --classpath "$classpath" --name "$name"` + + ` --srcs "$out.rsp" --srcs "$srcJarDir/list"` + + ` --out "$kotlinBuildFile" && ` + `${config.KotlincCmd} ${config.KotlincSuppressJDK9Warnings} ${config.JavacHeapFlags} $kotlincFlags ` + `-Xplugin=${config.KotlinKaptJar} ` + `-P plugin:org.jetbrains.kotlin.kapt3:sources=$kaptDir/sources ` + @@ -162,7 +166,7 @@ func kotlinKapt(ctx android.ModuleContext, srcJarOutputFile, resJarOutputFile an Inputs: srcFiles, Implicits: deps, Args: map[string]string{ - "classpath": flags.kotlincClasspath.FormJavaClassPath("-classpath"), + "classpath": flags.kotlincClasspath.FormJavaClassPath(""), "kotlincFlags": flags.kotlincFlags, "srcJars": strings.Join(srcJars.Strings(), " "), "srcJarDir": android.PathForModuleOut(ctx, "kapt", "srcJars").String(), diff --git a/scripts/Android.bp b/scripts/Android.bp index 7f2ddb882..d962a4187 100644 --- a/scripts/Android.bp +++ b/scripts/Android.bp @@ -152,5 +152,17 @@ python_test_host { python_binary_host { name: "lint-project-xml", main: "lint-project-xml.py", - srcs: ["lint-project-xml.py"], + srcs: [ + "lint-project-xml.py", + "ninja_rsp.py", + ], +} + +python_binary_host { + name: "gen-kotlin-build-file.py", + main: "gen-kotlin-build-file.py", + srcs: [ + "gen-kotlin-build-file.py", + "ninja_rsp.py", + ], } diff --git a/scripts/gen-kotlin-build-file.py b/scripts/gen-kotlin-build-file.py new file mode 100644 index 000000000..83b4cd8a3 --- /dev/null +++ b/scripts/gen-kotlin-build-file.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +# +# Copyright 2018 Google Inc. All rights reserved. +# +# 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. + +# Generates kotlinc module xml file to drive kotlinc + +import argparse +import os + +from ninja_rsp import NinjaRspFileReader + +def parse_args(): + """Parse commandline arguments.""" + + def convert_arg_line_to_args(arg_line): + for arg in arg_line.split(): + if arg.startswith('#'): + return + if not arg.strip(): + continue + yield arg + + parser = argparse.ArgumentParser(fromfile_prefix_chars='@') + parser.convert_arg_line_to_args = convert_arg_line_to_args + parser.add_argument('--out', dest='out', + help='file to which the module.xml contents will be written.') + parser.add_argument('--classpath', dest='classpath', action='append', default=[], + help='classpath to pass to kotlinc.') + parser.add_argument('--name', dest='name', + help='name of the module.') + parser.add_argument('--out_dir', dest='out_dir', + help='directory to which kotlinc will write output files.') + parser.add_argument('--srcs', dest='srcs', action='append', default=[], + help='file containing whitespace separated list of source files.') + parser.add_argument('--common_srcs', dest='common_srcs', action='append', default=[], + help='file containing whitespace separated list of common multiplatform source files.') + + return parser.parse_args() + +def main(): + """Program entry point.""" + args = parse_args() + + if not args.out: + raise RuntimeError('--out argument is required') + + if not args.name: + raise RuntimeError('--name argument is required') + + with open(args.out, 'w') as f: + # Print preamble + f.write('\n') + f.write(' \n' % (args.name, args.out_dir or '')) + + # Print classpath entries + for c in args.classpath: + for entry in c.split(':'): + path = os.path.abspath(entry) + f.write(' \n' % path) + + # For each rsp file, print source entries + for rsp_file in args.srcs: + for src in NinjaRspFileReader(rsp_file): + path = os.path.abspath(src) + if src.endswith('.java'): + f.write(' \n' % path) + elif src.endswith('.kt'): + f.write(' \n' % path) + else: + raise RuntimeError('unknown source file type %s' % file) + + for rsp_file in args.common_srcs: + for src in NinjaRspFileReader(rsp_file): + path = os.path.abspath(src) + f.write(' \n' % path) + f.write(' \n' % path) + + f.write(' \n') + f.write('\n') + +if __name__ == '__main__': + main() diff --git a/scripts/gen-kotlin-build-file.sh b/scripts/gen-kotlin-build-file.sh deleted file mode 100755 index 177ca1b04..000000000 --- a/scripts/gen-kotlin-build-file.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/bin/bash -e - -# Copyright 2018 Google Inc. All rights reserved. -# -# 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. - -# Generates kotlinc module xml file to standard output based on rsp files - -if [[ -z "$1" ]]; then - echo "usage: $0 ..." >&2 - exit 1 -fi - -# Classpath variable has a tendency to be prefixed by "-classpath", remove it. -if [[ $1 == "-classpath" ]]; then - shift -fi; - -classpath=$1 -name=$2 -out_dir=$3 -shift 3 - -# Path in the build file may be relative to the build file, we need to make them -# absolute -prefix="$(pwd)" - -get_abs_path () { - local file="$1" - if [[ "${file:0:1}" == '/' ]] ; then - echo "${file}" - else - echo "${prefix}/${file}" - fi -} - -# Print preamble -echo "" - -# Print classpath entries -for file in $(echo "$classpath" | tr ":" "\n"); do - path="$(get_abs_path "$file")" - echo " " -done - -# For each rsp file, print source entries -while (( "$#" )); do - for file in $(cat "$1"); do - path="$(get_abs_path "$file")" - if [[ $file == *.java ]]; then - echo " " - elif [[ $file == *.kt ]]; then - echo " " - else - echo "Unknown source file type ${file}" - exit 1 - fi - done - - shift -done - -echo "" diff --git a/scripts/lint-project-xml.py b/scripts/lint-project-xml.py index 38c57cadf..f1ef85dcc 100755 --- a/scripts/lint-project-xml.py +++ b/scripts/lint-project-xml.py @@ -19,6 +19,8 @@ import argparse +from ninja_rsp import NinjaRspFileReader + def check_action(check_type): """ @@ -91,74 +93,6 @@ def parse_args(): return parser.parse_args() -class NinjaRspFileReader: - """ - Reads entries from a Ninja rsp file. Ninja escapes any entries in the file that contain a - non-standard character by surrounding the whole entry with single quotes, and then replacing - any single quotes in the entry with the escape sequence '\''. - """ - - def __init__(self, filename): - self.f = open(filename, 'r') - self.r = self.character_reader(self.f) - - def __iter__(self): - return self - - def character_reader(self, f): - """Turns a file into a generator that returns one character at a time.""" - while True: - c = f.read(1) - if c: - yield c - else: - return - - def __next__(self): - entry = self.read_entry() - if entry: - return entry - else: - raise StopIteration - - def read_entry(self): - c = next(self.r, "") - if not c: - return "" - elif c == "'": - return self.read_quoted_entry() - else: - entry = c - for c in self.r: - if c == " " or c == "\n": - break - entry += c - return entry - - def read_quoted_entry(self): - entry = "" - for c in self.r: - if c == "'": - # Either the end of the quoted entry, or the beginning of an escape sequence, read the next - # character to find out. - c = next(self.r) - if not c or c == " " or c == "\n": - # End of the item - return entry - elif c == "\\": - # Escape sequence, expect a ' - c = next(self.r) - if c != "'": - # Malformed escape sequence - raise "malformed escape sequence %s'\\%s" % (entry, c) - entry += "'" - else: - raise "malformed escape sequence %s'%s" % (entry, c) - else: - entry += c - raise "unterminated quoted entry %s" % entry - - def write_project_xml(f, args): test_attr = "test='true' " if args.test else "" diff --git a/scripts/ninja_rsp.py b/scripts/ninja_rsp.py new file mode 100644 index 000000000..004ce4733 --- /dev/null +++ b/scripts/ninja_rsp.py @@ -0,0 +1,83 @@ +# Copyright (C) 2020 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. +# + +"""This file reads entries from a Ninja rsp file.""" + +class NinjaRspFileReader: + """ + Reads entries from a Ninja rsp file. Ninja escapes any entries in the file that contain a + non-standard character by surrounding the whole entry with single quotes, and then replacing + any single quotes in the entry with the escape sequence '\''. + """ + + def __init__(self, filename): + self.f = open(filename, 'r') + self.r = self.character_reader(self.f) + + def __iter__(self): + return self + + def character_reader(self, f): + """Turns a file into a generator that returns one character at a time.""" + while True: + c = f.read(1) + if c: + yield c + else: + return + + def __next__(self): + entry = self.read_entry() + if entry: + return entry + else: + raise StopIteration + + def read_entry(self): + c = next(self.r, "") + if not c: + return "" + elif c == "'": + return self.read_quoted_entry() + else: + entry = c + for c in self.r: + if c == " " or c == "\n": + break + entry += c + return entry + + def read_quoted_entry(self): + entry = "" + for c in self.r: + if c == "'": + # Either the end of the quoted entry, or the beginning of an escape sequence, read the next + # character to find out. + c = next(self.r) + if not c or c == " " or c == "\n": + # End of the item + return entry + elif c == "\\": + # Escape sequence, expect a ' + c = next(self.r) + if c != "'": + # Malformed escape sequence + raise "malformed escape sequence %s'\\%s" % (entry, c) + entry += "'" + else: + raise "malformed escape sequence %s'%s" % (entry, c) + else: + entry += c + raise "unterminated quoted entry %s" % entry