From 00d0ff145b4a9efca6b7e74f9494a80b8675137f Mon Sep 17 00:00:00 2001 From: Alex Archambault Date: Fri, 3 Oct 2025 13:00:38 +0200 Subject: [PATCH 1/3] Run examples with local repo under out/ rather than ~/.ivy2/local --- dist/package.mill | 86 +++++++++++++++++++++++++++++++++------- integration/package.mill | 5 ++- 2 files changed, 75 insertions(+), 16 deletions(-) diff --git a/dist/package.mill b/dist/package.mill index 0ee1ca34cebc..e118f95872a4 100644 --- a/dist/package.mill +++ b/dist/package.mill @@ -9,6 +9,9 @@ import mill.util.Jvm import millbuild.* import mill.api.BuildCtx import scala.util.Using +import scala.util.Properties +import java.nio.file.Files +import java.nio.file.attribute.PosixFilePermission trait DistModule extends Module { // All modules that we want to aggregate as part of this `dev` assembly. @@ -19,9 +22,49 @@ trait DistModule extends Module { def executableRaw: T[PathRef] + def localRepo: T[PathRef] = Task { + val dest = Task.dest + val repos = Task.traverse(allPublishModules)(m => m.publishLocalTestRepo)().map(_.path) + for (repo <- repos; elem <- os.list(repo)) + os.copy.into(elem, dest, mergeFolders = true) + PathRef(dest) + } + def executable = Task { - Task.traverse(allPublishModules)(m => m.publishLocal(doc = false))() - executableRaw() + val rawExecutable = executableRaw() + if (Properties.isWin) { + val launcher = Task.dest / "mill.bat" + val launcherContent = + s"""@echo off + |set "NEW_COURSIER_REPOSITORIES=${localRepo().path.toNIO.toUri.toASCIIString}|ivy2Local|central" + |if defined COURSIER_REPOSITORIES ( + | set "NEW_COURSIER_REPOSITORIES=%NEW_COURSIER_REPOSITORIES%|%COURSIER_REPOSITORIES%" + |) else ( + | set "NEW_COURSIER_REPOSITORIES=%NEW_COURSIER_REPOSITORIES%|ivy2Local|central" + |) + |set "COURSIER_REPOSITORIES=%NEW_COURSIER_REPOSITORIES%" + |set NEW_COURSIER_REPOSITORIES= + |"${rawExecutable.path.toString.replace("\"", "\\\"")}" %* + |if errorlevel 1 exit /b %errorlevel% + |""".stripMargin + os.write(launcher, launcherContent) + PathRef(launcher) + } else { + val launcher = Task.dest / "mill" + val launcherContent = + s"""#!/usr/bin/env bash + |set -e + |export COURSIER_REPOSITORIES="${localRepo().path.toNIO.toUri.toASCIIString}|$${COURSIER_REPOSITORIES:-ivy2Local|central}" + |exec '${rawExecutable.path.toString.replace("'", "\\'")}' "$$@" + |""".stripMargin + os.write(launcher, launcherContent) + val perms = Files.getPosixFilePermissions(launcher.toNIO) + perms.add(PosixFilePermission.OWNER_EXECUTE) + perms.add(PosixFilePermission.GROUP_EXECUTE) + perms.add(PosixFilePermission.OTHERS_EXECUTE) + Files.setPosixFilePermissions(launcher.toNIO, perms) + PathRef(launcher) + } } def localBinName: String @@ -32,11 +75,10 @@ trait DistModule extends Module { * Build and install Mill locally. * * @param binFile The location where the Mill binary should be installed - * @param ivyRepo The local Ivy repository where Mill modules should be published to */ - def installLocal(binFile: String = localBinName, ivyRepo: String = null) = + def installLocal(binFile: String = localBinName) = Task.Command { - PathRef(installLocalTask(Task.Anon(binFile), ivyRepo)()) + PathRef(installLocalTask(Task.Anon(binFile), globalInstall = true)()) } val batExt = if (scala.util.Properties.isWin) ".bat" else "" @@ -53,16 +95,30 @@ trait DistModule extends Module { PathRef(path) } - def installLocalTask(binFile: Task[String], ivyRepo: String = null): Task[os.Path] = Task.Anon { - val targetFile = os.Path(binFile(), BuildCtx.workspaceRoot) - if (os.exists(targetFile)) - Task.log.info(s"Overwriting existing local Mill binary at ${targetFile}") - os.copy.over(executable().path, targetFile, createFolders = true) - Task.log.info( - s"Published ${build.dist.allPublishModules.size} modules and installed ${targetFile}" - ) - targetFile - } + def installLocalTask(binFile: Task[String], globalInstall: Boolean = false): Task[os.Path] = + if (globalInstall) + Task.Anon { + val targetFile = os.Path(binFile(), BuildCtx.workspaceRoot) + if (os.exists(targetFile)) + Task.log.info(s"Overwriting existing local Mill binary at ${targetFile}") + Task.traverse(allPublishModules)(m => m.publishLocal(doc = false))() + os.copy.over(executableRaw().path, targetFile, createFolders = true) + Task.log.info( + s"Published ${build.dist.allPublishModules.size} modules globally and installed ${targetFile}" + ) + targetFile + } + else + Task.Anon { + val targetFile = os.Path(binFile(), BuildCtx.workspaceRoot) + if (os.exists(targetFile)) + Task.log.info(s"Overwriting existing local Mill binary at ${targetFile}") + os.copy.over(executable().path, targetFile, createFolders = true) + Task.log.info( + s"Published ${build.dist.allPublishModules.size} modules under local repo and installed ${targetFile}" + ) + targetFile + } def artifactName: T[String] def artifact = Task { Artifact(Settings.pomOrg, artifactName(), build.millVersion()) } diff --git a/integration/package.mill b/integration/package.mill index 016fb9f01bf1..83047da8a0e9 100644 --- a/integration/package.mill +++ b/integration/package.mill @@ -168,7 +168,10 @@ object `package` extends mill.Module { def testMill: T[PathRef] = { val name = if (scala.util.Properties.isWin) "mill.bat" else "mill" Task { - PathRef(build.dist.installLocalTask(binFile = Task.Anon((Task.dest / name).toString()))()) + val executable = build.dist.executable().path + val dest = Task.dest / name + os.copy.over(executable, dest) + PathRef(dest) } } From f3b7e7ed92bc16c3629474f307a37935bff079a7 Mon Sep 17 00:00:00 2001 From: Alex Archambault Date: Fri, 3 Oct 2025 16:42:16 +0200 Subject: [PATCH 2/3] Update some test data --- .../build-targets-jvm-test-environments.json | 6 +- .../src/BuildClasspathContentsTests.scala | 49 +++++++------- .../extended/idea/mill_modules/mill-build.iml | 62 ++++++++--------- .../mill_modules/mill-build.mill-build.iml | 66 +++++++++---------- .../idea/mill_modules/mill-build.iml | 62 ++++++++--------- 5 files changed, 123 insertions(+), 122 deletions(-) diff --git a/integration/ide/bsp-server/resources/snapshots/build-targets-jvm-test-environments.json b/integration/ide/bsp-server/resources/snapshots/build-targets-jvm-test-environments.json index 555076f46b88..c14bf2eee68f 100644 --- a/integration/ide/bsp-server/resources/snapshots/build-targets-jvm-test-environments.json +++ b/integration/ide/bsp-server/resources/snapshots/build-targets-jvm-test-environments.json @@ -18,7 +18,7 @@ "file:///workspace/app/test/compile-resources", "file:///workspace/app/test/resources", "file:///workspace/out/mill-bsp-out/app/test/compile.dest/classes", - "file:///user-home/.ivy2/local/com.lihaoyi/mill-libs-javalib-testrunner-entrypoint/SNAPSHOT/jars/mill-libs-javalib-testrunner-entrypoint.jar", + "file:///mill-workspace/out/dist/localRepo.dest/com/lihaoyi/mill-libs-javalib-testrunner-entrypoint/SNAPSHOT/mill-libs-javalib-testrunner-entrypoint-SNAPSHOT.jar", "file:///coursier-cache/https/repo1.maven.org/maven2/org/scala-sbt/test-interface/1.0/test-interface-1.0.jar" ], "jvmOptions": [], @@ -52,7 +52,7 @@ "file:///workspace/hello-java/test/compile-resources", "file:///workspace/hello-java/test/resources", "file:///workspace/out/mill-bsp-out/hello-java/test/compile.dest/classes", - "file:///user-home/.ivy2/local/com.lihaoyi/mill-libs-javalib-testrunner-entrypoint/SNAPSHOT/jars/mill-libs-javalib-testrunner-entrypoint.jar", + "file:///mill-workspace/out/dist/localRepo.dest/com/lihaoyi/mill-libs-javalib-testrunner-entrypoint/SNAPSHOT/mill-libs-javalib-testrunner-entrypoint-SNAPSHOT.jar", "file:///coursier-cache/https/repo1.maven.org/maven2/org/scala-sbt/test-interface/1.0/test-interface-1.0.jar" ], "jvmOptions": [], @@ -87,7 +87,7 @@ "file:///workspace/hello-scala/test/compile-resources", "file:///workspace/hello-scala/test/resources", "file:///workspace/out/mill-bsp-out/hello-scala/test/compile.dest/classes", - "file:///user-home/.ivy2/local/com.lihaoyi/mill-libs-javalib-testrunner-entrypoint/SNAPSHOT/jars/mill-libs-javalib-testrunner-entrypoint.jar", + "file:///mill-workspace/out/dist/localRepo.dest/com/lihaoyi/mill-libs-javalib-testrunner-entrypoint/SNAPSHOT/mill-libs-javalib-testrunner-entrypoint-SNAPSHOT.jar", "file:///coursier-cache/https/repo1.maven.org/maven2/org/scala-sbt/test-interface/1.0/test-interface-1.0.jar" ], "jvmOptions": [], diff --git a/integration/ide/build-classpath-contents/src/BuildClasspathContentsTests.scala b/integration/ide/build-classpath-contents/src/BuildClasspathContentsTests.scala index 637a1be3733e..48c597bff282 100644 --- a/integration/ide/build-classpath-contents/src/BuildClasspathContentsTests.scala +++ b/integration/ide/build-classpath-contents/src/BuildClasspathContentsTests.scala @@ -18,36 +18,37 @@ object BuildClasspathContentsTests extends UtestIntegrationTestSuite { .filter(_.startsWith(BuildCtx.workspaceRoot)) .map(_.subRelativeTo(BuildCtx.workspaceRoot)) .filter(!_.startsWith("out/integration")) + .filter(!_.startsWith("out/dist/localRepo.dest")) .map(_.toString) .sorted if (sys.env("MILL_INTEGRATION_IS_PACKAGED_LAUNCHER") == "true") { assertGoldenLiteral( millPublishedJars, List( - "mill-core-api-daemon_3.jar", - "mill-core-api_3.jar", - "mill-core-constants.jar", - "mill-libs-androidlib-databinding_3.jar", - "mill-libs-androidlib_3.jar", - "mill-libs-daemon-client.jar", - "mill-libs-daemon-server_3.jar", - "mill-libs-javalib-api_3.jar", - "mill-libs-javalib-testrunner-entrypoint.jar", - "mill-libs-javalib-testrunner_3.jar", - "mill-libs-javalib_3.jar", - "mill-libs-javascriptlib_3.jar", - "mill-libs-kotlinlib-api_3.jar", - "mill-libs-kotlinlib-ksp2-api_3.jar", - "mill-libs-kotlinlib_3.jar", - "mill-libs-pythonlib_3.jar", - "mill-libs-rpc_3.jar", - "mill-libs-scalajslib-api_3.jar", - "mill-libs-scalajslib_3.jar", - "mill-libs-scalalib_3.jar", - "mill-libs-scalanativelib-api_3.jar", - "mill-libs-scalanativelib_3.jar", - "mill-libs-util_3.jar", - "mill-libs_3.jar", + "mill-core-api-daemon_3-SNAPSHOT.jar", + "mill-core-api_3-SNAPSHOT.jar", + "mill-core-constants-SNAPSHOT.jar", + "mill-libs-androidlib-databinding_3-SNAPSHOT.jar", + "mill-libs-androidlib_3-SNAPSHOT.jar", + "mill-libs-daemon-client-SNAPSHOT.jar", + "mill-libs-daemon-server_3-SNAPSHOT.jar", + "mill-libs-javalib-api_3-SNAPSHOT.jar", + "mill-libs-javalib-testrunner-entrypoint-SNAPSHOT.jar", + "mill-libs-javalib-testrunner_3-SNAPSHOT.jar", + "mill-libs-javalib_3-SNAPSHOT.jar", + "mill-libs-javascriptlib_3-SNAPSHOT.jar", + "mill-libs-kotlinlib-api_3-SNAPSHOT.jar", + "mill-libs-kotlinlib-ksp2-api_3-SNAPSHOT.jar", + "mill-libs-kotlinlib_3-SNAPSHOT.jar", + "mill-libs-pythonlib_3-SNAPSHOT.jar", + "mill-libs-rpc_3-SNAPSHOT.jar", + "mill-libs-scalajslib-api_3-SNAPSHOT.jar", + "mill-libs-scalajslib_3-SNAPSHOT.jar", + "mill-libs-scalalib_3-SNAPSHOT.jar", + "mill-libs-scalanativelib-api_3-SNAPSHOT.jar", + "mill-libs-scalanativelib_3-SNAPSHOT.jar", + "mill-libs-util_3-SNAPSHOT.jar", + "mill-libs_3-SNAPSHOT.jar", "mill-moduledefs_3-0.11.10.jar" ) ) diff --git a/integration/ide/gen-idea/resources/extended/idea/mill_modules/mill-build.iml b/integration/ide/gen-idea/resources/extended/idea/mill_modules/mill-build.iml index 9d0eecca65c3..3c45f9a4957b 100644 --- a/integration/ide/gen-idea/resources/extended/idea/mill_modules/mill-build.iml +++ b/integration/ide/gen-idea/resources/extended/idea/mill_modules/mill-build.iml @@ -53,37 +53,37 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/integration/ide/gen-idea/resources/extended/idea/mill_modules/mill-build.mill-build.iml b/integration/ide/gen-idea/resources/extended/idea/mill_modules/mill-build.mill-build.iml index d310b590c1b6..6e750f3aa4f7 100644 --- a/integration/ide/gen-idea/resources/extended/idea/mill_modules/mill-build.mill-build.iml +++ b/integration/ide/gen-idea/resources/extended/idea/mill_modules/mill-build.mill-build.iml @@ -54,40 +54,40 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + diff --git a/integration/ide/gen-idea/resources/hello-idea/idea/mill_modules/mill-build.iml b/integration/ide/gen-idea/resources/hello-idea/idea/mill_modules/mill-build.iml index f7e2278d9d1d..80c356fbf0da 100644 --- a/integration/ide/gen-idea/resources/hello-idea/idea/mill_modules/mill-build.iml +++ b/integration/ide/gen-idea/resources/hello-idea/idea/mill_modules/mill-build.iml @@ -50,37 +50,37 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From eb838fa33356e49336e4fd628c32baf9b124726e Mon Sep 17 00:00:00 2001 From: Alex Archambault Date: Mon, 13 Oct 2025 12:12:10 +0200 Subject: [PATCH 3/3] Address review comment --- dist/package.mill | 54 ++++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/dist/package.mill b/dist/package.mill index e118f95872a4..0bdbcb133e18 100644 --- a/dist/package.mill +++ b/dist/package.mill @@ -76,10 +76,14 @@ trait DistModule extends Module { * * @param binFile The location where the Mill binary should be installed */ - def installLocal(binFile: String = localBinName) = + def installLocal(binFile: String = localBinName) = { + val binFile0 = os.Path(binFile, BuildCtx.workspaceRoot) + val task = installIvyLocalTask(binFile0) Task.Command { - PathRef(installLocalTask(Task.Anon(binFile), globalInstall = true)()) + task() + PathRef(binFile0) } + } val batExt = if (scala.util.Properties.isWin) ".bat" else "" @@ -95,30 +99,28 @@ trait DistModule extends Module { PathRef(path) } - def installLocalTask(binFile: Task[String], globalInstall: Boolean = false): Task[os.Path] = - if (globalInstall) - Task.Anon { - val targetFile = os.Path(binFile(), BuildCtx.workspaceRoot) - if (os.exists(targetFile)) - Task.log.info(s"Overwriting existing local Mill binary at ${targetFile}") - Task.traverse(allPublishModules)(m => m.publishLocal(doc = false))() - os.copy.over(executableRaw().path, targetFile, createFolders = true) - Task.log.info( - s"Published ${build.dist.allPublishModules.size} modules globally and installed ${targetFile}" - ) - targetFile - } - else - Task.Anon { - val targetFile = os.Path(binFile(), BuildCtx.workspaceRoot) - if (os.exists(targetFile)) - Task.log.info(s"Overwriting existing local Mill binary at ${targetFile}") - os.copy.over(executable().path, targetFile, createFolders = true) - Task.log.info( - s"Published ${build.dist.allPublishModules.size} modules under local repo and installed ${targetFile}" - ) - targetFile - } + def installLocalTask(binFile: Task[String]): Task[os.Path] = + Task.Anon { + val targetFile = os.Path(binFile(), BuildCtx.workspaceRoot) + if (os.exists(targetFile)) + Task.log.info(s"Overwriting existing local Mill binary at ${targetFile}") + os.copy.over(executable().path, targetFile, createFolders = true) + Task.log.info( + s"Published ${build.dist.allPublishModules.size} modules under Mill sources local repo ${localRepo().path} and installed ${targetFile}" + ) + targetFile + } + + def installIvyLocalTask(targetFile: os.Path): Task[Unit] = + Task.Anon { + if (os.exists(targetFile)) + Task.log.info(s"Overwriting existing local Mill binary at ${targetFile}") + Task.traverse(allPublishModules)(m => m.publishLocal(doc = false))() + os.copy.over(executableRaw().path, targetFile, createFolders = true) + Task.log.info( + s"Published ${build.dist.allPublishModules.size} modules under ~/.ivy2/local and installed ${targetFile}" + ) + } def artifactName: T[String] def artifact = Task { Artifact(Settings.pomOrg, artifactName(), build.millVersion()) }