|
1 | 1 | /*
|
2 |
| - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. |
| 2 | + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. |
3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
4 | 4 | *
|
5 | 5 | * This code is free software; you can redistribute it and/or modify it
|
|
57 | 57 | import com.oracle.svm.core.OS;
|
58 | 58 | import com.oracle.svm.core.option.BundleMember;
|
59 | 59 | import com.oracle.svm.core.util.ArchiveSupport;
|
| 60 | +import com.oracle.svm.core.util.ByteFormattingUtil; |
60 | 61 | import com.oracle.svm.driver.BundleOptions.BundleOption;
|
61 | 62 | import com.oracle.svm.driver.BundleOptions.ExtendedOption;
|
62 | 63 | import com.oracle.svm.driver.launcher.BundleLauncher;
|
@@ -112,12 +113,27 @@ final class BundleSupport {
|
112 | 113 | private static final String DRY_RUN_OPTION = "dry-run";
|
113 | 114 | private static final String CONTAINER_OPTION = "container";
|
114 | 115 | private static final String DOCKERFILE_OPTION = "dockerfile";
|
| 116 | + private static final String SIZE_WARNING_FILE_LIMIT_OPTION = "size-warning-file-limit"; |
| 117 | + private static final String SIZE_WARNING_TOTAL_LIMIT_OPTION = "size-warning-total-limit"; |
115 | 118 | static final String BUNDLE_FILE_EXTENSION = ".nib";
|
116 | 119 | static final String BUNDLE_ALIAS = "<BUNDLE>";
|
117 | 120 |
|
118 | 121 | ContainerSupport containerSupport;
|
119 | 122 | boolean useContainer;
|
120 | 123 |
|
| 124 | + private long fileSizeWarningFileLimit = 1024 * 1024 * 50; // 50 MB |
| 125 | + private long fileSizeWarningTotalLimit = 1024 * 1024 * 500; // 500 MB |
| 126 | + |
| 127 | + /** |
| 128 | + * Counter for all the files addd to a bundle (except .nil), to print warnings. |
| 129 | + */ |
| 130 | + private long cumulativeFileSize = 0; |
| 131 | + |
| 132 | + /** |
| 133 | + * Tracks whether a layers file (.nil) is part of the bundle. |
| 134 | + */ |
| 135 | + private boolean nilFileSeen = false; |
| 136 | + |
121 | 137 | private static final String DEFAULT_DOCKERFILE = getDockerfile("Dockerfile");
|
122 | 138 |
|
123 | 139 | private static String getDockerfile(String name) {
|
@@ -249,10 +265,31 @@ private void processExtendedOption(ExtendedOption option) {
|
249 | 265 | throw NativeImage.showError(String.format("native-image option %s requires a dockerfile argument. E.g. %s=path/to/Dockerfile.", option.key(), option.key()));
|
250 | 266 | }
|
251 | 267 | }
|
| 268 | + case SIZE_WARNING_FILE_LIMIT_OPTION -> { |
| 269 | + fileSizeWarningFileLimit = readSizeLimit(option.key(), option.value()); |
| 270 | + } |
| 271 | + case SIZE_WARNING_TOTAL_LIMIT_OPTION -> { |
| 272 | + fileSizeWarningTotalLimit = readSizeLimit(option.key(), option.value()); |
| 273 | + } |
252 | 274 | default -> throw NativeImage.showError(String.format("Unknown option %s. Use --help-extra for usage instructions.", option.key()));
|
253 | 275 | }
|
254 | 276 | }
|
255 | 277 |
|
| 278 | + private long readSizeLimit(String optionKey, String optionValue) { |
| 279 | + if (optionValue != null) { |
| 280 | + try { |
| 281 | + long limit = Long.parseLong(optionValue); |
| 282 | + if (limit >= 0) { |
| 283 | + return limit * 1024 * 1024; |
| 284 | + } else if (limit == -1) { |
| 285 | + return -1; // no limit |
| 286 | + } |
| 287 | + } catch (NumberFormatException ex) { |
| 288 | + } |
| 289 | + } |
| 290 | + throw NativeImage.showError(String.format("native-image option %s requires a size in MiB, or -1 to deactivate.", optionKey)); |
| 291 | + } |
| 292 | + |
256 | 293 | private BundleSupport(NativeImage nativeImage) {
|
257 | 294 | Objects.requireNonNull(nativeImage);
|
258 | 295 | this.nativeImage = nativeImage;
|
@@ -556,12 +593,50 @@ private void copyFile(Path sourceFile, Path target, boolean overwrite) {
|
556 | 593 | return;
|
557 | 594 | }
|
558 | 595 | CopyOption[] options = overwrite ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING} : new CopyOption[0];
|
| 596 | + if (warnSize()) { |
| 597 | + trackFileSize(sourceFile); |
| 598 | + } |
559 | 599 | Files.copy(sourceFile, target, options);
|
560 | 600 | } catch (IOException e) {
|
561 | 601 | throw NativeImage.showError("Failed to copy " + sourceFile + " to " + target, e);
|
562 | 602 | }
|
563 | 603 | }
|
564 | 604 |
|
| 605 | + /** |
| 606 | + * Tracks the size of a file added to a bundle. Prints a log warning if the individual size |
| 607 | + * exceeds a certain limit. Also prints a warning when the cumulative size of all files added so |
| 608 | + * far exceed a limit, and repeats that at every multiple of that limit (maximum once per file). |
| 609 | + * |
| 610 | + * Layers files (.nil) are ignored for individual warnings and don't contribute to the |
| 611 | + * cumulative limit. |
| 612 | + * |
| 613 | + * @param file the file to track the size of |
| 614 | + * @throws IOException |
| 615 | + */ |
| 616 | + private void trackFileSize(Path file) throws IOException { |
| 617 | + if (!warnSize()) { |
| 618 | + return; |
| 619 | + } |
| 620 | + if (file.getFileName().endsWith(".nil")) { |
| 621 | + nilFileSeen = true; |
| 622 | + } |
| 623 | + long fileSize = Files.size(file); |
| 624 | + if (fileSizeWarningFileLimit >= 0 && fileSize > fileSizeWarningFileLimit) { |
| 625 | + LogUtils.warning(file + " adds " + ByteFormattingUtil.bytesToHuman(fileSize) + " to the Native Image bundle."); |
| 626 | + } |
| 627 | + if (fileSizeWarningTotalLimit >= 0) { |
| 628 | + long nextLimitMultiple = cumulativeFileSize + fileSizeWarningTotalLimit - (cumulativeFileSize % fileSizeWarningTotalLimit); |
| 629 | + cumulativeFileSize += fileSize; |
| 630 | + if (cumulativeFileSize > nextLimitMultiple) { |
| 631 | + LogUtils.warning("Native Image bundle has grown to " + ByteFormattingUtil.bytesToHuman(cumulativeFileSize) + (nilFileSeen ? " (excluding .nil files)." : ".")); |
| 632 | + } |
| 633 | + } |
| 634 | + } |
| 635 | + |
| 636 | + private boolean warnSize() { |
| 637 | + return fileSizeWarningFileLimit >= 0 || fileSizeWarningTotalLimit >= 0; |
| 638 | + } |
| 639 | + |
565 | 640 | void complete() {
|
566 | 641 | boolean writeOutput;
|
567 | 642 | try (Stream<Path> pathOutputFiles = Files.list(imagePathOutputDir); Stream<Path> auxiliaryOutputFiles = Files.list(auxiliaryOutputDir)) {
|
|
0 commit comments