Skip to content

Commit

Permalink
First pass at first-class support for Java projects (#3261)
Browse files Browse the repository at this point in the history
Most of the functionality necessary to support Java projects is already
there, what's missing is a proper documentation and onboarding
experience. This PR aims to make a first pass at writing that and
setting up the supporting tooling necessary to do so. A Java programmer
should be able to land on `Introduction to Mill for Java`, read through
it, and competently make use of Mill for their Java project even without
significant Scala expertise.

* Since the Scala and Java modules are pretty tied together via Zinc,
for now I decided to avoid trying to split out a proper `javalib` module
and left everything in place inside `scalalib`. Instead, I defined a
`package object javalib` with a bunch of aliases pointing at the various
`scalalib` functionality; this should give us the UX benefits of letting
users `import javalib._`, without needing to invasively refactor the
codebase. There's some tradeoff with `scalalib` still appearing in stack
traces etc., but for a first pass I think that's fine.

* I added a new set of `example/basicjava/`, `javamodule`, and
`javabuilds`.
* These are duplicates of `example/basic/`, `scalamodule`, and
`scalabuilds`, but for Java. I implemented a janky DIY `build`.sc
templating/extension system, to allow me to share the bulk of the
ascidoc between the Scala/Java versions while only specifying the
differences: generally the build.sc Scala logic needs to be adapted, but
the asciidoc text and the Usage command-blocks can mostly be re-used.

* All of the Java versions of the examples have had their Scala code
ported to Java. This includes third party libraries, e.g. replacing
MainArgs with ArgParse4j, Scalatags with `escapeHtml4` from apache
commons text. Most of the `Usage` command-blocks testing the builds are
shared between the Java and Scala versions of each example, although
some had to be generalized a bit, and a handful only apply to a single
language

* The word "*Scala*" does not appear anywhere in the new documentation
pages. In general, a Java programmer should be comfortable running
through the docs, learning to use Mill, all without being scared off by
it being a "Scala" build tool

* Moved the docs for the `example/thirdparty/jimfs/` folder to the new
*Java Build Examples* section, and added a new
`example/thirdparty/commons-io/` folder to also include in those docs

With this PR, the docs have roughly the following sections:

* Scala Quick Start: Scala Specific
* Java Quick Start: Java Specific
* Mill in Depth, Mill Plugins, Reference: Language Agnostic

In future, if we include Kotlin support, we can have a `Kotlin Quick
Start` section next to that of the other two languages. It should be
pretty easy (O(days)) to duplicate all the work I did for Java for
Kotlin.

Doc changes can be reviewed locally via `./mill -i docs.localPages`

Pull Request: #3261

---------

Co-authored-by: Tobias Roeser <[email protected]>
  • Loading branch information
lihaoyi and lefou authored Jul 18, 2024
1 parent 0aa7bb3 commit 369a14c
Show file tree
Hide file tree
Showing 162 changed files with 2,362 additions and 280 deletions.
59 changes: 56 additions & 3 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -1185,18 +1185,65 @@ object example extends MillScalaModule {
def moduleDeps = Seq(integration)

object basic extends Cross[ExampleCrossModule](listIn(millSourcePath / "basic"))
object basicjava extends Cross[ExampleCrossModuleJava](listIn(millSourcePath / "basicjava"))
object scalabuilds extends Cross[ExampleCrossModule](listIn(millSourcePath / "scalabuilds"))
object javabuilds extends Cross[ExampleCrossModuleJava](listIn(millSourcePath / "javabuilds"))
object scalamodule extends Cross[ExampleCrossModule](listIn(millSourcePath / "scalamodule"))
object javamodule extends Cross[ExampleCrossModuleJava](listIn(millSourcePath / "javamodule"))
object tasks extends Cross[ExampleCrossModule](listIn(millSourcePath / "tasks"))
object cross extends Cross[ExampleCrossModule](listIn(millSourcePath / "cross"))
object misc extends Cross[ExampleCrossModule](listIn(millSourcePath / "misc"))
object web extends Cross[ExampleCrossModule](listIn(millSourcePath / "web"))

trait ExampleCrossModuleJava extends ExampleCrossModule {

def upstreamCross(s: String) = s match{
case "basicjava" => basic
case "javabuilds" => scalabuilds
case "javamodule" => scalamodule
}

def buildScLines = T{
val upstreamLines = os.read.lines(
upstreamCross(this.millModuleSegments.parts.dropRight(1).last)(crossValue)
.testRepoRoot().path / "build.sc"
)
val lines = os.read.lines(testRepoRoot().path / "build.sc")

import collection.mutable
val groupedLines = mutable.Map.empty[String, mutable.Buffer[String]]
var current = Option.empty[String]
lines.foreach{
case s"//// SNIPPET:$name" =>
current = Some(name)
groupedLines(name) = mutable.Buffer()
case s => groupedLines(current.get).append(s)
}

upstreamLines.flatMap{
case s"//// SNIPPET:$name" =>
if (name != "END") {

current = Some(name)
groupedLines(name)
} else{
current = None
Nil
}

case s =>
if (current.nonEmpty) None
else Some(s)
}
}
}
trait ExampleCrossModule extends IntegrationTestCrossModule {
// disable scalafix because these example modules don't have sources causing it to misbehave
def fix(args: String*): Command[Unit] = T.command {}
def testRepoRoot: T[PathRef] = T.source(millSourcePath)
def compile = example.compile()

def buildScLines = T{ os.read.lines(testRepoRoot().path / "build.sc") }
def forkEnv = super.forkEnv() ++ Map("MILL_EXAMPLE_PARSED" -> upickle.default.write(parsed()))

/**
Expand All @@ -1206,7 +1253,7 @@ object example extends MillScalaModule {
val states = collection.mutable.Buffer("scala")
val chunks = collection.mutable.Buffer(collection.mutable.Buffer.empty[String])

for (line <- os.read.lines(testRepoRoot().path / "build.sc")) {
for (line <- buildScLines()) {
val (newState, restOpt) = line match {
case s"/** Usage" => ("example", None)
case s"/** See Also: $path */" =>
Expand Down Expand Up @@ -1278,7 +1325,8 @@ object example extends MillScalaModule {
def repoInfo = Map(
"acyclic" -> ("com-lihaoyi/acyclic", "1ec221f377794db39e8ff9b43415f81c703c202f"),
"fansi" -> ("com-lihaoyi/fansi", "169ac96d7c6761a72590d312a433cf12c572573c"),
"jimfs" -> ("google/jimfs", "5b60a42eb9d3cd7a2073d549bd0cb833f5a7e7e9")
"jimfs" -> ("google/jimfs", "5b60a42eb9d3cd7a2073d549bd0cb833f5a7e7e9"),
"commons-io" -> ("apache/commons-io", "b91a48074231ef813bc9b91a815d77f6343ff8f0")
)
object thirdparty extends Cross[ThirdPartyModule](listIn(millSourcePath / "thirdparty"))
trait ThirdPartyModule extends ExampleCrossModule {
Expand Down Expand Up @@ -1467,6 +1515,7 @@ object dev extends MillPublishScalaModule {
contrib.buildinfo.testDep(),
contrib.scoverage.testDep(),
contrib.scoverage.worker2.testDep(),
contrib.jmh.testDep(),
contrib.playlib.testDep(),
contrib.playlib.worker("2.8").testDep(),
bsp.worker.testDep()
Expand Down Expand Up @@ -1704,7 +1753,7 @@ object docs extends Module {
s"""site:
| title: Mill
| url: ${if (authorMode) s"${T.dest}/site" else Settings.docUrl}
| start_page: mill::Intro_to_Mill.adoc
| start_page: mill::Intro_to_Mill_for_Scala.adoc
|
|content:
| sources:
Expand Down Expand Up @@ -1744,6 +1793,10 @@ object docs extends Module {
| - require: '@antora/lunr-extension'
| index_latest_only: true
|
|runtime:
| log:
| failure_level: error
|
|""".stripMargin
}

Expand Down
2 changes: 1 addition & 1 deletion ci/test-mill-bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ ci/prepare-mill-bootstrap.sh
# Run tests
target/mill-release -i "__.compile"
target/mill-release -i "{main,scalalib}.__.test"
target/mill-release -i "example.basic[1-simple-scala].server.test"
target/mill-release -i "example.basic[1-simple].server.test"
2 changes: 1 addition & 1 deletion ci/test-mill-dev.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

set -eux

EXAMPLE=example/scalabuilds/10-scala-realistic
EXAMPLE=example/scalabuilds/9-realistic

rm -rf $EXAMPLE/out

Expand Down
2 changes: 1 addition & 1 deletion ci/test-mill-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ set -eux
# Build Mill
./mill -i dev.assembly

EXAMPLE=example/scalabuilds/10-scala-realistic
EXAMPLE=example/scalabuilds/9-realistic

rm -rf $EXAMPLE/out

Expand Down
13 changes: 10 additions & 3 deletions docs/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@
// do this other thing, etc. We touch on a lot of topics about how Mill works,
// but we intentionally skim over them and do not go into depth: the focus is
// on end user goals and how to achieve them.
.Quick Start
* xref:Intro_to_Mill.adoc[]
* xref:Installation_IDE_Support.adoc[]
.Scala Quick Start
* xref:Intro_to_Mill_for_Scala.adoc[]
* xref:Scala_Installation_IDE_Support.adoc[]
* xref:Builtin_Commands.adoc[]
* xref:Scala_Build_Examples.adoc[]
* xref:Scala_Module_Config.adoc[]
* xref:Web_Build_Examples.adoc[]
.Java Quick Start
* xref:Intro_to_Mill_for_Java.adoc[]
* xref:Java_Installation_IDE_Support.adoc[]
* xref:Java_Builtin_Commands.adoc[]
* xref:Java_Build_Examples.adoc[]
* xref:Java_Module_Config.adoc[]
// This section is all about developing a deeper understanding of specific
// topics in Mill. This is the opposite of `Quick Start` above: while we touch
// on some end-user use cases, it is only to motivate the Mill features that we
Expand Down
4 changes: 4 additions & 0 deletions docs/modules/ROOT/pages/Extending_Mill.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ include::example/misc/4-mill-build-folder.adoc[]

include::example/misc/5-module-run-task.adoc[]

== Importing Contrib Modules

include::example/misc/6-contrib-import.adoc[]

== Evaluator Commands (experimental)

_Evaluator Command are experimental and suspected to change.
Expand Down
34 changes: 34 additions & 0 deletions docs/modules/ROOT/pages/Intro_to_Mill_for_Java.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

// Author Notes:
//
// This is the first page a user is expected to land on when learning about
// Mill. It is designed to be a quick, broad overview to get someone started:
// what is Mill, why should they care, and what some simple Mill builds look
// like and how to use them. We intentionally touch shallowly on a lot of
// topics without giving them a proper discussion, since the other pages have
// plenty of space to go in-depth.
//
// By the end of this page, a prospective Mill user should be familiar with
// what Mill is, hopefully have downloaded an example to try out, and be
// interested in learning more about the Mill build tool

= Introduction to Mill for Java

include::partial$Intro_to_Mill_Header.adoc[]


== Simple Java Module

include::example/basicjava/1-simple.adoc[]

== Custom Build Logic

include::example/basicjava/2-custom-build-logic.adoc[]

== Multi-Module Project

include::example/basicjava/3-multi-module.adoc[]



include::partial$Intro_to_Mill_Footer.adoc[]
34 changes: 34 additions & 0 deletions docs/modules/ROOT/pages/Intro_to_Mill_for_Scala.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Author Notes:
//
// This is the first page a user is expected to land on when learning about
// Mill. It is designed to be a quick, broad overview to get someone started:
// what is Mill, why should they care, and what some simple Mill builds look
// like and how to use them. We intentionally touch shallowly on a lot of
// topics without giving them a proper discussion, since the other pages have
// plenty of space to go in-depth.
//
// By the end of this page, a prospective Mill user should be familiar with
// what Mill is, hopefully have downloaded an example to try out, and be
// interested in learning more about the Mill build tool

= Introduction to Mill for Scala

:page-aliases: index.adoc

include::partial$Intro_to_Mill_Header.adoc[]


== Simple Scala Module

include::example/basic/1-simple.adoc[]

== Custom Build Logic

include::example/basic/2-custom-build-logic.adoc[]

== Multi-Module Project

include::example/basic/3-multi-module.adoc[]


include::partial$Intro_to_Mill_Footer.adoc[]
73 changes: 73 additions & 0 deletions docs/modules/ROOT/pages/Java_Build_Examples.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
= Java Build Examples

On this page, we will explore the Mill build tool via a series of simple Java
example projects. Each project demonstrates one particular feature of the Mill
build tool, and is also an executable codebase you can download and run. By the
end of this page, you will be familiar with how to configure Mill to work with
realistic Java codebases: cross-building, testing, and publishing them.


Many of the APIs covered here are listed in the API documentation:

* {mill-doc-url}/api/latest/mill/main/RootModule.html[`mill.scalalib.RootModule`]
* {mill-doc-url}/api/latest/mill/scalalib/TestModule$.html[`mill.scalalib.TestModule`]
* {mill-doc-url}/api/latest/mill/scalalib/PublishModule.html[`mill.scalalib.PublishModule`]
* {mill-doc-url}/api/latest/mill/scalalib/CrossScalaModule.html[`mill.scalalib.CrossScalaModule`]
* {mill-doc-url}/api/latest/mill/scalalib/MavenModule.html[`mill.scalalib.MavenModule`]
* {mill-doc-url}/api/latest/mill/scalalib/CrossSbtModule.html[`mill.scalalib.CrossSbtModule`]
* {mill-doc-url}/api/latest/mill/scalalib/JavaModule.html[`mill.scalalib.JavaModule`]
== Common Configuration Overrides

include::example/javabuilds/1-common-config.adoc[]

== Custom Tasks

include::example/javabuilds/2-custom-tasks.adoc[]

== Overriding Tasks

include::example/javabuilds/3-override-tasks.adoc[]

== Nesting Modules

include::example/javabuilds/4-nested-modules.adoc[]

== Java Module With Test Suite

include::example/javabuilds/5-test-suite.adoc[]

== Publish Module

include::example/javabuilds/6-publish-module.adoc[]


== Maven-Compatible Modules

include::example/javabuilds/8-compat-modules.adoc[]


== Realistic Java Example Project

include::example/javabuilds/9-realistic.adoc[]


== Example Builds for Real Projects

Mill comes bundled with example builds for real-world open-source projects,
demonstrating how Mill can be used to build code outside of tiny example codebases:

=== JimFS

include::example/thirdparty/jimfs.adoc[]

=== Apache Commons IO

include::example/thirdparty/commons-io.adoc[]

== Real World Mill Builds

=== C3P0

https://github.com/swaldman/c3p0[C3P0] is a JDBC connection pooling library
written in Java, built using the Mill build tool.
Loading

0 comments on commit 369a14c

Please sign in to comment.