Skip to content
Permalink
Browse files
8231283: Add support to jpackage to create install Linux packages in …
…/usr hierarchy

Reviewed-by: herrick, almatvee
  • Loading branch information
Alexey Semenyuk committed Jun 10, 2020
1 parent 714b345 commit 268d87018708347b1adc864f4048900c09feab4c
Showing 26 changed files with 874 additions and 162 deletions.
@@ -432,10 +432,18 @@ protected Map<String, String> createReplacementData(
}

private File getConfig_CopyrightFile(Map<String, ? super Object> params) {
PlatformPackage thePackage = createMetaPackage(params);
return thePackage.sourceRoot().resolve(Path.of(".",
LINUX_INSTALL_DIR.fetchFrom(params), PACKAGE_NAME.fetchFrom(
params), "share/doc/copyright")).toFile();
final String installDir = LINUX_INSTALL_DIR.fetchFrom(params);
final String packageName = PACKAGE_NAME.fetchFrom(params);

final Path installPath;
if (isInstallDirInUsrTree(installDir) || installDir.startsWith("/usr/")) {
installPath = Path.of("/usr/share/doc/", packageName, "copyright");
} else {
installPath = Path.of(installDir, packageName, "share/doc/copyright");
}

return createMetaPackage(params).sourceRoot().resolve(
Path.of("/").relativize(installPath)).toFile();
}

private File buildDeb(Map<String, ? super Object> params,
@@ -31,6 +31,7 @@
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static jdk.incubator.jpackage.internal.DesktopIntegration.*;
@@ -114,8 +115,8 @@ final public File execute(Map<String, ? super Object> params,
initAppImageLayout.apply(appImage).copy(
thePackage.sourceApplicationLayout());
} else {
appImage = appImageBundler.execute(params,
thePackage.sourceRoot().toFile());
final Path srcAppImageRoot = thePackage.sourceRoot().resolve("src");
appImage = appImageBundler.execute(params, srcAppImageRoot.toFile());
ApplicationLayout srcAppLayout = initAppImageLayout.apply(
appImage);
if (appImage.equals(PREDEFINED_RUNTIME_IMAGE.fetchFrom(params))) {
@@ -126,11 +127,7 @@ final public File execute(Map<String, ? super Object> params,
// Application image is a newly created directory tree.
// Move it.
srcAppLayout.move(thePackage.sourceApplicationLayout());
if (appImage.exists()) {
// Empty app image directory might remain after all application
// directories have been moved.
appImage.delete();
}
IOUtils.deleteRecursive(srcAppImageRoot.toFile());
}
}

@@ -240,6 +237,17 @@ abstract protected File buildPackageBundle(

final protected PlatformPackage createMetaPackage(
Map<String, ? super Object> params) {

Supplier<ApplicationLayout> packageLayout = () -> {
String installDir = LINUX_INSTALL_DIR.fetchFrom(params);
if (isInstallDirInUsrTree(installDir)) {
return ApplicationLayout.linuxUsrTreePackageImage(
Path.of("/").relativize(Path.of(installDir)),
packageName.fetchFrom(params));
}
return appImageLayout(params);
};

return new PlatformPackage() {
@Override
public String name() {
@@ -253,19 +261,23 @@ public Path sourceRoot() {

@Override
public ApplicationLayout sourceApplicationLayout() {
return appImageLayout(params).resolveAt(
return packageLayout.get().resolveAt(
applicationInstallDir(sourceRoot()));
}

@Override
public ApplicationLayout installedApplicationLayout() {
return appImageLayout(params).resolveAt(
return packageLayout.get().resolveAt(
applicationInstallDir(Path.of("/")));
}

private Path applicationInstallDir(Path root) {
Path installDir = Path.of(LINUX_INSTALL_DIR.fetchFrom(params),
name());
String installRoot = LINUX_INSTALL_DIR.fetchFrom(params);
if (isInstallDirInUsrTree(installRoot)) {
return root;
}

Path installDir = Path.of(installRoot, name());
if (installDir.isAbsolute()) {
installDir = Path.of("." + installDir.toString()).normalize();
}
@@ -284,10 +296,6 @@ private ApplicationLayout appImageLayout(

private static void validateInstallDir(String installDir) throws
ConfigException {
if (installDir.startsWith("/usr/") || installDir.equals("/usr")) {
throw new ConfigException(MessageFormat.format(I18N.getString(
"error.unsupported-install-dir"), installDir), null);
}

if (installDir.isEmpty()) {
throw new ConfigException(MessageFormat.format(I18N.getString(
@@ -312,6 +320,10 @@ private static void validateInstallDir(String installDir) throws
}
}

protected static boolean isInstallDirInUsrTree(String installDir) {
return Set.of("/usr/local", "/usr").contains(installDir);
}

private final BundlerParamInfo<String> packageName;
private final Bundler appImageBundler;
private boolean withFindNeededPackages;
@@ -159,8 +159,15 @@ protected Map<String, String> createReplacementData(
Map<String, ? super Object> params) throws IOException {
Map<String, String> data = new HashMap<>();

data.put("APPLICATION_DIRECTORY", Path.of(LINUX_INSTALL_DIR.fetchFrom(
params), PACKAGE_NAME.fetchFrom(params)).toString());
final Path prefix = Path.of(LINUX_INSTALL_DIR.fetchFrom(params));

Path appDirectory = prefix;
if (!isInstallDirInUsrTree(prefix.toString())) {
appDirectory = appDirectory.resolve(PACKAGE_NAME.fetchFrom(params));
}

data.put("APPLICATION_PREFIX", prefix.toString());
data.put("APPLICATION_DIRECTORY", appDirectory.toString());
data.put("APPLICATION_SUMMARY", APP_NAME.fetchFrom(params));
data.put("APPLICATION_LICENSE_TYPE", LICENSE_TYPE.fetchFrom(params));

@@ -44,7 +44,6 @@ error.tool-not-found.advice=Please install required packages
error.tool-old-version.advice=Please install required packages

error.invalid-install-dir=Invalid installation directory "{0}"
error.unsupported-install-dir=Installing to system directory "{0}" is currently unsupported

error.invalid-value-for-package-name=Invalid value "{0}" for the bundle name.
error.invalid-value-for-package-name.advice=Set the "linux-bundle-name" option to a valid Debian package name. Note that the package names must consist only of lower case letters (a-z), digits (0-9), plus (+) and minus (-) signs, and periods (.). They must be at least two characters long and must start with an alphanumeric character.
@@ -44,7 +44,6 @@ error.tool-not-found.advice=\u5FC5\u8981\u306A\u30D1\u30C3\u30B1\u30FC\u30B8\u30
error.tool-old-version.advice=\u5FC5\u8981\u306A\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\u3066\u304F\u3060\u3055\u3044

error.invalid-install-dir=\u7121\u52B9\u306A\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u30FB\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA"{0}"
error.unsupported-install-dir=\u30B7\u30B9\u30C6\u30E0\u30FB\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA"{0}"\u3078\u306E\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u306F\u73FE\u5728\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u307E\u305B\u3093

error.invalid-value-for-package-name=\u30D0\u30F3\u30C9\u30EB\u540D\u306E\u5024"{0}"\u304C\u7121\u52B9\u3067\u3059\u3002
error.invalid-value-for-package-name.advice="linux-bundle-name"\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u6709\u52B9\u306ADebian\u30D1\u30C3\u30B1\u30FC\u30B8\u540D\u306B\u8A2D\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u30D1\u30C3\u30B1\u30FC\u30B8\u540D\u306B\u306F\u3001\u5C0F\u6587\u5B57(a-z)\u3001\u6570\u5B57(0-9)\u3001\u30D7\u30E9\u30B9(+)\u3068\u30DE\u30A4\u30CA\u30B9(-)\u306E\u8A18\u53F7\u304A\u3088\u3073\u30D4\u30EA\u30AA\u30C9(.)\u306E\u307F\u3092\u542B\u3081\u308B\u3088\u3046\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u9577\u3055\u306F2\u6587\u5B57\u4EE5\u4E0A\u3068\u3057\u3001\u82F1\u6570\u5B57\u3067\u59CB\u3081\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002
@@ -44,7 +44,6 @@ error.tool-not-found.advice=\u8BF7\u5B89\u88C5\u6240\u9700\u7684\u7A0B\u5E8F\u53
error.tool-old-version.advice=\u8BF7\u5B89\u88C5\u6240\u9700\u7684\u7A0B\u5E8F\u5305

error.invalid-install-dir=\u5B89\u88C5\u76EE\u5F55 "{0}" \u65E0\u6548
error.unsupported-install-dir=\u5F53\u524D\u4E0D\u652F\u6301\u5B89\u88C5\u5230\u7CFB\u7EDF\u76EE\u5F55 "{0}"

error.invalid-value-for-package-name=\u5305\u540D\u7684\u503C "{0}" \u65E0\u6548\u3002
error.invalid-value-for-package-name.advice=\u5C06 "linux-bundle-name" \u9009\u9879\u8BBE\u7F6E\u4E3A\u6709\u6548\u7684 Debian \u7A0B\u5E8F\u5305\u540D\u79F0\u3002\u8BF7\u6CE8\u610F\uFF0C\u7A0B\u5E8F\u5305\u540D\u79F0\u53EA\u80FD\u5305\u542B\u5C0F\u5199\u5B57\u6BCD (a-z)\u3001\u6570\u5B57 (0-9)\u3001\u52A0\u53F7 (+) \u548C\u51CF\u53F7 (-) \u4EE5\u53CA\u53E5\u70B9 (.)\u3002\u540D\u79F0\u957F\u5EA6\u5FC5\u987B\u81F3\u5C11\u4E3A\u4E24\u4E2A\u5B57\u7B26\u5E76\u4E14\u5FC5\u987B\u4EE5\u5B57\u6BCD\u6570\u5B57\u5B57\u7B26\u5F00\u5934\u3002
@@ -4,8 +4,13 @@ Version: APPLICATION_VERSION
Release: APPLICATION_RELEASE
License: APPLICATION_LICENSE_TYPE
Vendor: APPLICATION_VENDOR
Prefix: %{dirname:APPLICATION_DIRECTORY}

%if "xAPPLICATION_PREFIX" != x
Prefix: APPLICATION_PREFIX
%endif

Provides: APPLICATION_PACKAGE

%if "xAPPLICATION_GROUP" != x
Group: APPLICATION_GROUP
%endif
@@ -21,6 +26,12 @@ Requires: PACKAGE_DEFAULT_DEPENDENCIES PACKAGE_CUSTOM_DEPENDENCIES
#build time will substantially increase and it may require unpack200/system java to install
%define __jar_repack %{nil}

%define package_filelist %{_tmppath}/%{name}.files
%define app_filelist %{_tmppath}/%{name}.app.files
%define filesystem_filelist %{_tmppath}/%{name}.filesystem.files

%define default_filesystem / /opt /usr /usr/bin /usr/lib /usr/local /usr/local/bin /usr/local/lib

%description
APPLICATION_DESCRIPTION

@@ -34,19 +45,22 @@ install -d -m 755 %{buildroot}APPLICATION_DIRECTORY
cp -r %{_sourcedir}APPLICATION_DIRECTORY/* %{buildroot}APPLICATION_DIRECTORY
%if "xAPPLICATION_LICENSE_FILE" != x
%define license_install_file %{_defaultlicensedir}/%{name}-%{version}/%{basename:APPLICATION_LICENSE_FILE}
install -d -m 755 %{buildroot}%{dirname:%{license_install_file}}
install -m 644 APPLICATION_LICENSE_FILE %{buildroot}%{license_install_file}
install -d -m 755 "%{buildroot}%{dirname:%{license_install_file}}"
install -m 644 "APPLICATION_LICENSE_FILE" "%{buildroot}%{license_install_file}"
%endif
(cd %{buildroot} && find . -type d) | sed -e 's/^\.//' -e '/^$/d' | sort > %{app_filelist}
{ rpm -ql filesystem || echo %{default_filesystem}; } | sort > %{filesystem_filelist}
comm -23 %{app_filelist} %{filesystem_filelist} > %{package_filelist}
sed -i -e 's/.*/%dir "&"/' %{package_filelist}
(cd %{buildroot} && find . -not -type d) | sed -e 's/^\.//' -e 's/.*/"&"/' >> %{package_filelist}
%if "xAPPLICATION_LICENSE_FILE" != x
sed -i -e 's|"%{license_install_file}"||' -e '/^$/d' %{package_filelist}
%endif

%files
%files -f %{package_filelist}
%if "xAPPLICATION_LICENSE_FILE" != x
%license %{license_install_file}
%{dirname:%{license_install_file}}
%license "%{license_install_file}"
%endif
# If installation directory for the application is /a/b/c, we want only root
# component of the path (/a) in the spec file to make sure all subdirectories
# are owned by the package.
%(echo APPLICATION_DIRECTORY | sed -e "s|\(^/[^/]\{1,\}\).*$|\1|")

%post
DESKTOP_COMMANDS_INSTALL
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2020, 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 <stdio.h>
#include <stdlib.h>
#include "Executor.h"
#include "Log.h"
#include "ErrorHandling.h"


int executeCommandLineAndReadStdout(const std::string& cmd,
CommandOutputConsumer& consumer) {
FILE * stream = popen(cmd.c_str(), "r");
if (!stream) {
JP_THROW(tstrings::any() << "popen(" << cmd
<< ") failed. Error: " << lastCRTError());
}

LOG_TRACE(tstrings::any() << "Reading output of [" << cmd << "] command");

try {
bool useConsumer = true;
std::string buf;
for (;;) {
const int c = fgetc(stream);
if(c == EOF) {
if (useConsumer && !buf.empty()) {
LOG_TRACE(tstrings::any() << "Next line: [" << buf << "]");
consumer.accept(buf);
}
break;
}

if (c == '\n' && useConsumer) {
LOG_TRACE(tstrings::any() << "Next line: [" << buf << "]");
useConsumer = !consumer.accept(buf);
buf.clear();
} else {
buf.push_back(static_cast<char>(c));
}
}
return pclose(stream);
} catch (...) {
if (stream) {
pclose(stream);
}
throw;
}
}
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2020, 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 EXECUTOR_H
#define EXECUTOR_H

#include "tstrings.h"


class CommandOutputConsumer {
public:
virtual ~CommandOutputConsumer() {}

virtual bool accept(const std::string& line) {
return true;
};
};

int executeCommandLineAndReadStdout(const std::string& cmd,
CommandOutputConsumer& consumer);

#endif // #ifndef EXECUTOR_H
@@ -23,29 +23,41 @@
* questions.
*/

#include <stdio.h>
#include "AppLauncher.h"
#include "FileUtils.h"
#include "UnixSysInfo.h"
#include "Package.h"


namespace {


void launchApp() {
setlocale(LC_ALL, "en_US.utf8");

const tstring launcherPath = SysInfo::getProcessModulePath();

// Launcher should be in "bin" subdirectory of app image.
const tstring appImageRoot = FileUtils::dirname(
FileUtils::dirname(launcherPath));

AppLauncher()
.setImageRoot(appImageRoot)
.addJvmLibName(_T("lib/libjli.so"))
.setAppDir(FileUtils::mkpath() << appImageRoot << _T("lib/app"))
.setDefaultRuntimePath(FileUtils::mkpath() << appImageRoot
<< _T("lib/runtime"))
.launch();
const Package ownerPackage = Package::findOwnerOfFile(launcherPath);

AppLauncher appLauncher;
appLauncher.addJvmLibName(_T("lib/libjli.so"));

if (ownerPackage.name().empty()) {
// Launcher should be in "bin" subdirectory of app image.
const tstring appImageRoot = FileUtils::dirname(
FileUtils::dirname(launcherPath));

appLauncher
.setImageRoot(appImageRoot)
.setAppDir(FileUtils::mkpath() << appImageRoot << _T("lib/app"))
.setDefaultRuntimePath(FileUtils::mkpath() << appImageRoot
<< _T("lib/runtime"));
} else {
ownerPackage.initAppLauncher(appLauncher);
}

appLauncher.launch();
}

} // namespace

0 comments on commit 268d870

Please sign in to comment.