如何使用命令行工具为Mac OS X创建漂亮的DMG?

时间:2008-09-18 20:57:58

标签: macos scripting installer dmg

我需要为Mac应用程序创建一个不错的安装程序。我希望它是一个磁盘映像(DMG),具有预定义的大小,布局和背景图像。

我需要在脚本中以编程方式执行此操作,以集成到现有的构建系统中(实际上更多的是打包系统,因为它只创建安装程序。构建是单独完成的)。

我已经使用“hdiutil”完成了DMG创建,我还没有发现如何制作图标布局并指定背景位图。

15 个答案:

答案 0 :(得分:191)

经过大量研究后,我想出了这个答案,我现在把它放在这里作为我自己问题的答案,供参考:

  1. 确保在系统偏好设置>> Universal Access中选中“启用辅助设备访问权限”。 AppleScript需要它才能工作。您可能必须在此更改后重新启动(在Mac OS X Server 10.4上不起作用)。

  2. 创建一个R / W DMG。它必须大于结果。在此示例中,bash变量“size”包含Kb中的大小,“source”bash变量中文件夹的内容将复制到DMG中:

    hdiutil create -srcfolder "${source}" -volname "${title}" -fs HFS+ \
          -fsargs "-c c=64,a=16,e=16" -format UDRW -size ${size}k pack.temp.dmg
    
  3. 挂载磁盘映像,并存储设备名称(此操作后您可能希望使用sleep几秒钟):

    device=$(hdiutil attach -readwrite -noverify -noautoopen "pack.temp.dmg" | \
             egrep '^/dev/' | sed 1q | awk '{print $1}')
    
  4. 将背景图片(PNG格式)存储在DMG中名为“.background”的文件夹中,并将其名称存储在“backgroundPictureName”变量中。

  5. 使用AppleScript设置视觉样式(.app的名称必须在bash变量“applicationName”中,根据需要使用其他属性的变量):

    echo '
       tell application "Finder"
         tell disk "'${title}'"
               open
               set current view of container window to icon view
               set toolbar visible of container window to false
               set statusbar visible of container window to false
               set the bounds of container window to {400, 100, 885, 430}
               set theViewOptions to the icon view options of container window
               set arrangement of theViewOptions to not arranged
               set icon size of theViewOptions to 72
               set background picture of theViewOptions to file ".background:'${backgroundPictureName}'"
               make new alias file at container window to POSIX file "/Applications" with properties {name:"Applications"}
               set position of item "'${applicationName}'" of container window to {100, 100}
               set position of item "Applications" of container window to {375, 100}
               update without registering applications
               delay 5
               close
         end tell
       end tell
    ' | osascript
    
  6. 通过正确设置权限,压缩和释放DMG来完成DMG:

    chmod -Rf go-w /Volumes/"${title}"
    sync
    sync
    hdiutil detach ${device}
    hdiutil convert "/pack.temp.dmg" -format UDZO -imagekey zlib-level=9 -o "${finalDMGName}"
    rm -f /pack.temp.dmg 
    
  7. 在Snow Leopard上,上面的AppleScript不会正确设置图标位置 - 它似乎是一个Snow Leopard bug。一种解决方法是在设置图标后简单地调用close / open,即:

    ..
    set position of item "'${applicationName}'" of container window to {100, 100}
    set position of item "Applications" of container window to {375, 100}
    close
    open
    

答案 1 :(得分:55)

有一个叫做create-dmg的小Bash脚本可以构建具有自定义背景,自定义图标定位和卷名的花哨DMG。

我很多年前为我当时经营的公司建造了它;从那以后,它在其他人的贡献中幸存下来,据报道效果很好。

还有node-appdmg看起来像是基于Node.js的更现代和更积极的努力;检查一下。

答案 2 :(得分:37)

不要去那里。作为一名长期的Mac开发人员,我可以向您保证,没有任何解决方案能够正常运行。我尝试了很多解决方案,但它们都不太好。我认为问题在于Apple并没有真正记录必要数据的元数据格式。

以下是我很长时间以来非常成功的方式:

  1. 创建一个新的DMG,可写(!),足以容纳预期的二进制文件和额外的文件,如readme(稀疏可能有效)。

  2. 安装DMG并在Finder中手动布局或使用适合您的任何工具(请参阅底部的FileStorm链接以获得一个好工具)。背景图像通常是我们放入DMG上隐藏文件夹(“.something”)的图像。在那里放一个你的应用程序的副本(任何版本,甚至过时的版本都可以)。复制你想要的其他文件(别名,自述文件等),再次,过时的版本会做得很好。确保图标具有正确的尺寸和位置(IOW,按照您希望的方式布置DMG)。

  3. 再次卸载DMG,现在应该存储所有设置。

  4. 编写一个create DMG脚本,其工作方式如下:

    • 它会复制DMG,因此永远不会再碰到原来的那个。
    • 它安装了副本。
    • 它会替换所有具有最新文件的文件(例如构建后的最新应用程序)。您可以在命令行中使用 mv ditto 。请注意,当您替换这样的文件时,图标将保持不变,位置将保持不变,除文件(或目录)内容之外的所有内容保持不变(至少与我们通常用于该任务的ditto相同) 。您当然也可以用另一个替换背景图像(只需确保它具有相同的尺寸)。
    • 更换文件后,让脚本再次卸载DMG副本。
    • 最后调用hdiutil将可写的转换为压缩的(以及不可写的)DMG。
  5. 这种方法可能听起来不是最佳,但相信我,它在实践中非常有效。您可以将原始DMG(DMG模板)置于版本控制之下(例如SVN),因此如果您不小心更改/销毁它,您可以返回到仍然可以的版本。您可以将DMG模板与属于DMG的所有其他文件(自述文件,URL文件,背景图像)一起添加到Xcode项目中,所有文件都在版本控制下,然后创建目标(例如,名为“创建DMG”的外部目标)然后运行上面的DMG脚本并将旧的主目标添加为依赖目标。您可以使用脚本中的$ {SRCROOT}访问Xcode树中的文件(始终是产品的源根目录),并且可以使用$ {BUILT_PRODUCTS_DIR}访问构建产品(始终是Xcode创建构建结果的目录)

    结果:实际上Xcode可以在构建结束时生成DMG。准备发布的DMG。你不仅可以通过这种方式轻松创建一个相关的DMG,你可以在一个自动化的过程中(如果你喜欢在无头服务器上),在命令行中使用xcodebuild(例如自动夜间构建)。

    关于模板的初始布局,FileStorm是一个很好的工具。它是商业的,但非常强大且易于使用。正常版本不到20美元,所以它真的很实惠。也许可以自动化FileStorm来创建一个DMG(例如通过AppleScript),从未尝试过,但是一旦你找到了完美的模板DMG,就可以很容易地为每个版本更新它。

答案 3 :(得分:25)

通过提供此答案,使这个问题更新。

appdmg是一个简单易用的开源命令行程序,它从简单的json规范创建dmg文件。请看官方网站上的自述文件:

https://github.com/LinusU/node-appdmg

快速举例:

  1. 安装appdmg

    npm install -g appdmg
    
  2. 写一个json文件(spec.json

    {
      "title": "Test Title",
      "background": "background.png",
      "icon-size": 80,
      "contents": [
        { "x": 192, "y": 344, "type": "file", "path": "TestApp.app" },
        { "x": 448, "y": 344, "type": "link", "path": "/Applications" }
      ]
    }
    
  3. 运行程序

    appdmg spec.json test.dmg
    
  4. (免责声明。我是appdmg的创造者)

答案 4 :(得分:22)

对于那些对此主题感兴趣的人,我应该提一下我如何创建DMG:

hdiutil create XXX.dmg -volname "YYY" -fs HFS+ -srcfolder "ZZZ"

,其中

XXX == disk image file name (duh!)
YYY == window title displayed when DMG is opened
ZZZ == Path to a folder containing the files that will be copied into the DMG

答案 5 :(得分:14)

我的应用DropDMG是一种使用背景图片,图标布局,自定义音量图标和软件许可协议创建磁盘映像的简便方法。它可以通过“dropdmg”命令行工具或AppleScript从构建系统进行控制。如果需要,图片和许可证RTF文件可以存储在您的版本控制系统下。

答案 6 :(得分:5)

我发现这个很棒的mac应用程序可以自动完成这个过程 - http://www.araelium.com/dmgcanvas/ 如果要为mac app创建dmg安装程序,你必须看一看

答案 7 :(得分:4)

如果要设置自定义音量图标,请使用以下命令

/*Add a drive icon*/
cp "/Volumes/customIcon.icns" "/Volumes/dmgName/.VolumeIcon.icns"  


/*SetFile -c icnC will change the creator of the file to icnC*/
SetFile -c icnC /<your path>/.VolumeIcon.icns

现在创建读/写dmg

/*to set custom icon attribute*/
SetFile -a C /Volumes/dmgName

答案 8 :(得分:3)

为了创建一个漂亮的DMG,你现在可以使用一些写得很好的开源:

答案 9 :(得分:2)

.DS_Store文件存储Mac中的窗口设置。 Windows设置包括图标布局,窗口背景,窗口大小等。创建已安装图像的窗口时需要.DS_Store文件,以保留文件排列和窗口背景。

创建.DS_Store文件后,您只需将其复制到创建的安装程序(DMG)即可。

答案 10 :(得分:2)

我还需要使用命令行方法“以编程方式在脚本中”进行打包和dmg创建。到目前为止,我找到的最佳答案来自Adium项目'发布构建框架(参见R1)。有一个自定义脚本(AdiumApplescriptRunner)允许您避免OSX WindowsServer GUI交互。 “osascript applescript.scpt”方法要求您以构建器身份登录并从命令行vt100会话运行dmg创建。

与其他Unixen相比,OSX包管理系统不是那么先进,它可以轻松,系统地完成这项任务。

R1:http://hg.adium.im/adium-1.4/file/00d944a3ef16/Release

答案 11 :(得分:2)

我终于在我自己的项目中工作(恰好在Xcode中)。将这3个脚本添加到构建阶段将自动为您的产品创建一个漂亮而整洁的磁盘映像。您所要做的就是构建项目,DMG将在您的产品文件夹中等待。

脚本1(创建临时磁盘映像):

#!/bin/bash
#Create a R/W DMG

dir="$TEMP_FILES_DIR/disk"
dmg="$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.temp.dmg"

rm -rf "$dir"
mkdir "$dir"
cp -R "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.app" "$dir"
ln -s "/Applications" "$dir/Applications"
mkdir "$dir/.background"
cp "$PROJECT_DIR/$PROJECT_NAME/some_image.png" "$dir/.background"
rm -f "$dmg"
hdiutil create "$dmg" -srcfolder "$dir" -volname "$PRODUCT_NAME" -format UDRW

#Mount the disk image, and store the device name
hdiutil attach "$dmg" -noverify -noautoopen -readwrite

脚本2(设置窗口属性脚本):

#!/usr/bin/osascript
#get the dimensions of the main window using a bash script

set {width, height, scale} to words of (do shell script "system_profiler SPDisplaysDataType | awk '/Main Display: Yes/{found=1} /Resolution/{width=$2; height=$4} /Retina/{scale=($2 == \"Yes\" ? 2 : 1)} /^ {8}[^ ]+/{if(found) {exit}; scale=1} END{printf \"%d %d %d\\n\", width, height, scale}'")
set x to ((width / 2) / scale)
set y to ((height / 2) / scale)

#get the product name using a bash script
set {product_name} to words of (do shell script "printf \"%s\", $PRODUCT_NAME")
set background to alias ("Volumes:"&product_name&":.background:some_image.png")

tell application "Finder"
    tell disk product_name
        open
        set current view of container window to icon view
        set toolbar visible of container window to false
        set statusbar visible of container window to false
        set the bounds of container window to {x, y, (x + 479), (y + 383)}
        set theViewOptions to the icon view options of container window
        set arrangement of theViewOptions to not arranged
        set icon size of theViewOptions to 128
        set background picture of theViewOptions to background
        set position of item (product_name & ".app") of container window to {100, 225}
        set position of item "Applications" of container window to {375, 225}
        update without registering applications
        close
    end tell
end tell

我项目窗口工作的上述测量,特别是由于我的背景图片和图标分辨率的大小;您可能需要为自己的项目修改这些值。

脚本3(制作最终磁盘映像脚本):

#!/bin/bash
dir="$TEMP_FILES_DIR/disk"
cp "$PROJECT_DIR/$PROJECT_NAME/some_other_image.png" "$dir/"

#unmount the temp image file, then convert it to final image file
sync
sync
hdiutil detach /Volumes/$PRODUCT_NAME
rm -f "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.dmg"
hdiutil convert "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.temp.dmg" -format UDZO -imagekey zlib-level=9 -o "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.dmg"
rm -f "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.temp.dmg"

#Change the icon of the image file
sips -i "$dir/some_other_image.png"
DeRez -only icns "$dir/some_other_image.png" > "$dir/tmpicns.rsrc"
Rez -append "$dir/tmpicns.rsrc" -o "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.dmg"
SetFile -a C "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.dmg"

rm -rf "$dir"

确保您使用的图片文件位于$ PROJECT_DIR / $ PROJECT_NAME /目录中!

答案 12 :(得分:1)

这些答案太复杂了,时间也变了。以下适用于10.9就好了,权限是正确的,看起来不错。

从目录

创建只读DMG
#!/bin/sh
# create_dmg Frobulator Frobulator.dmg path/to/frobulator/dir [ 'Your Code Sign Identity' ]
set -e

VOLNAME="$1"
DMG="$2"
SRC_DIR="$3"
CODESIGN_IDENTITY="$4"

hdiutil create -srcfolder "$SRC_DIR" \
  -volname "$VOLNAME" \
  -fs HFS+ -fsargs "-c c=64,a=16,e=16" \
  -format UDZO -imagekey zlib-level=9 "$DMG"

if [ -n "$CODESIGN_IDENTITY" ]; then
  codesign -s "$CODESIGN_IDENTITY" -v "$DMG"
fi

创建带有图标(.icns类型)

的只读DMG
#!/bin/sh
# create_dmg_with_icon Frobulator Frobulator.dmg path/to/frobulator/dir path/to/someicon.icns [ 'Your Code Sign Identity' ]
set -e
VOLNAME="$1"
DMG="$2"
SRC_DIR="$3"
ICON_FILE="$4"
CODESIGN_IDENTITY="$5"

TMP_DMG="$(mktemp -u -t XXXXXXX)"
trap 'RESULT=$?; rm -f "$TMP_DMG"; exit $RESULT' INT QUIT TERM EXIT
hdiutil create -srcfolder "$SRC_DIR" -volname "$VOLNAME" -fs HFS+ \
               -fsargs "-c c=64,a=16,e=16" -format UDRW "$TMP_DMG"
TMP_DMG="${TMP_DMG}.dmg" # because OSX appends .dmg
DEVICE="$(hdiutil attach -readwrite -noautoopen "$TMP_DMG" | awk 'NR==1{print$1}')"
VOLUME="$(mount | grep "$DEVICE" | sed 's/^[^ ]* on //;s/ ([^)]*)$//')"
# start of DMG changes
cp "$ICON_FILE" "$VOLUME/.VolumeIcon.icns"
SetFile -c icnC "$VOLUME/.VolumeIcon.icns"
SetFile -a C "$VOLUME"
# end of DMG changes
hdiutil detach "$DEVICE"
hdiutil convert "$TMP_DMG" -format UDZO -imagekey zlib-level=9 -o "$DMG"
if [ -n "$CODESIGN_IDENTITY" ]; then
  codesign -s "$CODESIGN_IDENTITY" -v "$DMG"
fi

如果还需要其他任何事情,最简单的方法是制作SRC_DIR的临时副本,并在创建DMG之前对其进行更改。

答案 13 :(得分:1)

我刚刚编写了一个新的(友好的)命令行实用程序来执行此操作。它不依赖于Finder / AppleScript,也不依赖于任何(已弃用的)Alias Manager API,并且易于配置和使用。

无论如何,任何有兴趣的人都可以find it on PyPi;文档是available on Read The Docs

答案 14 :(得分:0)

我使用了 dmgbuild

  • 安装:pip3 install dmgbuild
  • 安装音量
  • 创建设置文件:
{
    "title": "NAME",
    "background": "YOUR_BACKGROUND.png",
    "format": "UDZO",
    "compression-level": 9,
    "window": { "position": { "x": 100, "y": 100 },
                "size": { "width": 640, "height": 300 } },
    "contents": [
        { "x": 140, "y": 165, "type": "file", "path": "/Volumes/YOUR_VOLUME_NAME/YOUR_APP.app" },
        { "x": 480, "y": 165, "type": "link", "path": "/Applications" }
    ]
}
  • 宽度值是背景的宽度。

  • 高度值应该是窗口栏的背景高度 + 20。

  • 在终端中:dmgbuild -s settings.json "YOUR_VOLUME_NAME" output.dmg