8289771: jpackage: ResourceEditor error when path is overly long on Windows
Reviewed-by: almatvee
This commit is contained in:
parent
c4c6b1fe06
commit
080f1cc8cd
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2024, 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
|
||||
@ -30,6 +30,7 @@ import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
@ -40,6 +41,7 @@ import java.util.Properties;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.function.Supplier;
|
||||
import static jdk.jpackage.internal.OverridableResource.createResource;
|
||||
import static jdk.jpackage.internal.ShortPathUtils.adjustPath;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.APP_NAME;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.COPYRIGHT;
|
||||
import static jdk.jpackage.internal.StandardBundlerParam.DESCRIPTION;
|
||||
@ -112,7 +114,7 @@ final class ExecutableRebrander {
|
||||
}
|
||||
|
||||
private void rebrandExecutable(Map<String, ? super Object> params,
|
||||
Path target, UpdateResourceAction action) throws IOException {
|
||||
final Path target, UpdateResourceAction action) throws IOException {
|
||||
try {
|
||||
String tempDirectory = TEMP_ROOT.fetchFrom(params)
|
||||
.toAbsolutePath().toString();
|
||||
@ -125,10 +127,11 @@ final class ExecutableRebrander {
|
||||
|
||||
target.toFile().setWritable(true, true);
|
||||
|
||||
long resourceLock = lockResource(target.toString());
|
||||
var shortTargetPath = ShortPathUtils.toShortPath(target);
|
||||
long resourceLock = lockResource(shortTargetPath.orElse(target).toString());
|
||||
if (resourceLock == 0) {
|
||||
throw new RuntimeException(MessageFormat.format(
|
||||
I18N.getString("error.lock-resource"), target));
|
||||
I18N.getString("error.lock-resource"), shortTargetPath.orElse(target)));
|
||||
}
|
||||
|
||||
final boolean resourceUnlockedSuccess;
|
||||
@ -144,6 +147,14 @@ final class ExecutableRebrander {
|
||||
resourceUnlockedSuccess = true;
|
||||
} else {
|
||||
resourceUnlockedSuccess = unlockResource(resourceLock);
|
||||
if (shortTargetPath.isPresent()) {
|
||||
// Windows will rename the excuatble in the unlock operation.
|
||||
// Should restore executable's name.
|
||||
var tmpPath = target.getParent().resolve(
|
||||
target.getFileName().toString() + ".restore");
|
||||
Files.move(shortTargetPath.get(), tmpPath);
|
||||
Files.move(tmpPath, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -236,6 +247,7 @@ final class ExecutableRebrander {
|
||||
|
||||
private static void iconSwapWrapper(long resourceLock,
|
||||
String iconTarget) {
|
||||
iconTarget = adjustPath(iconTarget);
|
||||
if (iconSwap(resourceLock, iconTarget) != 0) {
|
||||
throw new RuntimeException(MessageFormat.format(I18N.getString(
|
||||
"error.icon-swap"), iconTarget));
|
||||
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
@SuppressWarnings("restricted")
|
||||
final class ShortPathUtils {
|
||||
static String adjustPath(String path) {
|
||||
return toShortPath(path).orElse(path);
|
||||
}
|
||||
|
||||
static Path adjustPath(Path path) {
|
||||
return toShortPath(path).orElse(path);
|
||||
}
|
||||
|
||||
static Optional<String> toShortPath(String path) {
|
||||
Objects.requireNonNull(path);
|
||||
return toShortPath(Path.of(path)).map(Path::toString);
|
||||
}
|
||||
|
||||
static Optional<Path> toShortPath(Path path) {
|
||||
if (!Files.exists(path)) {
|
||||
throw new IllegalArgumentException(String.format("[%s] path does not exist", path));
|
||||
}
|
||||
|
||||
var normPath = path.normalize().toAbsolutePath().toString();
|
||||
if (normPath.length() > MAX_PATH) {
|
||||
return Optional.of(Path.of(getShortPathWrapper(normPath)));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private static String getShortPathWrapper(final String longPath) {
|
||||
String effectivePath;
|
||||
if (!longPath.startsWith(LONG_PATH_PREFIX)) {
|
||||
effectivePath = LONG_PATH_PREFIX + longPath;
|
||||
} else {
|
||||
effectivePath = longPath;
|
||||
}
|
||||
|
||||
return Optional.ofNullable(getShortPath(effectivePath)).orElseThrow(
|
||||
() -> new ShortPathException(MessageFormat.format(I18N.getString(
|
||||
"error.short-path-conv-fail"), effectivePath)));
|
||||
}
|
||||
|
||||
static final class ShortPathException extends RuntimeException {
|
||||
|
||||
ShortPathException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
private static native String getShortPath(String longPath);
|
||||
|
||||
private static final int MAX_PATH = 240;
|
||||
// See https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getshortpathnamew
|
||||
private static final String LONG_PATH_PREFIX = "\\\\?\\";
|
||||
|
||||
static {
|
||||
System.loadLibrary("jpackage");
|
||||
}
|
||||
}
|
@ -526,9 +526,10 @@ public class WinMsiBundler extends AbstractBundler {
|
||||
"message.preparing-msi-config"), msiOut.toAbsolutePath()
|
||||
.toString()));
|
||||
|
||||
WixPipeline wixPipeline = new WixPipeline()
|
||||
.setToolset(wixToolset)
|
||||
.setWixObjDir(TEMP_ROOT.fetchFrom(params).resolve("wixobj"))
|
||||
var wixObjDir = TEMP_ROOT.fetchFrom(params).resolve("wixobj");
|
||||
|
||||
var wixPipeline = WixPipeline.build()
|
||||
.setWixObjDir(wixObjDir)
|
||||
.setWorkDir(WIN_APP_IMAGE.fetchFrom(params))
|
||||
.addSource(CONFIG_ROOT.fetchFrom(params).resolve("main.wxs"),
|
||||
wixVars);
|
||||
@ -605,13 +606,13 @@ public class WinMsiBundler extends AbstractBundler {
|
||||
// Cultures from custom files and a single primary Culture are
|
||||
// included into "-cultures" list
|
||||
for (var wxl : primaryWxlFiles) {
|
||||
wixPipeline.addLightOptions("-loc", wxl.toAbsolutePath().normalize().toString());
|
||||
wixPipeline.addLightOptions("-loc", wxl.toString());
|
||||
}
|
||||
|
||||
List<String> cultures = new ArrayList<>();
|
||||
for (var wxl : customWxlFiles) {
|
||||
wxl = configDir.resolve(wxl.getFileName());
|
||||
wixPipeline.addLightOptions("-loc", wxl.toAbsolutePath().normalize().toString());
|
||||
wixPipeline.addLightOptions("-loc", wxl.toString());
|
||||
cultures.add(getCultureFromWxlFile(wxl));
|
||||
}
|
||||
|
||||
@ -638,7 +639,8 @@ public class WinMsiBundler extends AbstractBundler {
|
||||
}
|
||||
}
|
||||
|
||||
wixPipeline.buildMsi(msiOut.toAbsolutePath());
|
||||
Files.createDirectories(wixObjDir);
|
||||
wixPipeline.create(wixToolset).buildMsi(msiOut.toAbsolutePath());
|
||||
|
||||
return msiOut;
|
||||
}
|
||||
@ -678,14 +680,14 @@ public class WinMsiBundler extends AbstractBundler {
|
||||
if (nodes.getLength() != 1) {
|
||||
throw new IOException(MessageFormat.format(I18N.getString(
|
||||
"error.extract-culture-from-wix-l10n-file"),
|
||||
wxlPath.toAbsolutePath()));
|
||||
wxlPath.toAbsolutePath().normalize()));
|
||||
}
|
||||
|
||||
return nodes.item(0).getNodeValue();
|
||||
} catch (XPathExpressionException | ParserConfigurationException
|
||||
| SAXException ex) {
|
||||
throw new IOException(MessageFormat.format(I18N.getString(
|
||||
"error.read-wix-l10n-file"), wxlPath.toAbsolutePath()), ex);
|
||||
"error.read-wix-l10n-file"), wxlPath.toAbsolutePath().normalize()), ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ abstract class WixFragmentBuilder {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
void configureWixPipeline(WixPipeline wixPipeline) {
|
||||
void configureWixPipeline(WixPipeline.Builder wixPipeline) {
|
||||
wixPipeline.addSource(configRoot.resolve(outputFileName),
|
||||
Optional.ofNullable(wixVariables).map(WixVariables::getValues).orElse(
|
||||
null));
|
||||
|
@ -29,65 +29,130 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import static jdk.jpackage.internal.ShortPathUtils.adjustPath;
|
||||
import jdk.jpackage.internal.util.PathUtils;
|
||||
|
||||
/**
|
||||
* WiX pipeline. Compiles and links WiX sources.
|
||||
*/
|
||||
public class WixPipeline {
|
||||
WixPipeline() {
|
||||
sources = new ArrayList<>();
|
||||
lightOptions = new ArrayList<>();
|
||||
final class WixPipeline {
|
||||
|
||||
static final class Builder {
|
||||
Builder() {
|
||||
}
|
||||
|
||||
WixPipeline setToolset(WixToolset v) {
|
||||
toolset = v;
|
||||
return this;
|
||||
WixPipeline create(WixToolset toolset) {
|
||||
Objects.requireNonNull(toolset);
|
||||
Objects.requireNonNull(workDir);
|
||||
Objects.requireNonNull(wixObjDir);
|
||||
if (sources.isEmpty()) {
|
||||
throw new IllegalArgumentException("no sources");
|
||||
}
|
||||
|
||||
WixPipeline setWixVariables(Map<String, String> v) {
|
||||
wixVariables = v;
|
||||
return this;
|
||||
final var absWorkDir = workDir.normalize().toAbsolutePath();
|
||||
|
||||
final UnaryOperator<Path> normalizePath = path -> {
|
||||
return path.normalize().toAbsolutePath();
|
||||
};
|
||||
|
||||
final var absObjWorkDir = normalizePath.apply(wixObjDir);
|
||||
|
||||
var relSources = sources.stream().map(source -> {
|
||||
return source.overridePath(normalizePath.apply(source.path));
|
||||
}).toList();
|
||||
|
||||
return new WixPipeline(toolset, adjustPath(absWorkDir), absObjWorkDir,
|
||||
wixVariables, mapLightOptions(normalizePath), relSources);
|
||||
}
|
||||
|
||||
WixPipeline setWixObjDir(Path v) {
|
||||
Builder setWixObjDir(Path v) {
|
||||
wixObjDir = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
WixPipeline setWorkDir(Path v) {
|
||||
Builder setWorkDir(Path v) {
|
||||
workDir = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
WixPipeline addSource(Path source, Map<String, String> wixVariables) {
|
||||
WixSource entry = new WixSource();
|
||||
entry.source = source;
|
||||
entry.variables = wixVariables;
|
||||
sources.add(entry);
|
||||
Builder setWixVariables(Map<String, String> v) {
|
||||
wixVariables.clear();
|
||||
wixVariables.putAll(v);
|
||||
return this;
|
||||
}
|
||||
|
||||
WixPipeline addLightOptions(String ... v) {
|
||||
Builder addSource(Path source, Map<String, String> wixVariables) {
|
||||
sources.add(new WixSource(source, wixVariables));
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder addLightOptions(String ... v) {
|
||||
lightOptions.addAll(List.of(v));
|
||||
return this;
|
||||
}
|
||||
|
||||
private List<String> mapLightOptions(UnaryOperator<Path> normalizePath) {
|
||||
var pathOptions = Set.of("-b", "-loc");
|
||||
List<String> reply = new ArrayList<>();
|
||||
boolean convPath = false;
|
||||
for (var opt : lightOptions) {
|
||||
if (convPath) {
|
||||
opt = normalizePath.apply(Path.of(opt)).toString();
|
||||
convPath = false;
|
||||
} else if (pathOptions.contains(opt)) {
|
||||
convPath = true;
|
||||
}
|
||||
reply.add(opt);
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
private Path workDir;
|
||||
private Path wixObjDir;
|
||||
private final Map<String, String> wixVariables = new HashMap<>();
|
||||
private final List<String> lightOptions = new ArrayList<>();
|
||||
private final List<WixSource> sources = new ArrayList<>();
|
||||
}
|
||||
|
||||
static Builder build() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
private WixPipeline(WixToolset toolset, Path workDir, Path wixObjDir,
|
||||
Map<String, String> wixVariables, List<String> lightOptions,
|
||||
List<WixSource> sources) {
|
||||
this.toolset = toolset;
|
||||
this.workDir = workDir;
|
||||
this.wixObjDir = wixObjDir;
|
||||
this.wixVariables = wixVariables;
|
||||
this.lightOptions = lightOptions;
|
||||
this.sources = sources;
|
||||
}
|
||||
|
||||
void buildMsi(Path msi) throws IOException {
|
||||
Objects.requireNonNull(workDir);
|
||||
|
||||
// Use short path to the output msi to workaround
|
||||
// WiX limitations of handling long paths.
|
||||
var transientMsi = wixObjDir.resolve("a.msi");
|
||||
|
||||
switch (toolset.getType()) {
|
||||
case Wix3 -> buildMsiWix3(msi);
|
||||
case Wix4 -> buildMsiWix4(msi);
|
||||
case Wix3 -> buildMsiWix3(transientMsi);
|
||||
case Wix4 -> buildMsiWix4(transientMsi);
|
||||
default -> throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
IOUtils.copyFile(workDir.resolve(transientMsi), msi);
|
||||
}
|
||||
|
||||
private void addWixVariblesToCommandLine(
|
||||
@ -141,7 +206,7 @@ public class WixPipeline {
|
||||
"build",
|
||||
"-nologo",
|
||||
"-pdbtype", "none",
|
||||
"-intermediatefolder", wixObjDir.toAbsolutePath().toString(),
|
||||
"-intermediatefolder", wixObjDir.toString(),
|
||||
"-ext", "WixToolset.Util.wixext",
|
||||
"-arch", WixFragmentBuilder.is64Bit() ? "x64" : "x86"
|
||||
));
|
||||
@ -151,7 +216,7 @@ public class WixPipeline {
|
||||
addWixVariblesToCommandLine(mergedSrcWixVars, cmdline);
|
||||
|
||||
cmdline.addAll(sources.stream().map(wixSource -> {
|
||||
return wixSource.source.toAbsolutePath().toString();
|
||||
return wixSource.path.toString();
|
||||
}).toList());
|
||||
|
||||
cmdline.addAll(List.of("-out", msi.toString()));
|
||||
@ -182,15 +247,15 @@ public class WixPipeline {
|
||||
|
||||
private Path compileWix3(WixSource wixSource) throws IOException {
|
||||
Path wixObj = wixObjDir.toAbsolutePath().resolve(PathUtils.replaceSuffix(
|
||||
IOUtils.getFileName(wixSource.source), ".wixobj"));
|
||||
wixSource.path.getFileName(), ".wixobj"));
|
||||
|
||||
List<String> cmdline = new ArrayList<>(List.of(
|
||||
toolset.getToolPath(WixTool.Candle3).toString(),
|
||||
"-nologo",
|
||||
wixSource.source.toAbsolutePath().toString(),
|
||||
wixSource.path.toString(),
|
||||
"-ext", "WixUtilExtension",
|
||||
"-arch", WixFragmentBuilder.is64Bit() ? "x64" : "x86",
|
||||
"-out", wixObj.toAbsolutePath().toString()
|
||||
"-out", wixObj.toString()
|
||||
));
|
||||
|
||||
addWixVariblesToCommandLine(wixSource.variables, cmdline);
|
||||
@ -201,19 +266,19 @@ public class WixPipeline {
|
||||
}
|
||||
|
||||
private void execute(List<String> cmdline) throws IOException {
|
||||
Executor.of(new ProcessBuilder(cmdline).directory(workDir.toFile())).
|
||||
executeExpectSuccess();
|
||||
Executor.of(new ProcessBuilder(cmdline).directory(workDir.toFile())).executeExpectSuccess();
|
||||
}
|
||||
|
||||
private static final class WixSource {
|
||||
Path source;
|
||||
Map<String, String> variables;
|
||||
private record WixSource(Path path, Map<String, String> variables) {
|
||||
WixSource overridePath(Path path) {
|
||||
return new WixSource(path, variables);
|
||||
}
|
||||
}
|
||||
|
||||
private WixToolset toolset;
|
||||
private Map<String, String> wixVariables;
|
||||
private List<String> lightOptions;
|
||||
private Path wixObjDir;
|
||||
private Path workDir;
|
||||
private List<WixSource> sources;
|
||||
private final WixToolset toolset;
|
||||
private final Map<String, String> wixVariables;
|
||||
private final List<String> lightOptions;
|
||||
private final Path wixObjDir;
|
||||
private final Path workDir;
|
||||
private final List<WixSource> sources;
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ final class WixUiFragmentBuilder extends WixFragmentBuilder {
|
||||
}
|
||||
|
||||
@Override
|
||||
void configureWixPipeline(WixPipeline wixPipeline) {
|
||||
void configureWixPipeline(WixPipeline.Builder wixPipeline) {
|
||||
super.configureWixPipeline(wixPipeline);
|
||||
|
||||
if (withShortcutPromptDlg || withInstallDirChooserDlg || withLicenseDlg) {
|
||||
@ -518,7 +518,7 @@ final class WixUiFragmentBuilder extends WixFragmentBuilder {
|
||||
wxsFileName), wxsFileName);
|
||||
}
|
||||
|
||||
void addToWixPipeline(WixPipeline wixPipeline) {
|
||||
void addToWixPipeline(WixPipeline.Builder wixPipeline) {
|
||||
wixPipeline.addSource(getConfigRoot().toAbsolutePath().resolve(
|
||||
wxsFileName), wixVariables.getValues());
|
||||
}
|
||||
|
@ -56,6 +56,7 @@ error.lock-resource=Failed to lock: {0}
|
||||
error.unlock-resource=Failed to unlock: {0}
|
||||
error.read-wix-l10n-file=Failed to parse {0} file
|
||||
error.extract-culture-from-wix-l10n-file=Failed to read value of culture from {0} file
|
||||
error.short-path-conv-fail=Failed to get short version of "{0}" path
|
||||
|
||||
message.icon-not-ico=The specified icon "{0}" is not an ICO file and will not be used. The default icon will be used in it's place.
|
||||
message.potential.windows.defender.issue=Warning: Windows Defender may prevent jpackage from functioning. If there is an issue, it can be addressed by either disabling realtime monitoring, or adding an exclusion for the directory "{0}".
|
||||
|
@ -56,6 +56,7 @@ error.lock-resource=Sperren nicht erfolgreich: {0}
|
||||
error.unlock-resource=Aufheben der Sperre nicht erfolgreich: {0}
|
||||
error.read-wix-l10n-file=Datei {0} konnte nicht geparst werden
|
||||
error.extract-culture-from-wix-l10n-file=Kulturwert konnte nicht aus Datei {0} gelesen werden
|
||||
error.short-path-conv-fail=Failed to get short version of "{0}" path
|
||||
|
||||
message.icon-not-ico=Das angegebene Symbol "{0}" ist keine ICO-Datei und wird nicht verwendet. Stattdessen wird das Standardsymbol verwendet.
|
||||
message.potential.windows.defender.issue=Warnung: Windows Defender verhindert eventuell die korrekte Ausführung von jpackage. Wenn ein Problem auftritt, deaktivieren Sie das Echtzeitmonitoring, oder fügen Sie einen Ausschluss für das Verzeichnis "{0}" hinzu.
|
||||
|
@ -56,6 +56,7 @@ error.lock-resource=ロックに失敗しました: {0}
|
||||
error.unlock-resource=ロック解除に失敗しました: {0}
|
||||
error.read-wix-l10n-file={0}ファイルの解析に失敗しました
|
||||
error.extract-culture-from-wix-l10n-file={0}ファイルからのカルチャの値の読取りに失敗しました
|
||||
error.short-path-conv-fail=Failed to get short version of "{0}" path
|
||||
|
||||
message.icon-not-ico=指定したアイコン"{0}"はICOファイルではなく、使用されません。デフォルト・アイコンがその位置に使用されます。
|
||||
message.potential.windows.defender.issue=警告: Windows Defenderが原因でjpackageが機能しないことがあります。問題が発生した場合は、リアルタイム・モニタリングを無効にするか、ディレクトリ"{0}"の除外を追加することにより、問題に対処できます。
|
||||
|
@ -56,6 +56,7 @@ error.lock-resource=无法锁定:{0}
|
||||
error.unlock-resource=无法解锁:{0}
|
||||
error.read-wix-l10n-file=无法解析 {0} 文件
|
||||
error.extract-culture-from-wix-l10n-file=无法从 {0} 文件读取文化值
|
||||
error.short-path-conv-fail=Failed to get short version of "{0}" path
|
||||
|
||||
message.icon-not-ico=指定的图标 "{0}" 不是 ICO 文件, 不会使用。将使用默认图标代替。
|
||||
message.potential.windows.defender.issue=警告:Windows Defender 可能会阻止 jpackage 正常工作。如果存在问题,可以通过禁用实时监视或者为目录 "{0}" 添加排除项来解决。
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2024, 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
|
||||
@ -668,4 +668,23 @@ tstring stripExeSuffix(const tstring& path) {
|
||||
return path.substr(0, pos);
|
||||
}
|
||||
|
||||
tstring toShortPath(const tstring& path) {
|
||||
const DWORD len = GetShortPathName(path.c_str(), nullptr, 0);
|
||||
if (0 == len) {
|
||||
JP_THROW(SysError(tstrings::any() << "GetShortPathName("
|
||||
<< path << ") failed", GetShortPathName));
|
||||
}
|
||||
|
||||
std::vector<TCHAR> buf;
|
||||
buf.resize(len);
|
||||
const DWORD copied = GetShortPathName(path.c_str(), buf.data(),
|
||||
static_cast<DWORD>(buf.size()));
|
||||
if (copied != buf.size() - 1) {
|
||||
JP_THROW(SysError(tstrings::any() << "GetShortPathName("
|
||||
<< path << ") failed", GetShortPathName));
|
||||
}
|
||||
|
||||
return tstring(buf.data(), buf.size() - 1);
|
||||
}
|
||||
|
||||
} // namespace FileUtils
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2024, 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
|
||||
@ -315,6 +315,8 @@ namespace FileUtils {
|
||||
std::ofstream tmp;
|
||||
tstring dstPath;
|
||||
};
|
||||
|
||||
tstring toShortPath(const tstring& path);
|
||||
} // FileUtils
|
||||
|
||||
#endif // WINFILEUTILS_H
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
#include "ResourceEditor.h"
|
||||
#include "ErrorHandling.h"
|
||||
#include "FileUtils.h"
|
||||
#include "WinFileUtils.h"
|
||||
#include "IconSwap.h"
|
||||
#include "VersionInfo.h"
|
||||
#include "JniUtils.h"
|
||||
@ -162,4 +164,25 @@ extern "C" {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: jdk_jpackage_internal_ShortPathUtils
|
||||
* Method: getShortPath
|
||||
* Signature: (Ljava/lang/String;)Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_jdk_jpackage_internal_ShortPathUtils_getShortPath(
|
||||
JNIEnv *pEnv, jclass c, jstring jLongPath) {
|
||||
|
||||
JP_TRY;
|
||||
|
||||
const std::wstring longPath = jni::toUnicodeString(pEnv, jLongPath);
|
||||
std::wstring shortPath = FileUtils::toShortPath(longPath);
|
||||
|
||||
return jni::toJString(pEnv, shortPath);
|
||||
|
||||
JP_CATCH_ALL;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // extern "C"
|
@ -53,7 +53,7 @@ public final class Executor extends CommandArguments<Executor> {
|
||||
|
||||
public Executor() {
|
||||
saveOutputType = new HashSet<>(Set.of(SaveOutputType.NONE));
|
||||
removePath = false;
|
||||
removePathEnvVar = false;
|
||||
}
|
||||
|
||||
public Executor setExecutable(String v) {
|
||||
@ -85,8 +85,8 @@ public final class Executor extends CommandArguments<Executor> {
|
||||
return setExecutable(v.getPath());
|
||||
}
|
||||
|
||||
public Executor setRemovePath(boolean value) {
|
||||
removePath = value;
|
||||
public Executor setRemovePathEnvVar(boolean value) {
|
||||
removePathEnvVar = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -348,7 +348,7 @@ public final class Executor extends CommandArguments<Executor> {
|
||||
builder.directory(directory.toFile());
|
||||
sb.append(String.format("; in directory [%s]", directory));
|
||||
}
|
||||
if (removePath) {
|
||||
if (removePathEnvVar) {
|
||||
// run this with cleared Path in Environment
|
||||
TKit.trace("Clearing PATH in environment");
|
||||
builder.environment().remove("PATH");
|
||||
@ -478,7 +478,7 @@ public final class Executor extends CommandArguments<Executor> {
|
||||
private Path executable;
|
||||
private Set<SaveOutputType> saveOutputType;
|
||||
private Path directory;
|
||||
private boolean removePath;
|
||||
private boolean removePathEnvVar;
|
||||
private String winTmpDir = null;
|
||||
|
||||
private static enum SaveOutputType {
|
||||
|
@ -354,12 +354,12 @@ public final class HelloApp {
|
||||
|
||||
if (TKit.isWindows()) {
|
||||
// When running app launchers on Windows, clear users environment (JDK-8254920)
|
||||
removePath(true);
|
||||
removePathEnvVar(true);
|
||||
}
|
||||
}
|
||||
|
||||
public AppOutputVerifier removePath(boolean v) {
|
||||
removePath = v;
|
||||
public AppOutputVerifier removePathEnvVar(boolean v) {
|
||||
removePathEnvVar = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -455,7 +455,7 @@ public final class HelloApp {
|
||||
Path outputFile = TKit.workDir().resolve(OUTPUT_FILENAME);
|
||||
ThrowingFunction.toFunction(Files::deleteIfExists).apply(outputFile);
|
||||
|
||||
final Path executablePath;
|
||||
Path executablePath;
|
||||
if (launcherPath.isAbsolute()) {
|
||||
executablePath = launcherPath;
|
||||
} else {
|
||||
@ -463,18 +463,27 @@ public final class HelloApp {
|
||||
executablePath = Path.of(".").resolve(launcherPath.normalize());
|
||||
}
|
||||
|
||||
if (TKit.isWindows()) {
|
||||
var absExecutablePath = executablePath.toAbsolutePath().normalize();
|
||||
var shortPath = WindowsHelper.toShortPath(absExecutablePath);
|
||||
if (shortPath.isPresent()) {
|
||||
TKit.trace(String.format("Will run [%s] as [%s]", executablePath, shortPath.get()));
|
||||
executablePath = shortPath.get();
|
||||
}
|
||||
}
|
||||
|
||||
final List<String> launcherArgs = List.of(args);
|
||||
return new Executor()
|
||||
.setDirectory(outputFile.getParent())
|
||||
.saveOutput(saveOutput)
|
||||
.dumpOutput()
|
||||
.setRemovePath(removePath)
|
||||
.setRemovePathEnvVar(removePathEnvVar)
|
||||
.setExecutable(executablePath)
|
||||
.addArguments(launcherArgs);
|
||||
}
|
||||
|
||||
private boolean launcherNoExit;
|
||||
private boolean removePath;
|
||||
private boolean removePathEnvVar;
|
||||
private boolean saveOutput;
|
||||
private final Path launcherPath;
|
||||
private Path outputFilePath;
|
||||
|
@ -23,6 +23,7 @@
|
||||
package jdk.jpackage.test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
@ -36,7 +37,9 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import static jdk.jpackage.internal.util.function.ExceptionBox.rethrowUnchecked;
|
||||
import jdk.jpackage.internal.util.function.ThrowingRunnable;
|
||||
import static jdk.jpackage.internal.util.function.ThrowingSupplier.toSupplier;
|
||||
import jdk.jpackage.test.PackageTest.PackageHandlers;
|
||||
|
||||
public class WindowsHelper {
|
||||
@ -94,8 +97,9 @@ public class WindowsHelper {
|
||||
static PackageHandlers createMsiPackageHandlers() {
|
||||
BiConsumer<JPackageCommand, Boolean> installMsi = (cmd, install) -> {
|
||||
cmd.verifyIsOfType(PackageType.WIN_MSI);
|
||||
var msiPath = TransientMsi.create(cmd).path();
|
||||
runMsiexecWithRetries(Executor.of("msiexec", "/qn", "/norestart",
|
||||
install ? "/i" : "/x").addArgument(cmd.outputBundle().normalize()));
|
||||
install ? "/i" : "/x").addArgument(msiPath));
|
||||
};
|
||||
|
||||
PackageHandlers msi = new PackageHandlers();
|
||||
@ -112,6 +116,8 @@ public class WindowsHelper {
|
||||
TKit.removeRootFromAbsolutePath(
|
||||
getInstallationRootDirectory(cmd)));
|
||||
|
||||
final Path msiPath = TransientMsi.create(cmd).path();
|
||||
|
||||
// Put msiexec in .bat file because can't pass value of TARGETDIR
|
||||
// property containing spaces through ProcessBuilder properly.
|
||||
// Set folder permissions to allow msiexec unpack msi bundle.
|
||||
@ -121,7 +127,7 @@ public class WindowsHelper {
|
||||
String.join(" ", List.of(
|
||||
"msiexec",
|
||||
"/a",
|
||||
String.format("\"%s\"", cmd.outputBundle().normalize()),
|
||||
String.format("\"%s\"", msiPath),
|
||||
"/qn",
|
||||
String.format("TARGETDIR=\"%s\"",
|
||||
unpackDir.toAbsolutePath().normalize())))));
|
||||
@ -155,6 +161,49 @@ public class WindowsHelper {
|
||||
return msi;
|
||||
}
|
||||
|
||||
record TransientMsi(Path path) {
|
||||
static TransientMsi create(JPackageCommand cmd) {
|
||||
var outputMsiPath = cmd.outputBundle().normalize();
|
||||
if (isPathTooLong(outputMsiPath)) {
|
||||
return toSupplier(() -> {
|
||||
var transientMsiPath = TKit.createTempDirectory("msi-copy").resolve("a.msi").normalize();
|
||||
TKit.trace(String.format("Copy [%s] to [%s]", outputMsiPath, transientMsiPath));
|
||||
Files.copy(outputMsiPath, transientMsiPath);
|
||||
return new TransientMsi(transientMsiPath);
|
||||
}).get();
|
||||
} else {
|
||||
return new TransientMsi(outputMsiPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum WixType {
|
||||
WIX3,
|
||||
WIX4
|
||||
}
|
||||
|
||||
public static WixType getWixTypeFromVerboseJPackageOutput(Executor.Result result) {
|
||||
return result.getOutput().stream().map(str -> {
|
||||
if (str.contains("[light.exe]")) {
|
||||
return WixType.WIX3;
|
||||
} else if (str.contains("[wix.exe]")) {
|
||||
return WixType.WIX4;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}).filter(Objects::nonNull).reduce((a, b) -> {
|
||||
throw new IllegalArgumentException("Invalid input: multiple invocations of WiX tools");
|
||||
}).orElseThrow(() -> new IllegalArgumentException("Invalid input: no invocations of WiX tools"));
|
||||
}
|
||||
|
||||
static Optional<Path> toShortPath(Path path) {
|
||||
if (isPathTooLong(path)) {
|
||||
return Optional.of(ShortPathUtils.toShortPath(path));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
static PackageHandlers createExePackageHandlers() {
|
||||
BiConsumer<JPackageCommand, Boolean> installExe = (cmd, install) -> {
|
||||
cmd.verifyIsOfType(PackageType.WIN_EXE);
|
||||
@ -303,6 +352,10 @@ public class WindowsHelper {
|
||||
return cmd.hasArgument("--win-per-user-install");
|
||||
}
|
||||
|
||||
private static boolean isPathTooLong(Path path) {
|
||||
return path.toString().length() > WIN_MAX_PATH;
|
||||
}
|
||||
|
||||
private static class DesktopIntegrationVerifier {
|
||||
|
||||
DesktopIntegrationVerifier(JPackageCommand cmd, String launcherName) {
|
||||
@ -525,6 +578,32 @@ public class WindowsHelper {
|
||||
return value;
|
||||
}
|
||||
|
||||
private static final class ShortPathUtils {
|
||||
private ShortPathUtils() {
|
||||
try {
|
||||
var shortPathUtilsClass = Class.forName("jdk.jpackage.internal.ShortPathUtils");
|
||||
|
||||
getShortPathWrapper = shortPathUtilsClass.getDeclaredMethod(
|
||||
"getShortPathWrapper", String.class);
|
||||
// Note: this reflection call requires
|
||||
// --add-opens jdk.jpackage/jdk.jpackage.internal=ALL-UNNAMED
|
||||
getShortPathWrapper.setAccessible(true);
|
||||
} catch (ClassNotFoundException | NoSuchMethodException
|
||||
| SecurityException ex) {
|
||||
throw rethrowUnchecked(ex);
|
||||
}
|
||||
}
|
||||
|
||||
static Path toShortPath(Path path) {
|
||||
return Path.of(toSupplier(() -> (String) INSTANCE.getShortPathWrapper.invoke(
|
||||
null, path.toString())).get());
|
||||
}
|
||||
|
||||
private final Method getShortPathWrapper;
|
||||
|
||||
private static final ShortPathUtils INSTANCE = new ShortPathUtils();
|
||||
}
|
||||
|
||||
static final Set<Path> CRITICAL_RUNTIME_FILES = Set.of(Path.of(
|
||||
"bin\\server\\jvm.dll"));
|
||||
|
||||
@ -540,4 +619,6 @@ public class WindowsHelper {
|
||||
private static final String USER_SHELL_FOLDERS_REGKEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders";
|
||||
|
||||
private static final Map<String, String> REGISTRY_VALUES = new HashMap<>();
|
||||
|
||||
private static final int WIN_MAX_PATH = 260;
|
||||
}
|
||||
|
@ -35,6 +35,8 @@ import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.test.Executor;
|
||||
import static jdk.jpackage.test.WindowsHelper.WixType.WIX3;
|
||||
import static jdk.jpackage.test.WindowsHelper.getWixTypeFromVerboseJPackageOutput;
|
||||
|
||||
/*
|
||||
* @test
|
||||
@ -109,13 +111,13 @@ public class WinL10nTest {
|
||||
});
|
||||
}
|
||||
|
||||
private static Stream<String> getBuildCommandLine(Executor.Result result) {
|
||||
private static Stream<String> getWixCommandLine(Executor.Result result) {
|
||||
return result.getOutput().stream().filter(createToolCommandLinePredicate("light").or(
|
||||
createToolCommandLinePredicate("wix")));
|
||||
}
|
||||
|
||||
private static boolean isWix3(Executor.Result result) {
|
||||
return result.getOutput().stream().anyMatch(createToolCommandLinePredicate("light"));
|
||||
return getWixTypeFromVerboseJPackageOutput(result) == WIX3;
|
||||
}
|
||||
|
||||
private final static Predicate<String> createToolCommandLinePredicate(String wixToolName) {
|
||||
@ -127,10 +129,10 @@ public class WinL10nTest {
|
||||
};
|
||||
}
|
||||
|
||||
private static List<TKit.TextStreamVerifier> createDefaultL10nFilesLocVerifiers(Path tempDir) {
|
||||
private static List<TKit.TextStreamVerifier> createDefaultL10nFilesLocVerifiers(Path wixSrcDir) {
|
||||
return Arrays.stream(DEFAULT_L10N_FILES).map(loc ->
|
||||
TKit.assertTextStream("-loc " + tempDir.resolve(
|
||||
String.format("config/MsiInstallerStrings_%s.wxl", loc)).normalize()))
|
||||
TKit.assertTextStream("-loc " + wixSrcDir.resolve(
|
||||
String.format("MsiInstallerStrings_%s.wxl", loc))))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@ -183,16 +185,20 @@ public class WinL10nTest {
|
||||
cmd.addArguments("--temp", tempDir);
|
||||
})
|
||||
.addBundleVerifier((cmd, result) -> {
|
||||
final List<String> wixCmdline = getWixCommandLine(result).toList();
|
||||
|
||||
final var isWix3 = isWix3(result);
|
||||
|
||||
if (expectedCultures != null) {
|
||||
String expected;
|
||||
if (isWix3(result)) {
|
||||
if (isWix3) {
|
||||
expected = "-cultures:" + String.join(";", expectedCultures);
|
||||
} else {
|
||||
expected = Stream.of(expectedCultures).map(culture -> {
|
||||
return String.join(" ", "-culture", culture);
|
||||
}).collect(Collectors.joining(" "));
|
||||
}
|
||||
TKit.assertTextStream(expected).apply(getBuildCommandLine(result));
|
||||
TKit.assertTextStream(expected).apply(wixCmdline.stream());
|
||||
}
|
||||
|
||||
if (expectedErrorMessage != null) {
|
||||
@ -201,25 +207,27 @@ public class WinL10nTest {
|
||||
}
|
||||
|
||||
if (wxlFileInitializers != null) {
|
||||
var wixSrcDir = Path.of(cmd.getArgumentValue("--temp")).resolve("config");
|
||||
var wixSrcDir = Path.of(cmd.getArgumentValue("--temp")).resolve(
|
||||
"config").normalize().toAbsolutePath();
|
||||
|
||||
if (allWxlFilesValid) {
|
||||
for (var v : wxlFileInitializers) {
|
||||
if (!v.name.startsWith("MsiInstallerStrings_")) {
|
||||
v.createCmdOutputVerifier(wixSrcDir).apply(getBuildCommandLine(result));
|
||||
v.createCmdOutputVerifier(wixSrcDir).apply(wixCmdline.stream());
|
||||
}
|
||||
}
|
||||
var tempDir = Path.of(cmd.getArgumentValue("--temp")).toAbsolutePath();
|
||||
for (var v : createDefaultL10nFilesLocVerifiers(tempDir)) {
|
||||
v.apply(getBuildCommandLine(result));
|
||||
|
||||
for (var v : createDefaultL10nFilesLocVerifiers(wixSrcDir)) {
|
||||
v.apply(wixCmdline.stream());
|
||||
}
|
||||
} else {
|
||||
Stream.of(wxlFileInitializers)
|
||||
.filter(Predicate.not(WixFileInitializer::isValid))
|
||||
.forEach(v -> v.createCmdOutputVerifier(
|
||||
wixSrcDir).apply(result.getOutput().stream()));
|
||||
TKit.assertFalse(getBuildCommandLine(result).findAny().isPresent(),
|
||||
"Check light.exe was not invoked");
|
||||
TKit.assertTrue(wixCmdline.stream().findAny().isEmpty(),
|
||||
String.format("Check %s.exe was not invoked",
|
||||
isWix3 ? "light" : "wix"));
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -276,10 +284,9 @@ public class WinL10nTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
TKit.TextStreamVerifier createCmdOutputVerifier(Path root) {
|
||||
TKit.TextStreamVerifier createCmdOutputVerifier(Path wixSrcDir) {
|
||||
return TKit.assertTextStream(String.format(
|
||||
"Failed to parse %s file",
|
||||
root.resolve("b.wxl").toAbsolutePath()));
|
||||
"Failed to parse %s file", wixSrcDir.resolve("b.wxl")));
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -297,9 +304,8 @@ public class WinL10nTest {
|
||||
+ "\" xmlns=\"http://schemas.microsoft.com/wix/2006/localization\" Codepage=\"1252\"/>"));
|
||||
}
|
||||
|
||||
TKit.TextStreamVerifier createCmdOutputVerifier(Path root) {
|
||||
return TKit.assertTextStream(
|
||||
"-loc " + root.resolve(name).toAbsolutePath().normalize());
|
||||
TKit.TextStreamVerifier createCmdOutputVerifier(Path wixSrcDir) {
|
||||
return TKit.assertTextStream("-loc " + wixSrcDir.resolve(name));
|
||||
}
|
||||
|
||||
boolean isValid() {
|
||||
|
87
test/jdk/tools/jpackage/windows/WinLongPathTest.java
Normal file
87
test/jdk/tools/jpackage/windows/WinLongPathTest.java
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import jdk.jpackage.test.Annotations.Parameters;
|
||||
import jdk.jpackage.test.PackageTest;
|
||||
import jdk.jpackage.test.PackageType;
|
||||
import jdk.jpackage.test.Annotations.Test;
|
||||
import jdk.jpackage.test.JPackageCommand;
|
||||
import jdk.jpackage.test.RunnablePackageTest.Action;
|
||||
import jdk.jpackage.test.TKit;
|
||||
|
||||
/*
|
||||
/* @test
|
||||
* @bug 8289771
|
||||
* @summary jpackage with long paths on windows
|
||||
* @library /test/jdk/tools/jpackage/helpers
|
||||
* @key jpackagePlatformPackage
|
||||
* @build jdk.jpackage.test.*
|
||||
* @requires (os.family == "windows")
|
||||
* @compile WinLongPathTest.java
|
||||
* @run main/othervm/timeout=540 -Xmx512m jdk.jpackage.test.Main
|
||||
* --jpt-space-subst=*
|
||||
* --jpt-exclude=WinLongPathTest(false,*--temp)
|
||||
* --jpt-run=WinLongPathTest
|
||||
*/
|
||||
|
||||
public record WinLongPathTest(Boolean appImage, String optionName) {
|
||||
|
||||
@Parameters
|
||||
public static List<Object[]> input() {
|
||||
List<Object[]> data = new ArrayList<>();
|
||||
for (var appImage : List.of(Boolean.TRUE, Boolean.FALSE)) {
|
||||
for (var option : List.of("--dest", "--temp")) {
|
||||
data.add(new Object[]{appImage, option});
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() throws IOException {
|
||||
if (appImage) {
|
||||
var cmd = JPackageCommand.helloAppImage();
|
||||
setOptionLongPath(cmd, optionName);
|
||||
cmd.executeAndAssertHelloAppImageCreated();
|
||||
} else {
|
||||
new PackageTest()
|
||||
.forTypes(PackageType.WINDOWS)
|
||||
.configureHelloApp()
|
||||
.addInitializer(cmd -> setOptionLongPath(cmd, optionName))
|
||||
.run(Action.CREATE_AND_UNPACK);
|
||||
}
|
||||
}
|
||||
|
||||
private static void setOptionLongPath(JPackageCommand cmd, String option) throws IOException {
|
||||
var root = TKit.createTempDirectory("long-path");
|
||||
// 261 characters in total, which alone is above the 260 threshold
|
||||
var longPath = root.resolve(Path.of("a".repeat(80), "b".repeat(90), "c".repeat(91)));
|
||||
Files.createDirectories(longPath);
|
||||
cmd.setArgumentValue(option, longPath);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user