Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8244576

[macos] Volume icon deleted by osascript for background image

    XMLWordPrintable

Details

    • x86
    • os_x
    • Verified

    Description

      ADDITIONAL SYSTEM INFORMATION :
      Mac mini (Late 2012), macOS 10.15.2, macOS 10.15.4, OpenJDK version 14.0.1 2020-04-14

      A DESCRIPTION OF THE PROBLEM :
      When specifying a resource directory for jpackage that contains an application icon app-name.icns and a volume icon app-name-volumne.icns but no custom background image, the osascript that sets the background image deletes the volume icon from the disk image.

      Reordering the steps "copy volume icon", "copy background icon", "set volume icon", "set background icon" in jdk.incubator.jpackage.internal.MacDmgBundler to "copy background icon", "set background icon", "copy volume icon", "set volume icon" fixes this issue.

      I.e. the broken code is:
      // volume icon
      File volumeIconFile = new File(mountedRoot, ".VolumeIcon.icns");
      IOUtils.copyFile(getConfig_VolumeIcon(params),
              volumeIconFile);

      // background image
      File bgdir = new File(mountedRoot, ".background");
      bgdir.mkdirs();
      IOUtils.copyFile(getConfig_VolumeBackground(params),
              new File(bgdir, "background.tiff"));

      // Indicate that we want a custom icon
      // NB: attributes of the root directory are ignored
      // when creating the volume
      // Therefore we have to do this after we mount image
      String setFileUtility = findSetFileUtility();
      if (setFileUtility != null) {
          //can not find utility => keep going without icon
          try {
              volumeIconFile.setWritable(true);
              // The "creator" attribute on a file is a legacy attribute
              // but it seems Finder excepts these bytes to be
              // "icnC" for the volume icon
              // (might not work on Mac 10.13 with old XCode)
              pb = new ProcessBuilder(
                      setFileUtility,
                      "-c", "icnC",
                      volumeIconFile.getAbsolutePath());
              IOUtils.exec(pb);
              volumeIconFile.setReadOnly();

              pb = new ProcessBuilder(
                      setFileUtility,
                      "-a", "C",
                      mountedRoot.getAbsolutePath());
              IOUtils.exec(pb);
          } catch (IOException ex) {
              Log.error(ex.getMessage());
              Log.verbose("Cannot enable custom icon using SetFile utility");
          }
      } else {
          Log.verbose(I18N.getString("message.setfile.dmg"));
      }

      // We will not consider setting background image and creating link to
      // /Application folder in DMG as critical error, since it can fail in
      // headless enviroment.
      try {
          pb = new ProcessBuilder("osascript",
                  getConfig_VolumeScript(params).getAbsolutePath());
          IOUtils.exec(pb);
      } catch (IOException ex) {
          Log.verbose(ex);
      }

      and the working code is:
      // background image
      File bgdir = new File(mountedRoot, ".background");
      bgdir.mkdirs();
      IOUtils.copyFile(getConfig_VolumeBackground(params),
              new File(bgdir, "background.tiff"));

      // We will not consider setting background image and creating link to
      // /Application folder in DMG as critical error, since it can fail in
      // headless enviroment.
      try {
          pb = new ProcessBuilder("osascript",
                  getConfig_VolumeScript(params).getAbsolutePath());
          IOUtils.exec(pb);
      } catch (IOException ex) {
          Log.verbose(ex);
      }

      // volume icon
      File volumeIconFile = new File(mountedRoot, ".VolumeIcon.icns");
      IOUtils.copyFile(getConfig_VolumeIcon(params),
              volumeIconFile);

      // Indicate that we want a custom icon
      // NB: attributes of the root directory are ignored
      // when creating the volume
      // Therefore we have to do this after we mount image
      String setFileUtility = findSetFileUtility();
      if (setFileUtility != null) {
          //can not find utility => keep going without icon
          try {
              volumeIconFile.setWritable(true);
              // The "creator" attribute on a file is a legacy attribute
              // but it seems Finder excepts these bytes to be
              // "icnC" for the volume icon
              // (might not work on Mac 10.13 with old XCode)
              pb = new ProcessBuilder(
                      setFileUtility,
                      "-c", "icnC",
                      volumeIconFile.getAbsolutePath());
              IOUtils.exec(pb);
              volumeIconFile.setReadOnly();

              pb = new ProcessBuilder(
                      setFileUtility,
                      "-a", "C",
                      mountedRoot.getAbsolutePath());
              IOUtils.exec(pb);
          } catch (IOException ex) {
              Log.error(ex.getMessage());
              Log.verbose("Cannot enable custom icon using SetFile utility");
          }
      } else {
          Log.verbose(I18N.getString("message.setfile.dmg"));
      }


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. Create an application icon named app.icns in an arbitrary directory.
      2. Create a volume icon named app-volume.icns in the same directory.
      3. Copy the script below (see source code) into an empty directory.
      3. Adjust variable JDK_HOME and ICNS_ROOT in the script.
      4. Run the script.
      5. Mount the disk image tgt/image/HelloWorld-1.0.dmg (path relative to the directory where the script was placed).



      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      A macOS disk image that when mounted displays the volume icon from the resources directory.
      ACTUAL -
      A macOS disk image that when mounted displays no volume icon.

      ---------- BEGIN SOURCE ----------
      #!/bin/bash

      BASE_DIR=`dirname "$0"`;

      #
      # Specify path to JDK 14 home, typically this will be
      # /Library/Java/JavaVirtualMachines/jdk-14.0.1.jdk/Contents/Home
      #
      JDK_HOME="";
      if [ -z "$JDK_HOME" ]
      then
        echo "JDK home path required.";
        exit 1;
      fi

      #
      # Specify a directory that contains two icns files
      # app.icns and
      # app-volume.icns
      # to be used as application icon and volume icon
      #
      ICNS_ROOT="";
      if [ -z "$ICNS_ROOT" ]
      then
        echo "Icons source directory required.";
        exit 1;
      fi


      #
      # 1. Create test data
      #
      if [ -d "$BASE_DIR/src" ]
      then
        echo "Source directory already exists. Place script in empty directory.";
        exit 1;
      fi

      mkdir -p "$BASE_DIR/src/test";

      #
      # 1.1 Create java source file for trivial main class
      #
      echo "\
      package test;

      public class Main {
        public static void main( String[] args ) {
          System.out.println(\"Hello world.\");
        }
      }
      " > "$BASE_DIR/src/test/Main.java";
       

      if [ -d "$BASE_DIR/tgt" ]
      then
        echo "Target directory already exists. Place script in empty directory.";
        exit 1;
      fi

      mkdir -p "$BASE_DIR/tgt/classes";
      mkdir -p "$BASE_DIR/tgt/app";
      mkdir -p "$BASE_DIR/tgt/resources";
      mkdir -p "$BASE_DIR/tgt/image";

      #
      # 1.2 Compile source file
      #
      $JDK_HOME/bin/javac -sourcepath "$BASE_DIR/src" -d "$BASE_DIR/tgt/classes" "$BASE_DIR/src/test/Main.java";


      #
      # 1.3 Bundle class file in executable jar
      #
      echo "Main-Class: test.Main" > "$BASE_DIR/tgt/MANIFEST.MF";
      $JDK_HOME/bin/jar cfm "$BASE_DIR/tgt/app/app.jar" "$BASE_DIR/tgt/MANIFEST.MF" -C "$BASE_DIR/tgt/classes" "test/Main.class";


      #
      # 1.4 Copy app icon and volume icon to resources directory
      #
      cp -v "$ICNS_ROOT/app.icns" "$BASE_DIR/tgt/resources/HelloWorld.icns";
      cp -v "$ICNS_ROOT/app-volume.icns" "$BASE_DIR/tgt/resources/HelloWorld-volume.icns";


      #
      # 2. Package the test data
      #
      $JDK_HOME/bin/jpackage\
       --input "$BASE_DIR/tgt/app"\
       --main-jar app.jar\
       --dest "$BASE_DIR/tgt/image"\
       --name "HelloWorld"\
       --resource-dir "$BASE_DIR/tgt/resources"\
      ;
      ---------- END SOURCE ----------

      FREQUENCY : always


      Attachments

        Activity

          People

            herrick Andy Herrick (Inactive)
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: