Skip to content

Commit 327b7c3

Browse files
author
Alexey Semenyuk
committed
8370100: Redundant .png files in Linux app-image cause unnecessary bloat
Reviewed-by: almatvee
1 parent 460a69b commit 327b7c3

25 files changed

+516
-134
lines changed

src/jdk.jpackage/linux/classes/jdk/jpackage/internal/DesktopIntegration.java

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,10 @@
2626

2727
import static jdk.jpackage.internal.ApplicationImageUtils.createLauncherIconResource;
2828
import static jdk.jpackage.internal.model.LauncherShortcut.toRequest;
29-
import static jdk.jpackage.internal.util.function.ThrowingFunction.toFunction;
3029

3130
import java.awt.image.BufferedImage;
3231
import java.io.File;
3332
import java.io.IOException;
34-
import java.io.UncheckedIOException;
3533
import java.nio.file.Path;
3634
import java.util.ArrayList;
3735
import java.util.Arrays;
@@ -82,22 +80,12 @@ private DesktopIntegration(BuildEnv env, LinuxPackage pkg, LinuxLauncher launche
8280
// - user explicitly requested to create a shortcut
8381
boolean withDesktopFile = !associations.isEmpty() || toRequest(launcher.shortcut()).orElse(false);
8482

85-
var curIconResource = createLauncherIconResource(pkg.app(), launcher,
86-
env::createResource);
87-
88-
if (curIconResource.isEmpty()) {
83+
if (!launcher.hasIcon()) {
8984
// This is additional launcher with explicit `no icon` configuration.
9085
withDesktopFile = false;
91-
} else {
92-
try {
93-
if (curIconResource.get().saveToFile((Path)null) != OverridableResource.Source.DefaultResource) {
94-
// This launcher has custom icon configured.
95-
withDesktopFile = true;
96-
}
97-
} catch (IOException ex) {
98-
// Should never happen as `saveToFile((Path)null)` should not perform any actual I/O operations.
99-
throw new UncheckedIOException(ex);
100-
}
86+
} else if (launcher.hasCustomIcon()) {
87+
// This launcher has custom icon configured.
88+
withDesktopFile = true;
10189
}
10290

10391
desktopFileResource = env.createResource("template.desktop")
@@ -119,17 +107,12 @@ private DesktopIntegration(BuildEnv env, LinuxPackage pkg, LinuxLauncher launche
119107
if (withDesktopFile) {
120108
desktopFile = Optional.of(createDesktopFile(desktopFileName));
121109
iconFile = Optional.of(createDesktopFile(escapedAppFileName + ".png"));
122-
123-
if (curIconResource.isEmpty()) {
124-
// Create default icon.
125-
curIconResource = createLauncherIconResource(pkg.app(), pkg.app().mainLauncher().orElseThrow(), env::createResource);
126-
}
127110
} else {
128111
desktopFile = Optional.empty();
129112
iconFile = Optional.empty();
130113
}
131114

132-
iconResource = curIconResource;
115+
iconResource = createLauncherIconResource(launcher, env::createResource);
133116

134117
desktopFileData = createDataForDesktopFile();
135118

src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxAppBundler.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@
2525

2626
package jdk.jpackage.internal;
2727

28+
import java.util.Optional;
29+
2830
public class LinuxAppBundler extends AppImageBundler {
2931
public LinuxAppBundler() {
3032
setAppImageSupplier((params, output) -> {
3133
// Order is important!
3234
var app = LinuxFromParams.APPLICATION.fetchFrom(params);
3335
var env = BuildEnvFromParams.BUILD_ENV.fetchFrom(params);
34-
LinuxPackagingPipeline.build()
36+
LinuxPackagingPipeline.build(Optional.empty())
3537
.excludeDirFromCopying(output.getParent())
3638
.create().execute(BuildEnv.withAppImageDir(env, output), app);
3739
});

src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxDebBundler.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import java.nio.file.Path;
2929
import java.util.Map;
30+
import java.util.Optional;
3031
import jdk.jpackage.internal.model.LinuxDebPackage;
3132
import jdk.jpackage.internal.model.PackagerException;
3233
import jdk.jpackage.internal.model.StandardPackageType;
@@ -51,12 +52,14 @@ public String getID() {
5152
@Override
5253
public Path execute(Map<String, ? super Object> params, Path outputParentDir) throws PackagerException {
5354

55+
var pkg = LinuxFromParams.DEB_PACKAGE.fetchFrom(params);
56+
5457
return Packager.<LinuxDebPackage>build().outputDir(outputParentDir)
55-
.pkg(LinuxFromParams.DEB_PACKAGE.fetchFrom(params))
58+
.pkg(pkg)
5659
.env(BuildEnvFromParams.BUILD_ENV.fetchFrom(params))
57-
.pipelineBuilderMutatorFactory((env, pkg, outputDir) -> {
60+
.pipelineBuilderMutatorFactory((env, _, outputDir) -> {
5861
return new LinuxDebPackager(env, pkg, outputDir, sysEnv.orElseThrow());
59-
}).execute(LinuxPackagingPipeline.build());
62+
}).execute(LinuxPackagingPipeline.build(Optional.of(pkg)));
6063
}
6164

6265
@Override

src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxFromParams.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import jdk.jpackage.internal.model.LinuxLauncher;
4444
import jdk.jpackage.internal.model.LinuxLauncherMixin;
4545
import jdk.jpackage.internal.model.LinuxRpmPackage;
46+
import jdk.jpackage.internal.model.Launcher;
4647
import jdk.jpackage.internal.model.StandardPackageType;
4748

4849
final class LinuxFromParams {
@@ -55,7 +56,9 @@ private static LinuxApplication createLinuxApplication(
5556
final var launcher = launcherFromParams.create(launcherParams);
5657
final var shortcut = findLauncherShortcut(LINUX_SHORTCUT_HINT, params, launcherParams);
5758
return LinuxLauncher.create(launcher, new LinuxLauncherMixin.Stub(shortcut));
58-
}), APPLICATION_LAYOUT).create();
59+
}), (LinuxLauncher linuxLauncher, Launcher launcher) -> {
60+
return LinuxLauncher.create(launcher, linuxLauncher);
61+
}, APPLICATION_LAYOUT).create();
5962
return LinuxApplication.create(app);
6063
}
6164

src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxPackagingPipeline.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,20 @@
2525
package jdk.jpackage.internal;
2626

2727
import static jdk.jpackage.internal.ApplicationImageUtils.createLauncherIconResource;
28-
import jdk.jpackage.internal.PackagingPipeline.AppImageBuildEnv;
2928

3029
import java.io.IOException;
3130
import java.io.UncheckedIOException;
3231
import java.nio.file.Files;
3332
import java.nio.file.Path;
33+
import java.util.Optional;
34+
import jdk.jpackage.internal.PackagingPipeline.AppImageBuildEnv;
3435
import jdk.jpackage.internal.PackagingPipeline.BuildApplicationTaskID;
3536
import jdk.jpackage.internal.PackagingPipeline.PrimaryTaskID;
3637
import jdk.jpackage.internal.PackagingPipeline.TaskID;
3738
import jdk.jpackage.internal.model.Application;
3839
import jdk.jpackage.internal.model.ApplicationLayout;
40+
import jdk.jpackage.internal.model.Launcher;
41+
import jdk.jpackage.internal.model.LinuxPackage;
3942
import jdk.jpackage.internal.resources.ResourceLocator;
4043

4144
final class LinuxPackagingPipeline {
@@ -45,14 +48,20 @@ enum LinuxAppImageTaskID implements TaskID {
4548
LAUNCHER_ICONS
4649
}
4750

48-
static PackagingPipeline.Builder build() {
49-
return PackagingPipeline.buildStandard()
51+
static PackagingPipeline.Builder build(Optional<LinuxPackage> pkg) {
52+
var builder = PackagingPipeline.buildStandard()
5053
.task(LinuxAppImageTaskID.LAUNCHER_LIB)
5154
.addDependent(PrimaryTaskID.BUILD_APPLICATION_IMAGE)
5255
.applicationAction(LinuxPackagingPipeline::writeLauncherLib).add()
5356
.task(LinuxAppImageTaskID.LAUNCHER_ICONS)
5457
.addDependent(BuildApplicationTaskID.CONTENT)
5558
.applicationAction(LinuxPackagingPipeline::writeLauncherIcons).add();
59+
60+
pkg.ifPresent(_ -> {
61+
builder.task(LinuxAppImageTaskID.LAUNCHER_ICONS).noaction().add();
62+
});
63+
64+
return builder;
5665
}
5766

5867
private static void writeLauncherLib(
@@ -68,8 +77,8 @@ private static void writeLauncherLib(
6877
private static void writeLauncherIcons(
6978
AppImageBuildEnv<Application, ApplicationLayout> env) throws IOException {
7079

71-
for (var launcher : env.app().launchers()) {
72-
createLauncherIconResource(env.app(), launcher, env.env()::createResource).ifPresent(iconResource -> {
80+
env.app().launchers().stream().filter(Launcher::hasCustomIcon).forEach(launcher -> {
81+
createLauncherIconResource(launcher, env.env()::createResource).ifPresent(iconResource -> {
7382
String iconFileName = launcher.executableName() + ".png";
7483
Path iconTarget = env.resolvedLayout().desktopIntegrationDirectory().resolve(iconFileName);
7584
try {
@@ -78,7 +87,7 @@ private static void writeLauncherIcons(
7887
throw new UncheckedIOException(ex);
7988
}
8089
});
81-
}
90+
});
8291
}
8392

8493
static final LinuxApplicationLayout APPLICATION_LAYOUT = LinuxApplicationLayout.create(

src/jdk.jpackage/linux/classes/jdk/jpackage/internal/LinuxRpmBundler.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import java.nio.file.Path;
2929
import java.util.Map;
30+
import java.util.Optional;
3031
import jdk.jpackage.internal.model.LinuxRpmPackage;
3132
import jdk.jpackage.internal.model.PackagerException;
3233
import jdk.jpackage.internal.model.StandardPackageType;
@@ -52,12 +53,14 @@ public String getID() {
5253
@Override
5354
public Path execute(Map<String, ? super Object> params, Path outputParentDir) throws PackagerException {
5455

56+
var pkg = LinuxFromParams.RPM_PACKAGE.fetchFrom(params);
57+
5558
return Packager.<LinuxRpmPackage>build().outputDir(outputParentDir)
56-
.pkg(LinuxFromParams.RPM_PACKAGE.fetchFrom(params))
59+
.pkg(pkg)
5760
.env(BuildEnvFromParams.BUILD_ENV.fetchFrom(params))
58-
.pipelineBuilderMutatorFactory((env, pkg, outputDir) -> {
61+
.pipelineBuilderMutatorFactory((env, _, outputDir) -> {
5962
return new LinuxRpmPackager(env, pkg, outputDir, sysEnv.orElseThrow());
60-
}).execute(LinuxPackagingPipeline.build());
63+
}).execute(LinuxPackagingPipeline.build(Optional.of(pkg)));
6164
}
6265

6366
@Override

src/jdk.jpackage/macosx/classes/jdk/jpackage/internal/MacFromParams.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,9 @@ private static MacApplication createMacApplication(
9292
final var superAppBuilder = createApplicationBuilder(params, toFunction(launcherParams -> {
9393
var launcher = launcherFromParams.create(launcherParams);
9494
return MacLauncher.create(launcher);
95-
}), APPLICATION_LAYOUT, RUNTIME_BUNDLE_LAYOUT, predefinedRuntimeLayout.map(RuntimeLayout::unresolve));
95+
}), (MacLauncher _, Launcher launcher) -> {
96+
return MacLauncher.create(launcher);
97+
}, APPLICATION_LAYOUT, RUNTIME_BUNDLE_LAYOUT, predefinedRuntimeLayout.map(RuntimeLayout::unresolve));
9698

9799
if (hasPredefinedAppImage(params)) {
98100
// Set the main launcher start up info.

src/jdk.jpackage/share/classes/jdk/jpackage/internal/ApplicationBuilder.java

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,19 @@
3232
import java.util.Map;
3333
import java.util.Objects;
3434
import java.util.Optional;
35+
import java.util.function.BiFunction;
3536
import java.util.function.Function;
37+
import java.util.function.Predicate;
3638
import jdk.jpackage.internal.model.AppImageLayout;
3739
import jdk.jpackage.internal.model.Application;
3840
import jdk.jpackage.internal.model.ApplicationLaunchers;
3941
import jdk.jpackage.internal.model.ConfigException;
4042
import jdk.jpackage.internal.model.ExternalApplication;
4143
import jdk.jpackage.internal.model.ExternalApplication.LauncherInfo;
4244
import jdk.jpackage.internal.model.Launcher;
45+
import jdk.jpackage.internal.model.LauncherIcon;
4346
import jdk.jpackage.internal.model.LauncherStartupInfo;
47+
import jdk.jpackage.internal.model.ResourceDirLauncherIcon;
4448
import jdk.jpackage.internal.model.RuntimeBuilder;
4549

4650
final class ApplicationBuilder {
@@ -168,6 +172,97 @@ ApplicationBuilder contentDirs(List<Path> v) {
168172
return this;
169173
}
170174

175+
static <T extends Launcher> ApplicationLaunchers normalizeIcons(
176+
ApplicationLaunchers appLaunchers, Optional<Path> resourceDir, BiFunction<T, Launcher, T> launcherOverrideCtor) {
177+
178+
Objects.requireNonNull(resourceDir);
179+
180+
return normalizeLauncherProperty(appLaunchers, Launcher::hasDefaultIcon, (T launcher) -> {
181+
return resourceDir.<LauncherIcon>flatMap(dir -> {
182+
var resource = LauncherBuilder.createLauncherIconResource(launcher, _ -> {
183+
return new OverridableResource()
184+
.setResourceDir(dir)
185+
.setSourceOrder(OverridableResource.Source.ResourceDir);
186+
});
187+
if (resource.probe() == OverridableResource.Source.ResourceDir) {
188+
return Optional.of(ResourceDirLauncherIcon.create(resource.getPublicName().toString()));
189+
} else {
190+
return Optional.empty();
191+
}
192+
});
193+
}, launcher -> {
194+
return launcher.icon().orElseThrow();
195+
}, (launcher, icon) -> {
196+
return launcherOverrideCtor.apply(launcher, overrideIcon(launcher, icon));
197+
});
198+
}
199+
200+
static <T, U extends Launcher> ApplicationLaunchers normalizeLauncherProperty(
201+
ApplicationLaunchers appLaunchers,
202+
Predicate<U> needsNormalization,
203+
Function<U, Optional<T>> normalizedPropertyValueFinder,
204+
BiFunction<U, T, U> propertyOverrider) {
205+
206+
return normalizeLauncherProperty(
207+
appLaunchers,
208+
needsNormalization,
209+
normalizedPropertyValueFinder,
210+
launcher -> {
211+
return normalizedPropertyValueFinder.apply(launcher).orElseThrow();
212+
},
213+
propertyOverrider);
214+
}
215+
216+
static <T, U extends Launcher> ApplicationLaunchers normalizeLauncherProperty(
217+
ApplicationLaunchers appLaunchers,
218+
Predicate<U> needsNormalization,
219+
Function<U, Optional<T>> normalizedPropertyValueFinder,
220+
Function<U, T> normalizedPropertyValueGetter,
221+
BiFunction<U, T, U> propertyOverrider) {
222+
223+
Objects.requireNonNull(appLaunchers);
224+
Objects.requireNonNull(needsNormalization);
225+
Objects.requireNonNull(normalizedPropertyValueFinder);
226+
Objects.requireNonNull(normalizedPropertyValueGetter);
227+
Objects.requireNonNull(propertyOverrider);
228+
229+
boolean[] modified = new boolean[1];
230+
231+
@SuppressWarnings("unchecked")
232+
var newLaunchers = appLaunchers.asList().stream().map(launcher -> {
233+
return (U)launcher;
234+
}).map(launcher -> {
235+
if (needsNormalization.test(launcher)) {
236+
return normalizedPropertyValueFinder.apply(launcher).map(normalizedPropertyValue -> {
237+
modified[0] = true;
238+
return propertyOverrider.apply(launcher, normalizedPropertyValue);
239+
}).orElse(launcher);
240+
} else {
241+
return launcher;
242+
}
243+
}).toList();
244+
245+
var newMainLauncher = newLaunchers.getFirst();
246+
if (!needsNormalization.test(newMainLauncher)) {
247+
// The main launcher doesn't require normalization.
248+
newLaunchers = newLaunchers.stream().map(launcher -> {
249+
if (needsNormalization.test(launcher)) {
250+
var normalizedPropertyValue = normalizedPropertyValueGetter.apply(newMainLauncher);
251+
modified[0] = true;
252+
return propertyOverrider.apply(launcher, normalizedPropertyValue);
253+
} else {
254+
return launcher;
255+
}
256+
}).toList();
257+
}
258+
259+
if (modified[0]) {
260+
return ApplicationLaunchers.fromList(newLaunchers).orElseThrow();
261+
} else {
262+
return appLaunchers;
263+
}
264+
}
265+
171266
static Launcher overrideLauncherStartupInfo(Launcher launcher, LauncherStartupInfo startupInfo) {
172267
return new Launcher.Stub(
173268
launcher.name(),
@@ -195,6 +290,18 @@ static Application overrideAppImageLayout(Application app, AppImageLayout appIma
195290
app.extraAppImageFileData());
196291
}
197292

293+
private static Launcher overrideIcon(Launcher launcher, LauncherIcon icon) {
294+
return new Launcher.Stub(
295+
launcher.name(),
296+
launcher.startupInfo(),
297+
launcher.fileAssociations(),
298+
launcher.isService(),
299+
launcher.description(),
300+
Optional.of(icon),
301+
launcher.defaultIconResourceName(),
302+
launcher.extraAppImageFileData());
303+
}
304+
198305
record MainLauncherStartupInfo(String qualifiedClassName) implements LauncherStartupInfo {
199306
@Override
200307
public List<String> javaOptions() {

0 commit comments

Comments
 (0)