Skip to content

Commit

Permalink
Extended mill init to support an existing Maven project (com-lihaoy…
Browse files Browse the repository at this point in the history
…i#3756)

This PR adds support to convert a Maven build to Mill by generating Mill
build file(s) from POM file(s).
The generator output should be considered scaffolding and will likely
require edits to complete conversion.

### Capabilities
The conversion
 - handles deeply nested modules
 - captures project metadata
 - configures dependencies for scopes:
   - compile
   - provided
   - runtime
   - test
 - configures testing frameworks:
   - JUnit 4
   - JUnit 5
   - TestNG
 - configures multiple, compile and test, resource directories

### Limitations
The conversion does not support:
 - build extensions
 - build profiles
 - plugins, other than maven-compiler-plugin

### Future work
 Add support for:
 - [os-maven-plugin](https://github.com/trustin/os-maven-plugin)
 - [moditect-maven-plugin](https://github.com/moditect/moditect)
-
[hawtjni-maven-plugin](https://github.com/fusesource/hawtjni/tree/master/hawtjni-maven-plugin)
 
Resolves com-lihaoyi#3449.

---------

Co-authored-by: Li Haoyi <[email protected]>
  • Loading branch information
ajaychandran and lihaoyi authored Nov 14, 2024
1 parent 4d59da3 commit 3b94c5d
Show file tree
Hide file tree
Showing 49 changed files with 3,733 additions and 5 deletions.
8 changes: 8 additions & 0 deletions build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,14 @@ object Deps {
val sonatypeCentralClient = ivy"com.lumidion::sonatype-central-client-requests:0.3.0"
val kotlinVersion = "2.0.21"
val kotlinCompiler = ivy"org.jetbrains.kotlin:kotlin-compiler:$kotlinVersion"
val mavenVersion = "3.9.9"
val mavenEmbedder = ivy"org.apache.maven:maven-embedder:$mavenVersion"
val mavenResolverVersion = "1.9.22"
val mavenResolverConnectorBasic = ivy"org.apache.maven.resolver:maven-resolver-connector-basic:$mavenResolverVersion"
val mavenResolverSupplier = ivy"org.apache.maven.resolver:maven-resolver-supplier:$mavenResolverVersion"
val mavenResolverTransportFile = ivy"org.apache.maven.resolver:maven-resolver-transport-file:$mavenResolverVersion"
val mavenResolverTransportHttp = ivy"org.apache.maven.resolver:maven-resolver-transport-http:$mavenResolverVersion"
val mavenResolverTransportWagon = ivy"org.apache.maven.resolver:maven-resolver-transport-wagon:$mavenResolverVersion"

object RuntimeDeps {
val dokkaVersion = "1.9.20"
Expand Down
1 change: 1 addition & 0 deletions dist/package.mill
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ object `package` extends RootModule with build.MillPublishJavaModule {

def testTransitiveDeps = build.runner.testTransitiveDeps() ++ Seq(
build.main.graphviz.testDep(),
build.main.maven.testDep(),
build.runner.linenumbers.testDep(),
build.scalalib.backgroundwrapper.testDep(),
build.contrib.bloop.testDep(),
Expand Down
2 changes: 2 additions & 0 deletions docs/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
** xref:cli/flags.adoc[]
** xref:cli/alternate-installation.adoc[]
** xref:cli/builtin-commands.adoc[]
* Migrating to Mill
** xref:migrating/maven.adoc[]
// This section gives a tour of the various user-facing features of Mill:
// library deps, out folder, queries, tasks, etc.. These are things that
// every Mill user will likely encounter, and are touched upon in the various
Expand Down
155 changes: 155 additions & 0 deletions docs/modules/ROOT/pages/migrating/maven.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
= Migrating From Maven to Mill
:page-aliases: Migrating_A_Maven_Build_to_Mill.adoc
:icons: font

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

The Mill `init` command can be used to convert a Maven build to Mill. This has
xref:#limitations[limitations] and is not intended to reliably migrate 100% of
Maven builds out there in the wild, but is instead meant to provide the basic
scaffolding of a Mill build for you to further refine and update manually.

Each Maven module with a `pom.xml` is converted to a Mill `build.mill`/`package.mill`
file containing a top-level `MavenModule`. A nested `test` module is defined if both:

* `src/test` exists
* a supported xref:javalib/testing.adoc[test framework] is detected (for a _tests only_
module with test sources in `src/main/java`)
Again, note that `mill init` importing Maven builds is best effort.
This means that while small projects can be expected to complete without issue:

include::partial$example/javalib/migrating/1-maven-complete.adoc[]

More larger projects often require some manual tweaking in order to work:

include::partial$example/javalib/migrating/2-maven-incomplete.adoc[]

Nevertheless, even for larger builds `mill init` automates most of the tedious
busy-work of writing `build.mill`/`package.mill` files, and makes it much quicker
to get a working Mill build for any existing Maven project.


== Capabilities

The conversion

* handles deeply nested modules
* captures project metadata
* configures dependencies for scopes:
** compile
** provided
** runtime
** test
* configures testing frameworks:
** JUnit 4
** JUnit 5
** TestNG
* configures multiple, compile and test, resource directories

=== Command line arguments
.name of generated base module trait defining project metadata settings
[source,sh]
----
./mill init --base-module MyModule
----
.name of generated nested test module (defaults to `test`)
[source,sh]
----
./mill init --test-module test
----
.name of generated companion object defining constants for dependencies
[source,sh]
----
./mill init --deps-object Deps
----
.capture properties defined in `pom.xml` for publishing
[source,sh]
----
./mill init --publish-properties
----
.merge build files generated for a multi-module build
[source,sh]
----
./mill init --merge
----

.use cache for Maven repository system
[source,sh]
----
./mill init --cache-repository
----
.process Maven plugin executions and configurations
[source,sh]
----
./mill init --process-plugins
----

=== Verified projects

The conversion has been tested with the following projects:

* https://github.com/fusesource/jansi/archive/refs/tags/jansi-2.4.1.zip[jansi]
[source,sh]
----
./mill init --base-module JansiModule --deps-object Deps --cache-repository --process-plugins
----

* https://github.com/davidmoten/geo/archive/refs/tags/0.8.1.zip[geo] (multi-module build)
[source,sh]
----
./mill init --base-module GeoModule --deps-object Deps --merge --cache-repository --process-plugins
----

Post `init`, the following tasks were executed successfully:

* `compile`
* `test`
* `publishLocal`

[#limitations]
== Limitations

The conversion does not support

* build extensions
* build profiles
* non-Java (native) sources

Maven plugin support is limited to

* https://maven.apache.org/plugins/maven-compiler-plugin/[maven-compiler-plugin]

[TIP]
====
These limitations can be overcome by:
* configuring equivalent Mill xref:extending/contrib-plugins.adoc[contrib]
or xref:extending/thirdparty-plugins.adoc[third party] plugins
* defining custom xref:extending/writing-plugins.adoc[plugins]
* defining custom xref:fundamentals/tasks.adoc[tasks]
* defining custom xref:fundamentals/cross-builds.adoc[cross modules]
====

== FAQ

.How to fix compilation errors in generated build files?

This could happen if a module and task name collision occurs. Either rename the module or enclose the name in backticks.


.How to fix JPMS `module not found` compilation errors?

Set https://github.com/tfesenko/Java-Modules-JPMS-CheatSheet#how-to-export-or-open-a-package[additional command line options]
for dependencies.


.How to fix test compilation errors?

* The test framework configured may be for an unsupported version; try upgrading the
corresponding dependencies.
* Mill does not add `provided` dependencies to the transitive dependencies of the nested
test module; specify the dependencies again, in one of `ivyDeps`, `compileIvyDeps`, `runIvyDeps`, in the test module.


29 changes: 29 additions & 0 deletions example/javalib/migrating/1-maven-complete/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/** Usage

> rm build.mill # remove any existing build file

> git init .
> git remote add -f origin https://github.com/davidmoten/geo.git
> git checkout 0.8.1 # example multi-module Java project using JUnit4

> ./mill init
converting Maven build
writing Mill build file to geo/package.mill
writing Mill build file to geo-mem/package.mill
writing Mill build file to build.mill
init completed, run "mill resolve _" to list available tasks

> ./mill __.compile
compiling 9 Java sources to .../out/geo/compile.dest/classes ...
compiling 2 Java sources to .../out/geo-mem/compile.dest/classes ...
compiling 10 Java sources to .../out/geo/test/compile.dest/classes ...
done compiling
compiling 1 Java source to .../out/geo-mem/test/compile.dest/classes ...
done compiling

> ./mill __.test # all tests pass immediately
Test run com.github.davidmoten.geo.GeoHashTest finished: 0 failed, 0 ignored, 66 total, ...
Test run com.github.davidmoten.geo.DirectionTest finished: 0 failed, 0 ignored, 1 total, ...
...

*/
24 changes: 24 additions & 0 deletions example/javalib/migrating/2-maven-incomplete/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/** Usage

> rm build.mill # remove any existing build file

> git init .
> git remote add -f origin https://github.com/dhatim/fastexcel.git
> git checkout 0.18.4
> # pom.xml has custom profiles for JPMS that mill init does not support
> # https://github.com/dhatim/fastexcel/blob/de56e786a1fe29351e2f8dc1d81b7cdd9196de4a/pom.xml#L251

> ./mill init
converting Maven build
writing Mill build file to fastexcel-writer/package.mill
writing Mill build file to fastexcel-reader/package.mill
writing Mill build file to e2e/package.mill
writing Mill build file to build.mill
init completed, run "mill resolve _" to list available tasks

> ./mill -k __.compile # Compilation needs manual tweaking to pass
error: ...fastexcel-reader/src/main/java/module-info.java:3:32: module not found: org.apache.commons.compress
error: ...fastexcel-reader/src/main/java/module-info.java:4:27: module not found: com.fasterxml.aalto
error: ...fastexcel-writer/src/main/java/module-info.java:2:14: module not found: opczip

*/
1 change: 1 addition & 0 deletions example/package.mill
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ object `package` extends RootModule with Module {
object dependencies extends Cross[ExampleCrossModuleJava](build.listIn(millSourcePath / "dependencies"))
object testing extends Cross[ExampleCrossModuleJava](build.listIn(millSourcePath / "testing"))
object linting extends Cross[ExampleCrossModuleJava](build.listIn(millSourcePath / "linting"))
object migrating extends Cross[ExampleCrossModuleJava](build.listIn(millSourcePath / "migrating"))
object publishing extends Cross[ExampleCrossModuleJava](build.listIn(millSourcePath / "publishing"))
object web extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "web"))
}
Expand Down
Loading

0 comments on commit 3b94c5d

Please sign in to comment.