8250950: Allow per-user and system wide configuration of a jpackaged app
Reviewed-by: almatvee
This commit is contained in:
parent
124ba45fb8
commit
c37c8e5d34
@ -71,6 +71,18 @@ void launchApp() {
|
||||
<< _T("lib/runtime"));
|
||||
} else {
|
||||
ownerPackage.initAppLauncher(appLauncher);
|
||||
|
||||
tstring homeDir;
|
||||
JP_TRY;
|
||||
homeDir = SysInfo::getEnvVariable("HOME");
|
||||
JP_CATCH_ALL;
|
||||
|
||||
if (!homeDir.empty()) {
|
||||
appLauncher.addCfgFileLookupDir(FileUtils::mkpath()
|
||||
<< homeDir << ".local" << ownerPackage.name());
|
||||
appLauncher.addCfgFileLookupDir(FileUtils::mkpath()
|
||||
<< homeDir << "." + ownerPackage.name());
|
||||
}
|
||||
}
|
||||
|
||||
const std::string _JPACKAGE_LAUNCHER = "_JPACKAGE_LAUNCHER";
|
||||
|
@ -46,7 +46,7 @@ import static jdk.jpackage.internal.StandardBundlerParam.SIGN_BUNDLE;
|
||||
|
||||
public abstract class MacBaseInstallerBundler extends AbstractBundler {
|
||||
|
||||
public final BundlerParamInfo<Path> APP_IMAGE_TEMP_ROOT =
|
||||
private final BundlerParamInfo<Path> APP_IMAGE_TEMP_ROOT =
|
||||
new StandardBundlerParam<>(
|
||||
"mac.app.imageRoot",
|
||||
Path.class,
|
||||
@ -156,15 +156,25 @@ public abstract class MacBaseInstallerBundler extends AbstractBundler {
|
||||
}
|
||||
|
||||
protected Path prepareAppBundle(Map<String, ? super Object> params)
|
||||
throws PackagerException {
|
||||
throws PackagerException, IOException {
|
||||
Path appDir;
|
||||
Path appImageRoot = APP_IMAGE_TEMP_ROOT.fetchFrom(params);
|
||||
Path predefinedImage =
|
||||
StandardBundlerParam.getPredefinedAppImage(params);
|
||||
if (predefinedImage != null) {
|
||||
return predefinedImage;
|
||||
appDir = appImageRoot.resolve(APP_NAME.fetchFrom(params) + ".app");
|
||||
IOUtils.copyRecursive(predefinedImage, appDir);
|
||||
} else {
|
||||
appDir = appImageBundler.execute(params, appImageRoot);
|
||||
}
|
||||
Path appImageRoot = APP_IMAGE_TEMP_ROOT.fetchFrom(params);
|
||||
|
||||
return appImageBundler.execute(params, appImageRoot);
|
||||
if (!StandardBundlerParam.isRuntimeInstaller(params)) {
|
||||
new PackageFile(APP_NAME.fetchFrom(params)).save(
|
||||
ApplicationLayout.macAppImage().resolveAt(appDir));
|
||||
Files.deleteIfExists(AppImageFile.getPathInAppImage(appDir));
|
||||
}
|
||||
|
||||
return appDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -277,11 +277,8 @@ public class MacDmgBundler extends MacBaseInstallerBundler {
|
||||
Path finalDMG = outdir.resolve(MAC_INSTALLER_NAME.fetchFrom(params)
|
||||
+ INSTALLER_SUFFIX.fetchFrom(params) + ".dmg");
|
||||
|
||||
Path srcFolder = APP_IMAGE_TEMP_ROOT.fetchFrom(params);
|
||||
Path predefinedImage = StandardBundlerParam.getPredefinedAppImage(params);
|
||||
if (predefinedImage != null) {
|
||||
srcFolder = predefinedImage;
|
||||
} else if (StandardBundlerParam.isRuntimeInstaller(params)) {
|
||||
Path srcFolder = appLocation.getParent();
|
||||
if (StandardBundlerParam.isRuntimeInstaller(params)) {
|
||||
Path newRoot = Files.createTempDirectory(TEMP_ROOT.fetchFrom(params),
|
||||
"root-");
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2022, 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
|
||||
@ -26,8 +26,10 @@
|
||||
#include "AppLauncher.h"
|
||||
#include "app.h"
|
||||
#include "FileUtils.h"
|
||||
#include "PackageFile.h"
|
||||
#include "UnixSysInfo.h"
|
||||
#include "JvmLauncher.h"
|
||||
#include "ErrorHandling.h"
|
||||
|
||||
|
||||
namespace {
|
||||
@ -49,17 +51,36 @@ void initJvmLauncher() {
|
||||
const tstring appImageRoot = FileUtils::dirname(FileUtils::dirname(
|
||||
FileUtils::dirname(launcherPath)));
|
||||
|
||||
const tstring appDirPath = FileUtils::mkpath() << appImageRoot
|
||||
<< _T("Contents/app");
|
||||
|
||||
const PackageFile pkgFile = PackageFile::loadFromAppDir(appDirPath);
|
||||
|
||||
// Create JVM launcher and save in global variable.
|
||||
jvmLauncher = AppLauncher()
|
||||
AppLauncher appLauncher = AppLauncher()
|
||||
.setImageRoot(appImageRoot)
|
||||
.addJvmLibName(_T("Contents/Home/lib/libjli.dylib"))
|
||||
// add backup - older version such as JDK11 have it in jli sub-dir
|
||||
.addJvmLibName(_T("Contents/Home/lib/jli/libjli.dylib"))
|
||||
.setAppDir(FileUtils::mkpath() << appImageRoot << _T("Contents/app"))
|
||||
.setAppDir(appDirPath)
|
||||
.setLibEnvVariableName(_T("DYLD_LIBRARY_PATH"))
|
||||
.setDefaultRuntimePath(FileUtils::mkpath() << appImageRoot
|
||||
<< _T("Contents/runtime"))
|
||||
.createJvmLauncher();
|
||||
<< _T("Contents/runtime"));
|
||||
|
||||
if (!pkgFile.getPackageName().empty()) {
|
||||
tstring homeDir;
|
||||
JP_TRY;
|
||||
homeDir = SysInfo::getEnvVariable("HOME");
|
||||
JP_CATCH_ALL;
|
||||
|
||||
if (!homeDir.empty()) {
|
||||
appLauncher.addCfgFileLookupDir(FileUtils::mkpath()
|
||||
<< homeDir << "Library/Application Support"
|
||||
<< pkgFile.getPackageName());
|
||||
}
|
||||
}
|
||||
|
||||
jvmLauncher = appLauncher.createJvmLauncher();
|
||||
|
||||
// Kick start JVM launching. The function wouldn't return!
|
||||
launchJvm();
|
||||
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class PackageFile {
|
||||
|
||||
/**
|
||||
* Returns path to package file.
|
||||
* @param appImageDir - path to application image
|
||||
*/
|
||||
public static Path getPathInAppImage(Path appImageDir) {
|
||||
return ApplicationLayout.platformAppImage()
|
||||
.resolveAt(appImageDir)
|
||||
.appDirectory()
|
||||
.resolve(FILENAME);
|
||||
}
|
||||
|
||||
PackageFile(String packageName) {
|
||||
Objects.requireNonNull(packageName);
|
||||
this.packageName = packageName;
|
||||
}
|
||||
|
||||
void save(ApplicationLayout appLayout) throws IOException {
|
||||
Path dst = Optional.ofNullable(appLayout.appDirectory()).map(appDir -> {
|
||||
return appDir.resolve(FILENAME);
|
||||
}).orElse(null);
|
||||
|
||||
if (dst != null) {
|
||||
Files.createDirectories(dst.getParent());
|
||||
Files.writeString(dst, packageName);
|
||||
}
|
||||
}
|
||||
|
||||
private final String packageName;
|
||||
|
||||
private final static String FILENAME = ".package";
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2022, 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
|
||||
@ -110,9 +110,7 @@ bool AppLauncher::libEnvVariableContainsAppDir() const {
|
||||
}
|
||||
|
||||
Jvm* AppLauncher::createJvmLauncher() const {
|
||||
const tstring cfgFilePath = FileUtils::mkpath()
|
||||
<< appDirPath << FileUtils::stripExeSuffix(
|
||||
FileUtils::basename(launcherPath)) + _T(".cfg");
|
||||
const tstring cfgFilePath = getCfgFilePath();
|
||||
|
||||
LOG_TRACE(tstrings::any() << "Launcher config file path: \""
|
||||
<< cfgFilePath << "\"");
|
||||
@ -160,3 +158,20 @@ Jvm* AppLauncher::createJvmLauncher() const {
|
||||
void AppLauncher::launch() const {
|
||||
std::unique_ptr<Jvm>(createJvmLauncher())->launch();
|
||||
}
|
||||
|
||||
|
||||
tstring AppLauncher::getCfgFilePath() const {
|
||||
tstring_array::const_iterator it = cfgFileLookupDirs.begin();
|
||||
tstring_array::const_iterator end = cfgFileLookupDirs.end();
|
||||
const tstring cfgFileName = FileUtils::stripExeSuffix(
|
||||
FileUtils::basename(launcherPath)) + _T(".cfg");
|
||||
for (; it != end; ++it) {
|
||||
const tstring cfgFilePath = FileUtils::mkpath() << *it << cfgFileName;
|
||||
LOG_TRACE(tstrings::any() << "Check [" << cfgFilePath << "] file exit");
|
||||
if (FileUtils::isFileExists(cfgFilePath)) {
|
||||
return cfgFilePath;
|
||||
}
|
||||
}
|
||||
|
||||
return FileUtils::mkpath() << appDirPath << cfgFileName;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2022, 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
|
||||
@ -45,6 +45,11 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
AppLauncher& addCfgFileLookupDir(const tstring& v) {
|
||||
cfgFileLookupDirs.push_back(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AppLauncher& setAppDir(const tstring& v) {
|
||||
appDirPath = v;
|
||||
return *this;
|
||||
@ -71,6 +76,9 @@ public:
|
||||
|
||||
void launch() const;
|
||||
|
||||
private:
|
||||
tstring getCfgFilePath() const;
|
||||
|
||||
private:
|
||||
tstring_array args;
|
||||
tstring launcherPath;
|
||||
@ -79,6 +87,7 @@ private:
|
||||
tstring libEnvVarName;
|
||||
tstring imageRoot;
|
||||
tstring_array jvmLibNames;
|
||||
tstring_array cfgFileLookupDirs;
|
||||
bool initJvmFromCmdlineOnly;
|
||||
};
|
||||
|
||||
|
61
src/jdk.jpackage/share/native/applauncher/PackageFile.cpp
Normal file
61
src/jdk.jpackage/share/native/applauncher/PackageFile.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
#include "kludge_c++11.h"
|
||||
|
||||
#include <fstream>
|
||||
#include "PackageFile.h"
|
||||
#include "Log.h"
|
||||
#include "FileUtils.h"
|
||||
#include "ErrorHandling.h"
|
||||
|
||||
|
||||
PackageFile::PackageFile(const tstring& v): packageName(v) {
|
||||
}
|
||||
|
||||
|
||||
PackageFile PackageFile::loadFromAppDir(const tstring& appDirPath) {
|
||||
tstring packageName;
|
||||
const tstring packageFilePath =
|
||||
FileUtils::mkpath() << appDirPath << _T(".package");
|
||||
if (FileUtils::isFileExists(packageFilePath)) {
|
||||
LOG_TRACE(tstrings::any() << "Read \"" << packageFilePath
|
||||
<< "\" package file");
|
||||
std::ifstream input(packageFilePath);
|
||||
if (!input.good()) {
|
||||
JP_THROW(tstrings::any() << "Error opening \"" << packageFilePath
|
||||
<< "\" file: " << lastCRTError());
|
||||
}
|
||||
|
||||
std::string utf8line;
|
||||
if (std::getline(input, utf8line)) {
|
||||
LOG_TRACE(tstrings::any()
|
||||
<< "Package name is [" << utf8line << "]");
|
||||
packageName = tstrings::any(utf8line).tstr();
|
||||
}
|
||||
}
|
||||
|
||||
return PackageFile(packageName);
|
||||
}
|
48
src/jdk.jpackage/share/native/applauncher/PackageFile.h
Normal file
48
src/jdk.jpackage/share/native/applauncher/PackageFile.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PackageFile_h
|
||||
#define PackageFile_h
|
||||
|
||||
#include "tstrings.h"
|
||||
|
||||
|
||||
class PackageFile {
|
||||
public:
|
||||
static PackageFile loadFromAppDir(const tstring& appDirPath);
|
||||
|
||||
tstring getPackageName() const {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
private:
|
||||
PackageFile(const tstring& packageName);
|
||||
|
||||
private:
|
||||
tstring packageName;
|
||||
};
|
||||
|
||||
#endif // PackageFile_h
|
@ -382,10 +382,12 @@ public class WinMsiBundler extends AbstractBundler {
|
||||
.runtimeDirectory()
|
||||
.resolve(Path.of("bin", "java.exe"));
|
||||
} else {
|
||||
installerIcon = ApplicationLayout.windowsAppImage()
|
||||
.resolveAt(appDir)
|
||||
.launchersDirectory()
|
||||
var appLayout = ApplicationLayout.windowsAppImage().resolveAt(appDir);
|
||||
|
||||
installerIcon = appLayout.launchersDirectory()
|
||||
.resolve(appName + ".exe");
|
||||
|
||||
new PackageFile(appName).save(appLayout);
|
||||
}
|
||||
installerIcon = installerIcon.toAbsolutePath();
|
||||
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "WinApp.h"
|
||||
#include "Toolbox.h"
|
||||
#include "FileUtils.h"
|
||||
#include "PackageFile.h"
|
||||
#include "UniqueHandle.h"
|
||||
#include "ErrorHandling.h"
|
||||
#include "WinSysInfo.h"
|
||||
@ -133,6 +134,22 @@ tstring getJvmLibPath(const Jvm& jvm) {
|
||||
}
|
||||
|
||||
|
||||
void addCfgFileLookupDirForEnvVariable(
|
||||
const PackageFile& pkgFile, AppLauncher& appLauncher,
|
||||
const tstring& envVarName) {
|
||||
|
||||
tstring path;
|
||||
JP_TRY;
|
||||
path = SysInfo::getEnvVariable(envVarName);
|
||||
JP_CATCH_ALL;
|
||||
|
||||
if (!path.empty()) {
|
||||
appLauncher.addCfgFileLookupDir(FileUtils::mkpath() << path
|
||||
<< pkgFile.getPackageName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void launchApp() {
|
||||
// [RT-31061] otherwise UI can be left in back of other windows.
|
||||
::AllowSetForegroundWindow(ASFW_ANY);
|
||||
@ -141,13 +158,20 @@ void launchApp() {
|
||||
const tstring appImageRoot = FileUtils::dirname(launcherPath);
|
||||
const tstring appDirPath = FileUtils::mkpath() << appImageRoot << _T("app");
|
||||
|
||||
const AppLauncher appLauncher = AppLauncher().setImageRoot(appImageRoot)
|
||||
const PackageFile pkgFile = PackageFile::loadFromAppDir(appDirPath);
|
||||
|
||||
AppLauncher appLauncher = AppLauncher().setImageRoot(appImageRoot)
|
||||
.addJvmLibName(_T("bin\\jli.dll"))
|
||||
.setAppDir(appDirPath)
|
||||
.setLibEnvVariableName(_T("PATH"))
|
||||
.setDefaultRuntimePath(FileUtils::mkpath() << appImageRoot
|
||||
<< _T("runtime"));
|
||||
|
||||
if (!pkgFile.getPackageName().empty()) {
|
||||
addCfgFileLookupDirForEnvVariable(pkgFile, appLauncher, _T("LOCALAPPDATA"));
|
||||
addCfgFileLookupDirForEnvVariable(pkgFile, appLauncher, _T("APPDATA"));
|
||||
}
|
||||
|
||||
const bool restart = !appLauncher.libEnvVariableContainsAppDir();
|
||||
|
||||
std::unique_ptr<Jvm> jvm(appLauncher.createJvmLauncher());
|
||||
|
@ -47,6 +47,7 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import jdk.jpackage.internal.AppImageFile;
|
||||
import jdk.jpackage.internal.ApplicationLayout;
|
||||
import jdk.jpackage.internal.PackageFile;
|
||||
import static jdk.jpackage.test.AdditionalLauncher.forEachAdditionalLauncher;
|
||||
import jdk.jpackage.test.Functional.ThrowingConsumer;
|
||||
import jdk.jpackage.test.Functional.ThrowingFunction;
|
||||
@ -761,38 +762,8 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
}
|
||||
|
||||
JPackageCommand assertAppLayout() {
|
||||
if (isPackageUnpacked() || isImagePackageType()) {
|
||||
final Path rootDir = isPackageUnpacked() ? pathToUnpackedPackageFile(
|
||||
appInstallationDirectory()) : outputBundle();
|
||||
final Path appImageFileName = AppImageFile.getPathInAppImage(
|
||||
Path.of("")).getFileName();
|
||||
try (Stream<Path> walk = ThrowingSupplier.toSupplier(
|
||||
() -> Files.walk(rootDir)).get()) {
|
||||
List<String> appImageFiles = walk
|
||||
.filter(path -> path.getFileName().equals(appImageFileName))
|
||||
.map(Path::toString)
|
||||
.collect(Collectors.toList());
|
||||
if (isImagePackageType() || (TKit.isOSX() && !isRuntime())) {
|
||||
List<String> expected = List.of(
|
||||
AppImageFile.getPathInAppImage(rootDir).toString());
|
||||
TKit.assertStringListEquals(expected, appImageFiles,
|
||||
String.format(
|
||||
"Check there is only one file with [%s] name in the package",
|
||||
appImageFileName));
|
||||
} else {
|
||||
TKit.assertStringListEquals(List.of(), appImageFiles,
|
||||
String.format(
|
||||
"Check there are no files with [%s] name in the package",
|
||||
appImageFileName));
|
||||
}
|
||||
}
|
||||
} else if (TKit.isOSX() && !isRuntime()) {
|
||||
TKit.assertFileExists(AppImageFile.getPathInAppImage(
|
||||
appInstallationDirectory()));
|
||||
} else {
|
||||
TKit.assertPathExists(AppImageFile.getPathInAppImage(
|
||||
appInstallationDirectory()), false);
|
||||
}
|
||||
assertAppImageFile();
|
||||
assertPackageFile();
|
||||
|
||||
TKit.assertDirectoryExists(appRuntimeDirectory());
|
||||
|
||||
@ -809,6 +780,54 @@ public final class JPackageCommand extends CommandArguments<JPackageCommand> {
|
||||
return this;
|
||||
}
|
||||
|
||||
private void assertAppImageFile() {
|
||||
final Path lookupPath = AppImageFile.getPathInAppImage(Path.of(""));
|
||||
|
||||
if (isRuntime() || !isImagePackageType()) {
|
||||
assertFileInAppImage(lookupPath, null);
|
||||
} else {
|
||||
assertFileInAppImage(lookupPath, lookupPath);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertPackageFile() {
|
||||
final Path lookupPath = PackageFile.getPathInAppImage(Path.of(""));
|
||||
|
||||
if (isRuntime() || isImagePackageType() || TKit.isLinux()) {
|
||||
assertFileInAppImage(lookupPath, null);
|
||||
} else {
|
||||
assertFileInAppImage(lookupPath, lookupPath);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertFileInAppImage(Path filename, Path expectedPath) {
|
||||
if (filename.getNameCount() > 1) {
|
||||
assertFileInAppImage(filename.getFileName(), expectedPath);
|
||||
return;
|
||||
}
|
||||
|
||||
final Path rootDir = isImagePackageType() ? outputBundle() : pathToUnpackedPackageFile(
|
||||
appInstallationDirectory());
|
||||
|
||||
try ( Stream<Path> walk = ThrowingSupplier.toSupplier(() -> Files.walk(
|
||||
rootDir)).get()) {
|
||||
List<String> files = walk.filter(path -> path.getFileName().equals(
|
||||
filename)).map(Path::toString).toList();
|
||||
|
||||
if (expectedPath == null) {
|
||||
TKit.assertStringListEquals(List.of(), files, String.format(
|
||||
"Check there are no files with [%s] name in the package",
|
||||
filename));
|
||||
} else {
|
||||
List<String> expected = List.of(
|
||||
rootDir.resolve(expectedPath).toString());
|
||||
TKit.assertStringListEquals(expected, files, String.format(
|
||||
"Check there is only one file with [%s] name in the package",
|
||||
filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JPackageCommand setUnpackedPackageLocation(Path path) {
|
||||
verifyIsOfType(PackageType.NATIVE);
|
||||
if (path != null) {
|
||||
|
@ -22,6 +22,7 @@
|
||||
*/
|
||||
package jdk.jpackage.test;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
@ -46,6 +47,7 @@ import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -769,6 +771,34 @@ final public class TKit {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a directory by creating all nonexistent parent directories first
|
||||
* just like java.nio.file.Files#createDirectories() and returns
|
||||
* java.io.Closeable that will delete all created nonexistent parent
|
||||
* directories.
|
||||
*/
|
||||
public static Closeable createDirectories(Path dir) throws IOException {
|
||||
Objects.requireNonNull(dir);
|
||||
|
||||
Collection<Path> dirsToDelete = new ArrayList<>();
|
||||
|
||||
Path curDir = dir;
|
||||
while (!Files.exists(curDir)) {
|
||||
dirsToDelete.add(curDir);
|
||||
curDir = curDir.getParent();
|
||||
}
|
||||
Files.createDirectories(dir);
|
||||
|
||||
return new Closeable() {
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
for (var dirToDelete : dirsToDelete) {
|
||||
Files.deleteIfExists(dirToDelete);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public final static class TextStreamVerifier {
|
||||
TextStreamVerifier(String value) {
|
||||
this.value = value;
|
||||
|
@ -254,6 +254,14 @@ if [ -z "$run_all_tests" ]; then
|
||||
jtreg_args+=(-Djpackage.test.SQETest=yes)
|
||||
fi
|
||||
|
||||
if [ -n "$APPDATA" ]; then
|
||||
# Looks like this is Windows.
|
||||
# Explicitly add LOCALAPPDATA and APPDATA environment variables to the list
|
||||
# of environment variables jtreg will pass to tests as by default it will not.
|
||||
# This is needed for PerUserCfgTest test.
|
||||
jtreg_args+=("-e:LOCALAPPDATA,APPDATA")
|
||||
fi
|
||||
|
||||
jtreg_args+=("$test_actions")
|
||||
|
||||
# Drop arguments separator
|
||||
|
185
test/jdk/tools/jpackage/share/PerUserCfgTest.java
Normal file
185
test/jdk/tools/jpackage/share/PerUserCfgTest.java
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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.List;
|
||||
import java.util.Optional;
|
||||
import jdk.jpackage.test.AdditionalLauncher;
|
||||
import jdk.jpackage.test.PackageTest;
|
||||
import jdk.jpackage.test.Annotations.Test;
|
||||
import jdk.jpackage.test.Functional.ThrowingConsumer;
|
||||
import jdk.jpackage.test.HelloApp;
|
||||
import jdk.jpackage.test.JPackageCommand;
|
||||
import jdk.jpackage.test.LinuxHelper;
|
||||
import jdk.jpackage.test.PackageType;
|
||||
import jdk.jpackage.test.TKit;
|
||||
|
||||
/**
|
||||
* Test per-user configuration of app launchers created by jpackage.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary pre-user configuration of app launchers
|
||||
* @library ../helpers
|
||||
* @key jpackagePlatformPackage
|
||||
* @requires jpackage.test.SQETest == null
|
||||
* @build jdk.jpackage.test.*
|
||||
* @compile PerUserCfgTest.java
|
||||
* @modules jdk.jpackage/jdk.jpackage.internal
|
||||
* @run main/othervm/timeout=360 -Xmx512m jdk.jpackage.test.Main
|
||||
* --jpt-run=PerUserCfgTest
|
||||
*/
|
||||
public class PerUserCfgTest {
|
||||
|
||||
@Test
|
||||
public static void test() throws IOException {
|
||||
// Create a number of .cfg files with different startup args
|
||||
JPackageCommand cfgCmd = JPackageCommand.helloAppImage().setFakeRuntime()
|
||||
.setArgumentValue("--dest", TKit.createTempDirectory("cfg-files").toString());
|
||||
|
||||
addLauncher(cfgCmd, "a");
|
||||
addLauncher(cfgCmd, "b");
|
||||
|
||||
cfgCmd.execute();
|
||||
|
||||
new PackageTest().configureHelloApp().addInstallVerifier(cmd -> {
|
||||
if (cmd.isPackageUnpacked("Not running per-user configuration tests")) {
|
||||
return;
|
||||
}
|
||||
|
||||
Path launcherPath = cmd.appLauncherPath();
|
||||
if (!cmd.canRunLauncher(String.format(
|
||||
"Not running %s launcher and per-user configuration tests",
|
||||
launcherPath))) {
|
||||
return;
|
||||
}
|
||||
|
||||
final PackageType type = cmd.packageType();
|
||||
if (PackageType.MAC.contains(type)) {
|
||||
withConfigFile(cmd, cfgCmd.appLauncherCfgPath("a"),
|
||||
getUserHomeDir().resolve("Library/Application Support").resolve(
|
||||
cmd.name()), theCmd -> {
|
||||
runMainLauncher(cmd, "a");
|
||||
});
|
||||
} else if (PackageType.LINUX.contains(type)) {
|
||||
final String pkgName = LinuxHelper.getPackageName(cmd);
|
||||
final Path homeDir = getUserHomeDir();
|
||||
|
||||
withConfigFile(cmd, cfgCmd.appLauncherCfgPath("a"),
|
||||
homeDir.resolve(".local").resolve(pkgName), theCmd -> {
|
||||
runMainLauncher(cmd, "a");
|
||||
});
|
||||
|
||||
withConfigFile(cmd, cfgCmd.appLauncherCfgPath("b"),
|
||||
homeDir.resolve("." + pkgName), theCmd -> {
|
||||
runMainLauncher(cmd, "b");
|
||||
});
|
||||
|
||||
withConfigFile(cmd, cfgCmd.appLauncherCfgPath("b"),
|
||||
homeDir.resolve("." + pkgName), theCmd -> {
|
||||
runMainLauncher(cmd, "b");
|
||||
|
||||
withConfigFile(cmd, cfgCmd.appLauncherCfgPath("a"),
|
||||
homeDir.resolve(".local").resolve(pkgName),
|
||||
theCmd2 -> {
|
||||
runMainLauncher(cmd, "a");
|
||||
});
|
||||
});
|
||||
} else if (PackageType.WINDOWS.contains(type)) {
|
||||
final Path appData = getDirFromEnvVariable("APPDATA");
|
||||
final Path localAppData = getDirFromEnvVariable("LOCALAPPDATA");
|
||||
|
||||
if (appData == null || localAppData == null) {
|
||||
TKit.trace(String.format(
|
||||
"Not running per-user configuration tests because some of the environment varibles are not set. "
|
||||
+ "Run jtreg with -e:APPDATA,LOCALAPPDATA option to fix the problem"));
|
||||
} else {
|
||||
withConfigFile(cmd, cfgCmd.appLauncherCfgPath("a"),
|
||||
appData.resolve(cmd.name()), theCmd -> {
|
||||
runMainLauncher(cmd, "a");
|
||||
});
|
||||
|
||||
withConfigFile(cmd, cfgCmd.appLauncherCfgPath("b"),
|
||||
localAppData.resolve(cmd.name()), theCmd -> {
|
||||
runMainLauncher(cmd, "b");
|
||||
});
|
||||
|
||||
withConfigFile(cmd, cfgCmd.appLauncherCfgPath("b"),
|
||||
appData.resolve(cmd.name()), theCmd -> {
|
||||
runMainLauncher(cmd, "b");
|
||||
|
||||
withConfigFile(cmd, cfgCmd.appLauncherCfgPath("a"),
|
||||
localAppData.resolve(cmd.name()),
|
||||
theCmd2 -> {
|
||||
runMainLauncher(cmd, "a");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
runMainLauncher(cmd);
|
||||
}).run();
|
||||
}
|
||||
|
||||
private static void addLauncher(JPackageCommand cmd, String name) {
|
||||
new AdditionalLauncher(name) {
|
||||
@Override
|
||||
protected void verify(JPackageCommand cmd) {}
|
||||
}.setDefaultArguments(name).applyTo(cmd);
|
||||
}
|
||||
|
||||
private static Path getUserHomeDir() {
|
||||
return getDirFromEnvVariable("HOME");
|
||||
}
|
||||
|
||||
private static Path getDirFromEnvVariable(String envVariableName) {
|
||||
return Optional.ofNullable(System.getenv(envVariableName)).map(Path::of).orElse(
|
||||
null);
|
||||
}
|
||||
|
||||
private static void withConfigFile(JPackageCommand cmd, Path srcCfgFile,
|
||||
Path outputCfgFileDir, ThrowingConsumer<JPackageCommand> action) throws
|
||||
Throwable {
|
||||
Path targetCfgFile = outputCfgFileDir.resolve(cmd.appLauncherCfgPath(
|
||||
null).getFileName());
|
||||
TKit.assertPathExists(targetCfgFile, false);
|
||||
try (var dirCleaner = TKit.createDirectories(targetCfgFile.getParent())) {
|
||||
Files.copy(srcCfgFile, targetCfgFile);
|
||||
try {
|
||||
TKit.traceFileContents(targetCfgFile, "cfg file");
|
||||
action.accept(cmd);
|
||||
} finally {
|
||||
Files.deleteIfExists(targetCfgFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void runMainLauncher(JPackageCommand cmd,
|
||||
String... expectedArgs) {
|
||||
|
||||
HelloApp.assertApp(cmd.appLauncherPath()).addDefaultArguments(List.of(
|
||||
expectedArgs)).executeAndVerifyOutput();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user