Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Android Linting Example #3931

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
43ed8c7
add draft androoid linting
c0d33ngr Nov 10, 2024
8bae57e
add android linting example
c0d33ngr Nov 10, 2024
577375f
fix linting error in scala files
c0d33ngr Nov 10, 2024
5cab0a8
Merge branch 'main' into add-android-linting-example
c0d33ngr Nov 10, 2024
92a165e
Update AndroidAppModule.scala
c0d33ngr Nov 10, 2024
4e82c26
update code
c0d33ngr Nov 11, 2024
1d3805a
fix build error
c0d33ngr Nov 11, 2024
bd67ec5
add some changes
c0d33ngr Nov 17, 2024
6588076
Merge branch 'main' into add-android-linting-example
c0d33ngr Nov 17, 2024
c8f33f1
udpate code structure
c0d33ngr Nov 18, 2024
2c78931
udpate code
c0d33ngr Nov 18, 2024
1429e01
udpate code
c0d33ngr Nov 20, 2024
d0c7f11
Merge branch 'main' into add-android-linting-example
c0d33ngr Nov 20, 2024
9e21cee
Update build.mill
c0d33ngr Nov 22, 2024
60b1cbb
Update build.mill
c0d33ngr Nov 22, 2024
f6420eb
Update AndroidLintModule.scala
c0d33ngr Nov 22, 2024
0994b1a
fix format errors
c0d33ngr Nov 22, 2024
67deba8
fix formatting
c0d33ngr Nov 23, 2024
03c0381
restructure code
c0d33ngr Nov 23, 2024
bea5080
fix doc test errors
c0d33ngr Nov 24, 2024
8c32d89
Update build.mill
c0d33ngr Nov 24, 2024
1be883b
update cdoe
c0d33ngr Nov 25, 2024
2cae0ea
update cdoe
c0d33ngr Nov 25, 2024
428e7c0
update cdoe to add some defaults
c0d33ngr Nov 26, 2024
24d91e0
update doc to clean sepcific module
c0d33ngr Nov 26, 2024
3d01603
Update build.mill via GitHub web
c0d33ngr Nov 29, 2024
9585600
Update AndroidLintModule.scala via GitHub web
c0d33ngr Nov 29, 2024
df95eb3
Update AndroidLintModule.scala via GitHub web
c0d33ngr Nov 29, 2024
49d4717
update code
c0d33ngr Dec 9, 2024
54ded22
fix lint and test-doc errors
c0d33ngr Dec 9, 2024
b910ada
add abortOnError and baseline
c0d33ngr Dec 10, 2024
a6e2fc5
update code
c0d33ngr Dec 10, 2024
195a51e
remove old codes
c0d33ngr Dec 10, 2024
e7de9dc
update code
c0d33ngr Dec 10, 2024
20b4107
update code
c0d33ngr Dec 14, 2024
b4801fe
update code
c0d33ngr Dec 14, 2024
7e24224
fix error
c0d33ngr Dec 14, 2024
4133561
fix incrorect java suppress lint test
c0d33ngr Dec 14, 2024
44cbf19
update code
c0d33ngr Dec 14, 2024
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 docs/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
** xref:javalib/publishing.adoc[]
** xref:javalib/build-examples.adoc[]
** xref:javalib/web-examples.adoc[]
* xref:scalalib/intro.adoc[]
** xref:scalalib/module-config.adoc[]
** xref:scalalib/dependencies.adoc[]
Expand All @@ -31,6 +30,7 @@
** xref:kotlinlib/web-examples.adoc[]
* (Experimental) Android with Mill
** xref:android/java.adoc[]
** xref:android/android-linting.adoc[]
** xref:android/kotlin.adoc[]
* xref:comparisons/why-mill.adoc[]
** xref:comparisons/maven.adoc[]
Expand Down
12 changes: 12 additions & 0 deletions docs/modules/ROOT/pages/android/android-linting.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
= Linting Android Projects
:page-aliases: Linting_Android_Projects.adoc

include::partial$gtag-config.adoc[]

This page covers essential practices for maintaining and enforcing code quality
in Android projects using the Mill build tool. Proper linting helps detect
and resolve potential issues early, promoting better performance, security,
and user experience.


include::partial$example/android/javalib/3-linting.adoc[]
12 changes: 12 additions & 0 deletions example/android/javalib/3-linting/app/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.helloworld.app" android:versionCode="1" android:versionName="1.0">
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="35"/>
<application android:label="@string/app_name" android:theme="@android:style/Theme.Light.NoTitleBar" android:debuggable="true">
<activity android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
4 changes: 4 additions & 0 deletions example/android/javalib/3-linting/app/lint.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<lint>
<issue id="MissingApplicationIcon" severity="ignore" />
</lint>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<resources>
<color name="white">#FFFFFF</color>
<color name="text_green">#34A853</color>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<resources>
<string name="app_name">HelloWorldApp</string>
<string name="hello_world">Hello, World Java!</string>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.helloworld.app;

import android.app.Activity;
import android.os.Bundle;
import android.view.Gravity;
import android.view.ViewGroup.LayoutParams;
import android.widget.TextView;

public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Create a new TextView
TextView textView = new TextView(this);

// Set the text to the string resource
textView.setText(getString(R.string.hello_world));

// Set text size
textView.setTextSize(32);

// Center the text within the view
textView.setGravity(Gravity.CENTER);

// Set the layout parameters (width and height)
textView.setLayoutParams(
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

// Set the text color using a resource
textView.setTextColor(getResources().getColor(R.color.text_green));

// Set the background color using a resource
textView.setBackgroundColor(getResources().getColor(R.color.white));

// Set the content view to display the TextView
setContentView(textView);
}
}
95 changes: 95 additions & 0 deletions example/android/javalib/3-linting/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// == Linting with Basic Config

// This Mill build configuration includes a linting step, which is essential for ensuring code
// quality and adhering to best practices in Android development projects. Running the `androidLintRun` task
// produces a detailed HTML report by default, identifying potential issues in the code, such as performance,
// security, and usability warnings. This helps maintain the health and quality of the codebase.

// In this example, we set a custom config to generate the report file in txt format and not HTML.

//// SNIPPET:BUILD
package build

import mill._
import mill.javalib.android.{AndroidSdkModule, AndroidLintModule}

// Create and configure an Android SDK module to manage Android SDK paths and tools.
object androidSdkModule0 extends AndroidSdkModule {
def buildToolsVersion = "35.0.0"
def bundleToolVersion = "1.17.2"
}

// Actual android application with linting config
object app extends AndroidLintModule {
def androidSdkModule = mill.define.ModuleRef(androidSdkModule0)

// Set path to the custom `lint.xml` config file. It is usually at the root of the project
def androidLintConfigPath = Task { Some(PathRef(millSourcePath / "lint.xml")) }

// Set the linting report to be generated as a text file
def androidLintReportFmt = Task { "txt" }
c0d33ngr marked this conversation as resolved.
Show resolved Hide resolved

// Set path to generated `.jar` files in this case (or `.class` files)
def classPath = Task.Source { androidJar() }
c0d33ngr marked this conversation as resolved.
Show resolved Hide resolved

// Set path to the location of the project source codes
def sourcesPath = Task.Source { (millSourcePath / "src" / "main" / "java") }
c0d33ngr marked this conversation as resolved.
Show resolved Hide resolved

// Specify additional flag options for Android Lint not defined in `AndroidLintModule`
def androidLintArgs: T[Seq[String]] = Task {
Seq(
"--classpath",
classPath().path.toString,
"--sources",
sourcesPath().path.toString
)
}

}

/** See Also: app/AndroidManifest.xml */

// The `AndroidManifest.xml` file shown above is flawed with hardcoded string linting problem so it can be spoted by running the `androidLintRun` task.

/** See Also: app/resources/values/colors.xml */
/** See Also: app/resources/values/strings.xml */
/** See Also: app/src/main/java/com/helloworld/app/MainActivity.java */
/** See Also: app/lint.xml */

// The `lint.xml` file shown above ignores the `MissingApplicationIcon` error in the code as
// the project is for demostration. There are no icons for the demo project.

////SNIPPET:END

/** Usage

> ./mill show app.androidLintRun # Display full path to the linting report in text file
".../out/app/androidLintRun.dest/report.txt"

> cat out/app/androidLintRun.dest/report.txt # Display content of the linting report
AndroidManifest.xml:3: Error: Avoid hardcoding the debug mode; leaving it out allows debug and release builds to automatically assign one [HardcodedDebugMode]

> sed -i.bak 's/ android:debuggable="true"//g' app/AndroidManifest.xml # Fix the HardcodedDebugMode warning issue from `AndroidManifest.xml`

> ./mill app.androidLintRun # Rerun it for new changes to reflect

> cat out/app/androidLintRun.dest/report.txt # Check the content of report again
No issues found.

*/

// == Linting with Custom Configurations

// The commands below set the serverity level of `MissingApplicationIcon` to `warning` in the `lint.xml` file that was shown above
// and run the `androidLintRun` task. Some output of the changes is shown below.

/** Usage

> sed -i.bak 's/severity="ignore"/severity="warning"/g' app/lint.xml # Set `MissingApplicationIcon` severity level to warning
c0d33ngr marked this conversation as resolved.
Show resolved Hide resolved

> ./mill app.androidLintRun # Rerun it for new changes to reflect

> cat out/app/androidLintRun.dest/report.txt # Output the changes in the report
AndroidManifest.xml:3: Warning: Should explicitly set android:icon, there is no default [MissingApplicationIcon]

*/
1 change: 1 addition & 0 deletions scalalib/src/mill/javalib/android/AndroidAppModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -339,4 +339,5 @@ trait AndroidAppModule extends JavaModule {

PathRef(keystoreFile)
}

}
78 changes: 78 additions & 0 deletions scalalib/src/mill/javalib/android/AndroidLintModule.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package mill.javalib.android

import mill._
import mill.api.PathRef
import mill.javalib.android.AndroidAppModule

/**
* Android Lint Module for integrating the Android Lint tool in a Mill build.
*
* This module provides configuration options for executing Android Lint, including setting paths,
* specifying lint rules, managing reports, and more.
*/
@mill.api.experimental
trait AndroidLintModule extends AndroidAppModule {
c0d33ngr marked this conversation as resolved.
Show resolved Hide resolved

/**
* Specifies the file format of lint report. Available file formats are "html", "xml", "txt" and "sarif".
* Specifies additional arguments for the Android Lint tool.
* Allows for complete customization of the lint command.
*/
def androidLintReportFmt: T[String] = Task { "html" }

/**
* Specifies the lint configuration XML file path. This allows setting custom lint rules or modifying existing ones.
*/
def androidLintConfigPath: T[Option[PathRef]] = Task { None }
c0d33ngr marked this conversation as resolved.
Show resolved Hide resolved

/**
* Specifies additional arguments for the Android Lint tool.
* Allows for complete customization of the lint command.
*/
def androidLintArgs: T[Seq[String]] = Task { Seq.empty[String] }

/**
* Runs the Android Lint tool to generate a report on code quality issues.
*
* This method utilizes Android Lint, a tool provided by the Android SDK,
* to analyze the source code for potential bugs, performance issues, and
* best practices compliance. It generates a report in the specified format.
*
* The lint tool requires the Android SDK's command-line tools to be installed.
* The report is saved in the task's destination directory as "report.html".
*
* For more details on the Android Lint tool, refer to:
* [[https://developer.android.com/studio/write/lint]]
*/
def androidLintRun: T[PathRef] = Task.Command {

val format = androidLintReportFmt()
val lintReport: os.Path = T.dest / s"report.$format"

// Map the report format to the corresponding lint command flag
val lintReportFlag = format match {
case "html" => "--html"
case "txt" => "--text"
case "xml" => "--xml"
case "sarif" => "--sarif"
case _ => throw new Exception(s"Unsupported report format: $format")
}

// Prepare the lint configuration argument if the config path is set
val configArg = androidLintConfigPath().map(config =>
Seq("--config", config.path.toString)
).getOrElse(Seq.empty)

os.call(
Seq(
androidSdkModule().cmdlineToolsPath().path.toString + "/lint",
millSourcePath.toString,
lintReportFlag.toString,
lintReport.toString
) ++ configArg ++ androidLintArgs()
)

PathRef(lintReport)
}

}
8 changes: 8 additions & 0 deletions scalalib/src/mill/javalib/android/AndroidSdkModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ trait AndroidSdkModule extends Module {
PathRef(sdkPath().path / "build-tools" / buildToolsVersion())
}

/**
* Provides path to the Android cmdline tools for the selected version.
*/
def cmdlineToolsPath: T[PathRef] = Task {
installAndroidSdkComponents()
PathRef(sdkPath().path / "cmdline-tools" / "latest" / "bin")
}

/**
* Provides path to D8 Dex compiler, used for converting Java bytecode into Dalvik bytecode.
*
Expand Down
Loading