4848import org .graalvm .util .json .JSONParserException ;
4949
5050import com .oracle .svm .core .OS ;
51- import com .oracle .svm .core .SubstrateUtil ;
5251import com .oracle .svm .core .configure .ConfigurationParser ;
5352import com .oracle .svm .core .option .BundleMember ;
5453import com .oracle .svm .core .util .json .JsonPrinter ;
@@ -87,6 +86,8 @@ boolean show() {
8786 final Path modulePathDir ;
8887 final Path auxiliaryDir ;
8988 final Path outputDir ;
89+ final Path imagePathOutputDir ;
90+ final Path auxiliaryOutputDir ;
9091
9192 Map <Path , Path > pathCanonicalizations = new HashMap <>();
9293 Map <Path , Path > pathSubstitutions = new HashMap <>();
@@ -141,7 +142,6 @@ static BundleSupport create(NativeImage nativeImage, String bundleArg, NativeIma
141142 } else {
142143 bundleSupport = new BundleSupport (nativeImage , bundleStatus );
143144 }
144- bundleSupport .bundleFile = nativeImage .config .getWorkingDirectory ().resolve ("unnamed.nib" );
145145 return bundleSupport ;
146146 }
147147
@@ -159,6 +159,8 @@ private BundleSupport(NativeImage nativeImage, BundleStatus status) {
159159 classPathDir = Files .createDirectories (classesDir .resolve ("cp" ));
160160 modulePathDir = Files .createDirectories (classesDir .resolve ("p" ));
161161 outputDir = Files .createDirectories (rootDir .resolve ("output" ));
162+ imagePathOutputDir = Files .createDirectories (outputDir .resolve ("default" ));
163+ auxiliaryOutputDir = Files .createDirectories (outputDir .resolve ("other" ));
162164 } catch (IOException e ) {
163165 throw NativeImage .showError ("Unable to create bundle directory layout" , e );
164166 }
@@ -171,32 +173,32 @@ private BundleSupport(NativeImage nativeImage, BundleStatus status, String bundl
171173 this .nativeImage = nativeImage ;
172174 this .status = status ;
173175
174- Path bundlePath = Path .of (bundleFilename );
175- if (!Files .isReadable (bundlePath )) {
176+ bundleFile = Path .of (bundleFilename );
177+ if (!Files .isReadable (bundleFile )) {
176178 throw NativeImage .showError ("The given bundle file " + bundleFilename + " cannot be read" );
177179 }
178180
179- if (Files .isDirectory (bundlePath )) {
180- rootDir = bundlePath ;
181+ if (Files .isDirectory (bundleFile )) {
182+ throw NativeImage . showError ( "The given bundle file " + bundleFilename + " is a directory and not a file" ) ;
181183 } else {
182184 try {
183185 rootDir = Files .createTempDirectory (bundleTempDirPrefix );
184- try (JarFile archive = new JarFile (bundlePath .toFile ())) {
186+ try (JarFile archive = new JarFile (bundleFile .toFile ())) {
185187 archive .stream ().forEach (jarEntry -> {
186- Path bundleFile = rootDir .resolve (jarEntry .getName ());
188+ Path bundleEntry = rootDir .resolve (jarEntry .getName ());
187189 try {
188- Path bundleFileParent = bundleFile .getParent ();
190+ Path bundleFileParent = bundleEntry .getParent ();
189191 if (bundleFileParent != null ) {
190192 Files .createDirectories (bundleFileParent );
191193 }
192- Files .copy (archive .getInputStream (jarEntry ), bundleFile );
194+ Files .copy (archive .getInputStream (jarEntry ), bundleEntry );
193195 } catch (IOException e ) {
194- throw NativeImage .showError ("Unable to copy " + jarEntry .getName () + " from bundle " + bundlePath + " to " + bundleFile , e );
196+ throw NativeImage .showError ("Unable to copy " + jarEntry .getName () + " from bundle " + bundleEntry + " to " + bundleEntry , e );
195197 }
196198 });
197199 }
198200 } catch (IOException e ) {
199- throw NativeImage .showError ("Unable to create bundle directory layout from file " + bundlePath , e );
201+ throw NativeImage .showError ("Unable to create bundle directory layout from file " + bundleFile , e );
200202 }
201203 }
202204
@@ -207,6 +209,8 @@ private BundleSupport(NativeImage nativeImage, BundleStatus status, String bundl
207209 classPathDir = classesDir .resolve ("cp" );
208210 modulePathDir = classesDir .resolve ("p" );
209211 outputDir = rootDir .resolve ("output" );
212+ imagePathOutputDir = outputDir .resolve ("default" );
213+ auxiliaryOutputDir = outputDir .resolve ("other" );
210214
211215 Path pathCanonicalizationsFile = stageDir .resolve ("path_canonicalizations.json" );
212216 try (Reader reader = Files .newBufferedReader (pathCanonicalizationsFile )) {
@@ -230,6 +234,10 @@ private BundleSupport(NativeImage nativeImage, BundleStatus status, String bundl
230234 }
231235 }
232236
237+ public boolean isBundleCreation () {
238+ return !status .loadBundle ;
239+ }
240+
233241 public List <String > getBuildArgs () {
234242 return buildArgs ;
235243 }
@@ -266,14 +274,19 @@ Path substituteAuxiliaryPath(Path origPath, BundleMember.Role bundleMemberRole)
266274 destinationDir = auxiliaryDir ;
267275 break ;
268276 case Output :
269- destinationDir = outputDir ;
277+ destinationDir = auxiliaryOutputDir ;
270278 break ;
271279 default :
272280 return origPath ;
273281 }
274282 return substitutePath (origPath , destinationDir );
275283 }
276284
285+ Path substituteImagePath (Path origPath ) {
286+ pathSubstitutions .put (origPath , imagePathOutputDir );
287+ return imagePathOutputDir ;
288+ }
289+
277290 Path substituteClassPath (Path origPath ) {
278291 try {
279292 return substitutePath (origPath , classPathDir );
@@ -298,11 +311,6 @@ static final class BundlePathSubstitutionError extends Error {
298311 super (message );
299312 this .origPath = origPath ;
300313 }
301-
302- BundlePathSubstitutionError (String message , Path origPath , Throwable cause ) {
303- super (message , cause );
304- this .origPath = origPath ;
305- }
306314 }
307315
308316 @ SuppressWarnings ("try" )
@@ -367,23 +375,16 @@ private Path substitutePath(Path origPath, Path destinationDir) {
367375 baseName = origFileName ;
368376 extension = "" ;
369377 }
370- String substitutedPathFilename = baseName + "_" + SubstrateUtil .digest (origPath .toString ()) + extension ;
371- Path substitutedPath = destinationDir .resolve (substitutedPathFilename );
372- if (Files .exists (substitutedPath )) {
373- /* If we ever see this, we have to implement substitutedPath collision-handling */
374- throw new BundlePathSubstitutionError ("Failed to create a unique path-name in " + destinationDir + ". " + substitutedPath + " already exists" , origPath );
378+
379+ Path substitutedPath = destinationDir .resolve (baseName + extension );
380+ int collisionIndex = 0 ;
381+ while (Files .exists (substitutedPath )) {
382+ collisionIndex += 1 ;
383+ substitutedPath = destinationDir .resolve (baseName + "_" + collisionIndex + extension );
375384 }
376385
377386 if (!destinationDir .startsWith (outputDir )) {
378- if (Files .isDirectory (origPath )) {
379- try (Stream <Path > walk = Files .walk (origPath )) {
380- walk .forEach (sourcePath -> copyFile (sourcePath , substitutedPath .resolve (origPath .relativize (sourcePath ))));
381- } catch (IOException e ) {
382- throw new BundlePathSubstitutionError ("Failed to iterate through directory " + origPath , origPath , e );
383- }
384- } else {
385- copyFile (origPath , substitutedPath );
386- }
387+ copyFiles (origPath , substitutedPath );
387388 }
388389
389390 Path relativeSubstitutedPath = rootDir .relativize (substitutedPath );
@@ -394,25 +395,45 @@ private Path substitutePath(Path origPath, Path destinationDir) {
394395 return substitutedPath ;
395396 }
396397
397- private void copyFile (Path source , Path target ) {
398+ private void copyFiles (Path source , Path target ) {
399+ if (Files .isDirectory (source )) {
400+ try (Stream <Path > walk = Files .walk (source )) {
401+ walk .forEach (sourcePath -> copyFile (sourcePath , target .resolve (source .relativize (sourcePath ))));
402+ } catch (IOException e ) {
403+ throw NativeImage .showError ("Failed to iterate through directory " + source , e );
404+ }
405+ } else {
406+ copyFile (source , target );
407+ }
408+ }
409+
410+ private void copyFile (Path sourceFile , Path target ) {
398411 try {
399- if (nativeImage .isVerbose ()) {
400- System .out .println ("> Copy to bundle: " + nativeImage .config .workDir .relativize (source ));
412+ if (nativeImage .isVerbose () && target . startsWith ( rootDir ) ) {
413+ System .out .println ("> Copy to bundle: " + nativeImage .config .workDir .relativize (sourceFile ));
401414 }
402- Files .copy (source , target );
415+ Files .copy (sourceFile , target );
403416 } catch (IOException e ) {
404- throw NativeImage .showError ("Failed to copy " + source + " to " + target , e );
417+ throw NativeImage .showError ("Failed to copy " + sourceFile + " to " + target , e );
405418 }
406419 }
407420
408421 void shutdown () {
409- if (!status .loadBundle ) {
410- writeBundle ();
422+ Path originalImagePath = bundleFile .getParent ();
423+ copyFiles (outputDir , originalImagePath .resolve (outputDir .getFileName ()));
424+
425+ try {
426+ if (isBundleCreation ()) {
427+ writeBundle ();
428+ }
429+ } finally {
430+ nativeImage .deleteAllFiles (rootDir );
411431 }
412- nativeImage .deleteAllFiles (rootDir );
413432 }
414433
415434 void writeBundle () {
435+ assert isBundleCreation ();
436+
416437 Path pathCanonicalizationsFile = stageDir .resolve ("path_canonicalizations.json" );
417438 try (JsonWriter writer = new JsonWriter (pathCanonicalizationsFile )) {
418439 /* Printing as list with defined sort-order ensures useful diffs are possible */
@@ -436,6 +457,15 @@ void writeBundle() {
436457 throw NativeImage .showError ("Failed to write bundle-file " + pathSubstitutionsFile , e );
437458 }
438459
460+ /*
461+ * Provide a fallback to ensure we even get a bundle if there are errors before we are able
462+ * to determine the final bundle name (see use of BundleSupport.isBundleCreation() in
463+ * NativeImage.completeImageBuild() to know where this happens).
464+ */
465+ if (bundleFile == null ) {
466+ bundleFile = nativeImage .config .getWorkingDirectory ().resolve ("unnamed.nib" );
467+ }
468+
439469 try (JarOutputStream jarOutStream = new JarOutputStream (Files .newOutputStream (bundleFile ), new Manifest ())) {
440470 try (Stream <Path > walk = Files .walk (rootDir )) {
441471 walk .forEach (bundleEntry -> {
0 commit comments