|
| 1 | +//> using scala "2.13" |
| 2 | +//> using lib "com.lihaoyi::mill-scalalib:0.10.4" |
| 3 | +//> using option "-deprecation" |
| 4 | + |
| 5 | +package scala.cli.mill |
| 6 | + |
| 7 | +import coursier.cache.{ArchiveCache, FileCache} |
| 8 | +import coursier.cache.loggers.{FallbackRefreshDisplay, ProgressBarRefreshDisplay, RefreshLogger} |
| 9 | +import coursier.util.Artifact |
| 10 | +import mill._ |
| 11 | +import mill.scalalib.ScalaModule |
| 12 | +import mill.scalalib.api.CompilationResult |
| 13 | + |
| 14 | +import java.io.File |
| 15 | +import java.util.Locale |
| 16 | + |
| 17 | +import scala.util.Properties |
| 18 | + |
| 19 | +trait ScalaCliCompile extends ScalaModule { |
| 20 | + |
| 21 | + protected object ScalaCliInternal { |
| 22 | + // disable using scala-cli to build scala-cli on unsupported architectures |
| 23 | + lazy val isSupportedArch: Boolean = { |
| 24 | + val supportedArch = Seq("x86_64", "amd64") |
| 25 | + val osArch = sys.props("os.arch").toLowerCase(Locale.ROOT) |
| 26 | + supportedArch.exists(osArch.contains(_)) |
| 27 | + } |
| 28 | + |
| 29 | + lazy val compileScalaCliImpl = compileScalaCliUrl.map { url => |
| 30 | + val logger = RefreshLogger.create( |
| 31 | + if (coursier.paths.Util.useAnsiOutput()) |
| 32 | + ProgressBarRefreshDisplay.create() |
| 33 | + else |
| 34 | + new FallbackRefreshDisplay |
| 35 | + ) |
| 36 | + val cache = FileCache().withLogger(logger) |
| 37 | + val archiveCache = ArchiveCache() |
| 38 | + .withCache(cache) |
| 39 | + val artifact = Artifact(url).withChanging(compileScalaCliIsChanging) |
| 40 | + val file = archiveCache.get(artifact).unsafeRun()(cache.ec) match { |
| 41 | + case Left(e) => throw new Exception(e) |
| 42 | + case Right(f) => |
| 43 | + if (Properties.isWin) |
| 44 | + os.list(os.Path(f, os.pwd)).filter(_.last.endsWith(".exe")).headOption match { |
| 45 | + case None => sys.error(s"No .exe found under $f") |
| 46 | + case Some(exe) => exe |
| 47 | + } |
| 48 | + else { |
| 49 | + f.setExecutable(true) |
| 50 | + os.Path(f, os.pwd) |
| 51 | + } |
| 52 | + } |
| 53 | + PathRef(file) |
| 54 | + } |
| 55 | + } |
| 56 | + import ScalaCliInternal._ |
| 57 | + |
| 58 | + def enableScalaCli: Boolean = |
| 59 | + System.getenv("CI") == null && isSupportedArch |
| 60 | + def scalaCliVersion: String = |
| 61 | + "0.1.5" |
| 62 | + |
| 63 | + def compileScalaCliUrl: Option[String] = { |
| 64 | + val ver = scalaCliVersion |
| 65 | + if (Properties.isLinux) Some( |
| 66 | + s"https://github.com/VirtusLab/scala-cli/releases/download/v$ver/scala-cli-x86_64-pc-linux.gz" |
| 67 | + ) |
| 68 | + else if (Properties.isWin) Some( |
| 69 | + s"https://github.com/VirtusLab/scala-cli/releases/download/v$ver/scala-cli-x86_64-pc-win32.zip" |
| 70 | + ) |
| 71 | + else if (Properties.isMac) Some( |
| 72 | + s"https://github.com/VirtusLab/scala-cli/releases/download/v$ver/scala-cli-x86_64-apple-darwin.gz" |
| 73 | + ) |
| 74 | + else None |
| 75 | + } |
| 76 | + def compileScalaCliIsChanging: Boolean = false |
| 77 | + |
| 78 | + def compileScalaCli: Option[PathRef] = compileScalaCliImpl |
| 79 | + |
| 80 | + def extraScalaCliOptions: T[List[String]] = |
| 81 | + T { |
| 82 | + List.empty[String] |
| 83 | + } |
| 84 | + |
| 85 | + override def compile: T[CompilationResult] = |
| 86 | + if (enableScalaCli) |
| 87 | + compileScalaCli.map(_.path) match { |
| 88 | + case None => super.compile |
| 89 | + case Some(cli) => |
| 90 | + T.persistent { |
| 91 | + val out = os.pwd / ".scala-build" / ".unused" |
| 92 | + |
| 93 | + val sourceFiles = allSources() |
| 94 | + .map(_.path) |
| 95 | + .filter(os.exists(_)) |
| 96 | + val workspace = T.dest / "workspace" |
| 97 | + os.makeDir.all(workspace) |
| 98 | + val classFilesDir = |
| 99 | + if (sourceFiles.isEmpty) out / "classes" |
| 100 | + else { |
| 101 | + def asOpt[T](opt: String, values: IterableOnce[T]): Seq[String] = |
| 102 | + values.iterator.toList.flatMap(v => Seq(opt, v.toString)) |
| 103 | + |
| 104 | + val proc = os.proc( |
| 105 | + cli, |
| 106 | + Seq("compile", "--classpath"), |
| 107 | + Seq("-S", scalaVersion()), |
| 108 | + asOpt("-O", scalacOptions()), |
| 109 | + asOpt("--jar", compileClasspath().map(_.path)), |
| 110 | + asOpt("-O", scalacPluginClasspath().map(p => s"-Xplugin:${p.path}")), |
| 111 | + extraScalaCliOptions(), |
| 112 | + // "--strict-bloop-json-check=false", // don't check Bloop JSON files at each run |
| 113 | + workspace, |
| 114 | + sourceFiles |
| 115 | + ) |
| 116 | + |
| 117 | + val compile = proc.call() |
| 118 | + val out = compile.out.trim() |
| 119 | + |
| 120 | + os.Path(out.split(File.pathSeparator).head) |
| 121 | + } |
| 122 | + |
| 123 | + CompilationResult(out / "unused.txt", PathRef(classFilesDir)) |
| 124 | + } |
| 125 | + } |
| 126 | + else |
| 127 | + super.compile |
| 128 | + |
| 129 | +} |
0 commit comments