-
-
Notifications
You must be signed in to change notification settings - Fork 422
Extended mill init to support an existing Maven project
#3756
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
Merged
Merged
Changes from 12 commits
Commits
Show all changes
71 commits
Select commit
Hold shift + click to select a range
ab80850
Extended `mill init` to support an existing Maven project
ajaychandran 9cfa813
Renamed variable
ajaychandran 6b1d573
Added magic import to generated build.mill
ajaychandran 0e1dbe6
Added provided dependencies to compileIvyDeps
ajaychandran 6c4ad41
Extended `mill init` to support an existing Maven project
ajaychandran b60147e
Renamed variable
ajaychandran db77ae6
Added magic import to generated build.mill
ajaychandran e28b83f
Added provided dependencies to compileIvyDeps
ajaychandran 6fc9eb2
Merge remote-tracking branch 'origin/main'
ajaychandran 229e95c
Added magic import to resolve base module in package build files
ajaychandran 4b7948c
Fix test
ajaychandran 6e7061e
Refactored out code generation
ajaychandran 76e4bd5
Merge branch 'com-lihaoyi:main' into main
ajaychandran b784532
Merge branch 'com-lihaoyi:main' into main
ajaychandran d16915f
Merge branch 'com-lihaoyi:main' into main
ajaychandran 3b5d140
Merge branch 'com-lihaoyi:main' into main
ajaychandran 69273e8
Merge branch 'com-lihaoyi:main' into main
ajaychandran 3658abb
- Addressed review comments
ajaychandran 50f87b9
Renamed exception
ajaychandran 70e4614
Fixed formatting
ajaychandran 84098b7
Renamed symbolic operators
ajaychandran a71c46f
Added test case for avaje-config
ajaychandran 34ac1e0
Added location of compile errors
ajaychandran dab05d0
Separated variable declarations
ajaychandran 1d6e756
Changed implicit parameters to arguments.
ajaychandran acae1a8
Cleaned folder names
ajaychandran d5778a7
Cleaned object names
ajaychandran dd41e49
Merge branch 'com-lihaoyi:main' into main
ajaychandran 52a11a0
Replaced arrow assertions with assert
ajaychandran f092903
Format build files generated in tests
ajaychandran 50fe2ac
Removed support for build-helper-maven-plugin
ajaychandran 86501ff
Aligned inheritance scheme with existing capabilities
ajaychandran 4785683
Merge branch 'com-lihaoyi:main' into main
ajaychandran 7b15c28
Added test class for owner project and cleaned existing tests
ajaychandran b0d45da
Reordered parameters
ajaychandran 17d8e92
Improved generator algorithm
ajaychandran 0bb1824
Annotated compile tasks
ajaychandran d641e2a
Replaced System.lineSeparator()
ajaychandran 65ddee1
Renamed method and updated Scaladoc
ajaychandran 5622b78
Simplified build IR
ajaychandran 76b21c6
Added feature flag to compact build tree
ajaychandran ee93973
Updated comments for JPMS errors
ajaychandran 4df7e8d
Added documentation with example
ajaychandran e3aa816
Refactored integration tests
ajaychandran 8a229d8
Removed compile tasks for "pom" modules
ajaychandran ff40e9e
Merge branch 'com-lihaoyi:main' into main
ajaychandran 46cd3b2
Improved integration tests
ajaychandran c3bcb72
Updated documentation
ajaychandran 2ab9b05
- Fix error due to use of /* in command
ajaychandran f0b487f
Updated test golden files
ajaychandran 75e704f
Compare lines instead of content to avoid file EOL differences
ajaychandran 7018dde
Merge remote-tracking branch 'upstream/main'
ajaychandran 895ffe2
Removed default parameter value
ajaychandran a01a8d2
Suppressed test assertion to avoid CI failure
ajaychandran fe6cc7b
Cleanup
ajaychandran b0839c8
Escape single backslash to handle Windows paths
ajaychandran e8683d9
Use raw interpolator for single backslashes
ajaychandran 88cdcf6
debug
lihaoyi fbffb2a
use pprint literalize
lihaoyi 0c56337
cleanup
lihaoyi e309e9b
docs
lihaoyi 3449411
docs
lihaoyi 0d2ddc0
fixtests
lihaoyi 639c2a4
docs
lihaoyi 6010bce
docs
lihaoyi 219cef4
docs
lihaoyi ef66ca9
docs
lihaoyi 9cb7be5
.
lihaoyi 47a71fc
.
lihaoyi c08d6d0
.
lihaoyi a0a0d2c
.
lihaoyi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| package mill.integration | ||
|
|
||
| import mill.testkit.UtestIntegrationTestSuite | ||
| import utest._ | ||
|
|
||
| object MillInitMavenTests extends UtestIntegrationTestSuite { | ||
|
|
||
| private def downloadExtractTo(workspace: os.Path, zipUrl: String, zipContentDir: String): Unit = { | ||
| val zipFile = os.temp(requests.get(zipUrl)) | ||
| val contentDir = os.unzip(zipFile, os.temp.dir()) / zipContentDir | ||
| os.list(contentDir).foreach(os.move.into(_, workspace)) | ||
| } | ||
|
|
||
| def tests: Tests = Tests { | ||
|
|
||
| test("Mill init imports a Maven project") - integrationTest { tester => | ||
| import tester._ | ||
|
|
||
| downloadExtractTo( | ||
| workspacePath, | ||
| // - uses Junit5 | ||
| // - uses --release javac option | ||
| "https://github.com/fusesource/jansi/archive/refs/tags/jansi-2.4.1.zip", | ||
| "jansi-jansi-2.4.1" | ||
| ) | ||
|
|
||
| val initRes = eval("init") | ||
| initRes.isSuccess ==> true | ||
|
|
||
| val resolveRes = eval(("resolve", "_")) | ||
| Seq( | ||
| "compile", | ||
| "test", | ||
| "publishLocal" | ||
| ).forall(resolveRes.out.contains) ==> true | ||
|
|
||
| val compileRes = eval("compile") | ||
| compileRes.isSuccess ==> true | ||
|
|
||
| val testRes = eval("test") | ||
| testRes.isSuccess ==> true | ||
| testRes.out.contains( | ||
| "Test run finished: 0 failed, 1 ignored, 90 total" | ||
| ) ==> true | ||
|
|
||
| val publishLocalRes = eval("publishLocal") | ||
| publishLocalRes.isSuccess ==> true | ||
| publishLocalRes.err.contains( // why is this in err? | ||
| "Publishing Artifact(FuseSource, Corp.,jansi,2.4.1)") ==> true | ||
| } | ||
|
|
||
| test("Mill init imports a multi-module Maven project") - integrationTest { tester => | ||
| import tester._ | ||
|
|
||
| downloadExtractTo( | ||
| workspacePath, | ||
| // - uses unsupported test framework | ||
| // - uses hyphens in module names | ||
| "https://github.com/avaje/avaje-config/archive/refs/tags/4.0.zip", | ||
| "avaje-config-4.0" | ||
| ) | ||
|
|
||
| val initRes = eval("init") | ||
| initRes.isSuccess ==> true | ||
|
|
||
| val resolveRes = eval(("resolve", "_")) | ||
| resolveRes.isSuccess ==> true | ||
| Seq( | ||
| "avaje-config", | ||
| "avaje-aws-appconfig", | ||
| "avaje-dynamic-logback" | ||
| ).forall(resolveRes.out.contains) ==> true | ||
|
|
||
| val compileRes = eval("avaje-config.compile") | ||
| // probably a JPMS issue but we only care about package task resolution | ||
| compileRes.err.contains( | ||
| "src/main/java/module-info.java:5:31: module not found: io.avaje.lang" | ||
| ) ==> true | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package mill.init.maven | ||
|
|
||
| import mill.define.{Discover, ExternalModule, TaskModule} | ||
| import mill.{Command, Module, T, Task} | ||
|
|
||
| object InitModule extends ExternalModule with InitModule with TaskModule { | ||
|
|
||
| override def defaultCommandName(): String = "init" | ||
|
|
||
| lazy val millDiscover: Discover = Discover[this.type] | ||
| } | ||
|
|
||
| /** | ||
| * Provides a [[InitModule.init task]] to generate Mill build files for an existing Maven project. | ||
| */ | ||
| trait InitModule extends Module { | ||
|
|
||
| /** | ||
| * Converts a Maven build to Mill automatically. | ||
| * | ||
| * @note The conversion may be incomplete, requiring manual edits to generated Mill build file(s). | ||
| */ | ||
| def init( | ||
| @mainargs.arg(doc = "build base module (with shared settings) name") | ||
| baseModule: String = "BaseMavenModule" | ||
| ): Command[Unit] = Task.Command { | ||
|
|
||
| val buildFiles = codegen(millSourcePath, baseModule, PomReader()) | ||
|
|
||
| T.log.info(s"generated ${buildFiles.size} Mill build file(s)") | ||
|
|
||
| buildFiles.foreach { | ||
| case (source, file) => | ||
| T.log.info(s"writing build file $file ...") | ||
| os.write(file, source) | ||
| } | ||
|
|
||
| T.log.info(s"converted Maven project to Mill") | ||
| T.log.info(s"run \"mill resolve _\" to (verify conversion and) list available tasks") | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| package mill.init.maven | ||
|
|
||
| import org.apache.maven.model.building.* | ||
| import org.apache.maven.model.resolution.{ModelResolver, UnresolvableModelException} | ||
| import org.apache.maven.model.{Dependency, Model, Parent, Repository} | ||
| import org.apache.maven.repository.internal.{ArtifactDescriptorUtils, MavenRepositorySystemUtils} | ||
| import org.eclipse.aether.artifact.DefaultArtifact | ||
| import org.eclipse.aether.repository.{LocalRepository, RemoteRepository} | ||
| import org.eclipse.aether.resolution.{ArtifactRequest, ArtifactResolutionException} | ||
| import org.eclipse.aether.supplier.RepositorySystemSupplier | ||
| import org.eclipse.aether.{RepositorySystem, RepositorySystemSession} | ||
|
|
||
| import java.io.File | ||
| import java.util.Properties | ||
| import scala.jdk.CollectionConverters.* | ||
|
|
||
| /** | ||
| * Parses a POM file to generate a [[Model]]. | ||
| * | ||
| * The implementation is inspired by [[https://github.com/sbt/sbt-pom-reader/ sbt-pom-reader]]. | ||
| */ | ||
| private[maven] class PomReader( | ||
| builder: ModelBuilder, | ||
| resolver: ModelResolver, | ||
| systemProperties: Properties | ||
| ) { | ||
|
|
||
| /** Returns the effective POM [[Model]] from a `pom.xml` file under `pomDir`. */ | ||
| def apply(pomDir: os.Path): Model = | ||
| read((pomDir / "pom.xml").toIO) | ||
|
|
||
| /** Returns the effective POM [[Model]] from `pomFile` */ | ||
| def read(pomFile: File): Model = { | ||
| val request = new DefaultModelBuildingRequest() | ||
| request.setModelResolver(resolver) | ||
| request.setSystemProperties(systemProperties) | ||
| request.setPomFile(pomFile) | ||
|
|
||
| builder.build(request).getEffectiveModel | ||
| } | ||
| } | ||
| object PomReader { | ||
|
|
||
| def apply( | ||
| local: LocalRepository = defaultLocalRepository, | ||
| remotes: Seq[RemoteRepository] = defaultRemoteRepositories, | ||
| context: String = "", | ||
| systemProperties: Properties = defaultSystemProperties | ||
| ): PomReader = { | ||
| val builder = new DefaultModelBuilderFactory().newInstance() | ||
| val system = new RepositorySystemSupplier().get() | ||
| val session = MavenRepositorySystemUtils.newSession() | ||
| session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, local)) | ||
| new PomReader(builder, resolver(system, session, remotes, context), systemProperties) | ||
| } | ||
|
|
||
| private def defaultLocalRepository: LocalRepository = | ||
| new LocalRepository((os.home / ".m2" / "repository").toIO) | ||
|
|
||
| private def defaultRemoteRepositories: Seq[RemoteRepository] = | ||
| Seq( | ||
| new RemoteRepository.Builder("central", "default", "https://repo.maven.apache.org/maven2") | ||
| .build() | ||
| ) | ||
|
|
||
| private def defaultSystemProperties: Properties = { | ||
| val props = new Properties() | ||
| sys.env.foreachEntry((k, v) => props.put(s"env.$k", v)) | ||
| sys.props.foreachEntry(props.put) | ||
| props | ||
| } | ||
|
|
||
| private def resolver( | ||
| system: RepositorySystem, | ||
| session: RepositorySystemSession, | ||
| remotes: Seq[RemoteRepository], | ||
| context: String | ||
| ): ModelResolver = | ||
| new ModelResolver { | ||
ajaychandran marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| private[this] var repositories = remotes | ||
| override def resolveModel( | ||
| groupId: String, | ||
| artifactId: String, | ||
| version: String | ||
| ): ModelSource = { | ||
| val artifact = new DefaultArtifact(groupId, artifactId, "", "pom", version) | ||
| val request = new ArtifactRequest(artifact, repositories.asJava, context) | ||
| try { | ||
| val result = system.resolveArtifact(session, request) | ||
| new FileModelSource(result.getArtifact.getFile) | ||
| } catch { | ||
| case e: ArtifactResolutionException => | ||
| throw new UnresolvableModelException(e.getMessage, groupId, artifactId, version, e) | ||
| } | ||
| } | ||
|
|
||
| override def resolveModel(parent: Parent): ModelSource = | ||
| resolveModel(parent.getGroupId, parent.getArtifactId, parent.getVersion) | ||
|
|
||
| override def resolveModel(dependency: Dependency): ModelSource = | ||
| resolveModel(dependency.getGroupId, dependency.getArtifactId, dependency.getVersion) | ||
|
|
||
| override def addRepository(repository: Repository): Unit = | ||
| addRepository(repository, replace = false) | ||
|
|
||
| override def addRepository(repository: Repository, replace: Boolean): Unit = { | ||
| val exists = repositories.exists(_.getId == repository.getId) | ||
| if (!exists || replace) { | ||
| repositories = repositories :+ ArtifactDescriptorUtils.toRemoteRepository(repository) | ||
| } | ||
| } | ||
|
|
||
| override def newCopy(): ModelResolver = | ||
| resolver(system, session, repositories, context) | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.