Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module(

bazel_dep(name = "rules_java", version = "8.6.3")
bazel_dep(name = "rules_jvm_external", version = "5.3")
bazel_dep(name = "rules_kotlin", version = "1.9.5")
bazel_dep(name = "rules_kotlin", version = "2.1.0")
bazel_dep(name = "bazel_skylib", version = "1.7.1")
bazel_dep(name = "platforms", version = "0.0.8")

Expand Down
6 changes: 4 additions & 2 deletions src/cli/AndroidLintAction.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import java.nio.file.Files
import kotlin.system.exitProcess

object AndroidLintAction {

@JvmStatic
fun main(args: Array<String>) {
val worker = Worker.fromArgs(args, AndroidLintExecutor())
Expand All @@ -15,7 +14,10 @@ object AndroidLintAction {
}

private class AndroidLintExecutor : Worker.WorkRequestCallback {
override fun processWorkRequest(args: List<String>, printStream: PrintStream): Int {
override fun processWorkRequest(
args: List<String>,
printStream: PrintStream,
): Int {
val workingDirectory = Files.createTempDirectory("rules")

try {
Expand Down
162 changes: 87 additions & 75 deletions src/cli/AndroidLintActionArgs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import java.nio.file.Paths
internal class AndroidLintActionArgs(
parser: ArgParser,
) {

private val argsParserPathTransformer: String.() -> Path = {
Paths.get(this)
}
Expand All @@ -26,10 +25,11 @@ internal class AndroidLintActionArgs(
help = "",
)

val androidHome: String? by parser.storing(
names = arrayOf("--android-home"),
help = "The relative location of Android home",
).default { null }
val androidHome: String? by parser
.storing(
names = arrayOf("--android-home"),
help = "The relative location of Android home",
).default { null }

val srcs: List<Path> by parser.adding(
names = arrayOf("--src"),
Expand All @@ -43,66 +43,77 @@ internal class AndroidLintActionArgs(
transform = argsParserPathTransformer,
)

val resources: List<Path> by parser.adding(
names = arrayOf("--resource"),
help = "",
transform = argsParserPathTransformer,
).default { emptyList() }

val androidManifest: Path? by parser.storing(
names = arrayOf("--android-manifest"),
help = "",
transform = argsParserPathTransformer,
).default { null }

val baselineFile: Path? by parser.storing(
names = arrayOf("--baseline-file"),
help = "",
transform = argsParserPathTransformer,
).default { null }

val config: Path? by parser.storing(
names = arrayOf("--config-file"),
help = "",
transform = argsParserPathTransformer,
).default { null }

val customChecks: List<Path> by parser.adding(
names = arrayOf("--custom-rule"),
help = "",
transform = argsParserPathTransformer,
).default { emptyList() }

val classpath: List<Path> by parser.adding(
names = arrayOf("--classpath"),
help = "",
transform = argsParserPathTransformer,
).default { emptyList() }

val autofix: Boolean by parser.flagging(
names = arrayOf("--autofix"),
help = "TODO Not supported yet",
).default { false }

val regenerateBaselineFile: Boolean by parser.flagging(
names = arrayOf("--regenerate-baseline-files"),
help = "",
).default { false }

val warningsAsErrors: Boolean by parser.flagging(
names = arrayOf("--warnings-as-errors"),
help = "",
).default { false }

val enableChecks: List<String> by parser.adding(
names = arrayOf("--enable-check"),
help = "",
).default { emptyList() }

val disableChecks: List<String> by parser.adding(
names = arrayOf("--disable-check"),
help = "",
).default { emptyList() }
val resources: List<Path> by parser
.adding(
names = arrayOf("--resource"),
help = "",
transform = argsParserPathTransformer,
).default { emptyList() }

val androidManifest: Path? by parser
.storing(
names = arrayOf("--android-manifest"),
help = "",
transform = argsParserPathTransformer,
).default { null }

val baselineFile: Path? by parser
.storing(
names = arrayOf("--baseline-file"),
help = "",
transform = argsParserPathTransformer,
).default { null }

val config: Path? by parser
.storing(
names = arrayOf("--config-file"),
help = "",
transform = argsParserPathTransformer,
).default { null }

val customChecks: List<Path> by parser
.adding(
names = arrayOf("--custom-rule"),
help = "",
transform = argsParserPathTransformer,
).default { emptyList() }

val classpath: List<Path> by parser
.adding(
names = arrayOf("--classpath"),
help = "",
transform = argsParserPathTransformer,
).default { emptyList() }

val autofix: Boolean by parser
.flagging(
names = arrayOf("--autofix"),
help = "TODO Not supported yet",
).default { false }

val regenerateBaselineFile: Boolean by parser
.flagging(
names = arrayOf("--regenerate-baseline-files"),
help = "",
).default { false }

val warningsAsErrors: Boolean by parser
.flagging(
names = arrayOf("--warnings-as-errors"),
help = "",
).default { false }

val enableChecks: List<String> by parser
.adding(
names = arrayOf("--enable-check"),
help = "",
).default { emptyList() }

val disableChecks: List<String> by parser
.adding(
names = arrayOf("--disable-check"),
help = "",
).default { emptyList() }

val compileSdkVersion: String by parser.storing(
names = arrayOf("--compile-sdk-version"),
Expand All @@ -119,20 +130,21 @@ internal class AndroidLintActionArgs(
help = "",
)

val enableCheckDependencies: Boolean by parser.flagging(
names = arrayOf("--enable-check-dependencies"),
help = "",
).default { false }
val enableCheckDependencies: Boolean by parser
.flagging(
names = arrayOf("--enable-check-dependencies"),
help = "",
).default { false }

companion object {

internal fun parseArgs(args: List<String>): AndroidLintActionArgs {
// TODO Need to handle the --flagfile argument here
val unwrappedArgs: Array<String> = if (args.size == 1 && args[0].startsWith("@")) {
File(args[0].removePrefix("@")).readLines(Charset.defaultCharset()).toTypedArray()
} else {
args.toTypedArray()
}
val unwrappedArgs: Array<String> =
if (args.size == 1 && args[0].startsWith("@")) {
File(args[0].removePrefix("@")).readLines(Charset.defaultCharset()).toTypedArray()
} else {
args.toTypedArray()
}

return AndroidLintActionArgs(ArgParser(unwrappedArgs))
}
Expand Down
41 changes: 23 additions & 18 deletions src/cli/AndroidLintCliInvoker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,18 @@ import kotlin.io.path.pathString
class AndroidLintCliInvoker(
classLoader: ClassLoader,
) {

private val mainClass = Class.forName("com.android.tools.lint.Main", true, classLoader)
private val cliFlags = classLoader.loadClass("com.android.tools.lint.LintCliFlags")
private val mainInstance = mainClass
.getDeclaredConstructor()
.newInstance()
private val flagsInstance = mainClass.getDeclaredField("flags").apply {
isAccessible = true
}.get(mainInstance)
private val mainInstance =
mainClass
.getDeclaredConstructor()
.newInstance()
private val flagsInstance =
mainClass
.getDeclaredField("flags")
.apply {
isAccessible = true
}.get(mainInstance)

/*
* Exit Status:
Expand All @@ -37,15 +40,15 @@ class AndroidLintCliInvoker(
}

fun setCheckDependencies(enableCheckDependencies: Boolean) {
val setCheckDependenciesMethod = cliFlags.getDeclaredMethod(
"setCheckDependencies",
Boolean::class.java,
)
val setCheckDependenciesMethod =
cliFlags.getDeclaredMethod(
"setCheckDependencies",
Boolean::class.java,
)
setCheckDependenciesMethod.invoke(flagsInstance, enableCheckDependencies)
}

companion object {

const val ERRNO_SUCCESS = 0
const val ERRNO_ERRORS = 1
const val ERRNO_USAGE = 2
Expand All @@ -64,12 +67,14 @@ class AndroidLintCliInvoker(
"Error: At least one jar must be provided when calling createUsingJars"
}

val classpath = jars.map { jar ->
require(jar.isRegularFile() && jar.exists()) {
"Error: The provided jar does not exist!: ${jar.pathString}"
}
URL("file:${jar.pathString}")
}.toTypedArray()
val classpath =
jars
.map { jar ->
require(jar.isRegularFile() && jar.exists()) {
"Error: The provided jar does not exist!: ${jar.pathString}"
}
URL("file:${jar.pathString}")
}.toTypedArray()

return AndroidLintCliInvoker(classLoader = URLClassLoader(classpath, parentClassloader))
}
Expand Down
29 changes: 16 additions & 13 deletions src/cli/AndroidLintProject.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@ internal fun createProjectXMLString(
projectElement.appendChild(it)
}

val moduleElement = document.createElement("module").also {
it.setAttribute("name", moduleName)
it.setAttribute("android", if (androidManifest != null) "true" else "false")
// it.setAttribute("library", "false")
// it.setAttribute("compile-sdk-version", "get-actual-value-here")
projectElement.appendChild(it)
}
val moduleElement =
document.createElement("module").also {
it.setAttribute("name", moduleName)
it.setAttribute("android", if (androidManifest != null) "true" else "false")
// it.setAttribute("library", "false")
// it.setAttribute("compile-sdk-version", "get-actual-value-here")
projectElement.appendChild(it)
}

customLintChecks.forEach { jar ->
document.createElement("lint-checks").also {
Expand Down Expand Up @@ -83,10 +84,12 @@ internal fun createProjectXMLString(
moduleElement.appendChild(element)
}

return StringWriter().apply {
val transformer = TransformerFactory.newInstance().newTransformer()
transformer.setOutputProperty(OutputKeys.INDENT, "yes")
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2")
transformer.transform(DOMSource(document), StreamResult(this))
}.buffer.toString()
return StringWriter()
.apply {
val transformer = TransformerFactory.newInstance().newTransformer()
transformer.setOutputProperty(OutputKeys.INDENT, "yes")
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2")
transformer.transform(DOMSource(document), StreamResult(this))
}.buffer
.toString()
}
Loading
Loading