mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8371076: jpackage will wrongly overwrite the plist file in the embedded runtime when executed with the "--app-image" option
Reviewed-by: almatvee
This commit is contained in:
parent
f7f4f903cf
commit
7c900da198
@ -127,7 +127,12 @@ final class AppImageSigner {
|
||||
// Sign runtime root directory if present
|
||||
app.asApplicationLayout().map(appLayout -> {
|
||||
return appLayout.resolveAt(appImage.root());
|
||||
}).map(MacApplicationLayout.class::cast).map(MacApplicationLayout::runtimeRootDirectory).ifPresent(codesigners);
|
||||
}).map(MacApplicationLayout.class::cast)
|
||||
.map(MacApplicationLayout::runtimeRootDirectory)
|
||||
.flatMap(MacBundle::fromPath)
|
||||
.filter(MacBundle::isValid)
|
||||
.map(MacBundle::root)
|
||||
.ifPresent(codesigners);
|
||||
|
||||
final var frameworkPath = appImage.contentsDir().resolve("Frameworks");
|
||||
if (Files.isDirectory(frameworkPath)) {
|
||||
|
||||
@ -133,7 +133,7 @@ final class MacPackagingPipeline {
|
||||
.addDependencies(CopyAppImageTaskID.COPY)
|
||||
.addDependents(PrimaryTaskID.COPY_APP_IMAGE).add()
|
||||
.task(MacCopyAppImageTaskID.COPY_RUNTIME_INFO_PLIST)
|
||||
.appImageAction(MacPackagingPipeline::writeRuntimeInfoPlist)
|
||||
.noaction()
|
||||
.addDependencies(CopyAppImageTaskID.COPY)
|
||||
.addDependents(PrimaryTaskID.COPY_APP_IMAGE).add()
|
||||
.task(MacCopyAppImageTaskID.COPY_RUNTIME_JLILIB)
|
||||
@ -186,14 +186,18 @@ final class MacPackagingPipeline {
|
||||
disabledTasks.add(MacCopyAppImageTaskID.COPY_PACKAGE_FILE);
|
||||
|
||||
if (predefinedRuntimeBundle.isPresent()) {
|
||||
// The predefined app image is a macOS bundle.
|
||||
// The input runtime image is a macOS bundle.
|
||||
// Disable all alterations of the input bundle, but keep the signing enabled.
|
||||
disabledTasks.addAll(List.of(MacCopyAppImageTaskID.values()));
|
||||
disabledTasks.remove(MacCopyAppImageTaskID.COPY_SIGN);
|
||||
} else {
|
||||
// The input runtime is not a macOS bundle and doesn't have the plist file. Create one.
|
||||
builder.task(MacCopyAppImageTaskID.COPY_RUNTIME_INFO_PLIST)
|
||||
.appImageAction(MacPackagingPipeline::writeRuntimeInfoPlist).add();
|
||||
}
|
||||
|
||||
if (predefinedRuntimeBundle.map(MacBundle::isSigned).orElse(false) && !((MacPackage)p).app().sign()) {
|
||||
// The predefined app image is a signed bundle; explicit signing is not requested for the package.
|
||||
// The input runtime is a signed bundle; explicit signing is not requested for the package.
|
||||
// Disable the signing, i.e. don't re-sign the input bundle.
|
||||
disabledTasks.add(MacCopyAppImageTaskID.COPY_SIGN);
|
||||
}
|
||||
|
||||
@ -431,6 +431,10 @@ final class PackagingPipeline {
|
||||
this.taskGraph = Objects.requireNonNull(taskGraph);
|
||||
this.taskConfig = Objects.requireNonNull(taskConfig);
|
||||
this.contextMapper = Objects.requireNonNull(contextMapper);
|
||||
|
||||
if (TRACE_TASK_GRAPTH) {
|
||||
taskGraph.dumpToStdout();
|
||||
}
|
||||
}
|
||||
|
||||
private TaskContext createTaskContext(BuildEnv env, Application app) {
|
||||
@ -630,9 +634,27 @@ final class PackagingPipeline {
|
||||
Objects.requireNonNull(id);
|
||||
Objects.requireNonNull(config);
|
||||
return () -> {
|
||||
if (config.action.isPresent() && context.test(id)) {
|
||||
|
||||
final var withAction = config.action.isPresent();
|
||||
final var accepted = withAction && context.test(id);
|
||||
|
||||
if (TRACE_TASK_ACTION) {
|
||||
var sb = new StringBuffer();
|
||||
sb.append("Execute task=[").append(id).append("]: ");
|
||||
if (!withAction) {
|
||||
sb.append("no action");
|
||||
} else if (!accepted) {
|
||||
sb.append("rejected");
|
||||
} else {
|
||||
sb.append("run");
|
||||
}
|
||||
System.out.println(sb);
|
||||
}
|
||||
|
||||
if (accepted) {
|
||||
context.execute(config.action.orElseThrow());
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
}
|
||||
@ -640,4 +662,7 @@ final class PackagingPipeline {
|
||||
private final FixedDAG<TaskID> taskGraph;
|
||||
private final Map<TaskID, TaskConfig> taskConfig;
|
||||
private final UnaryOperator<TaskContext> contextMapper;
|
||||
|
||||
private static final boolean TRACE_TASK_GRAPTH = false;
|
||||
private static final boolean TRACE_TASK_ACTION = false;
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
@ -160,15 +161,13 @@ public final class PListReader {
|
||||
* name in the underlying "dict" element
|
||||
*/
|
||||
public String queryValue(String keyName) {
|
||||
final var node = getNode(keyName);
|
||||
switch (node.getNodeName()) {
|
||||
case "string" -> {
|
||||
return node.getTextContent();
|
||||
}
|
||||
default -> {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
return findValue(keyName).orElseThrow(NoSuchElementException::new);
|
||||
}
|
||||
|
||||
public Optional<String> findValue(String keyName) {
|
||||
return findNode(keyName).filter(node -> {
|
||||
return "string".equals(node.getNodeName());
|
||||
}).map(Node::getTextContent);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -182,15 +181,13 @@ public final class PListReader {
|
||||
* name in the underlying "dict" element
|
||||
*/
|
||||
public PListReader queryDictValue(String keyName) {
|
||||
final var node = getNode(keyName);
|
||||
switch (node.getNodeName()) {
|
||||
case "dict" -> {
|
||||
return new PListReader(node);
|
||||
}
|
||||
default -> {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
return findDictValue(keyName).orElseThrow(NoSuchElementException::new);
|
||||
}
|
||||
|
||||
public Optional<PListReader> findDictValue(String keyName) {
|
||||
return findNode(keyName).filter(node -> {
|
||||
return "dict".equals(node.getNodeName());
|
||||
}).map(PListReader::new);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -204,18 +201,20 @@ public final class PListReader {
|
||||
* name in the underlying "dict" element
|
||||
*/
|
||||
public boolean queryBoolValue(String keyName) {
|
||||
final var node = getNode(keyName);
|
||||
switch (node.getNodeName()) {
|
||||
case "true" -> {
|
||||
return true;
|
||||
return findBoolValue(keyName).orElseThrow(NoSuchElementException::new);
|
||||
}
|
||||
|
||||
public Optional<Boolean> findBoolValue(String keyName) {
|
||||
return findNode(keyName).filter(node -> {
|
||||
switch (node.getNodeName()) {
|
||||
case "true", "false" -> {
|
||||
return true;
|
||||
}
|
||||
default -> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case "false" -> {
|
||||
return false;
|
||||
}
|
||||
default -> {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
}).map(Node::getNodeName).map(Boolean::parseBoolean);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -233,14 +232,20 @@ public final class PListReader {
|
||||
* name in the underlying "dict" element
|
||||
*/
|
||||
public List<String> queryStringArrayValue(String keyName) {
|
||||
return queryArrayValue(keyName, false).map(v -> {
|
||||
if (v instanceof Raw r) {
|
||||
if (r.type() == Raw.Type.STRING) {
|
||||
return r.value();
|
||||
return findStringArrayValue(keyName).orElseThrow(NoSuchElementException::new);
|
||||
}
|
||||
|
||||
public Optional<List<String>> findStringArrayValue(String keyName) {
|
||||
return findArrayValue(keyName, false).map(stream -> {
|
||||
return stream.map(v -> {
|
||||
if (v instanceof Raw r) {
|
||||
if (r.type() == Raw.Type.STRING) {
|
||||
return r.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
return (String)null;
|
||||
}).filter(Objects::nonNull).toList();
|
||||
return (String)null;
|
||||
}).filter(Objects::nonNull).toList();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -266,15 +271,21 @@ public final class PListReader {
|
||||
* in the underlying "dict" element
|
||||
*/
|
||||
public Stream<Object> queryArrayValue(String keyName, boolean fetchDictionaries) {
|
||||
final var node = getNode(keyName);
|
||||
switch (node.getNodeName()) {
|
||||
case "array" -> {
|
||||
return readArray(node, fetchDictionaries);
|
||||
}
|
||||
default -> {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
return findArrayValue(keyName, fetchDictionaries).orElseThrow(NoSuchElementException::new);
|
||||
}
|
||||
|
||||
public Optional<Stream<Object>> findArrayValue(String keyName, boolean fetchDictionaries) {
|
||||
return findNode(keyName).filter(node -> {
|
||||
return "array".equals(node.getNodeName());
|
||||
}).map(node -> {
|
||||
return readArray(node, fetchDictionaries);
|
||||
});
|
||||
}
|
||||
|
||||
public XmlConsumer toXmlConsumer() {
|
||||
return xml -> {
|
||||
XmlUtils.concatXml(xml, new DOMSource(root));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -333,12 +344,12 @@ public final class PListReader {
|
||||
}).filter(Optional::isPresent).map(Optional::get);
|
||||
}
|
||||
|
||||
private Node getNode(String keyName) {
|
||||
private Optional<Node> findNode(String keyName) {
|
||||
Objects.requireNonNull(keyName);
|
||||
final var query = String.format("*[preceding-sibling::key = \"%s\"][1]", keyName);
|
||||
return Optional.ofNullable(toSupplier(() -> {
|
||||
return (Node) XPathSingleton.INSTANCE.evaluate(query, root, XPathConstants.NODE);
|
||||
}).get()).orElseThrow(NoSuchElementException::new);
|
||||
}).get());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.jpackage.internal.util;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* A mutable container object for a value.
|
||||
* <p>
|
||||
* An alternative for cases where {@link AtomicReference} would be an overkill.
|
||||
* Sample usage:
|
||||
* {@snippet :
|
||||
* void foo(MessageNotifier messageNotifier) {
|
||||
* var lastMessage = Slot.createEmpty();
|
||||
*
|
||||
* messageNotifier.setListener(msg -> {
|
||||
* lastMessage.set(msg);
|
||||
* }).run();
|
||||
*
|
||||
* lastMessage.find().ifPresentOrElse(msg -> {
|
||||
* System.out.println(String.format("The last message: [%s]", msg));
|
||||
* }, () -> {
|
||||
* System.out.println("No messages received");
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* abstract class MessageNotifier {
|
||||
* MessageNotifier setListener(Consumer<String> messageConsumer) {
|
||||
* callback = messageConsumer;
|
||||
* return this;
|
||||
* }
|
||||
*
|
||||
* void run() {
|
||||
* for (;;) {
|
||||
* var msg = fetchNextMessage();
|
||||
* msg.ifPresent(callback);
|
||||
* if (msg.isEmpty()) {
|
||||
* break;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* abstract Optional<String> fetchNextMessage();
|
||||
*
|
||||
* private Consumer<String> callback;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* An alternative to the {@code Slot} would be either {@code
|
||||
* AtomicReference} or a single-element {@code String[]} or any other
|
||||
* suitable container type. {@code AtomicReference} would be an overkill if
|
||||
* thread-safety is not a concern and the use of other options would be
|
||||
* confusing.
|
||||
*
|
||||
* @param <T> value type
|
||||
*/
|
||||
public final class Slot<T> {
|
||||
|
||||
public static <T> Slot<T> createEmpty() {
|
||||
|
||||
return new Slot<>();
|
||||
}
|
||||
|
||||
public T get() {
|
||||
return find().orElseThrow();
|
||||
}
|
||||
|
||||
public Optional<T> find() {
|
||||
return Optional.ofNullable(value);
|
||||
}
|
||||
|
||||
public void set(T v) {
|
||||
value = Objects.requireNonNull(v);
|
||||
}
|
||||
|
||||
private T value;
|
||||
}
|
||||
@ -31,7 +31,7 @@ import java.io.Writer;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
@ -105,7 +105,11 @@ public final class XmlUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void mergeXmls(XMLStreamWriter xml, Collection<Source> sources)
|
||||
public static void concatXml(XMLStreamWriter xml, Source... sources) throws XMLStreamException, IOException {
|
||||
concatXml(xml, List.of(sources));
|
||||
}
|
||||
|
||||
public static void concatXml(XMLStreamWriter xml, Iterable<? extends Source> sources)
|
||||
throws XMLStreamException, IOException {
|
||||
xml = (XMLStreamWriter) Proxy.newProxyInstance(XMLStreamWriter.class.getClassLoader(),
|
||||
new Class<?>[]{XMLStreamWriter.class},
|
||||
|
||||
@ -111,7 +111,7 @@ class WixLauncherAsService extends LauncherAsService {
|
||||
sources.add(new DOMSource(n));
|
||||
}
|
||||
|
||||
XmlUtils.mergeXmls(xml, sources);
|
||||
XmlUtils.concatXml(xml, sources);
|
||||
|
||||
} catch (SAXException ex) {
|
||||
throw new IOException(ex);
|
||||
|
||||
@ -376,7 +376,7 @@ public class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
return cmd;
|
||||
}
|
||||
|
||||
public static Path createInputRuntimeImage() throws IOException {
|
||||
public static Path createInputRuntimeImage() {
|
||||
|
||||
final Path runtimeImageDir;
|
||||
|
||||
@ -406,7 +406,7 @@ public class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
}
|
||||
|
||||
public JPackageCommand setDefaultAppName() {
|
||||
return addArguments("--name", TKit.getCurrentDefaultAppName());
|
||||
return setArgumentValue("--name", TKit.getCurrentDefaultAppName());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -31,6 +31,7 @@ import static jdk.jpackage.internal.util.PListWriter.writeKey;
|
||||
import static jdk.jpackage.internal.util.PListWriter.writeString;
|
||||
import static jdk.jpackage.internal.util.PListWriter.writeStringArray;
|
||||
import static jdk.jpackage.internal.util.PListWriter.writeStringOptional;
|
||||
import static jdk.jpackage.internal.util.XmlUtils.initDocumentBuilder;
|
||||
import static jdk.jpackage.internal.util.XmlUtils.toXmlConsumer;
|
||||
import static jdk.jpackage.internal.util.function.ThrowingRunnable.toRunnable;
|
||||
|
||||
@ -60,6 +61,7 @@ import java.util.function.UnaryOperator;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
@ -71,6 +73,7 @@ import jdk.jpackage.internal.util.function.ThrowingConsumer;
|
||||
import jdk.jpackage.internal.util.function.ThrowingSupplier;
|
||||
import jdk.jpackage.test.MacSign.CertificateRequest;
|
||||
import jdk.jpackage.test.PackageTest.PackageHandlers;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
public final class MacHelper {
|
||||
|
||||
@ -297,79 +300,160 @@ public final class MacHelper {
|
||||
|
||||
public static void writeFaPListFragment(JPackageCommand cmd, XMLStreamWriter xml) {
|
||||
toRunnable(() -> {
|
||||
var allProps = Stream.of(cmd.getAllArgumentValues("--file-associations")).map(Path::of).map(propFile -> {
|
||||
try (var propFileReader = Files.newBufferedReader(propFile)) {
|
||||
var props = new Properties();
|
||||
props.load(propFileReader);
|
||||
return props;
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
if (cmd.hasArgument("--app-image")) {
|
||||
copyFaPListFragmentFromPredefinedAppImage(cmd, xml);
|
||||
} else {
|
||||
createFaPListFragmentFromFaProperties(cmd, xml);
|
||||
}
|
||||
}).run();
|
||||
}
|
||||
|
||||
private static void createFaPListFragmentFromFaProperties(JPackageCommand cmd, XMLStreamWriter xml)
|
||||
throws XMLStreamException, IOException {
|
||||
|
||||
var allProps = Stream.of(cmd.getAllArgumentValues("--file-associations")).map(Path::of).map(propFile -> {
|
||||
try (var propFileReader = Files.newBufferedReader(propFile)) {
|
||||
var props = new Properties();
|
||||
props.load(propFileReader);
|
||||
return props;
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}).toList();
|
||||
|
||||
if (!allProps.isEmpty()) {
|
||||
var bundleId = getPackageId(cmd);
|
||||
|
||||
Function<Properties, String> contentType = fa -> {
|
||||
return String.format("%s.%s", bundleId, Objects.requireNonNull(fa.getProperty("extension")));
|
||||
};
|
||||
|
||||
Function<Properties, Optional<String>> icon = fa -> {
|
||||
return Optional.ofNullable(fa.getProperty("icon")).map(Path::of).map(Path::getFileName).map(Path::toString);
|
||||
};
|
||||
|
||||
BiFunction<Properties, String, Optional<Boolean>> asBoolean = (fa, key) -> {
|
||||
return Optional.ofNullable(fa.getProperty(key)).map(Boolean::parseBoolean);
|
||||
};
|
||||
|
||||
BiFunction<Properties, String, List<String>> asList = (fa, key) -> {
|
||||
return Optional.ofNullable(fa.getProperty(key)).map(str -> {
|
||||
return List.of(str.split("[ ,]+"));
|
||||
}).orElseGet(List::of);
|
||||
};
|
||||
|
||||
writeKey(xml, "CFBundleDocumentTypes");
|
||||
writeArray(xml, toXmlConsumer(() -> {
|
||||
for (var fa : allProps) {
|
||||
writeDict(xml, toXmlConsumer(() -> {
|
||||
writeStringArray(xml, "LSItemContentTypes", List.of(contentType.apply(fa)));
|
||||
writeStringOptional(xml, "CFBundleTypeName", Optional.ofNullable(fa.getProperty("description")));
|
||||
writeString(xml, "LSHandlerRank", Optional.ofNullable(fa.getProperty("mac.LSHandlerRank")).orElse("Owner"));
|
||||
writeString(xml, "CFBundleTypeRole", Optional.ofNullable(fa.getProperty("mac.CFBundleTypeRole")).orElse("Editor"));
|
||||
writeStringOptional(xml, "NSPersistentStoreTypeKey", Optional.ofNullable(fa.getProperty("mac.NSPersistentStoreTypeKey")));
|
||||
writeStringOptional(xml, "NSDocumentClass", Optional.ofNullable(fa.getProperty("mac.NSDocumentClass")));
|
||||
writeBoolean(xml, "LSIsAppleDefaultForType", true);
|
||||
writeBooleanOptional(xml, "LSTypeIsPackage", asBoolean.apply(fa, "mac.LSTypeIsPackage"));
|
||||
writeBooleanOptional(xml, "LSSupportsOpeningDocumentsInPlace", asBoolean.apply(fa, "mac.LSSupportsOpeningDocumentsInPlace"));
|
||||
writeBooleanOptional(xml, "UISupportsDocumentBrowser", asBoolean.apply(fa, "mac.UISupportsDocumentBrowser"));
|
||||
writeStringOptional(xml, "CFBundleTypeIconFile", icon.apply(fa));
|
||||
}));
|
||||
}
|
||||
}).toList();
|
||||
}));
|
||||
|
||||
if (!allProps.isEmpty()) {
|
||||
var bundleId = getPackageId(cmd);
|
||||
|
||||
Function<Properties, String> contentType = fa -> {
|
||||
return String.format("%s.%s", bundleId, Objects.requireNonNull(fa.getProperty("extension")));
|
||||
};
|
||||
|
||||
Function<Properties, Optional<String>> icon = fa -> {
|
||||
return Optional.ofNullable(fa.getProperty("icon")).map(Path::of).map(Path::getFileName).map(Path::toString);
|
||||
};
|
||||
|
||||
BiFunction<Properties, String, Optional<Boolean>> asBoolean = (fa, key) -> {
|
||||
return Optional.ofNullable(fa.getProperty(key)).map(Boolean::parseBoolean);
|
||||
};
|
||||
|
||||
BiFunction<Properties, String, List<String>> asList = (fa, key) -> {
|
||||
return Optional.ofNullable(fa.getProperty(key)).map(str -> {
|
||||
return List.of(str.split("[ ,]+"));
|
||||
}).orElseGet(List::of);
|
||||
};
|
||||
|
||||
writeKey(xml, "CFBundleDocumentTypes");
|
||||
writeArray(xml, toXmlConsumer(() -> {
|
||||
for (var fa : allProps) {
|
||||
writeKey(xml, "UTExportedTypeDeclarations");
|
||||
writeArray(xml, toXmlConsumer(() -> {
|
||||
for (var fa : allProps) {
|
||||
writeDict(xml, toXmlConsumer(() -> {
|
||||
writeString(xml, "UTTypeIdentifier", contentType.apply(fa));
|
||||
writeStringOptional(xml, "UTTypeDescription", Optional.ofNullable(fa.getProperty("description")));
|
||||
if (fa.containsKey("mac.UTTypeConformsTo")) {
|
||||
writeStringArray(xml, "UTTypeConformsTo", asList.apply(fa, "mac.UTTypeConformsTo"));
|
||||
} else {
|
||||
writeStringArray(xml, "UTTypeConformsTo", List.of("public.data"));
|
||||
}
|
||||
writeStringOptional(xml, "UTTypeIconFile", icon.apply(fa));
|
||||
writeKey(xml, "UTTypeTagSpecification");
|
||||
writeDict(xml, toXmlConsumer(() -> {
|
||||
writeStringArray(xml, "LSItemContentTypes", List.of(contentType.apply(fa)));
|
||||
writeStringOptional(xml, "CFBundleTypeName", Optional.ofNullable(fa.getProperty("description")));
|
||||
writeString(xml, "LSHandlerRank", Optional.ofNullable(fa.getProperty("mac.LSHandlerRank")).orElse("Owner"));
|
||||
writeString(xml, "CFBundleTypeRole", Optional.ofNullable(fa.getProperty("mac.CFBundleTypeRole")).orElse("Editor"));
|
||||
writeStringOptional(xml, "NSPersistentStoreTypeKey", Optional.ofNullable(fa.getProperty("mac.NSPersistentStoreTypeKey")));
|
||||
writeStringOptional(xml, "NSDocumentClass", Optional.ofNullable(fa.getProperty("mac.NSDocumentClass")));
|
||||
writeBoolean(xml, "LSIsAppleDefaultForType", true);
|
||||
writeBooleanOptional(xml, "LSTypeIsPackage", asBoolean.apply(fa, "mac.LSTypeIsPackage"));
|
||||
writeBooleanOptional(xml, "LSSupportsOpeningDocumentsInPlace", asBoolean.apply(fa, "mac.LSSupportsOpeningDocumentsInPlace"));
|
||||
writeBooleanOptional(xml, "UISupportsDocumentBrowser", asBoolean.apply(fa, "mac.UISupportsDocumentBrowser"));
|
||||
writeStringOptional(xml, "CFBundleTypeIconFile", icon.apply(fa));
|
||||
writeStringArray(xml, "public.filename-extension", List.of(fa.getProperty("extension")));
|
||||
writeStringArray(xml, "public.mime-type", List.of(fa.getProperty("mime-type")));
|
||||
writeStringArray(xml, "NSExportableTypes", asList.apply(fa, "mac.NSExportableTypes"));
|
||||
}));
|
||||
}
|
||||
}));
|
||||
}));
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
writeKey(xml, "UTExportedTypeDeclarations");
|
||||
private static void copyFaPListFragmentFromPredefinedAppImage(JPackageCommand cmd, XMLStreamWriter xml)
|
||||
throws IOException, SAXException, XMLStreamException {
|
||||
|
||||
var predefinedAppImage = Path.of(Optional.ofNullable(cmd.getArgumentValue("--app-image")).orElseThrow(IllegalArgumentException::new));
|
||||
|
||||
var plistPath = ApplicationLayout.macAppImage().resolveAt(predefinedAppImage).contentDirectory().resolve("Info.plist");
|
||||
|
||||
try (var plistStream = Files.newInputStream(plistPath)) {
|
||||
var plist = new PListReader(initDocumentBuilder().parse(plistStream));
|
||||
|
||||
var entries = Stream.of("CFBundleDocumentTypes", "UTExportedTypeDeclarations").map(key -> {
|
||||
return plist.findArrayValue(key, false).map(stream -> {
|
||||
return stream.map(PListReader.class::cast).toList();
|
||||
}).map(plistList -> {
|
||||
return Map.entry(key, plistList);
|
||||
});
|
||||
}).filter(Optional::isPresent).map(Optional::get).toList();
|
||||
|
||||
for (var e : entries) {
|
||||
writeKey(xml, e.getKey());
|
||||
writeArray(xml, toXmlConsumer(() -> {
|
||||
for (var fa : allProps) {
|
||||
writeDict(xml, toXmlConsumer(() -> {
|
||||
writeString(xml, "UTTypeIdentifier", contentType.apply(fa));
|
||||
writeStringOptional(xml, "UTTypeDescription", Optional.ofNullable(fa.getProperty("description")));
|
||||
if (fa.containsKey("mac.UTTypeConformsTo")) {
|
||||
writeStringArray(xml, "UTTypeConformsTo", asList.apply(fa, "mac.UTTypeConformsTo"));
|
||||
} else {
|
||||
writeStringArray(xml, "UTTypeConformsTo", List.of("public.data"));
|
||||
}
|
||||
writeStringOptional(xml, "UTTypeIconFile", icon.apply(fa));
|
||||
writeKey(xml, "UTTypeTagSpecification");
|
||||
writeDict(xml, toXmlConsumer(() -> {
|
||||
writeStringArray(xml, "public.filename-extension", List.of(fa.getProperty("extension")));
|
||||
writeStringArray(xml, "public.mime-type", List.of(fa.getProperty("mime-type")));
|
||||
writeStringArray(xml, "NSExportableTypes", asList.apply(fa, "mac.NSExportableTypes"));
|
||||
}));
|
||||
}));
|
||||
for (var arrayElement : e.getValue()) {
|
||||
arrayElement.toXmlConsumer().accept(xml);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}).run();
|
||||
}
|
||||
}
|
||||
|
||||
public static Path createRuntimeBundle(Consumer<JPackageCommand> mutator) {
|
||||
return createRuntimeBundle(Optional.of(mutator));
|
||||
}
|
||||
|
||||
public static Path createRuntimeBundle() {
|
||||
return createRuntimeBundle(Optional.empty());
|
||||
}
|
||||
|
||||
public static Path createRuntimeBundle(Optional<Consumer<JPackageCommand>> mutator) {
|
||||
Objects.requireNonNull(mutator);
|
||||
|
||||
final var runtimeImage = JPackageCommand.createInputRuntimeImage();
|
||||
|
||||
final var runtimeBundleWorkDir = TKit.createTempDirectory("runtime-bundle");
|
||||
|
||||
final var unpackadeRuntimeBundleDir = runtimeBundleWorkDir.resolve("unpacked");
|
||||
|
||||
var cmd = new JPackageCommand()
|
||||
.useToolProvider(true)
|
||||
.ignoreDefaultRuntime(true)
|
||||
.dumpOutput(true)
|
||||
.setPackageType(PackageType.MAC_DMG)
|
||||
.setArgumentValue("--name", "foo")
|
||||
.addArguments("--runtime-image", runtimeImage)
|
||||
.addArguments("--dest", runtimeBundleWorkDir);
|
||||
|
||||
mutator.ifPresent(cmd::mutate);
|
||||
|
||||
cmd.execute();
|
||||
|
||||
MacHelper.withExplodedDmg(cmd, dmgImage -> {
|
||||
if (dmgImage.endsWith(cmd.appInstallationDirectory().getFileName())) {
|
||||
Executor.of("cp", "-R")
|
||||
.addArgument(dmgImage)
|
||||
.addArgument(unpackadeRuntimeBundleDir)
|
||||
.execute(0);
|
||||
}
|
||||
});
|
||||
|
||||
return unpackadeRuntimeBundleDir;
|
||||
}
|
||||
|
||||
public static Consumer<JPackageCommand> useKeychain(MacSign.ResolvedKeychain keychain) {
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
|
||||
package jdk.jpackage.internal.util;
|
||||
|
||||
import static jdk.jpackage.internal.util.XmlUtils.initDocumentBuilder;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
@ -30,6 +31,8 @@ import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -41,6 +44,7 @@ import java.util.stream.Stream;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import jdk.jpackage.internal.util.PListReader.Raw;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
import org.junit.jupiter.params.provider.EnumSource.Mode;
|
||||
@ -277,12 +281,49 @@ public class PListReaderTest {
|
||||
assertEquals("A", actualValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_toMap() {
|
||||
@ParameterizedTest
|
||||
@MethodSource("parsedPLists")
|
||||
public void test_toMap(ParsedPList data) {
|
||||
testSpec().xml(data.xml()).expect(data.xmlAsMap()).queryType(QueryType.TO_MAP_RECURSIVE).create().test();
|
||||
}
|
||||
|
||||
var builder = testSpec();
|
||||
@ParameterizedTest
|
||||
@MethodSource("parsedPLists")
|
||||
public void test_toConsumer(ParsedPList data, @TempDir Path workDir) throws IOException, SAXException {
|
||||
var node = createXml(data.xml);
|
||||
|
||||
builder.xml(
|
||||
var srcPList = new PListReader(node);
|
||||
|
||||
var sink = workDir.resolve("sink.xml");
|
||||
|
||||
XmlUtils.createXml(sink, xml -> {
|
||||
PListWriter.writePList(xml, srcPList.toXmlConsumer());
|
||||
});
|
||||
|
||||
try (var in = Files.newInputStream(sink)) {
|
||||
var dstPList = new PListReader(initDocumentBuilder().parse(in));
|
||||
|
||||
var src = srcPList.toMap(true);
|
||||
var dst = dstPList.toMap(true);
|
||||
|
||||
assertEquals(data.xmlAsMap(), src);
|
||||
assertEquals(data.xmlAsMap(), dst);
|
||||
}
|
||||
}
|
||||
|
||||
private record ParsedPList(Map<String, Object> xmlAsMap, String... xml) {
|
||||
ParsedPList {
|
||||
Objects.requireNonNull(xmlAsMap);
|
||||
}
|
||||
|
||||
ParsedPList(Map<String, Object> xmlAsMap, List<String> xml) {
|
||||
this(xmlAsMap, xml.toArray(String[]::new));
|
||||
}
|
||||
}
|
||||
|
||||
private static Stream<ParsedPList> parsedPLists() {
|
||||
|
||||
var xml = List.of(
|
||||
"<key>AppName</key>",
|
||||
"<string>Hello</string>",
|
||||
"<!-- Application version -->",
|
||||
@ -367,7 +408,7 @@ public class PListReaderTest {
|
||||
)
|
||||
);
|
||||
|
||||
builder.expect(expected).queryType(QueryType.TO_MAP_RECURSIVE).create().test();
|
||||
return Stream.of(new ParsedPList(expected, xml));
|
||||
}
|
||||
|
||||
private static List<TestSpec> test() {
|
||||
|
||||
@ -23,11 +23,13 @@
|
||||
|
||||
import static java.util.Collections.unmodifiableSortedSet;
|
||||
import static java.util.Map.entry;
|
||||
import jdk.jpackage.internal.util.Slot;
|
||||
import static jdk.jpackage.internal.util.PListWriter.writeDict;
|
||||
import static jdk.jpackage.internal.util.PListWriter.writePList;
|
||||
import static jdk.jpackage.internal.util.PListWriter.writeString;
|
||||
import static jdk.jpackage.internal.util.XmlUtils.createXml;
|
||||
import static jdk.jpackage.internal.util.XmlUtils.toXmlConsumer;
|
||||
import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
@ -43,15 +45,17 @@ import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import jdk.jpackage.internal.util.PListReader;
|
||||
import jdk.jpackage.internal.util.function.ThrowingBiConsumer;
|
||||
import jdk.jpackage.internal.util.function.ThrowingConsumer;
|
||||
import jdk.jpackage.test.Annotations.Parameter;
|
||||
import jdk.jpackage.test.Annotations.ParameterSupplier;
|
||||
import jdk.jpackage.test.Annotations.Test;
|
||||
import jdk.jpackage.test.ConfigurationTarget;
|
||||
import jdk.jpackage.test.JPackageCommand;
|
||||
import jdk.jpackage.test.JPackageStringBundle;
|
||||
import jdk.jpackage.test.MacHelper;
|
||||
@ -80,38 +84,67 @@ public class CustomInfoPListTest {
|
||||
@Test
|
||||
@ParameterSupplier("customPLists")
|
||||
public void testAppImage(TestConfig cfg) throws Throwable {
|
||||
var cmd = cfg.init(JPackageCommand.helloAppImage());
|
||||
var verifier = cfg.createPListFilesVerifier(cmd.executePrerequisiteActions());
|
||||
cmd.executeAndAssertHelloAppImageCreated();
|
||||
verifier.accept(cmd);
|
||||
testApp(new ConfigurationTarget(JPackageCommand.helloAppImage()), cfg);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ParameterSupplier("customPLists")
|
||||
public void testNativePackage(TestConfig cfg) {
|
||||
List<ThrowingConsumer<JPackageCommand>> verifier = new ArrayList<>();
|
||||
new PackageTest().configureHelloApp().addInitializer(cmd -> {
|
||||
cfg.init(cmd.setFakeRuntime());
|
||||
}).addRunOnceInitializer(() -> {
|
||||
verifier.add(cfg.createPListFilesVerifier(JPackageCommand.helloAppImage().executePrerequisiteActions()));
|
||||
public void testPackage(TestConfig cfg) {
|
||||
testApp(new ConfigurationTarget(new PackageTest().configureHelloApp()), cfg);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ParameterSupplier("customPLists")
|
||||
public void testFromAppImage(TestConfig cfg) {
|
||||
|
||||
var verifier = Slot.<Consumer<JPackageCommand>>createEmpty();
|
||||
|
||||
var appImageCmd = JPackageCommand.helloAppImage().setFakeRuntime();
|
||||
|
||||
new PackageTest().addRunOnceInitializer(() -> {
|
||||
// Create the input app image with custom plist file(s).
|
||||
// Call JPackageCommand.executePrerequisiteActions() to initialize
|
||||
// all command line options.
|
||||
cfg.init(appImageCmd.executePrerequisiteActions());
|
||||
appImageCmd.execute();
|
||||
verifier.set(cfg.createPListFilesVerifier(appImageCmd));
|
||||
}).addInitializer(cmd -> {
|
||||
cmd.removeArgumentWithValue("--input").setArgumentValue("--app-image", appImageCmd.outputBundle());
|
||||
}).addInstallVerifier(cmd -> {
|
||||
verifier.get(0).accept(cmd);
|
||||
verifier.get().accept(cmd);
|
||||
}).run(Action.CREATE_AND_UNPACK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRuntime() {
|
||||
final Path runtimeImage[] = new Path[1];
|
||||
@Parameter("true")
|
||||
@Parameter("false")
|
||||
public void testRuntime(boolean runtimeBundle) {
|
||||
|
||||
var runtimeImage = Slot.<Path>createEmpty();
|
||||
|
||||
var cfg = new TestConfig(Set.of(CustomPListType.RUNTIME));
|
||||
|
||||
new PackageTest().addRunOnceInitializer(() -> {
|
||||
runtimeImage[0] = JPackageCommand.createInputRuntimeImage();
|
||||
if (runtimeBundle) {
|
||||
// Use custom plist file with the input runtime bundle.
|
||||
runtimeImage.set(MacHelper.createRuntimeBundle(toConsumer(buildRuntimeBundleCmd -> {
|
||||
// Use the same name for the input runtime bundle as the name of the output bundle.
|
||||
// This is to make the plist file validation pass, as the custom plist file
|
||||
// is configured for the command building the input runtime bundle,
|
||||
// but the plist file from the output bundle is examined.
|
||||
buildRuntimeBundleCmd.setDefaultAppName();
|
||||
cfg.init(buildRuntimeBundleCmd);
|
||||
})));
|
||||
} else {
|
||||
runtimeImage.set(JPackageCommand.createInputRuntimeImage());
|
||||
}
|
||||
}).addInitializer(cmd -> {
|
||||
cmd.ignoreDefaultRuntime(true)
|
||||
.removeArgumentWithValue("--input")
|
||||
.setArgumentValue("--runtime-image", runtimeImage[0]);
|
||||
cfg.init(cmd);
|
||||
.setArgumentValue("--runtime-image", runtimeImage.get());
|
||||
if (!runtimeBundle) {
|
||||
cfg.init(cmd);
|
||||
}
|
||||
}).addInstallVerifier(cmd -> {
|
||||
cfg.createPListFilesVerifier(cmd).accept(cmd);
|
||||
}).run(Action.CREATE_AND_UNPACK);
|
||||
@ -128,6 +161,31 @@ public class CustomInfoPListTest {
|
||||
}).toList();
|
||||
}
|
||||
|
||||
private void testApp(ConfigurationTarget target, TestConfig cfg) {
|
||||
|
||||
List<Consumer<JPackageCommand>> verifier = new ArrayList<>();
|
||||
|
||||
target.addInitializer(JPackageCommand::setFakeRuntime);
|
||||
|
||||
target.addInitializer(toConsumer(cfg::init));
|
||||
|
||||
target.addRunOnceInitializer(_ -> {
|
||||
verifier.add(cfg.createPListFilesVerifier(
|
||||
target.cmd().orElseGet(JPackageCommand::helloAppImage).executePrerequisiteActions()
|
||||
));
|
||||
});
|
||||
|
||||
target.cmd().ifPresent(JPackageCommand::executeAndAssertHelloAppImageCreated);
|
||||
|
||||
target.addInstallVerifier(cmd -> {
|
||||
verifier.get(0).accept(cmd);
|
||||
});
|
||||
|
||||
target.test().ifPresent(test -> {
|
||||
test.run(Action.CREATE_AND_UNPACK);
|
||||
});
|
||||
}
|
||||
|
||||
private static List<String> toStringList(PListReader plistReader) {
|
||||
return MacHelper.flatMapPList(plistReader).entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).map(e -> {
|
||||
return String.format("%s: %s", e.getKey(), e.getValue());
|
||||
@ -171,27 +229,37 @@ public class CustomInfoPListTest {
|
||||
return cmd;
|
||||
}
|
||||
|
||||
ThrowingConsumer<JPackageCommand> createPListFilesVerifier(JPackageCommand cmd) throws IOException {
|
||||
ThrowingConsumer<JPackageCommand> defaultVerifier = otherCmd -> {
|
||||
Consumer<JPackageCommand> createPListFilesVerifier(JPackageCommand cmd) {
|
||||
Consumer<JPackageCommand> customPListFilesVerifier = toConsumer(otherCmd -> {
|
||||
for (var customPList : customPLists) {
|
||||
customPList.verifyPListFile(otherCmd);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Get the list of default plist files.
|
||||
// These are the plist files created from the plist file templates in jpackage resources.
|
||||
var defaultPListFiles = CustomPListType.defaultRoles(customPLists);
|
||||
|
||||
if (defaultPListFiles.isEmpty()) {
|
||||
return defaultVerifier;
|
||||
// All plist files in the bundle are customized.
|
||||
return customPListFilesVerifier;
|
||||
} else {
|
||||
var vanillaCmd = new JPackageCommand().setFakeRuntime()
|
||||
// There are some default plist files in the bundle.
|
||||
// Verify the expected default plist files are such.
|
||||
|
||||
// Create a copy of the `cmd` without the resource directory and with the app image bundling type.
|
||||
// Execute it and get the default plist files.
|
||||
var vanillaCmd = new JPackageCommand()
|
||||
.addArguments(cmd.getAllArguments())
|
||||
.setPackageType(PackageType.IMAGE)
|
||||
.removeArgumentWithValue("--resource-dir")
|
||||
.setArgumentValue("--dest", TKit.createTempDirectory("vanilla"));
|
||||
vanillaCmd.executeIgnoreExitCode().assertExitCodeIsZero();
|
||||
vanillaCmd.execute();
|
||||
|
||||
return otherCmd -> {
|
||||
defaultVerifier.accept(otherCmd);
|
||||
// Verify custom plist files.
|
||||
customPListFilesVerifier.accept(otherCmd);
|
||||
// Verify default plist files.
|
||||
for (var defaultPListFile : defaultPListFiles) {
|
||||
final var expectedPListPath = defaultPListFile.path(vanillaCmd);
|
||||
final var expectedPList = MacHelper.readPList(expectedPListPath);
|
||||
@ -234,7 +302,10 @@ public class CustomInfoPListTest {
|
||||
CustomPListFactory.PLIST_OUTPUT::writeAppPlist,
|
||||
"Info.plist"),
|
||||
|
||||
APP_WITH_FA(APP),
|
||||
APP_WITH_FA(
|
||||
CustomPListFactory.PLIST_INPUT::writeAppPlistWithFa,
|
||||
CustomPListFactory.PLIST_OUTPUT::writeAppPlistWithFa,
|
||||
"Info.plist"),
|
||||
|
||||
EMBEDDED_RUNTIME(
|
||||
CustomPListFactory.PLIST_INPUT::writeEmbeddedRuntimePlist,
|
||||
@ -256,12 +327,6 @@ public class CustomInfoPListTest {
|
||||
this.outputPlistFilename = outputPlistFilename;
|
||||
}
|
||||
|
||||
private CustomPListType(CustomPListType other) {
|
||||
this.inputPlistWriter = other.inputPlistWriter;
|
||||
this.outputPlistWriter = other.outputPlistWriter;
|
||||
this.outputPlistFilename = other.outputPlistFilename;
|
||||
}
|
||||
|
||||
void createInputPListFile(JPackageCommand cmd) throws IOException {
|
||||
createXml(Path.of(cmd.getArgumentValue("--resource-dir")).resolve(outputPlistFilename), xml -> {
|
||||
inputPlistWriter.accept(cmd, xml);
|
||||
@ -313,7 +378,15 @@ public class CustomInfoPListTest {
|
||||
PLIST_OUTPUT,
|
||||
;
|
||||
|
||||
private void writeAppPlistWithFa(JPackageCommand cmd, XMLStreamWriter xml) throws XMLStreamException, IOException {
|
||||
writeAppPlist(cmd, xml, true);
|
||||
}
|
||||
|
||||
private void writeAppPlist(JPackageCommand cmd, XMLStreamWriter xml) throws XMLStreamException, IOException {
|
||||
writeAppPlist(cmd, xml, false);
|
||||
}
|
||||
|
||||
private void writeAppPlist(JPackageCommand cmd, XMLStreamWriter xml, boolean withFa) throws XMLStreamException, IOException {
|
||||
writePList(xml, toXmlConsumer(() -> {
|
||||
writeDict(xml, toXmlConsumer(() -> {
|
||||
writeString(xml, "CustomAppProperty", "App");
|
||||
@ -326,7 +399,7 @@ public class CustomInfoPListTest {
|
||||
writeString(xml, "CFBundleVersion", value("DEPLOY_BUNDLE_CFBUNDLE_VERSION", cmd.version()));
|
||||
writeString(xml, "NSHumanReadableCopyright", value("DEPLOY_BUNDLE_COPYRIGHT",
|
||||
JPackageStringBundle.MAIN.cannedFormattedString("param.copyright.default", new Date()).getValue()));
|
||||
if (cmd.hasArgument("--file-associations")) {
|
||||
if (withFa) {
|
||||
if (this == PLIST_INPUT) {
|
||||
xml.writeCharacters("DEPLOY_FILE_ASSOCIATIONS");
|
||||
} else {
|
||||
|
||||
@ -23,19 +23,15 @@
|
||||
|
||||
import static jdk.jpackage.internal.util.function.ThrowingConsumer.toConsumer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.test.Annotations.Parameter;
|
||||
import jdk.jpackage.test.Annotations.Test;
|
||||
import jdk.jpackage.test.Executor;
|
||||
import jdk.jpackage.test.JPackageCommand;
|
||||
import jdk.jpackage.test.MacHelper;
|
||||
import jdk.jpackage.test.MacSign;
|
||||
import jdk.jpackage.test.PackageTest;
|
||||
import jdk.jpackage.test.PackageType;
|
||||
import jdk.jpackage.test.TKit;
|
||||
|
||||
/**
|
||||
* Tests generation of dmg and pkg with --mac-sign and related arguments. Test
|
||||
@ -97,37 +93,10 @@ public class SigningRuntimeImagePackageTest {
|
||||
return cmd;
|
||||
}
|
||||
|
||||
private static Path createInputRuntimeBundle(MacSign.ResolvedKeychain keychain, int certIndex) throws IOException {
|
||||
|
||||
final var runtimeImage = JPackageCommand.createInputRuntimeImage();
|
||||
|
||||
final var runtimeBundleWorkDir = TKit.createTempDirectory("runtime-bundle");
|
||||
|
||||
final var unpackadeRuntimeBundleDir = runtimeBundleWorkDir.resolve("unpacked");
|
||||
|
||||
var cmd = new JPackageCommand()
|
||||
.useToolProvider(true)
|
||||
.ignoreDefaultRuntime(true)
|
||||
.dumpOutput(true)
|
||||
.setPackageType(PackageType.MAC_DMG)
|
||||
.setArgumentValue("--name", "foo")
|
||||
.addArguments("--runtime-image", runtimeImage)
|
||||
.addArguments("--dest", runtimeBundleWorkDir);
|
||||
|
||||
addSignOptions(cmd, keychain, certIndex);
|
||||
|
||||
cmd.execute();
|
||||
|
||||
MacHelper.withExplodedDmg(cmd, dmgImage -> {
|
||||
if (dmgImage.endsWith(cmd.appInstallationDirectory().getFileName())) {
|
||||
Executor.of("cp", "-R")
|
||||
.addArgument(dmgImage)
|
||||
.addArgument(unpackadeRuntimeBundleDir)
|
||||
.execute(0);
|
||||
}
|
||||
private static Path createInputRuntimeBundle(MacSign.ResolvedKeychain keychain, int certIndex) {
|
||||
return MacHelper.createRuntimeBundle(cmd -> {
|
||||
addSignOptions(cmd, keychain, certIndex);
|
||||
});
|
||||
|
||||
return unpackadeRuntimeBundleDir;
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -26,7 +26,6 @@ import static jdk.internal.util.OperatingSystem.MACOS;
|
||||
import static jdk.jpackage.test.TKit.assertFalse;
|
||||
import static jdk.jpackage.test.TKit.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
@ -34,9 +33,7 @@ import java.util.function.Predicate;
|
||||
import jdk.jpackage.internal.util.function.ThrowingSupplier;
|
||||
import jdk.jpackage.test.Annotations.Parameter;
|
||||
import jdk.jpackage.test.Annotations.Test;
|
||||
import jdk.jpackage.test.Executor;
|
||||
import jdk.jpackage.test.JPackageCommand;
|
||||
import jdk.jpackage.test.JavaTool;
|
||||
import jdk.jpackage.test.LinuxHelper;
|
||||
import jdk.jpackage.test.MacHelper;
|
||||
import jdk.jpackage.test.PackageTest;
|
||||
@ -89,7 +86,7 @@ public class RuntimePackageTest {
|
||||
|
||||
@Test(ifOS = MACOS)
|
||||
public static void testFromBundle() {
|
||||
init(RuntimePackageTest::createInputRuntimeBundle).run();
|
||||
init(MacHelper::createRuntimeBundle).run();
|
||||
}
|
||||
|
||||
@Test(ifOS = LINUX)
|
||||
@ -114,7 +111,7 @@ public class RuntimePackageTest {
|
||||
}
|
||||
|
||||
private static PackageTest init() {
|
||||
return init(RuntimePackageTest::createInputRuntimeImage);
|
||||
return init(JPackageCommand::createInputRuntimeImage);
|
||||
}
|
||||
|
||||
private static PackageTest init(ThrowingSupplier<Path> createRuntime) {
|
||||
@ -168,58 +165,4 @@ public class RuntimePackageTest {
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
private static Path createInputRuntimeImage() throws IOException {
|
||||
|
||||
final Path runtimeImageDir;
|
||||
|
||||
if (JPackageCommand.DEFAULT_RUNTIME_IMAGE != null) {
|
||||
runtimeImageDir = JPackageCommand.DEFAULT_RUNTIME_IMAGE;
|
||||
} else {
|
||||
runtimeImageDir = TKit.createTempDirectory("runtime-image").resolve("data");
|
||||
|
||||
new Executor().setToolProvider(JavaTool.JLINK)
|
||||
.dumpOutput()
|
||||
.addArguments(
|
||||
"--output", runtimeImageDir.toString(),
|
||||
"--add-modules", "java.desktop",
|
||||
"--strip-debug",
|
||||
"--no-header-files",
|
||||
"--no-man-pages")
|
||||
.execute();
|
||||
}
|
||||
|
||||
return runtimeImageDir;
|
||||
}
|
||||
|
||||
private static Path createInputRuntimeBundle() throws IOException {
|
||||
|
||||
final var runtimeImage = createInputRuntimeImage();
|
||||
|
||||
final var runtimeBundleWorkDir = TKit.createTempDirectory("runtime-bundle");
|
||||
|
||||
final var unpackadeRuntimeBundleDir = runtimeBundleWorkDir.resolve("unpacked");
|
||||
|
||||
var cmd = new JPackageCommand()
|
||||
.useToolProvider(true)
|
||||
.ignoreDefaultRuntime(true)
|
||||
.dumpOutput(true)
|
||||
.setPackageType(PackageType.MAC_DMG)
|
||||
.setArgumentValue("--name", "foo")
|
||||
.addArguments("--runtime-image", runtimeImage)
|
||||
.addArguments("--dest", runtimeBundleWorkDir);
|
||||
|
||||
cmd.execute();
|
||||
|
||||
MacHelper.withExplodedDmg(cmd, dmgImage -> {
|
||||
if (dmgImage.endsWith(cmd.appInstallationDirectory().getFileName())) {
|
||||
Executor.of("cp", "-R")
|
||||
.addArgument(dmgImage)
|
||||
.addArgument(unpackadeRuntimeBundleDir)
|
||||
.execute(0);
|
||||
}
|
||||
});
|
||||
|
||||
return unpackadeRuntimeBundleDir;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user