@@ -9,6 +9,9 @@ import mill.util.Jvm
99import millbuild.*
1010import mill.api.BuildCtx
1111import scala.util.Using
12+ import scala.util.Properties
13+ import java.nio.file.Files
14+ import java.nio.file.attribute.PosixFilePermission
1215
1316trait DistModule extends Module {
1417 // All modules that we want to aggregate as part of this `dev` assembly.
@@ -19,9 +22,49 @@ trait DistModule extends Module {
1922
2023 def executableRaw: T[PathRef]
2124
25+ def localRepo: T[PathRef] = Task {
26+ val dest = Task.dest
27+ val repos = Task.traverse(allPublishModules)(m => m.publishLocalTestRepo)().map(_.path)
28+ for (repo <- repos; elem <- os.list(repo))
29+ os.copy.into(elem, dest, mergeFolders = true)
30+ PathRef(dest)
31+ }
32+
2233 def executable = Task {
23- Task.traverse(allPublishModules)(m => m.publishLocal(doc = false))()
24- executableRaw()
34+ val rawExecutable = executableRaw()
35+ if (Properties.isWin) {
36+ val launcher = Task.dest / "mill.bat"
37+ val launcherContent =
38+ s"""@echo off
39+ |set "NEW_COURSIER_REPOSITORIES=${localRepo().path.toNIO.toUri.toASCIIString}|ivy2Local|central"
40+ |if defined COURSIER_REPOSITORIES (
41+ | set "NEW_COURSIER_REPOSITORIES=%NEW_COURSIER_REPOSITORIES%|%COURSIER_REPOSITORIES%"
42+ |) else (
43+ | set "NEW_COURSIER_REPOSITORIES=%NEW_COURSIER_REPOSITORIES%|ivy2Local|central"
44+ |)
45+ |set "COURSIER_REPOSITORIES=%NEW_COURSIER_REPOSITORIES%"
46+ |set NEW_COURSIER_REPOSITORIES=
47+ |"${rawExecutable.path.toString.replace("\"", "\\\"")}" %*
48+ |if errorlevel 1 exit /b %errorlevel%
49+ |""".stripMargin
50+ os.write(launcher, launcherContent)
51+ PathRef(launcher)
52+ } else {
53+ val launcher = Task.dest / "mill"
54+ val launcherContent =
55+ s"""#!/usr/bin/env bash
56+ |set -e
57+ |export COURSIER_REPOSITORIES="${localRepo().path.toNIO.toUri.toASCIIString}|$${COURSIER_REPOSITORIES:-ivy2Local|central}"
58+ |exec '${rawExecutable.path.toString.replace("'", "\\'")}' "$$@"
59+ |""".stripMargin
60+ os.write(launcher, launcherContent)
61+ val perms = Files.getPosixFilePermissions(launcher.toNIO)
62+ perms.add(PosixFilePermission.OWNER_EXECUTE)
63+ perms.add(PosixFilePermission.GROUP_EXECUTE)
64+ perms.add(PosixFilePermission.OTHERS_EXECUTE)
65+ Files.setPosixFilePermissions(launcher.toNIO, perms)
66+ PathRef(launcher)
67+ }
2568 }
2669
2770 def localBinName: String
@@ -32,38 +75,61 @@ trait DistModule extends Module {
3275 * Build and install Mill locally.
3376 *
3477 * @param binFile The location where the Mill binary should be installed
35- * @param ivyRepo The local Ivy repository where Mill modules should be published to
3678 */
37- def installLocal(binFile: String = localBinName, ivyRepo: String = null) =
38- Task.Command {
39- PathRef(installLocalTask(Task.Anon(binFile), ivyRepo)())
79+ def installLocal(binFile: String = localBinName) = {
80+ val binFile0 = os.Path(binFile, BuildCtx.workspaceRoot)
81+ Task.Command[Unit] {
82+ installIvyLocalTask(Task.Anon(binFile0))()
4083 }
84+ }
4185
4286 val batExt = if (scala.util.Properties.isWin) ".bat" else ""
4387
44- def installLocalCachePath(suffixTask: Task[String]) = Task.Anon {
45- (os.home / ".cache/mill/download" / (build.millVersion() + suffixTask() + batExt)).toString()
46- }
88+ def installLocalCache() = {
4789
48- def installLocalCache() = Task.Command {
49- val path = installLocalTask(installLocalCachePath(build.dist.cacheBinarySuffix))()
50- val path2 = installLocalTask(installLocalCachePath(build.dist.native.cacheBinarySuffix))()
90+ def launcherPathTask(suffix: Task[String]) =
91+ Task.Anon {
92+ os.home / ".cache/mill/download" / (build.millVersion() + suffix() + batExt)
93+ }
5194
52- Task.log.streams.out.println(path.toString())
53- PathRef(path)
54- }
95+ val jarLauncherPathTask = launcherPathTask(build.dist.cacheBinarySuffix)
96+ val nativeLauncherPathTask = launcherPathTask(build.dist.native.cacheBinarySuffix)
5597
56- def installLocalTask(binFile: Task[String], ivyRepo: String = null): Task[os.Path] = Task.Anon {
57- val targetFile = os.Path(binFile(), BuildCtx.workspaceRoot)
58- if (os.exists(targetFile))
59- Task.log.info(s"Overwriting existing local Mill binary at ${targetFile}")
60- os.copy.over(executable().path, targetFile, createFolders = true)
61- Task.log.info(
62- s"Published ${build.dist.allPublishModules.size} modules and installed ${targetFile}"
63- )
64- targetFile
98+ Task.Command {
99+ val Seq(jarLauncherPath, _*) =
100+ installIvyLocalTask(jarLauncherPathTask, nativeLauncherPathTask)()
101+ Task.log.streams.out.println(jarLauncherPath.path.toString())
102+ jarLauncherPath
103+ }
65104 }
66105
106+ def installLocalTask(binFile: Task[String]): Task[os.Path] =
107+ Task.Anon {
108+ val targetFile = os.Path(binFile(), BuildCtx.workspaceRoot)
109+ if (os.exists(targetFile))
110+ Task.log.info(s"Overwriting existing local Mill binary at ${targetFile}")
111+ os.copy.over(executable().path, targetFile, createFolders = true)
112+ Task.log.info(
113+ s"Published ${build.dist.allPublishModules.size} modules under Mill sources local repo ${localRepo().path} and installed ${targetFile}"
114+ )
115+ targetFile
116+ }
117+
118+ def installIvyLocalTask(targetFiles: Task[os.Path]*) =
119+ Task.Anon[Seq[PathRef]] {
120+ Task.traverse(allPublishModules)(m => m.publishLocal(doc = false))()
121+ val targetFiles0 = Task.sequence(targetFiles)()
122+ for (targetFile <- targetFiles0) {
123+ if (os.exists(targetFile))
124+ Task.log.info(s"Overwriting existing local Mill binary at $targetFile")
125+ os.copy.over(executableRaw().path, targetFile, createFolders = true)
126+ }
127+ Task.log.info(
128+ s"Published ${build.dist.allPublishModules.size} modules under ~/.ivy2/local and installed ${targetFiles0.mkString(", ")}"
129+ )
130+ targetFiles0.map(PathRef(_))
131+ }
132+
67133 def artifactName: T[String]
68134 def artifact = Task { Artifact(Settings.pomOrg, artifactName(), build.millVersion()) }
69135 def pomSettings = Task { MillPublishJavaModule.commonPomSettings(artifactName()) }
0 commit comments