blob: 4ed84909958fc62fd0049b4e5870291cfa75cec7 [file]
/*
* 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;
import static jdk.jpackage.internal.I18N.buildConfigException;
import java.nio.file.Path;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Pattern;
import jdk.jpackage.internal.model.AppImageLayout;
import jdk.jpackage.internal.model.ApplicationLayout;
import jdk.jpackage.internal.model.ConfigException;
import jdk.jpackage.internal.model.LinuxPackage;
import jdk.jpackage.internal.model.LinuxPackageMixin;
import jdk.jpackage.internal.model.Package;
import jdk.jpackage.internal.model.StandardPackageType;
final class LinuxPackageBuilder {
LinuxPackageBuilder(PackageBuilder pkgBuilder) {
this.pkgBuilder = Objects.requireNonNull(pkgBuilder);
}
LinuxPackage create() throws ConfigException {
if (literalName != null) {
pkgBuilder.name(literalName);
} else {
// Lower case and turn spaces/underscores into dashes
pkgBuilder.name(pkgBuilder.create().packageName().toLowerCase().replaceAll("[ _]", "-"));
}
final var pkg = pkgBuilder.create();
final var stdPkgType = pkg.asStandardPackageType();
if (stdPkgType.isPresent()) {
validatePackageName(pkg.packageName(), stdPkgType.orElseThrow());
}
var reply = create(pkg, pkg.packageLayout());
if (reply.isInstallDirInUsrTree()) {
reply = create(pkg, usrTreePackageLayout(pkg.relativeInstallDir(), pkg.packageName()));
}
return reply;
}
private LinuxPackage create(Package pkg, AppImageLayout pkgLayout) throws ConfigException {
return LinuxPackage.create(pkg, new LinuxPackageMixin.Stub(
pkgLayout,
Optional.ofNullable(menuGroupName).orElseGet(DEFAULTS::menuGroupName),
Optional.ofNullable(category),
Optional.ofNullable(additionalDependencies),
Optional.ofNullable(release),
pkg.asStandardPackageType().map(LinuxPackageArch::getValue).orElseThrow()));
}
LinuxPackageBuilder literalName(String v) {
literalName = v;
return this;
}
LinuxPackageBuilder menuGroupName(String v) {
menuGroupName = v;
return this;
}
LinuxPackageBuilder category(String v) {
category = v;
return this;
}
Optional<String> category() {
return Optional.ofNullable(category);
}
LinuxPackageBuilder additionalDependencies(String v) {
additionalDependencies = v;
return this;
}
LinuxPackageBuilder release(String v) {
release = v;
return this;
}
Optional<String> release() {
return Optional.ofNullable(release);
}
private static LinuxApplicationLayout usrTreePackageLayout(Path prefix, String packageName) {
final var lib = prefix.resolve(Path.of("lib", packageName));
return LinuxApplicationLayout.create(
ApplicationLayout.build()
.launchersDirectory(prefix.resolve("bin"))
.appDirectory(lib.resolve("app"))
.runtimeDirectory(lib.resolve("runtime"))
.desktopIntegrationDirectory(lib)
.appModsDirectory(lib.resolve("app/mods"))
.contentDirectory(lib)
.create(),
lib.resolve("lib/libapplauncher.so"));
}
private static void validatePackageName(String packageName,
StandardPackageType pkgType) throws ConfigException {
switch (pkgType) {
case LINUX_DEB -> {
//
// Debian rules for package naming are used here
// https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Source
//
// 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.
//
var regexp = Pattern.compile("^[a-z][a-z\\d\\+\\-\\.]+");
if (!regexp.matcher(packageName).matches()) {
throw buildConfigException()
.message("error.deb-invalid-value-for-package-name", packageName)
.advice("error.deb-invalid-value-for-package-name.advice")
.create();
}
}
case LINUX_RPM -> {
//
// Fedora rules for package naming are used here
// https://fedoraproject.org/wiki/Packaging:NamingGuidelines?rd=Packaging/NamingGuidelines
//
// all Fedora packages must be named using only the following ASCII
// characters. These characters are displayed here:
//
// abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._+
//
var regexp = Pattern.compile("[a-z\\d\\+\\-\\.\\_]+", Pattern.CASE_INSENSITIVE);
if (!regexp.matcher(packageName).matches()) {
throw buildConfigException()
.message("error.rpm-invalid-value-for-package-name", packageName)
.advice("error.rpm-invalid-value-for-package-name.advice")
.create();
}
}
default -> {
}
}
}
private record Defaults(String menuGroupName) {
}
private String literalName;
private String menuGroupName;
private String category;
private String additionalDependencies;
private String release;
private final PackageBuilder pkgBuilder;
private static final Defaults DEFAULTS = new Defaults(I18N.getString(
"param.menu-group.default"));
}