Skip to content

Commit ed4d927

Browse files
committed
feat: let build.jbang auto-select sources
When no sources are specified, we now look for common source locations.
1 parent 8a20a72 commit ed4d927

File tree

6 files changed

+150
-4
lines changed

6 files changed

+150
-4
lines changed

src/main/java/dev/jbang/resources/ResourceRef.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.nio.file.Files;
77
import java.nio.file.Path;
88
import java.util.function.Function;
9+
import java.util.stream.Stream;
910

1011
import org.jspecify.annotations.NonNull;
1112
import org.jspecify.annotations.Nullable;
@@ -65,6 +66,28 @@ static boolean isStdin(@NonNull String scriptResource) {
6566
return scriptResource.equals("-") || scriptResource.equals("/dev/stdin");
6667
}
6768

69+
default ResourceRef parent() {
70+
throw new ResourceNotFoundException(getOriginalResource(), "Getting parent resource not supported");
71+
}
72+
73+
default boolean isParent() {
74+
return false;
75+
}
76+
77+
interface ResourceChildren extends ResourceResolver {
78+
Stream<Path> list() throws IOException;
79+
80+
@Override
81+
@Nullable
82+
default ResourceRef resolve(String resource) {
83+
return resolve(resource, true);
84+
}
85+
}
86+
87+
default ResourceChildren children() {
88+
throw new ResourceNotFoundException(getOriginalResource(), "Resource is not a parent");
89+
}
90+
6891
/**
6992
* Returns a file representation for this resource reference. If an original
7093
* resource was provided, this method will return a resolved file representation
@@ -77,6 +100,9 @@ static boolean isStdin(@NonNull String scriptResource) {
77100
*/
78101
@NonNull
79102
default Path getFile() {
103+
if (isParent()) {
104+
throw new ResourceNotFoundException(getOriginalResource(), "Resource is a parent");
105+
}
80106
throw new ResourceNotFoundException(getOriginalResource(), "Getting contents from resource not supported");
81107
}
82108

@@ -96,6 +122,9 @@ default Path getFile() {
96122
* @return an InputStream for the resource, or null if no file is associated
97123
*/
98124
default InputStream getInputStream() {
125+
if (isParent()) {
126+
throw new ResourceNotFoundException(getOriginalResource(), "Resource is a parent");
127+
}
99128
try {
100129
return Files.newInputStream(getFile());
101130
} catch (IOException e) {

src/main/java/dev/jbang/resources/resolvers/FileResourceResolver.java

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package dev.jbang.resources.resolvers;
22

3+
import java.io.IOException;
4+
import java.nio.file.FileVisitOption;
35
import java.nio.file.Files;
46
import java.nio.file.InvalidPathException;
57
import java.nio.file.Path;
68
import java.nio.file.Paths;
79
import java.util.Objects;
810
import java.util.Optional;
911
import java.util.function.Function;
12+
import java.util.stream.Stream;
1013

1114
import org.jspecify.annotations.NonNull;
1215
import org.jspecify.annotations.Nullable;
@@ -81,7 +84,7 @@ public String getOriginalResource() {
8184
@Override
8285
public boolean exists() {
8386
try {
84-
return Files.isRegularFile(getFile());
87+
return Files.exists(getFile());
8588
} catch (ResourceNotFoundException e) {
8689
return false;
8790
}
@@ -96,6 +99,9 @@ public Path getFile() {
9699
if (!file.isPresent()) {
97100
throw new ResourceNotFoundException(getOriginalResource(), "Could not obtain file resource");
98101
}
102+
if (isParent()) {
103+
throw new ResourceNotFoundException(getOriginalResource(), "Resource is a parent");
104+
}
99105
return file.get();
100106
}
101107

@@ -109,6 +115,51 @@ public String getExtension() {
109115
}
110116
}
111117

118+
@Override
119+
public ResourceRef parent() {
120+
Path parentPath = Paths.get(originalResource).getParent();
121+
return new FileResourceRef(parentPath.toString(), parentPath, resolver);
122+
}
123+
124+
@Override
125+
public boolean isParent() {
126+
if (file == null) {
127+
file = Optional.ofNullable(obtainer.apply(getOriginalResource()));
128+
}
129+
return file.isPresent() && Files.isDirectory(file.get());
130+
}
131+
132+
@Override
133+
public ResourceChildren children() {
134+
if (!isParent()) {
135+
throw new ResourceNotFoundException(getOriginalResource(), "Resource is not a parent");
136+
}
137+
return new ResourceChildren() {
138+
@Override
139+
public Stream<Path> list() throws IOException {
140+
if (file == null) {
141+
file = Optional.ofNullable(obtainer.apply(getOriginalResource()));
142+
}
143+
return file.isPresent() ? Files.walk(file.get(), FileVisitOption.FOLLOW_LINKS) : Stream.empty();
144+
}
145+
146+
@Override
147+
public @Nullable ResourceRef resolve(String resource, boolean trusted) {
148+
if (!Util.isValidPath(resource)) {
149+
return null;
150+
}
151+
Path baseDir = Paths.get(originalResource);
152+
String childRef = baseDir.resolve(resource).toString();
153+
return resolver.resolve(childRef, trusted);
154+
}
155+
156+
@Override
157+
public @NonNull String description() {
158+
return "Children of " + FileResourceRef.this.description();
159+
}
160+
};
161+
}
162+
112163
@Override
113164
public @Nullable ResourceRef resolve(String resource, boolean trusted) {
114165
if (!Util.isValidPath(resource)) {

src/main/java/dev/jbang/resources/resolvers/RenamingScriptResourceResolver.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package dev.jbang.resources.resolvers;
22

3+
import static dev.jbang.util.Util.MAIN_JAVA;
4+
35
import java.io.File;
46
import java.io.IOException;
57
import java.nio.file.InvalidPathException;
@@ -59,7 +61,7 @@ public ResourceRef resolve(String resource) {
5961
&& !knownExtensions.contains(ext)
6062
&& (!Util.isPreview() || !Project.BuildFile.fileNames().contains(probe.getName()))) {
6163
if (probe.isDirectory()) {
62-
File defaultApp = new File(probe, "main.java");
64+
File defaultApp = new File(probe, MAIN_JAVA);
6365
File buildFile = new File(probe, Project.BuildFile.jbang.fileName);
6466
if (Util.isPreview() && buildFile.exists()) {
6567
Util.verboseMsg("Directory where build.jbang exists. Running build.jbang.");

src/main/java/dev/jbang/source/ProjectBuilder.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package dev.jbang.source;
22

3+
import static dev.jbang.util.Util.MAIN_JAVA;
4+
35
import java.io.File;
46
import java.io.IOException;
57
import java.io.InputStream;
@@ -366,7 +368,24 @@ private Project createJbangProject(ResourceRef resourceRef) {
366368
}
367369

368370
boolean first = true;
369-
List<Source> includedSources = allToSource(directives.sources(), resourceRef, sibRes2);
371+
List<String> sources = directives.sources();
372+
if (sources.isEmpty()) {
373+
// if no sources are defined, we go look for a couple of possible options
374+
if (resourceRef.resolve(MAIN_JAVA) != null) {
375+
sources.add(MAIN_JAVA);
376+
} else {
377+
if (resourceRef.resolve("src/main/java") != null) {
378+
sources.add("src/main/java/**.java");
379+
}
380+
if (resourceRef.resolve("src/main/kotlin") != null) {
381+
sources.add("src/main/kotlin/**.kt");
382+
}
383+
if (resourceRef.resolve("src/main/groovy") != null) {
384+
sources.add("src/main/groovy/**.groovy");
385+
}
386+
}
387+
}
388+
List<Source> includedSources = allToSource(sources, resourceRef, sibRes2);
370389
for (Source includedSource : includedSources) {
371390
updateProject(includedSource, prj, resolver);
372391
if (first) {

src/main/java/dev/jbang/util/Util.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ public class Util {
9696
private static final Pattern subUrlPattern = Pattern.compile(
9797
"^(%?%https?://.+$)|(%?%\\{[a-z]+:[^}]+})");
9898

99+
public static final String MAIN_JAVA = "main.java";
100+
99101
private static boolean verbose;
100102
private static boolean quiet;
101103
private static boolean offline;
@@ -1297,7 +1299,7 @@ public static String swizzleURL(String url) {
12971299
URI u = new URI(url);
12981300
if (u.getPath().endsWith("/")) {
12991301
verboseMsg("Directory url, assuming user want to get default application at main.java");
1300-
url = url + "main.java";
1302+
url = url + MAIN_JAVA;
13011303
}
13021304
} catch (URISyntaxException e) {
13031305
// ignore

src/test/java/dev/jbang/source/TestProjectBuilder.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package dev.jbang.source;
22

3+
import static dev.jbang.util.Util.MAIN_JAVA;
34
import static org.hamcrest.MatcherAssert.assertThat;
45
import static org.hamcrest.Matchers.aMapWithSize;
56
import static org.hamcrest.Matchers.anEmptyMap;
67
import static org.hamcrest.Matchers.contains;
78
import static org.hamcrest.Matchers.containsInAnyOrder;
9+
import static org.hamcrest.Matchers.endsWith;
810
import static org.hamcrest.Matchers.equalTo;
911
import static org.hamcrest.Matchers.hasEntry;
1012
import static org.hamcrest.Matchers.instanceOf;
@@ -13,13 +15,15 @@
1315
import static org.hamcrest.Matchers.nullValue;
1416
import static org.junit.jupiter.api.Assertions.assertThrows;
1517

18+
import java.io.File;
1619
import java.io.IOException;
1720
import java.nio.file.Path;
1821
import java.nio.file.Paths;
1922
import java.util.Arrays;
2023
import java.util.stream.Collectors;
2124

2225
import org.junit.jupiter.api.Test;
26+
import org.junit.jupiter.api.io.TempDir;
2327

2428
import dev.jbang.BaseTest;
2529
import dev.jbang.cli.ExitException;
@@ -346,4 +350,43 @@ void testMissingFilesTags() {
346350
.filter(rt -> rt.getSource() instanceof ResourceRef.UnresolvableResourceRef)
347351
.collect(Collectors.toList()), iterableWithSize(2));
348352
}
353+
354+
@Test
355+
void testBuildJbangAutoSourcesMainJava(@TempDir Path tmpDir) throws IOException {
356+
Util.setPreview(true);
357+
358+
Path src = tmpDir.resolve("build.jbang");
359+
Util.writeString(src, "//DESCRIPTION Test\n");
360+
Util.writeString(tmpDir.resolve(MAIN_JAVA), "//Dummy source file");
361+
362+
ProjectBuilder pb = Project.builder();
363+
Project prj = pb.build(src);
364+
assertThat(prj.getMainSourceSet().getSources(), iterableWithSize(1));
365+
assertThat(prj.getMainSourceSet().getSources().get(0).getFile().toString(),
366+
endsWith(File.separator + MAIN_JAVA));
367+
assertThat(prj.getMainSourceSet().getSources(), containsInAnyOrder(
368+
ResourceRef.forFile(tmpDir.resolve(MAIN_JAVA))));
369+
}
370+
371+
@Test
372+
void testBuildJbangAutoSourcesDirs(@TempDir Path tmpDir) throws IOException {
373+
Util.setPreview(true);
374+
375+
Path src = tmpDir.resolve("build.jbang");
376+
Util.writeString(src, "//DESCRIPTION Test\n");
377+
Path dirs = tmpDir.resolve("src/main/java");
378+
dirs.toFile().mkdirs();
379+
Util.writeString(dirs.resolve("Foo.java"), "//Dummy source file");
380+
Util.writeString(dirs.resolve("Bar.java"), "//Dummy source file");
381+
dirs.resolve("baz").toFile().mkdirs();
382+
Util.writeString(dirs.resolve("baz/Baz.java"), "//Dummy source file");
383+
384+
ProjectBuilder pb = Project.builder();
385+
Project prj = pb.build(src);
386+
assertThat(prj.getMainSourceSet().getSources(), iterableWithSize(3));
387+
assertThat(prj.getMainSourceSet().getSources(), containsInAnyOrder(
388+
ResourceRef.forFile(dirs.resolve("Foo.java")),
389+
ResourceRef.forFile(dirs.resolve("Bar.java")),
390+
ResourceRef.forFile(dirs.resolve("baz/Baz.java"))));
391+
}
349392
}

0 commit comments

Comments
 (0)