added extra classpath manifest attribute
This commit is contained in:
@@ -1,24 +1,27 @@
|
|||||||
package net.woggioni.envelope;
|
package net.woggioni.envelope;
|
||||||
|
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.security.DigestInputStream;
|
import java.security.DigestInputStream;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.util.AbstractMap;
|
import java.util.AbstractMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.MissingFormatArgumentException;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import java.util.zip.CRC32;
|
import java.util.zip.CRC32;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import lombok.SneakyThrows;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
public class Common {
|
public class Common {
|
||||||
@@ -114,4 +117,144 @@ public class Common {
|
|||||||
OutputStream result = new FileOutputStream(file);
|
OutputStream result = new FileOutputStream(file);
|
||||||
return buffered ? new BufferedOutputStream(result) : result;
|
return buffered ? new BufferedOutputStream(result) : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param template Template text containing the variables to be replaced by this method. <br>
|
||||||
|
* Variables follow the format ${variable_name}. <br>
|
||||||
|
* Example: <br>
|
||||||
|
* "This template was created by ${author}."
|
||||||
|
* @param valuesMap A hashmap with the values of the variables to be replaced. <br>
|
||||||
|
* The key is the variable name and the value is the value to be replaced in the template. <br>
|
||||||
|
* Example: <br>
|
||||||
|
* {"author" => "John Doe"}
|
||||||
|
* @return The template text (String) with the variable names replaced by the values passed in the map. <br>
|
||||||
|
* If any of the variable names is not contained in the map it will be replaced by an empty string. <br>
|
||||||
|
* Example: <br>
|
||||||
|
* "This template was created by John Doe."
|
||||||
|
*/
|
||||||
|
public static String renderTemplate(String template, Map<String, Object> valuesMap) {
|
||||||
|
return renderTemplate(template, valuesMap, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static int indexOfWithEscape(String haystack, char needle, char escape, int begin, int end) {
|
||||||
|
int result = -1;
|
||||||
|
int cursor = begin;
|
||||||
|
if (end == 0) {
|
||||||
|
end = haystack.length();
|
||||||
|
}
|
||||||
|
int escapeCount = 0;
|
||||||
|
while (cursor < end) {
|
||||||
|
char c = haystack.charAt(cursor);
|
||||||
|
if (escapeCount > 0) {
|
||||||
|
--escapeCount;
|
||||||
|
if (c == escape) {
|
||||||
|
result = -1;
|
||||||
|
}
|
||||||
|
} else if (escapeCount == 0) {
|
||||||
|
if (c == escape) {
|
||||||
|
++escapeCount;
|
||||||
|
}
|
||||||
|
if (c == needle) {
|
||||||
|
result = cursor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result >= 0 && escapeCount == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++cursor;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String renderTemplate(
|
||||||
|
String template,
|
||||||
|
Map<String, Object> valuesMap,
|
||||||
|
Map<String, Map<String, Object>> dictMap) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
Object absent = new Object();
|
||||||
|
|
||||||
|
int cursor = 0;
|
||||||
|
TokenScanner tokenScanner = new TokenScanner(template, '$', '$');
|
||||||
|
while (cursor < template.length()) {
|
||||||
|
tokenScanner.next();
|
||||||
|
int nextPlaceHolder;
|
||||||
|
switch (tokenScanner.getTokenType()) {
|
||||||
|
case TOKEN: {
|
||||||
|
nextPlaceHolder = tokenScanner.getTokenIndex();
|
||||||
|
while (cursor < nextPlaceHolder) {
|
||||||
|
char ch = template.charAt(cursor++);
|
||||||
|
sb.append(ch);
|
||||||
|
}
|
||||||
|
if (cursor + 1 < template.length() && template.charAt(cursor + 1) == '{') {
|
||||||
|
String key;
|
||||||
|
String context = null;
|
||||||
|
String defaultValue = null;
|
||||||
|
Object value;
|
||||||
|
int end = template.indexOf('}', cursor + 1);
|
||||||
|
int colon;
|
||||||
|
if (dictMap == null)
|
||||||
|
colon = -1;
|
||||||
|
else {
|
||||||
|
colon = indexOfWithEscape(template, ':', '\\', cursor + 1, template.length());
|
||||||
|
if (colon >= end) colon = -1;
|
||||||
|
}
|
||||||
|
if (colon < 0) {
|
||||||
|
key = template.substring(cursor + 2, end);
|
||||||
|
value = valuesMap.getOrDefault(key, absent);
|
||||||
|
} else {
|
||||||
|
context = template.substring(cursor + 2, colon);
|
||||||
|
int secondColon = indexOfWithEscape(template, ':', '\\', colon + 1, end);
|
||||||
|
if (secondColon < 0) {
|
||||||
|
key = template.substring(colon + 1, end);
|
||||||
|
} else {
|
||||||
|
key = template.substring(colon + 1, secondColon);
|
||||||
|
defaultValue = template.substring(secondColon + 1, end);
|
||||||
|
}
|
||||||
|
value = Optional.ofNullable(dictMap.get(context))
|
||||||
|
.map(m -> m.get(key))
|
||||||
|
.orElse(absent);
|
||||||
|
}
|
||||||
|
if (value != absent) {
|
||||||
|
sb.append(value.toString());
|
||||||
|
} else {
|
||||||
|
if (defaultValue != null) {
|
||||||
|
sb.append(defaultValue);
|
||||||
|
} else {
|
||||||
|
throw new MissingFormatArgumentException(
|
||||||
|
String.format("Missing value for placeholder '%s'",
|
||||||
|
context == null ? key : context + ':' + key
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor = end + 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ESCAPE:
|
||||||
|
nextPlaceHolder = tokenScanner.getTokenIndex();
|
||||||
|
while (cursor < nextPlaceHolder) {
|
||||||
|
char ch = template.charAt(cursor++);
|
||||||
|
sb.append(ch);
|
||||||
|
}
|
||||||
|
cursor = nextPlaceHolder + 1;
|
||||||
|
sb.append(template.charAt(cursor++));
|
||||||
|
break;
|
||||||
|
case END:
|
||||||
|
default:
|
||||||
|
nextPlaceHolder = template.length();
|
||||||
|
while (cursor < nextPlaceHolder) {
|
||||||
|
char ch = template.charAt(cursor++);
|
||||||
|
sb.append(ch);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Stream<T> opt2Stream(Optional<T> opt) {
|
||||||
|
return opt.map(Stream::of).orElse(Stream.empty());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
package net.woggioni.envelope;
|
package net.woggioni.envelope;
|
||||||
|
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
public class Constants {
|
public class Constants {
|
||||||
@@ -16,10 +17,12 @@ public class Constants {
|
|||||||
public static final String SYSTEM_PROPERTIES_FILE = METADATA_FOLDER + "/system.properties";
|
public static final String SYSTEM_PROPERTIES_FILE = METADATA_FOLDER + "/system.properties";
|
||||||
|
|
||||||
public static final String LIBRARIES_TOC = METADATA_FOLDER + "/libraries.txt";
|
public static final String LIBRARIES_TOC = METADATA_FOLDER + "/libraries.txt";
|
||||||
|
public static final char EXTRA_CLASSPATH_ENTRY_SEPARATOR = ';';
|
||||||
|
|
||||||
public static class ManifestAttributes {
|
public static class ManifestAttributes {
|
||||||
public static final String MAIN_MODULE = "Executable-Jar-Main-Module";
|
public static final String MAIN_MODULE = "Executable-Jar-Main-Module";
|
||||||
public static final String MAIN_CLASS = "Executable-Jar-Main-Class";
|
public static final String MAIN_CLASS = "Executable-Jar-Main-Class";
|
||||||
|
public static final String EXTRA_CLASSPATH = "Executable-Jar-Extra-Classpath";
|
||||||
public static final String ENTRY_HASH = "SHA-256-Digest";
|
public static final String ENTRY_HASH = "SHA-256-Digest";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
74
common/src/main/java/net/woggioni/envelope/TokenScanner.java
Normal file
74
common/src/main/java/net/woggioni/envelope/TokenScanner.java
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package net.woggioni.envelope;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
public class TokenScanner {
|
||||||
|
|
||||||
|
public enum TokenType {
|
||||||
|
ESCAPE, TOKEN, END;
|
||||||
|
}
|
||||||
|
private final String haystack;
|
||||||
|
private final char needle;
|
||||||
|
private final char escape;
|
||||||
|
private int begin;
|
||||||
|
private final int end;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private int tokenIndex = -1;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private TokenType tokenType = null;
|
||||||
|
|
||||||
|
public TokenScanner(String haystack, char needle, char escape, int begin, int end) {
|
||||||
|
this.haystack = haystack;
|
||||||
|
this.needle = needle;
|
||||||
|
this.escape = escape;
|
||||||
|
this.begin = begin;
|
||||||
|
this.end = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TokenScanner(String haystack, char needle, char escape, int begin) {
|
||||||
|
this(haystack, needle, escape, begin, haystack.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
public TokenScanner(String haystack, char needle, char escape) {
|
||||||
|
this(haystack, needle, escape, 0, haystack.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void next() {
|
||||||
|
int result = -1;
|
||||||
|
int cursor = begin;
|
||||||
|
int escapeCount = 0;
|
||||||
|
while(true) {
|
||||||
|
if(cursor < end) {
|
||||||
|
char c = haystack.charAt(cursor);
|
||||||
|
if (escapeCount > 0) {
|
||||||
|
--escapeCount;
|
||||||
|
if(c == escape || c == needle) {
|
||||||
|
tokenIndex = cursor - 1;
|
||||||
|
tokenType = TokenType.ESCAPE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (escapeCount == 0) {
|
||||||
|
if (c == escape) {
|
||||||
|
++escapeCount;
|
||||||
|
}
|
||||||
|
if (c == needle) {
|
||||||
|
result = cursor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result >= 0 && escapeCount == 0) {
|
||||||
|
tokenIndex = result;
|
||||||
|
tokenType = TokenType.TOKEN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++cursor;
|
||||||
|
} else {
|
||||||
|
tokenIndex = result;
|
||||||
|
tokenType = result < 0 ? TokenType.END :TokenType.TOKEN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
begin = cursor + 1;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,7 +1,7 @@
|
|||||||
woggioniMavenRepositoryUrl=https://woggioni.net/mvn/
|
woggioniMavenRepositoryUrl=https://woggioni.net/mvn/
|
||||||
publishMavenRepositoryUrl=https://mvn.woggioni.net/
|
publishMavenRepositoryUrl=https://mvn.woggioni.net/
|
||||||
|
|
||||||
lys.version = 2023.06.13
|
lys.version = 2023.06.22
|
||||||
|
|
||||||
version.envelope=2023.06.13
|
version.envelope=2023.06.24
|
||||||
version.gradle=7.6
|
version.gradle=7.6
|
||||||
|
@@ -10,16 +10,27 @@ import java.io.Reader;
|
|||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
import java.util.AbstractMap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.TreeMap;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.jar.Attributes;
|
import java.util.jar.Attributes;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
import java.util.jar.Manifest;
|
import java.util.jar.Manifest;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static net.woggioni.envelope.Constants.EXTRA_CLASSPATH_ENTRY_SEPARATOR;
|
||||||
|
|
||||||
public class Launcher {
|
public class Launcher {
|
||||||
|
|
||||||
@@ -53,6 +64,65 @@ public class Launcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Map<String, Map<String, Object>> createContextMap() {
|
||||||
|
Map<String, Map<String, Object>> dictMap = new TreeMap<>();
|
||||||
|
dictMap.put("env", Collections.unmodifiableMap(System.getenv()
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
.map(entry -> new AbstractMap.SimpleEntry<>(entry.getKey(), (Object) entry.getValue()))
|
||||||
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))));
|
||||||
|
dictMap.put("sys", Collections.unmodifiableMap(System.getProperties().entrySet().stream()
|
||||||
|
.map((Map.Entry<? super String, Object> entry) -> (Map.Entry<String, Object>) entry)
|
||||||
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))));
|
||||||
|
return Collections.unmodifiableMap(dictMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream<JarFile> getExtraClasspath(Attributes mainAttributes) {
|
||||||
|
return Common.opt2Stream(Optional.ofNullable(mainAttributes.getValue(Constants.ManifestAttributes.EXTRA_CLASSPATH))
|
||||||
|
.map(manifestAttribute -> {
|
||||||
|
Map<String, Map<String, Object>> dictMap = createContextMap();
|
||||||
|
return Common.renderTemplate(manifestAttribute, Collections.emptyMap(), dictMap);
|
||||||
|
}).map(extraClasspathString -> {
|
||||||
|
List<String> paths = new ArrayList<>();
|
||||||
|
int cursor = 0;
|
||||||
|
while(true) {
|
||||||
|
int sep = Common.indexOfWithEscape(
|
||||||
|
extraClasspathString,
|
||||||
|
EXTRA_CLASSPATH_ENTRY_SEPARATOR,
|
||||||
|
EXTRA_CLASSPATH_ENTRY_SEPARATOR,
|
||||||
|
cursor,
|
||||||
|
extraClasspathString.length()
|
||||||
|
);
|
||||||
|
if(sep < 0) break;
|
||||||
|
paths.add(extraClasspathString.substring(cursor, sep));
|
||||||
|
cursor = sep + 1;
|
||||||
|
}
|
||||||
|
return paths;
|
||||||
|
}))
|
||||||
|
.flatMap(List::stream)
|
||||||
|
.map(Paths::get)
|
||||||
|
.flatMap(new Function<Path, Stream<Path>>() {
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public Stream<Path> apply(Path path) {
|
||||||
|
if(Files.isDirectory(path)) {
|
||||||
|
return Files.list(path).filter(childPath -> !Files.isDirectory(childPath));
|
||||||
|
} else {
|
||||||
|
return Stream.of(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(path -> path.getFileName().toString().toLowerCase().endsWith(".jar"))
|
||||||
|
.map(Path::toFile)
|
||||||
|
.map(new Function<File, JarFile>() {
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
public JarFile apply(File file) {
|
||||||
|
return new JarFile(file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
Enumeration<URL> it = Launcher.class.getClassLoader().getResources(Constants.SYSTEM_PROPERTIES_FILE);
|
Enumeration<URL> it = Launcher.class.getClassLoader().getResources(Constants.SYSTEM_PROPERTIES_FILE);
|
||||||
@@ -94,6 +164,8 @@ public class Launcher {
|
|||||||
else sb.append((char) c);
|
else sb.append((char) c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getExtraClasspath(mainAttributes).forEach(classpath::add);
|
||||||
Consumer<Class<?>> runner = new Consumer<Class<?>>() {
|
Consumer<Class<?>> runner = new Consumer<Class<?>>() {
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
|
@@ -20,6 +20,7 @@ import org.gradle.api.java.archives.internal.DefaultManifest;
|
|||||||
import org.gradle.api.model.ObjectFactory;
|
import org.gradle.api.model.ObjectFactory;
|
||||||
import org.gradle.api.plugins.BasePluginExtension;
|
import org.gradle.api.plugins.BasePluginExtension;
|
||||||
import org.gradle.api.plugins.JavaApplication;
|
import org.gradle.api.plugins.JavaApplication;
|
||||||
|
import org.gradle.api.provider.ListProperty;
|
||||||
import org.gradle.api.provider.Property;
|
import org.gradle.api.provider.Property;
|
||||||
import org.gradle.api.provider.Provider;
|
import org.gradle.api.provider.Provider;
|
||||||
import org.gradle.api.tasks.Input;
|
import org.gradle.api.tasks.Input;
|
||||||
@@ -86,6 +87,9 @@ public class EnvelopeJarTask extends AbstractArchiveTask {
|
|||||||
@Getter(onMethod_ = {@Input})
|
@Getter(onMethod_ = {@Input})
|
||||||
private final Map<String, String> systemProperties = new TreeMap<>();
|
private final Map<String, String> systemProperties = new TreeMap<>();
|
||||||
|
|
||||||
|
@Getter(onMethod_ = {@Input})
|
||||||
|
private final ListProperty<String> extraClasspath;
|
||||||
|
|
||||||
private final org.gradle.api.java.archives.Manifest manifest;
|
private final org.gradle.api.java.archives.Manifest manifest;
|
||||||
|
|
||||||
public org.gradle.api.java.archives.Manifest manifest() {
|
public org.gradle.api.java.archives.Manifest manifest() {
|
||||||
@@ -156,6 +160,7 @@ public class EnvelopeJarTask extends AbstractArchiveTask {
|
|||||||
manifest = new DefaultManifest(fileResolver);
|
manifest = new DefaultManifest(fileResolver);
|
||||||
mainClass = objects.property(String.class);
|
mainClass = objects.property(String.class);
|
||||||
mainModule = objects.property(String.class);
|
mainModule = objects.property(String.class);
|
||||||
|
extraClasspath = objects.listProperty(String.class);
|
||||||
JavaApplication javaApplication = getProject().getExtensions().findByType(JavaApplication.class);
|
JavaApplication javaApplication = getProject().getExtensions().findByType(JavaApplication.class);
|
||||||
if(!Objects.isNull(javaApplication)) {
|
if(!Objects.isNull(javaApplication)) {
|
||||||
mainClass.convention(javaApplication.getMainClass());
|
mainClass.convention(javaApplication.getMainClass());
|
||||||
@@ -286,6 +291,11 @@ public class EnvelopeJarTask extends AbstractArchiveTask {
|
|||||||
mainAttributes.put(new Attributes.Name("Launcher-Agent-Class"), Constants.AGENT_LAUNCHER);
|
mainAttributes.put(new Attributes.Name("Launcher-Agent-Class"), Constants.AGENT_LAUNCHER);
|
||||||
mainAttributes.put(new Attributes.Name("Can-Redefine-Classes"), "true");
|
mainAttributes.put(new Attributes.Name("Can-Redefine-Classes"), "true");
|
||||||
mainAttributes.put(new Attributes.Name("Can-Retransform-Classes"), "true");
|
mainAttributes.put(new Attributes.Name("Can-Retransform-Classes"), "true");
|
||||||
|
String separator = "" + Constants.EXTRA_CLASSPATH_ENTRY_SEPARATOR;
|
||||||
|
String extraClasspath = EnvelopeJarTask.this.extraClasspath.get().stream()
|
||||||
|
.map(it -> it.replace(separator, separator + separator)
|
||||||
|
).collect(Collectors.joining(separator));
|
||||||
|
mainAttributes.put(new Attributes.Name(Constants.ManifestAttributes.EXTRA_CLASSPATH), extraClasspath);
|
||||||
if(mainClass.isPresent()) {
|
if(mainClass.isPresent()) {
|
||||||
mainAttributes.putValue(Constants.ManifestAttributes.MAIN_CLASS, mainClass.get());
|
mainAttributes.putValue(Constants.ManifestAttributes.MAIN_CLASS, mainClass.get());
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user