Skip to content

Commit 1bfa853

Browse files
authored
feat: Added support for script dependencies (#1641)
* chore: minor cleanup of dependencies code * refactor: not passing Project around where we have BuildContext * refactor: minor simplification in dependency resolvers * feat: Added support for subprojects * fix: Improved JitPack URL regexes They were too lenient and matched invalid URLs * fix: GAV patterns are now more strict They were too lenient and would match too many invalid coordinates. * chore: display what jar we're building * fix: no longer rebuilding source dependencies Source dependencies would always be rebuilt whenever the parent script was built. This was because they were really built a part of (meaning inside of) the parent's build folder, not as an independent artifact. This is now no longer the case and building a script as part of a dependency or standalone will now result in the same artifact. * fix: self-referencing dependencies now show proper error message
1 parent d87feaa commit 1bfa853

Some content is hidden

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

46 files changed

+732
-733
lines changed

itests/dependencies.feature

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ Feature: dependency fetching
33
Scenario: fetch dependencies
44
* command('jbang --verbose version')
55
When command('jbang classpath_log.java', { JBANG_REPO: scratch + "/newrepo"})
6-
Then match err == '[jbang] Resolving dependencies...\n[jbang] log4j:log4j:1.2.17\n[jbang] Dependencies resolved\n[jbang] Building jar...\n'
6+
Then match err == '[jbang] Resolving dependencies...\n[jbang] log4j:log4j:1.2.17\n[jbang] Dependencies resolved\n[jbang] Building jar for classpath_log.java...\n'
77
And fileexist(scratch + "/newrepo")
88
And match exit == 0

itests/init.feature

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ When command('jbang init ' + scratch + '/test.java')
88
* def contents = read(scratch + '/test.java')
99
* match contents contains "class test"
1010
* command('jbang ' + scratch + '/test.java')
11-
* match err == "[jbang] Building jar...\n"
11+
* match err == "[jbang] Building jar for test.java...\n"
1212
* command('jbang ' + scratch + '/test.java')
1313
* match err !contains "[jbang] Building jar"
1414

@@ -20,7 +20,7 @@ When command('jbang init ' + scratch + '/newfolder/test.java')
2020
* def contents = read(scratch + '/newfolder/test.java')
2121
* match contents contains "class test"
2222
* command('jbang ' + scratch + '/newfolder/test.java')
23-
* match err == "[jbang] Building jar...\n"
23+
* match err == "[jbang] Building jar for test.java...\n"
2424
* command('jbang ' + scratch + '/newfolder/test.java')
2525
* match err !contains "[jbang] Building jar"
2626

itests/onedep.java

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
///usr/bin/env jbang "$0" "$@" ; exit $?
2+
//DEPS Two.java
3+
4+
public class onedep {
5+
public static void main(String... args) {
6+
Two.main();
7+
}
8+
}

itests/run-nix.feature

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ Background:
55

66
Scenario: as code option 2
77
* command('jbang --code "$(cat helloworld.java)" jbangtest')
8-
* match err == "[jbang] Building jar...\n"
8+
* match err == "[jbang] Building jar for helloworld.java...\n"
99
* match out == "Hello jbangtest\n"
1010

1111
Scenario: as code option 3
1212
* command('jbang "--code=$(cat helloworld.java)" jbangtest')
13-
* match err == "[jbang] Building jar...\n"
13+
* match err == "[jbang] Building jar for helloworld.java...\n"
1414
* match out == "Hello jbangtest\n"

itests/run.feature

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ Scenario: should fail on missing file
77

88
Scenario: parameter passing
99
* command('jbang helloworld.java jbangtest')
10-
* match err == "[jbang] Building jar...\n"
10+
* match err == "[jbang] Building jar for helloworld.java...\n"
1111
* match out == "Hello jbangtest\n"
1212

1313
Scenario: std in
1414
* command('cat helloworld.java | jbang - jbangtest')
15-
* match err == "[jbang] Building jar...\n"
15+
* match err == "[jbang] Building jar for helloworld.java...\n"
1616
* match out == "Hello jbangtest\n"
1717

1818
Scenario: java launch helloworld with jfr

itests/selfdep.java

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//DEPS selfdep.java
2+
3+
class selfdep {
4+
public static void main(String... args) {
5+
System.out.println("Hello, world!");
6+
}
7+
}
8+

src/main/java/dev/jbang/cli/Build.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public Integer doCall() throws IOException {
1818

1919
ProjectBuilder pb = createProjectBuilderForBuild();
2020
Project prj = pb.build(scriptMixin.scriptOrFile);
21-
prj.codeBuilder(BuildContext.forProject(prj, buildDir)).build();
21+
Project.codeBuilder(BuildContext.forProject(prj, buildDir)).build();
2222

2323
return EXIT_OK;
2424
}

src/main/java/dev/jbang/cli/Edit.java

+2-4
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,7 @@ public Integer doCall() throws IOException {
168168
throw new ExitException(EXIT_INVALID_INPUT, "You can only edit source files");
169169
}
170170

171-
Path project = createProjectForLinkedEdit(prj, Collections
172-
.emptyList(),
173-
false);
171+
Path project = createProjectForLinkedEdit(prj, Collections.emptyList(), false);
174172
String projectPathString = pathToString(project.toAbsolutePath());
175173
// err.println(project.getAbsolutePath());
176174

@@ -394,7 +392,7 @@ Path createProjectForLinkedEdit(Project prj, List<String> arguments, boolean rel
394392
Path originalFile = prj.getResourceRef().getFile();
395393

396394
List<String> dependencies = prj.getMainSourceSet().getDependencies();
397-
String cp = prj.resolveClassPath().getClassPath();
395+
String cp = BuildContext.forProject(prj).resolveClassPath().getClassPath();
398396
List<String> resolvedDependencies = Arrays.asList(cp.split(CP_SEPARATOR));
399397

400398
Path baseDir = Settings.getCacheDir(Cache.CacheClass.projects);

src/main/java/dev/jbang/cli/Export.java

+19-14
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,17 @@ public Integer doCall() throws IOException {
6565
ProjectBuilder pb = createProjectBuilder(exportMixin);
6666
Project prj = pb.build(exportMixin.scriptMixin.scriptOrFile);
6767
BuildContext ctx = BuildContext.forProject(prj);
68-
prj.codeBuilder(ctx).build();
68+
Project.codeBuilder(ctx).build();
6969
if (prj.getResourceRef() instanceof AliasResourceResolver.AliasedResourceRef) {
7070
Alias alias = ((AliasResourceResolver.AliasedResourceRef) prj.getResourceRef()).getAlias();
7171
if (prj.getMainClass() == null) {
7272
prj.setMainClass(alias.mainClass);
7373
}
7474
}
75-
return apply(prj, ctx);
75+
return apply(ctx);
7676
}
7777

78-
abstract int apply(Project prj, BuildContext ctx) throws IOException;
78+
abstract int apply(BuildContext ctx) throws IOException;
7979

8080
protected ProjectBuilder createProjectBuilder(ExportMixin exportMixin) {
8181
return Project
@@ -108,7 +108,7 @@ Path getJarOutputPath() {
108108
class ExportLocal extends BaseExportCommand {
109109

110110
@Override
111-
int apply(Project prj, BuildContext ctx) throws IOException {
111+
int apply(BuildContext ctx) throws IOException {
112112
// Copy the JAR
113113
Path source = ctx.getJarFile();
114114
Path outputPath = getJarOutputPath();
@@ -126,7 +126,8 @@ int apply(Project prj, BuildContext ctx) throws IOException {
126126

127127
// Update the JAR's MANIFEST.MF Class-Path to point to
128128
// its dependencies
129-
String newPath = prj.resolveClassPath().getManifestPath();
129+
Project prj = ctx.getProject();
130+
String newPath = ctx.resolveClassPath().getManifestPath();
130131
if (!newPath.isEmpty()) {
131132
Util.infoMsg("Updating jar...");
132133
String javaVersion = exportMixin.buildMixin.javaVersion != null
@@ -146,7 +147,7 @@ class ExportPortable extends BaseExportCommand {
146147
public static final String LIB = "lib";
147148

148149
@Override
149-
int apply(Project prj, BuildContext ctx) throws IOException {
150+
int apply(BuildContext ctx) throws IOException {
150151
// Copy the JAR
151152
Path source = ctx.getJarFile();
152153
Path outputPath = getJarOutputPath();
@@ -162,7 +163,8 @@ int apply(Project prj, BuildContext ctx) throws IOException {
162163
}
163164

164165
Files.copy(source, outputPath);
165-
List<ArtifactInfo> deps = prj.resolveClassPath().getArtifacts();
166+
Project prj = ctx.getProject();
167+
List<ArtifactInfo> deps = ctx.resolveClassPath().getArtifacts();
166168
if (!deps.isEmpty()) {
167169
// Copy dependencies to "./lib" dir
168170
Path libDir = outputPath.getParent().resolve(LIB);
@@ -197,7 +199,7 @@ class ExportMavenPublish extends BaseExportCommand {
197199
String version;
198200

199201
@Override
200-
int apply(Project prj, BuildContext ctx) throws IOException {
202+
int apply(BuildContext ctx) throws IOException {
201203
Path outputPath = exportMixin.outputFile;
202204

203205
if (outputPath == null) {
@@ -219,6 +221,7 @@ int apply(Project prj, BuildContext ctx) throws IOException {
219221
}
220222
}
221223

224+
Project prj = ctx.getProject();
222225
if (prj.getGav().isPresent()) {
223226
MavenCoordinate coord = MavenCoordinate.fromString(prj.getGav().get()).withVersion();
224227
if (group == null) {
@@ -283,7 +286,7 @@ int apply(Project prj, BuildContext ctx) throws IOException {
283286
.data("artifact", artifact)
284287
.data("version", version)
285288
.data("description", prj.getDescription().orElse(""))
286-
.data("dependencies", prj.resolveClassPath().getArtifacts())
289+
.data("dependencies", ctx.resolveClassPath().getArtifacts())
287290
.render();
288291
Util.infoMsg("Writing " + pomPath);
289292
Util.writeString(pomPath, pomfile);
@@ -299,7 +302,7 @@ int apply(Project prj, BuildContext ctx) throws IOException {
299302
class ExportNative extends BaseExportCommand {
300303

301304
@Override
302-
int apply(Project prj, BuildContext ctx) throws IOException {
305+
int apply(BuildContext ctx) throws IOException {
303306
// Copy the native binary
304307
Path source = ctx.getNativeImageFile();
305308
Path outputPath = getNativeOutputPath();
@@ -340,7 +343,7 @@ Path getNativeOutputPath() {
340343
class ExportFatjar extends BaseExportCommand {
341344

342345
@Override
343-
int apply(Project prj, BuildContext ctx) throws IOException {
346+
int apply(BuildContext ctx) throws IOException {
344347
// Copy the native binary
345348
Path source = ctx.getJarFile();
346349
Path outputPath = getFatjarOutputPath();
@@ -355,7 +358,8 @@ int apply(Project prj, BuildContext ctx) throws IOException {
355358
Util.mkdirs(outputPath.getParent());
356359
}
357360

358-
List<ArtifactInfo> deps = prj.resolveClassPath().getArtifacts();
361+
Project prj = ctx.getProject();
362+
List<ArtifactInfo> deps = ctx.resolveClassPath().getArtifacts();
359363
if (!deps.isEmpty()) {
360364
// Extract main jar and all dependencies to a temp dir
361365
Path tmpDir = Files.createTempDirectory("fatjar");
@@ -417,8 +421,9 @@ protected ProjectBuilder createProjectBuilder(ExportMixin exportMixin) {
417421
}
418422

419423
@Override
420-
int apply(Project prj, BuildContext ctx) throws IOException {
421-
List<ArtifactInfo> artifacts = prj.resolveClassPath().getArtifacts();
424+
int apply(BuildContext ctx) throws IOException {
425+
Project prj = ctx.getProject();
426+
List<ArtifactInfo> artifacts = ctx.resolveClassPath().getArtifacts();
422427
List<ArtifactInfo> nonMods = artifacts
423428
.stream()
424429
.filter(a -> !ModuleUtil.isModule(a.getFile()))

src/main/java/dev/jbang/cli/Info.java

+8-6
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,14 @@ static class ScriptInfo {
9595
String gav;
9696
String module;
9797

98-
public ScriptInfo(Project prj, BuildContext ctx, boolean assureJdkInstalled) {
98+
public ScriptInfo(BuildContext ctx, boolean assureJdkInstalled) {
99+
Project prj = ctx.getProject();
99100
originalResource = prj.getResourceRef().getOriginalResource();
100101

101102
if (scripts.add(originalResource)) {
102103
backingResource = prj.getResourceRef().getFile().toString();
103104

104-
init(prj);
105+
init(ctx);
105106

106107
applicationJar = ctx.getJarFile() == null ? null
107108
: ctx.getJarFile().toAbsolutePath().toString();
@@ -125,7 +126,7 @@ public ScriptInfo(Project prj, BuildContext ctx, boolean assureJdkInstalled) {
125126
// Ignore
126127
}
127128

128-
List<ArtifactInfo> artifacts = prj.resolveClassPath().getArtifacts();
129+
List<ArtifactInfo> artifacts = ctx.resolveClassPath().getArtifacts();
129130
if (artifacts.isEmpty()) {
130131
resolvedDependencies = Collections.emptyList();
131132
} else {
@@ -153,8 +154,9 @@ public ScriptInfo(Project prj, BuildContext ctx, boolean assureJdkInstalled) {
153154
}
154155
}
155156

156-
private void init(Project prj) {
157-
List<String> deps = prj.resolveClassPath().getClassPaths();
157+
private void init(BuildContext ctx) {
158+
Project prj = ctx.getProject();
159+
List<String> deps = ctx.resolveClassPath().getClassPaths();
158160
if (!deps.isEmpty()) {
159161
dependencies = deps;
160162
}
@@ -213,7 +215,7 @@ ScriptInfo getInfo(boolean assureJdkInstalled) {
213215

214216
scripts = new HashSet<>();
215217

216-
return new ScriptInfo(prj, BuildContext.forProject(prj, buildDir), assureJdkInstalled);
218+
return new ScriptInfo(BuildContext.forProject(prj, buildDir), assureJdkInstalled);
217219
}
218220

219221
ProjectBuilder createProjectBuilder() {

src/main/java/dev/jbang/cli/Run.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ public Integer doCall() throws IOException {
8686
}
8787

8888
BuildContext ctx = BuildContext.forProject(prj, buildDir);
89-
CmdGeneratorBuilder genb = prj.codeBuilder(ctx).build();
89+
CmdGeneratorBuilder genb = Project.codeBuilder(ctx).build();
9090

91-
buildAgents(prj, ctx);
91+
buildAgents(ctx);
9292

9393
String cmdline = updateGeneratorForRun(genb).build().generate();
9494

@@ -98,7 +98,8 @@ public Integer doCall() throws IOException {
9898
return EXIT_EXECUTE;
9999
}
100100

101-
void buildAgents(Project prj, BuildContext ctx) throws IOException {
101+
void buildAgents(BuildContext ctx) throws IOException {
102+
Project prj = ctx.getProject();
102103
Map<String, String> agents = runMixin.javaAgentSlots;
103104
if (agents == null && prj.getResourceRef() instanceof AliasResourceResolver.AliasedResourceRef) {
104105
AliasResourceResolver.AliasedResourceRef aref = (AliasResourceResolver.AliasedResourceRef) prj.getResourceRef();
@@ -119,7 +120,7 @@ void buildAgents(Project prj, BuildContext ctx) throws IOException {
119120
ProjectBuilder apb = createBaseProjectBuilder();
120121
Project aprj = apb.build(javaAgent);
121122
BuildContext actx = BuildContext.forProject(aprj);
122-
aprj.codeBuilder(actx).build();
123+
Project.codeBuilder(actx).build();
123124
runMixin.javaRuntimeOptions.addAll(javaAgentOptions(actx, javaAgentOptions));
124125
}
125126
}

src/main/java/dev/jbang/dependencies/DependencyCache.java

-12
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,9 @@
99
import java.nio.file.Files;
1010
import java.nio.file.Path;
1111
import java.nio.file.Paths;
12-
import java.util.Collection;
1312
import java.util.HashMap;
1413
import java.util.List;
1514
import java.util.Map;
16-
import java.util.Optional;
1715

1816
import com.google.gson.Gson;
1917
import com.google.gson.GsonBuilder;
@@ -111,16 +109,6 @@ public static List<ArtifactInfo> findDependenciesByHash(String depsHash) {
111109
return null;
112110
}
113111

114-
public static ArtifactInfo findArtifactByPath(Path artifactPath) {
115-
Map<String, List<ArtifactInfo>> cache = getCache();
116-
Optional<ArtifactInfo> result = cache .values()
117-
.stream()
118-
.flatMap(Collection::stream)
119-
.filter(art -> art.getFile().equals(artifactPath))
120-
.findFirst();
121-
return result.orElseGet(() -> new ArtifactInfo(null, artifactPath));
122-
}
123-
124112
public static void clear() {
125113
depCache = null;
126114
}

src/main/java/dev/jbang/dependencies/DependencyResolver.java

+10-20
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010
public class DependencyResolver {
1111
private final Set<MavenRepo> repositories;
1212
private final Set<String> dependencies;
13-
private final Set<ArtifactInfo> artifacts;
13+
private final Set<String> classPaths;
1414

1515
public DependencyResolver() {
1616
repositories = new LinkedHashSet<>();
1717
dependencies = new LinkedHashSet<>();
18-
artifacts = new LinkedHashSet<>();
18+
classPaths = new LinkedHashSet<>();
1919
}
2020

2121
public DependencyResolver addRepository(MavenRepo repository) {
@@ -50,19 +50,9 @@ public DependencyResolver addDependencies(List<String> dependencies) {
5050
return this;
5151
}
5252

53-
public DependencyResolver addArtifact(ArtifactInfo artifact) {
54-
artifacts.add(artifact);
55-
return this;
56-
}
57-
58-
public DependencyResolver addArtifacts(List<ArtifactInfo> artifacts) {
59-
this.artifacts.addAll(artifacts);
60-
return this;
61-
}
62-
6353
public DependencyResolver addClassPath(String classPath) {
64-
// WARN need File here because it's more lenient about paths than Path!
65-
return addArtifact(DependencyCache.findArtifactByPath(new File(classPath).toPath()));
54+
classPaths.add(classPath);
55+
return this;
6656
}
6757

6858
public DependencyResolver addClassPaths(List<String> classPaths) {
@@ -72,18 +62,18 @@ public DependencyResolver addClassPaths(List<String> classPaths) {
7262
return this;
7363
}
7464

75-
public DependencyResolver addClassPaths(String classPaths) {
76-
return addClassPaths(Arrays.asList(classPaths.split(" ")));
77-
}
78-
7965
public ModularClassPath resolve() {
8066
ModularClassPath mcp = DependencyUtil.resolveDependencies(
8167
new ArrayList<>(dependencies), new ArrayList<>(repositories),
8268
Util.isOffline(), Util.isFresh(), !Util.isQuiet(), Util.downloadSources());
83-
if (artifacts.isEmpty()) {
69+
if (classPaths.isEmpty()) {
8470
return mcp;
8571
} else {
86-
List<ArtifactInfo> arts = Stream.concat(mcp.getArtifacts().stream(), artifacts.stream())
72+
// WARN need File here because it's more lenient about paths than Path!
73+
Stream<ArtifactInfo> cpas = classPaths
74+
.stream()
75+
.map(p -> new ArtifactInfo(null, new File(p).toPath()));
76+
List<ArtifactInfo> arts = Stream.concat(mcp.getArtifacts().stream(), cpas)
8777
.collect(Collectors.toList());
8878
return new ModularClassPath(arts);
8979
}

0 commit comments

Comments
 (0)