appdmg

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 7, 2026 License: BSD-3-Clause Imports: 15 Imported by: 0

README

appdmg

The .dmg a Mac application is distributed in — an HFS+ volume carrying the .app, a background picture with the icons arranged on it, and the volume's own icon — written in pure Go, CGO_ENABLED=0, with no hdiutil anywhere.

err := appdmg.Build(appdmg.Spec{
    Output:           "MyApp.dmg",
    App:              "MyApp.app",
    Background:       "art/dmg-background.png",
    VolumeIcon:       "art/volume.icns",
    ApplicationsLink: true,
    Positions: map[string]appdmg.Point{
        "MyApp.app":    {X: 160, Y: 220},
        "Applications": {X: 480, Y: 220},
    },
})

That is the whole API. There is a command for release scripts too:

go run github.com/go-macos/appdmg/cmd/appdmg@latest \
    -o MyApp.dmg -background art/bg.png -icon art/volume.icns -applications \
    -at 'MyApp.app=160,220' -at 'Applications=480,220' \
    MyApp.app

The window takes the background picture's own size unless Spec.Window says otherwise, the volume takes the application's name unless Spec.VolumeName does, and the volume is sized from its content unless Spec.SizeBytes does.

What it composes

This package orders operations; it implements none of them. Each piece was proven against macOS on its own first:

go-filesystems/hfsplus lays out the volume, copies the bundle in, sets Finder flags
go-macos/dsstore writes the .DS_Store that carries the background and the icon positions
go-diskimages/dmg wraps the volume in UDIF and compresses it

HFS+ rather than APFS: it compresses far better under UDZO, and the alias inside the .DS_Store names the filesystem type, so the metadata expects an H+ volume.

The two steps that do nothing on their own

Both are Finder flags, and both are why a hand-assembled image looks wrong in ways no error message explains:

  • .VolumeIcon.icns is ignored until the volume's ROOT carries kHasCustomIcon. Writing the file is not enough.
  • .background is a perfectly ordinary folder until it carries kIsInvisible, and then the folder holding the picture stops appearing in the window the picture is decorating.

Building the volume

The volume is laid out in memory and written once. Every hfsplus mutator syncs the WHOLE image back to its backing file, so a volume opened on disk rewrites itself once per file copied in — quadratic in the size of a .app, for a result identical to writing the bytes at the end.

Licence

BSD-3-Clause.

One host limitation

The modes on the volume are the modes on the host, and Windows records no executable bit — so a bundle assembled there arrives unable to launch. Build the image on the machine that built the bundle.

Documentation

Overview

Package appdmg builds the .dmg a Mac application is distributed in — an HFS+ volume carrying the .app, a background picture with the icons placed on it, and the volume's own icon — in pure Go with CGO_ENABLED=0 and no shelling out to hdiutil.

It composes rather than implements. Each piece was proven against macOS on its own before this existed:

  • go-filesystems/hfsplus writes the volume and its Finder flags;
  • go-macos/dsstore writes the window's background and icon positions;
  • go-diskimages/dmg wraps the result in UDIF and compresses it.

HFS+ rather than APFS: it compresses far better under UDZO, and the alias inside the .DS_Store that points at the background names the filesystem type, so the metadata expects an "H+" volume.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Build

func Build(spec Spec) error

Build writes the image described by spec.

Example
err := Build(Spec{
	Output:           "MyApp.dmg",
	App:              "MyApp.app",
	Background:       "art/dmg-background.png",
	VolumeIcon:       "art/volume.icns",
	ApplicationsLink: true,
	Positions: map[string]Point{
		"MyApp.app":    {X: 160, Y: 220},
		"Applications": {X: 480, Y: 220},
	},
})
fmt.Println(err != nil)

Types

type Point

type Point struct{ X, Y uint32 }

A Point is an icon's centre in the window's coordinates.

type Spec

type Spec struct {
	// Output is the .dmg to write. VolumeName is what the Finder shows and
	// what the background's alias records; it defaults to the .app's name
	// without its extension.
	Output     string
	VolumeName string

	// App is a .app directory copied to the volume root. Extra names more
	// files or directories to copy in, keyed by their path on the volume.
	App   string
	Extra map[string]string

	// Background is a picture copied into /.background and shown behind the
	// window. VolumeIcon is an .icns shown instead of the generic disk —
	// go-macos/appbundle's ICNS writes one.
	Background string
	VolumeIcon string

	// Positions places icons by their name on the volume, in the window's
	// coordinates, measured to the icon's CENTRE. An entry with no position
	// is left where the Finder puts it.
	Positions map[string]Point

	// ApplicationsLink adds the /Applications symlink a drag-to-install
	// window needs.
	ApplicationsLink bool

	// Window is where the window opens and how large it is. A zero Window
	// with a background takes the picture's own size at (100, 100): the
	// Finder draws the picture unscaled from the window's top-left corner,
	// so a window that is not the picture's size shows part of a picture.
	Window Window

	// IconSize defaults to 96. Format is the UDIF format, "UDZO" by default
	// — a mostly-empty HFS+ volume compresses to a small fraction of its
	// size. "UDRW" writes the raw volume instead, because that is what a
	// writable image is: a UDIF container is read-only however its trailer
	// is stamped.
	IconSize float64
	Format   string

	// SizeBytes is the volume's size. Zero asks for the content's size plus
	// enough slack for the filesystem's own structures.
	SizeBytes int64
}

A Spec describes the image to build.

type Window

type Window = dsstore.Window

A Window is where the window opens and how large it is. It is dsstore's type: the record it becomes is the Finder's, and restating it here would buy nothing but a conversion.

Directories

Path Synopsis
cmd
appdmg command
Command appdmg builds the .dmg a Mac application is shipped in.
Command appdmg builds the .dmg a Mac application is shipped in.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL