Skip to content

Commit 3fc0b22

Browse files
authored
Merge pull request #477 from ckipp01/mill
2 parents 441a0d8 + 156a6dc commit 3fc0b22

File tree

8 files changed

+365
-7
lines changed

8 files changed

+365
-7
lines changed

build.sbt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ lazy val V =
2222
def semanticdbKotlinc = "0.2.0"
2323
def testcontainers = "0.39.3"
2424
def requests = "0.6.5"
25+
def minimalMillVersion = "0.10.0"
26+
def millScipVersion = "0.2.2"
2527
}
2628

2729
inThisBuild(
@@ -167,7 +169,9 @@ lazy val cli = project
167169
"scala213" -> V.scala213,
168170
"scala3" -> V.scala3,
169171
"bloopVersion" -> V.bloop,
170-
"bspVersion" -> V.bsp
172+
"bspVersion" -> V.bsp,
173+
"minimalMillVersion" -> V.minimalMillVersion,
174+
"millScipVersion" -> V.millScipVersion
171175
),
172176
buildInfoPackage := "com.sourcegraph.scip_java",
173177
libraryDependencies ++=

docs/getting-started.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ free to subscribe to the tracking issues to receive updates on your build tool.
243243
| Ant |||| [sourcegraph/scip-java#305](https://github.com/sourcegraph/scip-java/issues/305) |
244244
| Bazel |||| |
245245
| Buck |||| [sourcegraph/scip-java#99](https://github.com/sourcegraph/scip-java/issues/99) |
246-
| Mill || || [sourcegraph/scip-java#306](https://github.com/sourcegraph/scip-java/issues/306) |
246+
| Mill || ||
247247

248248
****: automatic indexing is fully supported. Please report a bug if the
249249
`scip-java index` command does not work on your codebase.
@@ -292,13 +292,23 @@ The following Maven integrations are not yet supported:
292292

293293
### sbt
294294

295-
The `scip-java index` build should be able to automatically index most Maven
295+
The `scip-java index` build should be able to automatically index most sbt
296296
projects, with the following caveats:
297297

298298
| Integration | Supported | Recommendation |
299299
| ------------- | --------- | ----------------------- |
300300
| sbt <v0.13.17 || Upgrade to sbt v0.13.17 |
301301

302+
### Mill
303+
304+
The `scip-java index` build should be able to automatically index most Mill
305+
projects, with the following caveats:
306+
307+
| Integration | Supported | Recommendation |
308+
| ------------- | --------- | -------------------------- |
309+
| Mill <v0.10.0 || Upgrade to Mill >= v0.10.0 |
310+
311+
302312
### Bazel
303313

304314
Bazel is supported by scip-java but it requires custom configuration to work

scip-java/src/main/scala/com/sourcegraph/scip_java/buildtools/BuildTool.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ object BuildTool {
2424
new GradleBuildTool(index),
2525
new MavenBuildTool(index),
2626
new ScipBuildTool(index),
27-
new SbtBuildTool(index)
27+
new SbtBuildTool(index),
28+
new MillBuildTool(index)
2829
)
2930
def allNames: String =
3031
all(IndexCommand()).filterNot(_.isHidden).map(_.name).mkString(", ")
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package com.sourcegraph.scip_java.buildtools
2+
3+
import java.nio.file.Files
4+
import java.nio.file.StandardCopyOption
5+
6+
import scala.jdk.CollectionConverters._
7+
8+
import com.sourcegraph.scip_java.BuildInfo
9+
import com.sourcegraph.scip_java.commands.IndexCommand
10+
11+
class MillBuildTool(index: IndexCommand) extends BuildTool("mill", index) {
12+
13+
override def usedInCurrentDirectory(): Boolean =
14+
Files.isRegularFile(index.workingDirectory.resolve("build.sc"))
15+
16+
override def generateScip(): Int =
17+
millVersion() match {
18+
case Some(version) if isSupportedMillVersion(version) =>
19+
unconditionallyGenerateScip()
20+
case Some(version) =>
21+
failFast(
22+
s"Unsupported Mill version '${version}'. " +
23+
s"To fix this problem, upgrade Mill to at least ${minimalMillVersion} and try again."
24+
)
25+
case None =>
26+
failFast(
27+
s"No Mill version detected. " +
28+
s"To fix this problem, run the following command and try again: " +
29+
s"echo '${minimalMillVersion}' >> .mill-version"
30+
)
31+
}
32+
33+
private def failFast(message: String): Int = {
34+
index.app.error(message)
35+
1
36+
}
37+
38+
private def unconditionallyGenerateScip(): Int = {
39+
val localMill = Files.isRegularFile(millFile)
40+
val command =
41+
if (localMill) {
42+
"./mill"
43+
} else {
44+
"mill"
45+
}
46+
val millProcess = index.process(
47+
List(
48+
command,
49+
"--import",
50+
s"ivy:io.chris-kipp::mill-scip::${BuildInfo.millScipVersion}",
51+
"io.kipp.mill.scip.Scip/generate"
52+
)
53+
)
54+
val scipFile = index
55+
.workingDirectory
56+
.resolve("out")
57+
.resolve("io")
58+
.resolve("kipp")
59+
.resolve("mill")
60+
.resolve("scip")
61+
.resolve("Scip")
62+
.resolve("generate.dest")
63+
.resolve("index.scip")
64+
65+
if (millProcess.exitCode == 0 && Files.isRegularFile(scipFile)) {
66+
val output = index.workingDirectory.resolve("index.scip")
67+
Files.copy(scipFile, output, StandardCopyOption.REPLACE_EXISTING)
68+
index.app.info(output.toString)
69+
}
70+
millProcess.exitCode
71+
}
72+
73+
private lazy val minimalMillVersion = BuildInfo.minimalMillVersion
74+
75+
private lazy val millFile = index.workingDirectory.resolve("mill")
76+
77+
private def isSupportedMillVersion(version: String): Boolean =
78+
if (version.startsWith("0.1"))
79+
true
80+
else
81+
false
82+
83+
/**
84+
* Try to grab the Mill version from the .mill-version file. If not found we
85+
* fall back to the mill file which could be the official mill launcher or the
86+
* millw launcher renamed as mill, both which will have a DEFAULT_MILL_VERSION
87+
* line.
88+
*/
89+
private def millVersion(): Option[String] = {
90+
val millVersionFile = index.workingDirectory.resolve(".mill-version")
91+
if (Files.isRegularFile(millVersionFile)) {
92+
Files.readAllLines(millVersionFile).asScala.headOption
93+
} else if (Files.isRegularFile(millFile)) {
94+
Files
95+
.readAllLines(millFile)
96+
.asScala
97+
.find(_.startsWith("DEFAULT_MILL_VERSION"))
98+
.map(line => line.dropWhile(!_.isDigit))
99+
} else {
100+
None
101+
}
102+
}
103+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
#!/usr/bin/env sh
2+
3+
# This is a wrapper script, that automatically download mill from GitHub release pages
4+
# You can give the required mill version with --mill-version parameter
5+
# If no version is given, it falls back to the value of DEFAULT_MILL_VERSION
6+
#
7+
# Project page: https://github.com/lefou/millw
8+
# Script Version: 0.4.2
9+
#
10+
# If you want to improve this script, please also contribute your changes back!
11+
#
12+
# Licensed under the Apache License, Version 2.0
13+
14+
15+
DEFAULT_MILL_VERSION=0.10.0
16+
17+
set -e
18+
19+
MILL_REPO_URL="https://github.com/com-lihaoyi/mill"
20+
21+
if [ -z "${CURL_CMD}" ] ; then
22+
CURL_CMD=curl
23+
fi
24+
25+
# Explicit commandline argument takes precedence over all other methods
26+
if [ "$1" = "--mill-version" ] ; then
27+
shift
28+
if [ "x$1" != "x" ] ; then
29+
MILL_VERSION="$1"
30+
shift
31+
else
32+
echo "You specified --mill-version without a version." 1>&2
33+
echo "Please provide a version that matches one provided on" 1>&2
34+
echo "${MILL_REPO_URL}/releases" 1>&2
35+
false
36+
fi
37+
fi
38+
39+
# Please note, that if a MILL_VERSION is already set in the environment,
40+
# We reuse it's value and skip searching for a value.
41+
42+
# If not already set, read .mill-version file
43+
if [ -z "${MILL_VERSION}" ] ; then
44+
if [ -f ".mill-version" ] ; then
45+
MILL_VERSION="$(head -n 1 .mill-version 2> /dev/null)"
46+
fi
47+
fi
48+
49+
if [ -n "${XDG_CACHE_HOME}" ] ; then
50+
MILL_DOWNLOAD_PATH="${XDG_CACHE_HOME}/mill/download"
51+
else
52+
MILL_DOWNLOAD_PATH="${HOME}/.cache/mill/download"
53+
fi
54+
55+
# If not already set, try to fetch newest from Github
56+
if [ -z "${MILL_VERSION}" ] ; then
57+
# TODO: try to load latest version from release page
58+
echo "No mill version specified." 1>&2
59+
echo "You should provide a version via '.mill-version' file or --mill-version option." 1>&2
60+
61+
mkdir -p "${MILL_DOWNLOAD_PATH}"
62+
LANG=C touch -d '1 hour ago' "${MILL_DOWNLOAD_PATH}/.expire_latest" 2>/dev/null || (
63+
# we might be on OSX or BSD which don't have -d option for touch
64+
# but probably a -A [-][[hh]mm]SS
65+
touch "${MILL_DOWNLOAD_PATH}/.expire_latest"; touch -A -010000 "${MILL_DOWNLOAD_PATH}/.expire_latest"
66+
) || (
67+
# in case we still failed, we retry the first touch command with the intention
68+
# to show the (previously suppressed) error message
69+
LANG=C touch -d '1 hour ago' "${MILL_DOWNLOAD_PATH}/.expire_latest"
70+
)
71+
72+
# POSIX shell variant of bash's -nt operator, see https://unix.stackexchange.com/a/449744/6993
73+
# if [ "${MILL_DOWNLOAD_PATH}/.latest" -nt "${MILL_DOWNLOAD_PATH}/.expire_latest" ] ; then
74+
if [ -n "$(find -L "${MILL_DOWNLOAD_PATH}/.latest" -prune -newer "${MILL_DOWNLOAD_PATH}/.expire_latest")" ]; then
75+
# we know a current latest version
76+
MILL_VERSION=$(head -n 1 "${MILL_DOWNLOAD_PATH}"/.latest 2> /dev/null)
77+
fi
78+
79+
if [ -z "${MILL_VERSION}" ] ; then
80+
# we don't know a current latest version
81+
echo "Retrieving latest mill version ..." 1>&2
82+
LANG=C ${CURL_CMD} -s -i -f -I ${MILL_REPO_URL}/releases/latest 2> /dev/null | grep --ignore-case Location: | sed s'/^.*tag\///' | tr -d '\r\n' > "${MILL_DOWNLOAD_PATH}/.latest"
83+
MILL_VERSION=$(head -n 1 "${MILL_DOWNLOAD_PATH}"/.latest 2> /dev/null)
84+
fi
85+
86+
if [ -z "${MILL_VERSION}" ] ; then
87+
# Last resort
88+
MILL_VERSION="${DEFAULT_MILL_VERSION}"
89+
echo "Falling back to hardcoded mill version ${MILL_VERSION}" 1>&2
90+
else
91+
echo "Using mill version ${MILL_VERSION}" 1>&2
92+
fi
93+
fi
94+
95+
MILL="${MILL_DOWNLOAD_PATH}/${MILL_VERSION}"
96+
97+
try_to_use_system_mill() {
98+
MILL_IN_PATH="$(command -v mill || true)"
99+
100+
if [ -z "${MILL_IN_PATH}" ]; then
101+
return
102+
fi
103+
104+
UNIVERSAL_SCRIPT_MAGIC="@ 2>/dev/null # 2>nul & echo off & goto BOF"
105+
106+
if ! head -c 128 "${MILL_IN_PATH}" | grep -qF "${UNIVERSAL_SCRIPT_MAGIC}"; then
107+
if [ -n "${MILLW_VERBOSE}" ]; then
108+
echo "Could not determine mill version of ${MILL_IN_PATH}, as it does not start with the universal script magic2" 1>&2
109+
fi
110+
return
111+
fi
112+
113+
# Roughly the size of the universal script.
114+
MILL_VERSION_SEARCH_RANGE="2403"
115+
MILL_IN_PATH_VERSION=$(head -c "${MILL_VERSION_SEARCH_RANGE}" "${MILL_IN_PATH}" |\
116+
sed -n 's/^.*-DMILL_VERSION=\([^\s]*\) .*$/\1/p' |\
117+
head -n 1)
118+
119+
if [ -z "${MILL_IN_PATH_VERSION}" ]; then
120+
echo "Could not determine mill version, even though ${MILL_IN_PATH} has the universal script magic" 1>&2
121+
return
122+
fi
123+
124+
if [ "${MILL_IN_PATH_VERSION}" = "${MILL_VERSION}" ]; then
125+
MILL="${MILL_IN_PATH}"
126+
fi
127+
}
128+
try_to_use_system_mill
129+
130+
# If not already downloaded, download it
131+
if [ ! -s "${MILL}" ] ; then
132+
133+
# support old non-XDG download dir
134+
MILL_OLD_DOWNLOAD_PATH="${HOME}/.mill/download"
135+
OLD_MILL="${MILL_OLD_DOWNLOAD_PATH}/${MILL_VERSION}"
136+
if [ -x "${OLD_MILL}" ] ; then
137+
MILL="${OLD_MILL}"
138+
else
139+
VERSION_PREFIX="$(echo $MILL_VERSION | cut -b -4)"
140+
case $VERSION_PREFIX in
141+
0.0. | 0.1. | 0.2. | 0.3. | 0.4. )
142+
DOWNLOAD_SUFFIX=""
143+
;;
144+
*)
145+
DOWNLOAD_SUFFIX="-assembly"
146+
;;
147+
esac
148+
unset VERSION_PREFIX
149+
150+
DOWNLOAD_FILE=$(mktemp mill.XXXXXX)
151+
# TODO: handle command not found
152+
echo "Downloading mill ${MILL_VERSION} from ${MILL_REPO_URL}/releases ..." 1>&2
153+
MILL_VERSION_TAG=$(echo $MILL_VERSION | sed -E 's/([^-]+)(-M[0-9]+)?(-.*)?/\1\2/')
154+
${CURL_CMD} -f -L -o "${DOWNLOAD_FILE}" "${MILL_REPO_URL}/releases/download/${MILL_VERSION_TAG}/${MILL_VERSION}${DOWNLOAD_SUFFIX}"
155+
chmod +x "${DOWNLOAD_FILE}"
156+
mkdir -p "${MILL_DOWNLOAD_PATH}"
157+
mv "${DOWNLOAD_FILE}" "${MILL}"
158+
159+
unset DOWNLOAD_FILE
160+
unset DOWNLOAD_SUFFIX
161+
fi
162+
fi
163+
164+
unset MILL_DOWNLOAD_PATH
165+
unset MILL_OLD_DOWNLOAD_PATH
166+
unset OLD_MILL
167+
unset MILL_VERSION
168+
unset MILL_VERSION_TAG
169+
unset MILL_REPO_URL
170+
171+
exec "${MILL}" "$@"

tests/buildTools/src/test/scala/tests/BaseBuildToolSuite.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,16 @@ abstract class BaseBuildToolSuite extends MopedSuite(ScipJava.app) {
5757
extraArguments: List[String] = Nil,
5858
expectedError: Option[String => Unit] = None,
5959
expectedPackages: String = "",
60-
initCommand: => List[String] = Nil
60+
initCommand: => List[String] = Nil,
61+
targetRoot: Option[String] = None
6162
): Unit = {
6263
test(options.withTags(options.tags ++ tags)) {
6364
if (initCommand.nonEmpty) {
6465
os.proc(Shellable(initCommand)).call(os.Path(workingDirectory))
6566
}
6667
FileLayout.fromString(original, root = workingDirectory)
67-
val targetroot = workingDirectory.resolve("targetroot")
68+
val targetroot = workingDirectory
69+
.resolve(targetRoot.getOrElse("targetroot"))
6870
val arguments =
6971
List[String](
7072
"index",

0 commit comments

Comments
 (0)