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


212

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

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

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


1
这不是Ask Different的事吗?
Lenar Hoyt

Answers:


199

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

  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,最终完成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

1
优秀的。关于此的两个问题:1.建议sleep在覆盖图像后使用。多久?确定性地等待该过程的完成是否可能?同样为delay 5您的AppleScript。我总是对如此随意的等待时间保持警惕,因为他们的经历有些糟糕。2.在第6步中,您拨打了sync两次–为什么?
康拉德·鲁道夫

我还没有找到确定地等待更新完成而无需注册应用程序的任何方法。我不确定在“ hdiutil attach”之后是否需要睡眠,您必须检查文档(man hdiutil)。同步只需要被调用一次,自从SunOS过去以来,我已经习惯了两次。
Ludvig A. Norin

1
它允许applescript模拟鼠标和键盘输入以及许多其他东西。
Ludvig A. Norin

1
如果脚本挂起,直到在“未注册应用程序的更新”行中超时,则表明步骤1尚未完成(或已撤消)。我只是很难发现这一点。
杰伊·梅纳德K5ZC'5

我想我发现了一个小故障-AppleScript中出现弹出,这是在尝试运行chmod之前完成的。chmod将失败,因为磁盘现在已弹出。
Trejkaz

56

有一个名为create-dmg的小Bash脚本,用于创建具有自定义背景,自定义图标位置和卷名的精美DMG。

我是在多年前为当时经营的公司建立的。从那时起,它就一直依靠别人的贡献生存下来,据说效果很好。

还有一个node-appdmg,它看起来像是基于Node.js的更现代,更积极的尝试。还要检查一下。


7
bash脚本的链接已断开。你能把它放在github.com的要点上吗?谢谢
dquimper

create-dmg 不能很好地放置图标。如果您需要此功能,请不要使用它。
laike9m 2015年

1
@ laike9m自从我上次接触它已有5年多了。我真的不认为DMG不再是分发Mac软件的最佳方法。因此它可以在其他人的贡献下幸存下来-希望有人也能弄清楚图标的问题。
Andrey Tarantsov 2015年

3
@Zmey作为用户,我到目前为止更喜欢zip文件。/Applications如果应用检测到正在运行,则自动复制到的奖励积分~/Downloads。另请参阅John Gruber的这篇旧文章
Andrey Tarantsov '16


37

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

这是我很长一段时间以来非常成功的方法:

  1. 创建一个新的DMG,writeable(!),其大小足以容纳所需的二进制文件和其他自述文件(稀疏文件可能会起作用)。

  2. 安装DMG并在Finder中或使用任何适合您的工具手动提供DMG的布局(请参阅底部的FileStorm链接以获取良好的工具)。背景图像通常是我们放入DMG上隐藏文件夹(“ .something”)中的图像。将您的应用程序副本放在那里(任何版本,甚至是过时的版本都可以)。同样,将您要复制的其他文件(别名,自述文件等)复制到那里,过时的版本就可以了。确保图标具有正确的尺寸和位置(IOW,以所需的方式布局DMG)。

  3. 再次卸载DMG,所有设置应立即存储。

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

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

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

结果:实际上,Xcode可以在构建结束时生成DMG。准备发布的DMG。您不仅可以通过这种方式轻松创建relase DMG,而且实际上可以使用命令行中的xcodebuild(例如,每晚自动生成)以自动化的过程(如果需要,可以在无头服务器上)进行。

关于模板的初始布局,FileStorm是一个很好的工具。它是商业性的,但功能强大且易于使用。普通版不到20美元,因此价格实在实惠。也许有人可以自动化FileStorm来创建DMG(例如通过AppleScript),但是从来没有尝试过,但是一旦找到了理想的DMG模板,就可以很容易地为每个发行版进行更新。


3
由于多种原因,我已经放弃了以这种方式进行操作的想法。这是其中的两个:安装程序的内容会随产品而有所不同,并且我们只希望依靠打包机和脚本上安装的软件-一个用于添加新产品的简单,最少的手动例程。
Ludvig A. Norin

这与我们的情况相同。我们有十多种产品;每个都有一个完全不同的DMG。为每个产品创建一个模板DMG是一项一次性任务,需要花费几分钟。您所说的“安装程序”是什么意思?PKG / MPKG安装包?
Mecki

情况不一样。我们会经常添加产品,恕不另行通知。最少的手动例程意味着运行一个脚本,为产品提供名称和其他一些属性。除此之外,还有其他原因使我们决定不使用这种解决方案。
Ludvig A. Norin

我们将打包过程与构建过程分开了,因为它是由不同的人在不同的时间完成的。打包过程还会为Windows,Windows CE,Symbian,AIX,Linux和Solaris创建安装程序。
Ludvig A. Norin

您可能指的是hdiutil,而不是hdutil。
伊万·武恰卡(EvanVučica)2009年

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
    

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


3
简单有效。唯一的缺点是需要安装npm
Jack James

@Creator:您可以像这样提供GUI而不用拖放来使它前进吗?s.sudre.free.fr/Software/Packages/about.html

如果.app文件与json文件不在同一文件夹中,则此操作不起作用,当您在“路径”中指定相对路径时,它会出现“找不到文件”错误
Joey

@Joey,如果对您不起作用,您可以在Github存储库上打开一个问题吗?
LinusUnnebäck17年

@Joey:不需要在同一个文件夹中,我的背景,应用程序都位于不同的路径中,只需传递相对路径即可,如../../../ABC
Anoop Vaidya

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

1
很好,但是不能满足实际需求(背景图像,文件夹中项目的位置等)
Tom Bogle

创建了DMG,但是我又必须使用CMD运行Script(.sh),我需要在创建DMG之后自动运行sh
SANTOSH

13

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


3
我的团队可以在Jenkins CI构建服务器上自动且精美地完成此工作,并在后台将其拖到Applications别名。运行,不要走到DropDMG来制作磁盘映像。
Paul Collins

优质的应用程序,我将在试用期结束后购买它。
kilik52

看起来不错,但似乎没有选择调整窗口大小的选项。
2015年

@avi DropDMG会自动将窗口调整为您设置的背景图片的大小(或根据需要插入该图片)。
蔡仔

十分感谢!没有背景图片是否可以调整大小?
avi 2015年


5

为了创建美观的DMG,您现在可以使用一些写得很好的开源代码:


可能是他们感动了。您可以使用create-dmg和node-appdmg。我正在使用create-dmg。很好。
Anni S

@ PamelaCook-LightBeCorp如果您仍然有兴趣。dmgbuild的链接已修复。

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

您能在这里解释“您的道路”是什么意思吗?我可以使用磁盘上的任何图标文件,而SetFile可以复制该文件,还是需要使用.dmg中的文件?我只有一台Mac,因此很难测试这些东西是否可以在其他计算机上运行。
米兰·巴布斯科夫

“您的路径”​​是DMG名称。(/卷/ dmgName)
Parag Bafna

您应该复制icns文件。cp“ /Volumes/customIcon.icns”“ /Volumes/dmgName/.VolumeIcon.icns”
Parag Bafna

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 /目录中!


它正在我的项目上创建空白磁盘映像。任何建议。
ManiaChamp '16

Shell脚本部分仅起作用,但是如何在Build Phase下的RunScript中使用Shell Script添加Apple脚本,却显示错误,找不到每个Apple脚本语句。
ManiaChamp '16

2

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

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


但是首先要通过将名称命名为.background.png之类的背景图像添加到dmg中,然后使用Cmd-J将其添加到背景中。
Gijs

2

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

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

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


2

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

无论如何,任何有兴趣的人都可以在PyPi上找到它;该文档位于“阅读文档”中


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

创建带有图标的只读DMG(.icns类型)

#!/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之前对其进行更改。


这个答案没有在之前的内容中添加任何内容,也没有回答原始的问题(这不仅仅是关于在dmg中放置图标或如何对其进行签名;问题在于关于以编程方式向其中添加图形和定位图标dmg)。
Ludvig A. Norin 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.