25
25
26
26
package jdk .jpackage .internal ;
27
27
28
+ import static jdk .jpackage .internal .util .PathUtils .normalizedAbsolutePathString ;
29
+
28
30
import java .io .File ;
29
31
import java .io .IOException ;
30
32
import java .nio .file .Files ;
@@ -153,17 +155,18 @@ enum DmgPackageTaskID implements TaskID {
153
155
}
154
156
155
157
Path volumePath () {
156
- return dmgWorkdir ();
158
+ return dmgWorkdir (). resolve ( volumeName ()) ;
157
159
}
158
160
159
161
String volumeName () {
160
162
return pkg .app ().name ();
161
163
}
162
164
163
165
String createVolumeUrlLocation () throws IOException {
164
- Files .createDirectories (volumePath ());
166
+ final var volumeParentDir = volumePath ().getParent ();
167
+ Files .createDirectories (volumeParentDir );
165
168
// Url should end with '/' and it should be real path (no symbolic links).
166
- return volumePath () .toRealPath ().resolve (volumeName ()).toUri ().toString () + File .separator ;
169
+ return volumeParentDir .toRealPath ().resolve (volumePath (). getFileName ()).toUri ().toString () + File .separator ;
167
170
}
168
171
169
172
Path volumeScript () {
@@ -183,19 +186,15 @@ Path licenseFile() {
183
186
}
184
187
185
188
Path protoDmg () {
186
- return dmgWorkdir ().resolve ("proto.dmg" ). toAbsolutePath () ;
189
+ return dmgWorkdir ().resolve ("proto.dmg" );
187
190
}
188
191
189
192
Path protoCopyDmg () {
190
- return dmgWorkdir ().resolve ("proto-copy.dmg" ).toAbsolutePath ();
191
- }
192
-
193
- Path mountedProtoDmgPath () {
194
- return dmgWorkdir ().resolve (volumeName ());
193
+ return dmgWorkdir ().resolve ("proto-copy.dmg" );
195
194
}
196
195
197
196
Path bgImageFileInMountedDmg () {
198
- return mountedProtoDmgPath ().resolve (".background/background.tiff" );
197
+ return volumePath ().resolve (".background/background.tiff" );
199
198
}
200
199
201
200
private Path dmgWorkdir () {
@@ -227,26 +226,23 @@ private void prepareDMGSetupScript() throws IOException {
227
226
228
227
data .put ("DEPLOY_VOLUME_PATH" , volumePath ().toString ());
229
228
data .put ("DEPLOY_APPLICATION_NAME" , pkg .app ().name ());
229
+
230
230
String targetItem = pkg .relativeInstallDir ().getFileName ().toString ();
231
231
data .put ("DEPLOY_TARGET" , targetItem );
232
- data .put ("DEPLOY_INSTALL_LOCATION" , installRoot (pkg ).toString ());
233
- data .put ("DEPLOY_INSTALL_LOCATION_DISPLAY_NAME" ,
234
- getInstallDirDisplayName ());
232
+
233
+ data .put ("DEPLOY_INSTALL_LOCATION" , pkg .installDir ().getParent ().toString ());
234
+
235
+ // "DEPLOY_INSTALL_LOCATION_DISPLAY_NAME" is the label for the default destination directory
236
+ // for DMG bundle on the right side from the "copy" arrow in the dialog
237
+ // that pops up when user clicks on a .dmg file.
238
+ data .put ("DEPLOY_INSTALL_LOCATION_DISPLAY_NAME" , getInstallDirDisplayName ());
235
239
236
240
env .createResource (DEFAULT_DMG_SETUP_SCRIPT )
237
241
.setCategory (I18N .getString ("resource.dmg-setup-script" ))
238
242
.setSubstitutionData (data )
239
243
.saveToFile (dmgSetup );
240
244
}
241
245
242
- private static Path installRoot (MacDmgPackage pkg ) {
243
- if (pkg .isRuntimeInstaller ()) {
244
- return Path .of ("/Library/Java/JavaVirtualMachines" );
245
- } else {
246
- return Path .of ("/Applications" );
247
- }
248
- }
249
-
250
246
private void prepareLicense () throws IOException {
251
247
final var licFile = pkg .licenseFile ();
252
248
if (licFile .isEmpty ()) {
@@ -283,11 +279,14 @@ private void prepareConfigFiles() throws IOException {
283
279
prepareDMGSetupScript ();
284
280
}
285
281
286
- // Returns display name of installation directory. Display name is used to
287
- // show user installation location and for well known (default only) we will
288
- // use "Applications" or "JavaVirtualMachines".
289
282
private String getInstallDirDisplayName () {
290
- return installRoot (pkg ).getFileName ().toString ();
283
+ final var defaultInstallDir = new PackageBuilder (pkg .app (), pkg .type ()).defaultInstallDir ().orElseThrow ();
284
+ if (defaultInstallDir .equals (pkg .installDir ())) {
285
+ // Return "Applications" for "/Applications/foo.app"
286
+ return defaultInstallDir .getParent ().getFileName ().toString ();
287
+ } else {
288
+ return pkg .installDir ().getParent ().toString ();
289
+ }
291
290
}
292
291
293
292
private void buildDMG () throws IOException {
@@ -320,9 +319,9 @@ private void buildDMG() throws IOException {
320
319
hdiutil .toString (),
321
320
"create" ,
322
321
hdiUtilVerbosityFlag ,
323
- "-srcfolder" , srcFolder . toAbsolutePath (). toString ( ),
322
+ "-srcfolder" , normalizedAbsolutePathString ( srcFolder ),
324
323
"-volname" , volumeName (),
325
- "-ov" , protoDMG . toString ( ),
324
+ "-ov" , normalizedAbsolutePathString ( protoDMG ),
326
325
"-fs" , "HFS+" ,
327
326
"-format" , "UDRW" );
328
327
try {
@@ -345,7 +344,7 @@ private void buildDMG() throws IOException {
345
344
hdiUtilVerbosityFlag ,
346
345
"-size" , String .valueOf (size ),
347
346
"-volname" , volumeName (),
348
- "-ov" , protoDMG . toString ( ),
347
+ "-ov" , normalizedAbsolutePathString ( protoDMG ),
349
348
"-fs" , "HFS+" );
350
349
new RetryExecutor ()
351
350
.setMaxAttemptsCount (10 )
@@ -358,17 +357,17 @@ private void buildDMG() throws IOException {
358
357
pb = new ProcessBuilder (
359
358
hdiutil .toString (),
360
359
"attach" ,
361
- protoDMG . toString ( ),
360
+ normalizedAbsolutePathString ( protoDMG ),
362
361
hdiUtilVerbosityFlag ,
363
362
"-mountroot" , protoDMG .getParent ().toString ());
364
363
IOUtils .exec (pb , false , null , true , Executor .INFINITE_TIMEOUT );
365
364
366
- final Path mountedRoot = mountedProtoDmgPath ();
365
+ final Path mountedVolume = volumePath ();
367
366
368
367
// Copy app image, since we did not create DMG with it, but instead we created
369
368
// empty one.
370
369
if (copyAppImage ) {
371
- FileUtils .copyRecursive (srcFolder , mountedRoot );
370
+ FileUtils .copyRecursive (srcFolder , mountedVolume );
372
371
}
373
372
374
373
try {
@@ -382,14 +381,14 @@ private void buildDMG() throws IOException {
382
381
// headless environment.
383
382
try {
384
383
pb = new ProcessBuilder ("/usr/bin/osascript" ,
385
- volumeScript (). toAbsolutePath (). toString ( ));
384
+ normalizedAbsolutePathString ( volumeScript ()));
386
385
IOUtils .exec (pb , 180 ); // Wait 3 minutes. See JDK-8248248.
387
386
} catch (IOException ex ) {
388
387
Log .verbose (ex );
389
388
}
390
389
391
390
// volume icon
392
- Path volumeIconFile = mountedRoot .resolve (".VolumeIcon.icns" );
391
+ Path volumeIconFile = mountedVolume .resolve (".VolumeIcon.icns" );
393
392
IOUtils .copyFile (volumeIcon (), volumeIconFile );
394
393
395
394
// Indicate that we want a custom icon
@@ -407,14 +406,14 @@ private void buildDMG() throws IOException {
407
406
pb = new ProcessBuilder (
408
407
setFileUtility .orElseThrow ().toString (),
409
408
"-c" , "icnC" ,
410
- volumeIconFile . toAbsolutePath (). toString ( ));
409
+ normalizedAbsolutePathString ( volumeIconFile ));
411
410
IOUtils .exec (pb );
412
411
volumeIconFile .toFile ().setReadOnly ();
413
412
414
413
pb = new ProcessBuilder (
415
414
setFileUtility .orElseThrow ().toString (),
416
415
"-a" , "C" ,
417
- mountedRoot . toAbsolutePath (). toString ( ));
416
+ normalizedAbsolutePathString ( mountedVolume ));
418
417
IOUtils .exec (pb );
419
418
} catch (IOException ex ) {
420
419
Log .error (ex .getMessage ());
@@ -430,14 +429,14 @@ private void buildDMG() throws IOException {
430
429
hdiutil .toString (),
431
430
"detach" ,
432
431
hdiUtilVerbosityFlag ,
433
- mountedRoot . toAbsolutePath (). toString ( ));
432
+ normalizedAbsolutePathString ( mountedVolume ));
434
433
// "hdiutil detach" might not work right away due to resource busy error, so
435
434
// repeat detach several times.
436
435
RetryExecutor retryExecutor = new RetryExecutor ();
437
436
// Image can get detach even if we got resource busy error, so stop
438
437
// trying to detach it if it is no longer attached.
439
438
retryExecutor .setExecutorInitializer (exec -> {
440
- if (!Files .exists (mountedRoot )) {
439
+ if (!Files .exists (mountedVolume )) {
441
440
retryExecutor .abort ();
442
441
}
443
442
});
@@ -448,13 +447,13 @@ private void buildDMG() throws IOException {
448
447
} catch (IOException ex ) {
449
448
if (!retryExecutor .isAborted ()) {
450
449
// Now force to detach if it still attached
451
- if (Files .exists (mountedRoot )) {
450
+ if (Files .exists (mountedVolume )) {
452
451
pb = new ProcessBuilder (
453
452
hdiutil .toString (),
454
453
"detach" ,
455
454
"-force" ,
456
455
hdiUtilVerbosityFlag ,
457
- mountedRoot . toAbsolutePath (). toString ( ));
456
+ normalizedAbsolutePathString ( mountedVolume ));
458
457
IOUtils .exec (pb , false , null , true , Executor .INFINITE_TIMEOUT );
459
458
}
460
459
}
@@ -465,10 +464,10 @@ private void buildDMG() throws IOException {
465
464
pb = new ProcessBuilder (
466
465
hdiutil .toString (),
467
466
"convert" ,
468
- protoDMG . toAbsolutePath (). toString ( ),
467
+ normalizedAbsolutePathString ( protoDMG ),
469
468
hdiUtilVerbosityFlag ,
470
469
"-format" , "UDZO" ,
471
- "-o" , finalDMG . toAbsolutePath (). toString ( ));
470
+ "-o" , normalizedAbsolutePathString ( finalDMG ));
472
471
try {
473
472
new RetryExecutor ()
474
473
.setMaxAttemptsCount (10 )
@@ -482,10 +481,10 @@ private void buildDMG() throws IOException {
482
481
pb = new ProcessBuilder (
483
482
hdiutil .toString (),
484
483
"convert" ,
485
- protoCopyDMG . toString ( ),
484
+ normalizedAbsolutePathString ( protoCopyDMG ),
486
485
hdiUtilVerbosityFlag ,
487
486
"-format" , "UDZO" ,
488
- "-o" , finalDMG . toAbsolutePath (). toString ( ));
487
+ "-o" , normalizedAbsolutePathString ( finalDMG ));
489
488
IOUtils .exec (pb , false , null , true , Executor .INFINITE_TIMEOUT );
490
489
} finally {
491
490
Files .deleteIfExists (protoCopyDMG );
@@ -497,9 +496,9 @@ private void buildDMG() throws IOException {
497
496
pb = new ProcessBuilder (
498
497
hdiutil .toString (),
499
498
"udifrez" ,
500
- finalDMG . toAbsolutePath (). toString ( ),
499
+ normalizedAbsolutePathString ( finalDMG ),
501
500
"-xml" ,
502
- licenseFile (). toAbsolutePath (). toString ( )
501
+ normalizedAbsolutePathString ( licenseFile ())
503
502
);
504
503
new RetryExecutor ()
505
504
.setMaxAttemptsCount (10 )
@@ -516,7 +515,7 @@ private void buildDMG() throws IOException {
516
515
517
516
Log .verbose (MessageFormat .format (I18N .getString (
518
517
"message.output-to-location" ),
519
- pkg .app ().name (), finalDMG . toAbsolutePath (). toString ( )));
518
+ pkg .app ().name (), normalizedAbsolutePathString ( finalDMG )));
520
519
521
520
}
522
521
0 commit comments