2828import java .io .IOException ;
2929import java .io .Reader ;
3030import java .net .URI ;
31+ import java .nio .file .CopyOption ;
3132import java .nio .file .FileSystems ;
3233import java .nio .file .Files ;
3334import java .nio .file .Path ;
35+ import java .nio .file .StandardCopyOption ;
3436import java .util .ArrayList ;
3537import java .util .Arrays ;
3638import java .util .Collections ;
@@ -95,8 +97,11 @@ boolean show() {
9597 private final List <String > buildArgs ;
9698
9799 private static final String bundleTempDirPrefix = "bundleRoot-" ;
100+ private static final String bundleFileExtension = ".nib" ;
101+ private static final String originalDirExtension = ".orig" ;
98102
99- Path bundleFile ;
103+ private Path bundlePath ;
104+ private String bundleName ;
100105
101106 static BundleSupport create (NativeImage nativeImage , String bundleArg , NativeImage .ArgumentQueue args ) {
102107 if (!nativeImage .userConfigProperties .isEmpty ()) {
@@ -130,6 +135,12 @@ static BundleSupport create(NativeImage nativeImage, String bundleArg, NativeIma
130135 assert !BundleStatus .valueOf (buildArg .substring (BUNDLE_OPTION .length () + 1 )).loadBundle ;
131136 continue ;
132137 }
138+ if (buildArg .startsWith (nativeImage .oHPath )) {
139+ continue ;
140+ }
141+ if (buildArg .equals (DefaultOptionHandler .verboseOption )) {
142+ continue ;
143+ }
133144 if (buildArg .startsWith ("-Dllvm.bin.dir=" )) {
134145 Optional <String > existing = nativeImage .config .getBuildArgs ().stream ().filter (arg -> arg .startsWith ("-Dllvm.bin.dir=" )).findFirst ();
135146 if (existing .isPresent () && !existing .get ().equals (buildArg )) {
@@ -146,7 +157,7 @@ static BundleSupport create(NativeImage nativeImage, String bundleArg, NativeIma
146157 }
147158
148159 private BundleSupport (NativeImage nativeImage , BundleStatus status ) {
149- assert !status .loadBundle ;
160+ assert !status .loadBundle : "This constructor is only used when a new bundle gets created" ;
150161
151162 this .nativeImage = nativeImage ;
152163 this .status = status ;
@@ -165,50 +176,66 @@ private BundleSupport(NativeImage nativeImage, BundleStatus status) {
165176 throw NativeImage .showError ("Unable to create bundle directory layout" , e );
166177 }
167178 this .buildArgs = Collections .unmodifiableList (nativeImage .config .getBuildArgs ());
179+
180+ setBundleLocation (nativeImage .config .getWorkingDirectory (), "unknown" );
168181 }
169182
170- private BundleSupport (NativeImage nativeImage , BundleStatus status , String bundleFilename ) {
171- assert status .loadBundle ;
183+ private BundleSupport (NativeImage nativeImage , BundleStatus status , String bundleFilenameArg ) {
184+ assert status .loadBundle : "This constructor is used when a previously created bundle gets applied" ;
172185
173186 this .nativeImage = nativeImage ;
174187 this .status = status ;
175188
176- bundleFile = Path .of (bundleFilename );
189+ Path bundleFile = Path .of (bundleFilenameArg ).toAbsolutePath ();
190+ String bundleFileName = bundleFile .getFileName ().toString ();
191+ if (!bundleFileName .endsWith (bundleFileExtension )) {
192+ throw NativeImage .showError ("The given bundle file " + bundleFileName + " does not end with '" + bundleFileExtension + "'" );
193+ }
194+
177195 if (!Files .isReadable (bundleFile )) {
178- throw NativeImage .showError ("The given bundle file " + bundleFilename + " cannot be read" );
196+ throw NativeImage .showError ("The given bundle file " + bundleFileName + " cannot be read" );
179197 }
180198
181199 if (Files .isDirectory (bundleFile )) {
182- throw NativeImage .showError ("The given bundle file " + bundleFilename + " is a directory and not a file" );
183- } else {
184- try {
185- rootDir = Files .createTempDirectory (bundleTempDirPrefix );
186- try (JarFile archive = new JarFile (bundleFile .toFile ())) {
187- archive .stream ().forEach (jarEntry -> {
188- Path bundleEntry = rootDir .resolve (jarEntry .getName ());
189- try {
190- Path bundleFileParent = bundleEntry .getParent ();
191- if (bundleFileParent != null ) {
192- Files .createDirectories (bundleFileParent );
193- }
194- Files .copy (archive .getInputStream (jarEntry ), bundleEntry );
195- } catch (IOException e ) {
196- throw NativeImage .showError ("Unable to copy " + jarEntry .getName () + " from bundle " + bundleEntry + " to " + bundleEntry , e );
200+ throw NativeImage .showError ("The given bundle file " + bundleFileName + " is a directory and not a file" );
201+ }
202+
203+ try {
204+ rootDir = Files .createTempDirectory (bundleTempDirPrefix );
205+ outputDir = rootDir .resolve ("output" );
206+ String originalOutputDirName = outputDir .getFileName ().toString () + originalDirExtension ;
207+
208+ try (JarFile archive = new JarFile (bundleFile .toFile ())) {
209+ archive .stream ().forEach (jarEntry -> {
210+ Path bundleEntry = rootDir .resolve (jarEntry .getName ());
211+ if (bundleEntry .startsWith (outputDir )) {
212+ /* Extract original output to different path */
213+ bundleEntry = rootDir .resolve (originalOutputDirName ).resolve (outputDir .relativize (bundleEntry ));
214+ }
215+ try {
216+ Path bundleFileParent = bundleEntry .getParent ();
217+ if (bundleFileParent != null ) {
218+ Files .createDirectories (bundleFileParent );
197219 }
198- });
199- }
200- } catch (IOException e ) {
201- throw NativeImage .showError ("Unable to create bundle directory layout from file " + bundleFile , e );
220+ Files .copy (archive .getInputStream (jarEntry ), bundleEntry );
221+ } catch (IOException e ) {
222+ throw NativeImage .showError ("Unable to copy " + jarEntry .getName () + " from bundle " + bundleEntry + " to " + bundleEntry , e );
223+ }
224+ });
202225 }
226+ } catch (IOException e ) {
227+ throw NativeImage .showError ("Unable to create bundle directory layout from file " + bundleFileName , e );
203228 }
204229
230+ bundlePath = bundleFile .getParent ();
231+ bundleName = bundleFileName ;
232+
205233 Path inputDir = rootDir .resolve ("input" );
206234 stageDir = inputDir .resolve ("stage" );
207235 auxiliaryDir = inputDir .resolve ("auxiliary" );
208236 Path classesDir = inputDir .resolve ("classes" );
209237 classPathDir = classesDir .resolve ("cp" );
210238 modulePathDir = classesDir .resolve ("p" );
211- outputDir = rootDir .resolve ("output" );
212239 imagePathOutputDir = outputDir .resolve ("default" );
213240 auxiliaryOutputDir = outputDir .resolve ("other" );
214241
@@ -384,7 +411,7 @@ private Path substitutePath(Path origPath, Path destinationDir) {
384411 }
385412
386413 if (!destinationDir .startsWith (outputDir )) {
387- copyFiles (origPath , substitutedPath );
414+ copyFiles (origPath , substitutedPath , false );
388415 }
389416
390417 Path relativeSubstitutedPath = rootDir .relativize (substitutedPath );
@@ -395,32 +422,37 @@ private Path substitutePath(Path origPath, Path destinationDir) {
395422 return substitutedPath ;
396423 }
397424
398- private void copyFiles (Path source , Path target ) {
425+ private void copyFiles (Path source , Path target , boolean overwrite ) {
399426 if (Files .isDirectory (source )) {
400427 try (Stream <Path > walk = Files .walk (source )) {
401- walk .forEach (sourcePath -> copyFile (sourcePath , target .resolve (source .relativize (sourcePath ))));
428+ walk .forEach (sourcePath -> copyFile (sourcePath , target .resolve (source .relativize (sourcePath )), overwrite ));
402429 } catch (IOException e ) {
403430 throw NativeImage .showError ("Failed to iterate through directory " + source , e );
404431 }
405432 } else {
406- copyFile (source , target );
433+ copyFile (source , target , overwrite );
407434 }
408435 }
409436
410- private void copyFile (Path sourceFile , Path target ) {
437+ private void copyFile (Path sourceFile , Path target , boolean overwrite ) {
411438 try {
412- if (nativeImage .isVerbose () && target .startsWith (rootDir )) {
413- System .out .println ("> Copy to bundle: " + nativeImage .config .workDir .relativize (sourceFile ));
439+ if (nativeImage .isVerbose ()) {
440+ System .out .println ("> Copy " + sourceFile + " to " + target );
441+ }
442+ if (overwrite && Files .isDirectory (sourceFile ) && Files .isDirectory (target )) {
443+ return ;
414444 }
415- Files .copy (sourceFile , target );
445+ CopyOption [] options = overwrite ? new CopyOption []{StandardCopyOption .REPLACE_EXISTING } : new CopyOption [0 ];
446+ Files .copy (sourceFile , target , options );
416447 } catch (IOException e ) {
417448 throw NativeImage .showError ("Failed to copy " + sourceFile + " to " + target , e );
418449 }
419450 }
420451
421- void shutdown () {
422- Path originalImagePath = bundleFile .getParent ();
423- copyFiles (outputDir , originalImagePath .resolve (outputDir .getFileName ()));
452+ void complete () {
453+ if (Files .exists (outputDir )) {
454+ copyFiles (outputDir , bundlePath .resolve (nativeImage .imageName + "." + outputDir .getFileName ()), true );
455+ }
424456
425457 try {
426458 if (isBundleCreation ()) {
@@ -431,6 +463,11 @@ void shutdown() {
431463 }
432464 }
433465
466+ public void setBundleLocation (Path imagePath , String imageName ) {
467+ bundlePath = imagePath ;
468+ bundleName = imageName + bundleFileExtension ;
469+ }
470+
434471 void writeBundle () {
435472 assert isBundleCreation ();
436473
@@ -457,16 +494,7 @@ void writeBundle() {
457494 throw NativeImage .showError ("Failed to write bundle-file " + pathSubstitutionsFile , e );
458495 }
459496
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-
469- try (JarOutputStream jarOutStream = new JarOutputStream (Files .newOutputStream (bundleFile ), new Manifest ())) {
497+ try (JarOutputStream jarOutStream = new JarOutputStream (Files .newOutputStream (bundlePath .resolve (bundleName )), new Manifest ())) {
470498 try (Stream <Path > walk = Files .walk (rootDir )) {
471499 walk .forEach (bundleEntry -> {
472500 if (Files .isDirectory (bundleEntry )) {
@@ -480,12 +508,12 @@ void writeBundle() {
480508 Files .copy (bundleEntry , jarOutStream );
481509 jarOutStream .closeEntry ();
482510 } catch (IOException e ) {
483- throw NativeImage .showError ("Failed to copy " + bundleEntry + " into bundle file " + bundleFile , e );
511+ throw NativeImage .showError ("Failed to copy " + bundleEntry + " into bundle file " + bundleName , e );
484512 }
485513 });
486514 }
487515 } catch (IOException e ) {
488- throw NativeImage .showError ("Failed to create bundle file " + bundleFile , e );
516+ throw NativeImage .showError ("Failed to create bundle file " + bundleName , e );
489517 }
490518 }
491519
0 commit comments