Enable kotlinc flags in blueprint files

Add support for adding kotlinc files in the module.

Some flags are unnecessary as they are added by
default (-no-jdk and -no-stdlib), or are not needed
on an Android build (-include-runtime), or may
conflict with the build (-kotlin-home and
-Xintellij-plugin-root), so the error stops the
build if they are added.

Test: part of java/java_test.go
Change-Id: If3b2777062daaa490a20c014e9b1bb4b1cb0a8df
Signed-off-by: Zoran Jovanovic <zoran.jovanovic@sony.com>
diff --git a/java/config/kotlin.go b/java/config/kotlin.go
index 35f9e9d..432840e 100644
--- a/java/config/kotlin.go
+++ b/java/config/kotlin.go
@@ -15,7 +15,11 @@
 package config
 
 var (
-	KotlinStdlibJar = "external/kotlinc/lib/kotlin-stdlib.jar"
+	KotlinStdlibJar     = "external/kotlinc/lib/kotlin-stdlib.jar"
+	KotlincIllegalFlags = []string{
+		"-no-jdk",
+		"-no-stdlib",
+	}
 )
 
 func init() {
diff --git a/java/java.go b/java/java.go
index fef9a21..5ed99f7 100644
--- a/java/java.go
+++ b/java/java.go
@@ -89,6 +89,9 @@
 	// list of module-specific flags that will be used for javac compiles
 	Javacflags []string `android:"arch_variant"`
 
+	// list of module-specific flags that will be used for kotlinc compiles
+	Kotlincflags []string `android:"arch_variant"`
+
 	// list of of java libraries that will be in the classpath
 	Libs []string `android:"arch_variant"`
 
@@ -1087,13 +1090,21 @@
 	var kotlinJars android.Paths
 
 	if srcFiles.HasExt(".kt") {
+		// user defined kotlin flags.
+		kotlincFlags := j.properties.Kotlincflags
+		CheckKotlincFlags(ctx, kotlincFlags)
+
 		// If there are kotlin files, compile them first but pass all the kotlin and java files
 		// kotlinc will use the java files to resolve types referenced by the kotlin files, but
 		// won't emit any classes for them.
-
-		flags.kotlincFlags = "-no-stdlib"
+		kotlincFlags = append(kotlincFlags, "-no-stdlib")
 		if ctx.Device() {
-			flags.kotlincFlags += " -no-jdk"
+			kotlincFlags = append(kotlincFlags, "-no-jdk")
+		}
+		if len(kotlincFlags) > 0 {
+			// optimization.
+			ctx.Variable(pctx, "kotlincFlags", strings.Join(kotlincFlags, " "))
+			flags.kotlincFlags += "$kotlincFlags"
 		}
 
 		var kotlinSrcFiles android.Paths
@@ -1332,6 +1343,31 @@
 	j.outputFile = outputFile.WithoutRel()
 }
 
+// Check for invalid kotlinc flags. Only use this for flags explicitly passed by the user,
+// since some of these flags may be used internally.
+func CheckKotlincFlags(ctx android.ModuleContext, flags []string) {
+	for _, flag := range flags {
+		flag = strings.TrimSpace(flag)
+
+		if !strings.HasPrefix(flag, "-") {
+			ctx.PropertyErrorf("kotlincflags", "Flag `%s` must start with `-`", flag)
+		} else if strings.HasPrefix(flag, "-Xintellij-plugin-root") {
+			ctx.PropertyErrorf("kotlincflags",
+				"Bad flag: `%s`, only use internal compiler for consistency.", flag)
+		} else if inList(flag, config.KotlincIllegalFlags) {
+			ctx.PropertyErrorf("kotlincflags", "Flag `%s` already used by build system", flag)
+		} else if flag == "-include-runtime" {
+			ctx.PropertyErrorf("kotlincflags", "Bad flag: `%s`, do not include runtime.", flag)
+		} else {
+			args := strings.Split(flag, " ")
+			if args[0] == "-kotlin-home" {
+				ctx.PropertyErrorf("kotlincflags",
+					"Bad flag: `%s`, kotlin home already set to default (path to kotlinc in the repo).", flag)
+			}
+		}
+	}
+}
+
 func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars android.Paths,
 	deps deps, flags javaBuilderFlags, jarName string, extraJars android.Paths) android.Path {
 
diff --git a/java/java_test.go b/java/java_test.go
index 86349fe..4d4b836 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1124,3 +1124,64 @@
 		}
 	}
 }
+
+var compilerFlagsTestCases = []struct {
+	in  string
+	out bool
+}{
+	{
+		in:  "a",
+		out: false,
+	},
+	{
+		in:  "-a",
+		out: true,
+	},
+	{
+		in:  "-no-jdk",
+		out: false,
+	},
+	{
+		in:  "-no-stdlib",
+		out: false,
+	},
+	{
+		in:  "-kotlin-home",
+		out: false,
+	},
+	{
+		in:  "-kotlin-home /some/path",
+		out: false,
+	},
+	{
+		in:  "-include-runtime",
+		out: false,
+	},
+	{
+		in:  "-Xintellij-plugin-root",
+		out: false,
+	},
+}
+
+type mockContext struct {
+	android.ModuleContext
+	result bool
+}
+
+func (ctx *mockContext) PropertyErrorf(property, format string, args ...interface{}) {
+	// CheckBadCompilerFlags calls this function when the flag should be rejected
+	ctx.result = false
+}
+
+func TestCompilerFlags(t *testing.T) {
+	for _, testCase := range compilerFlagsTestCases {
+		ctx := &mockContext{result: true}
+		CheckKotlincFlags(ctx, []string{testCase.in})
+		if ctx.result != testCase.out {
+			t.Errorf("incorrect output:")
+			t.Errorf("     input: %#v", testCase.in)
+			t.Errorf("  expected: %#v", testCase.out)
+			t.Errorf("       got: %#v", ctx.result)
+		}
+	}
+}