224

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

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

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

4

15 回答 15

204

经过大量研究,我想出了这个答案,特此将其放在这里作为我自己问题的答案,以供参考:

  1. 确保在系统偏好设置>>通用访问中选中“启用辅助设备访问”。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:

    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 
    

在 Snow Leopard 上,上面的 applescript 不会正确设置图标位置 - 这似乎是一个 Snow Leopard 错误。一种解决方法是在设置图标后简单地调用关闭/打开,即:

..
set position of item "'${applicationName}'" of container window to {100, 100}
set position of item "Applications" of container window to {375, 100}
close
open
于 2009-10-03T12:05:26.397 回答
61

有一个名为create-dmg的小 Bash 脚本可以构建具有自定义背景、自定义图标位置和卷名的精美 DMG。

我多年前为我当时经营的公司建立了它;从那时起,它依靠其他人的贡献而生存,据报道运作良好。

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

于 2009-03-10T03:40:41.997 回答
40

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

以下是我长期以来的做法,非常成功:

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

  2. 安装 DMG 并在 Finder 中手动设置布局,或者使用适合您的任何工具进行布局。背景图像通常是我们放入 DMG 上隐藏文件夹(“.something”)中的图像。将您的应用程序的副本放在那里(任何版本,甚至过时的版本都可以)。再次复制您想要的其他文件(别名、自述文件等),过时的版本就可以了。确保图标具有正确的大小和位置(IOW,按照您想要的方式布局 DMG)。

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

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

  • 它会复制 DMG,因此永远不会再触及原始的 DMG。
  • 它安装副本。
  • 它用最新的文件替换所有文件(例如构建后的最新应用程序)。您可以在命令行上简单地使用mv同上。请注意,当您替换这样的文件时,图标将保持不变,位置将保持不变,除了文件(或目录)内容之外的所有内容都保持不变(至少同上,我们通常用于该任务) . 您当然也可以用另一张图片替换背景图片(只要确保它具有相同的尺寸)。
  • 替换文件后,使脚本再次卸载 DMG 副本。
  • 最后调用 hdiutil 将可写转换为压缩(且不可写)DMG。

这种方法听起来可能不是最优的,但相信我,它在实践中非常有效。您甚至可以将原始 DMG(DMG 模板)置于版本控制(例如 SVN)之下,因此如果您不小心更改/破坏了它,您可以返回到它仍然可以的版本。您可以将 DMG 模板与属于 DMG 的所有其他文件(自述文件、URL 文件、背景图像)一起添加到您的 Xcode 项目中,都在版本控制下,然后创建一个目标(例如名为“Create DMG”的外部目标)并在那里运行上面的 DMG 脚本并将您的旧主目标添加为依赖目标。您可以在脚本中使用 ${SRCROOT} 访问 Xcode 树中的文件(始终是产品的源根目录),并且可以使用 ${BUILT_PRODUCTS_DIR} 访问构建产品(始终是 Xcode 创建构建结果的目录) .

结果:实际上 Xcode 可以在构建结束时生成 DMG。准备发布的 DMG。您不仅可以通过这种方式非常轻松地创建发布 DMG,而且您实际上可以在自动化过程中(如果您愿意,可以在无头服务器上)使用命令行中的 xcodebuild(例如自动夜间构建)来执行此操作。

于 2008-09-18T21:11:57.590 回答
25

通过提供此答案来更新此问题。

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

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
    

(免责声明。我是 appdmg 的创建者)

于 2015-01-23T15:12:07.197 回答
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
于 2008-09-18T21:13:35.620 回答
13

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

于 2010-06-30T15:38:26.373 回答
9

为了创建漂亮的 DMG,您现在可以使用一些编写良好的开源代码:

于 2015-12-20T13:23:04.200 回答
6

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

于 2012-02-22T14:05:40.860 回答
5

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

/*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
于 2013-08-26T12:16:54.023 回答
4

我终于在我自己的项目中得到了这个工作(恰好在 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/ 目录中!

于 2015-07-24T20:47:26.587 回答
2

.DS_Store 文件将 Windows 设置存储在 Mac 中。窗口设置包括图标布局、窗口背景、窗口大小等。为挂载图像创建窗口时需要 .DS_Store 文件,以保留文件排列和窗口背景。

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

于 2010-04-14T08:09:40.290 回答
2

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

OSX 包管理系统与其他可以轻松系统地完成此任务的 Unixen 相比,并没有那么先进。

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

于 2011-10-09T12:55:20.087 回答
2

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

无论如何,任何有兴趣的人都可以在 PyPi 上找到它;文档可在 Read The Docs 上找到

于 2014-02-17T10:35:14.363 回答
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 之前对其应用更改。

于 2014-01-02T09:11:19.020 回答
1

我使用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

于 2021-04-14T13:43:01.717 回答