Skip to content

Commit baf1b7d

Browse files
authored
Allow running standalone single-file scripts using Mill (#5820)
First pass at #5714 Initial pass, this works: ``` $ ./mill -f Foo.java run --text hello [6972/6972] dist.run watching processId file (expected content = 63396): /Users/lihaoyi/Github/mill/test/out/mill-no-daemon/pid-63396/processId ============================== run --text hello ============================== [55/55] run [55] SLF4J: No SLF4J providers were found. [55] SLF4J: Defaulting to no-operation (NOP) logger implementation [55] SLF4J: See https://www.slf4j.org/codes.html#noProviders for further details. [55] <h1>hello</h1> [55/55] ============================== run --text hello ============================== [6972/6972] ============================== dist.run test -i -f Foo.java run --text hello ============================== 1s ``` The basic approach is - Allow `-f`/`--file` to be passed to specify a script file to use rather than `build.mill` - If `-f` is passed, use an alternate shortened `MillBuildBootstrap` process that has a single build stage with a hard-coded `RootModule` based on the extension of the script: `mill.meta.ScriptModule.Java`, `.Scala`, or `.Kotlin` In the common case where someone wants to just run a script, they can run: ``` ./mill Foo.java --text hello ``` As a shorthand for the full `./mill -f Foo.java run --text hello`. We special-case the first token ending with `.java`, `.scala`, or `.kt` to support this. I expect this would be what people want to do with their scripts the vast majority of the time, so it deserves its own extra-concise syntax even if it has some theoretical overlap with user-defined tasks ending with those names Other notes: - For single-file projects, we assign them a subfolder in the out folder where all the build takes place e.g. `foo/bar/Baz.kt` gets assigned `out/foo/bar/Baz.kt/` as its out folder. Normal tasks and modules won't normally have `.`s in their name, so this ensures that the single-file projects won't collide with normal module and task output files - Some of the task outputs from `mill.javalib.publish` have been given short-hands `upickle.Reader`s for writing them in the YAML build header. I expect we will need to add more such shorthands in future - Started with some basic doc sections on the per-language `intro.adoc` and `module-config.adoc` pages touching on usage of these single-file projects, but I expect we'll need more documentation - For this PR, the single-file projects are entirely standalone and not connected to any enclosing Mill build, but that may be something we can integrate in future to allow single-file projects to define `moduleDeps` on the enclosing `build.mill`'s modules Tested with a example tests
1 parent cbb9e8a commit baf1b7d

File tree

79 files changed

+1083
-171
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+1083
-171
lines changed

.editorconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ indent_size = 2
1818
ktlint_code_style = intellij_idea
1919
ktlint_standard_no-wildcard-imports = disabled
2020

21+
[example/kotlinlib/basic/5-single-file/**/*]
22+
ktlint = disabled
23+
24+
[example/kotlinlib/module/1-single-file/**/*]
25+
ktlint = disabled
26+
2127
[example/kotlinlib/linting/**/*]
2228
ktlint = disabled
2329

build.mill

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//| mill-version: 1.0.4-26-a6e4c1
1+
//| mill-version: 1.0.4-38-cbb9e8
22
//| mill-jvm-opts: ["-XX:NonProfiledCodeHeapSize=250m", "-XX:ReservedCodeCacheSize=500m"]
33
//| mill-opts: ["--jobs=0.5C"]
44

core/constants/src/mill/constants/Util.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import java.lang.reflect.InvocationTargetException;
66
import java.lang.reflect.Method;
77
import java.nio.charset.StandardCharsets;
8+
import java.nio.file.Files;
9+
import java.nio.file.Path;
810
import java.security.MessageDigest;
911
import java.security.NoSuchAlgorithmException;
1012
import java.util.ArrayList;
@@ -84,15 +86,20 @@ private static String throwBuildHeaderError(
8486
+ ": " + line + "\n" + msg);
8587
}
8688

87-
public static String readBuildHeader(java.nio.file.Path buildFile, String errorFileName) {
89+
public static String readBuildHeader(Path buildFile, String errorFileName) {
90+
return readBuildHeader(buildFile, errorFileName, false);
91+
}
92+
93+
public static String readBuildHeader(
94+
Path buildFile, String errorFileName, boolean allowNonBuild) {
8895
try {
89-
java.util.List<String> lines = java.nio.file.Files.readAllLines(buildFile);
96+
java.util.List<String> lines = Files.readAllLines(buildFile);
9097
boolean readingBuildHeader = true;
9198
java.util.List<String> output = new ArrayList<>();
9299
for (int i = 0; i < lines.size(); i++) {
93100
String line = lines.get(i);
94101
if (!line.startsWith("//|")) readingBuildHeader = false;
95-
else if (!buildFile.getFileName().toString().startsWith("build.")) {
102+
else if (!allowNonBuild && !buildFile.getFileName().toString().startsWith("build.")) {
96103
throwBuildHeaderError(
97104
errorFileName,
98105
i,

core/internal/cli/package.mill

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,5 @@ import millbuild.*
99
object `package` extends MillPublishScalaModule {
1010
def moduleDeps = Seq(build.libs.util)
1111

12-
def mvnDeps = Seq(
13-
Deps.millModuledefs,
14-
Deps.mainargs
15-
)
12+
def mvnDeps = Seq(Deps.mainargs)
1613
}

core/internal/cli/src/mill/internal/MillCliConfig.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ case class MillCliConfig(
128128
doc = """Runs Mill in tab-completion mode"""
129129
)
130130
tabComplete: Flag = Flag(),
131+
@arg(
132+
short = 'f',
133+
doc = """Select the build.mill file or Java/Scala/Kotlin script file to run"""
134+
)
135+
file: Option[os.Path] = None,
131136

132137
// ==================== DEPRECATED CLI FLAGS ====================
133138
@arg(hidden = true, short = 'h', doc = "Unsupported")
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//| mvnDeps:
2+
//| - "net.sourceforge.argparse4j:argparse4j:0.9.0"
3+
//| - "org.thymeleaf:thymeleaf:3.1.1.RELEASE"
4+
import net.sourceforge.argparse4j.ArgumentParsers;
5+
import net.sourceforge.argparse4j.inf.ArgumentParser;
6+
import net.sourceforge.argparse4j.inf.Namespace;
7+
import org.thymeleaf.TemplateEngine;
8+
import org.thymeleaf.context.Context;
9+
10+
public class Foo {
11+
public static String generateHtml(String text) {
12+
Context context = new Context();
13+
context.setVariable("text", text);
14+
return new TemplateEngine().process("<h1 th:text=\"${text}\"></h1>", context);
15+
}
16+
17+
public static void main(String[] args) {
18+
ArgumentParser parser = ArgumentParsers.newFor("template")
19+
.build()
20+
.defaultHelp(true)
21+
.description("Inserts text into a HTML template");
22+
23+
parser.addArgument("-t", "--text").required(true).help("text to insert");
24+
25+
Namespace ns = null;
26+
try {
27+
ns = parser.parseArgs(args);
28+
} catch (Exception e) {
29+
System.out.println(e.getMessage());
30+
System.exit(1);
31+
}
32+
33+
System.out.println(generateHtml(ns.getString("text")));
34+
}
35+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//// SNIPPET:FILE
2+
/** See Also: Foo.java */
3+
/** Usage
4+
> ./mill Foo.java --text hello
5+
compiling 1 Java source to...
6+
<h1>hello</h1>
7+
*/
8+
9+
/** Usage
10+
> ./mill -f Foo.java run --text hello
11+
<h1>hello</h1>
12+
*/
13+
14+
//// SNIPPET:END
15+
//// SNIPPET:MORE
16+
17+
/** Usage
18+
> ./mill -f Foo.java show assembly # show the output of the assembly task
19+
".../out/Foo.java/assembly.dest/out.jar"
20+
21+
> java -jar ./out/Foo.java/assembly.dest/out.jar --text hello
22+
<h1>hello</h1>
23+
24+
> ./out/Foo.java/assembly.dest/out.jar --text hello # mac/linux
25+
<h1>hello</h1>
26+
27+
28+
*/
29+
30+
//// SNIPPET:END
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//| jvmId: "graalvm-community:24"
2+
//| nativeImageOptions: ["--no-fallback"]
3+
//| publishVersion: "0.0.1"
4+
//| artifactName: "example"
5+
//| pomSettings:
6+
//| description: "Example"
7+
//| organization: "com.lihaoyi"
8+
//| url: "https://github.com/com.lihaoyi/example"
9+
//| licenses: ["MIT"]
10+
//| versionControl: "https://github.com/com.lihaoyi/example"
11+
//| developers: [{"name": "Li Haoyi", "email": "[email protected]"}]
12+
13+
public class Bar {
14+
public static void main(String[] args) {
15+
System.out.println("Hello Graal Native: " + System.getProperty("java.version"));
16+
}
17+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//// SNIPPET:LAST
2+
3+
/** See Also: Bar.java */
4+
/** Usage
5+
6+
> ./mill -f Bar.java nativeImage
7+
8+
> out/Bar.java/nativeImage.dest/native-executable
9+
Hello Graal Native: 24...
10+
11+
> ./mill -f Bar.java publishLocal
12+
Publishing Artifact(com.lihaoyi,example...,0.0.1) to ivy repo ...
13+
14+
*/
15+
16+
// Apart from publishing locally, you can also publish this single-file project to
17+
// Sonatype Maven Central via:
18+
//
19+
// [source,console]
20+
// ----
21+
// > ./mill -f Bar.java mill.javalib.SonatypeCentralPublishModule/
22+
// ----
23+
24+
//// SNIPPET:END

0 commit comments

Comments
 (0)