macOS Utility Programs for Working with Packages

READER BEWARE: THE FOLLOWING WRITTEN ENTIRELY BY AI WITHOUT HUMAN EDITING.

Introduction

macOS provides a comprehensive suite of command-line utilities for creating, inspecting, installing, and managing software packages. Whether you’re a developer distributing applications, a system administrator managing deployments, or a curious user wanting to understand how macOS packages work, these tools are essential to your toolkit.

In this guide, we’ll explore five key utilities that form the backbone of macOS package management: installer, pkgutil, pkgbuild, productbuild, and productsign. Each tool serves a specific purpose in the package lifecycle, from creation to installation and verification.

Overview of macOS Package Management

Before diving into individual tools, it’s helpful to understand the macOS package ecosystem:

  • Flat packages (.pkg): Modern format introduced in Mac OS X 10.5, containing a payload and metadata in a single file
  • Bundle packages: Older directory-based format that appears as a single package in Finder
  • Product archives (.pkg): Can contain multiple packages and distribution logic
  • Component packages: Individual packages that can be combined into a product

installer: Installing Packages

The installer command is the command-line interface for installing macOS packages. It’s the same engine that powers the GUI installer application.

Primary Use Cases

  1. Automated deployments: Install packages on multiple machines without user interaction
  2. CI/CD pipelines: Integrate package installation into build and deployment workflows
  3. Remote management: Install packages via SSH or management tools
  4. Testing: Verify package installation behavior in development

Common Command-Line Options

# Basic installation
sudo installer -pkg /path/to/package.pkg -target /

# Install to a specific volume
sudo installer -pkg package.pkg -target /Volumes/ExternalDrive

# Verbose output for troubleshooting
sudo installer -pkg package.pkg -target / -verbose

# Display package information without installing
installer -pkg package.pkg -target / -dumplog

# Show package contents and requirements
installer -pkg package.pkg -target / -showChoicesXML

Key Options Explained

  • -pkg <path>: Specifies the package to install (required)
  • -target <path>: Target volume for installation (required, usually / for boot volume)
  • -verbose: Provides detailed output during installation
  • -dumplog: Displays installation log messages
  • -showChoicesXML: Shows customization options in XML format, useful for scripted installations
  • -applyChoiceChangesXML <file>: Applies custom choices from an XML file
  • -allowUntrusted: Allows installation of unsigned packages (use with caution)

Practical Example

# Install Xcode Command Line Tools package with verbose output
sudo installer -pkg /Library/Developer/CommandLineTools/Packages/CLTools.pkg \
  -target / -verbose

# Generate choices XML, modify it, then install with custom choices
installer -pkg package.pkg -target / -showChoicesXML > choices.xml
# Edit choices.xml to customize installation
sudo installer -pkg package.pkg -target / -applyChoiceChangesXML choices.xml

pkgutil: Package Inspection and Management

pkgutil is a versatile utility for examining, verifying, and managing installed packages. It’s invaluable for troubleshooting, auditing, and understanding what’s installed on your system.

Primary Use Cases

  1. Inventory management: List all installed packages and their components
  2. Verification: Check package integrity and identify modified files
  3. Uninstallation assistance: Identify files installed by a package
  4. Package inspection: Examine package contents before installation
  5. Receipt management: Query and manage package installation records

Common Command-Line Options

# List all installed packages
pkgutil --pkgs

# List packages matching a pattern
pkgutil --pkgs | grep -i adobe

# Show detailed information about a package
pkgutil --pkg-info com.apple.pkg.CLTools_Executables

# List all files installed by a package
pkgutil --files com.apple.pkg.CLTools_Executables

# Verify package integrity
pkgutil --verify com.apple.pkg.CLTools_Executables

# Check which package owns a file
pkgutil --file-info /usr/bin/git

# Expand a package without installing
pkgutil --expand package.pkg destination_directory

# Flatten an expanded package
pkgutil --flatten source_directory package.pkg

# Forget (unregister) a package receipt
sudo pkgutil --forget com.example.package

Key Options Explained

  • --pkgs: Lists all installed package identifiers
  • --pkg-info <id>: Shows detailed information (version, location, install time) for a package
  • --files <id>: Lists all files and directories installed by a package
  • --verify <id>: Verifies that installed files haven’t been modified
  • --file-info <path>: Shows which package installed a specific file
  • --expand <pkg> <dir>: Extracts package contents to a directory for inspection
  • --flatten <dir> <pkg>: Creates a flat package from an expanded directory
  • --forget <id>: Removes package receipt (doesn’t delete files, just the installation record)
  • --volume <path>: Operates on a specific volume instead of the boot volume

Practical Examples

# Find all Python-related packages
pkgutil --pkgs | grep -i python

# Get detailed info about a specific package
pkgutil --pkg-info org.python.Python.PythonFramework-3.11

# List all files from a package and count them
pkgutil --files org.python.Python.PythonFramework-3.11 | wc -l

# Verify package and show any modified files
pkgutil --verify --verbose org.python.Python.PythonFramework-3.11

# Extract package for inspection
pkgutil --expand suspicious-package.pkg /tmp/package-inspection

# Find what package owns the 'python3' binary
pkgutil --file-info /usr/local/bin/python3

# Remove a package receipt (useful for re-testing installations)
sudo pkgutil --forget com.example.myapp

pkgbuild: Creating Component Packages

pkgbuild creates flat package files from a directory of files (payload) and optional scripts. It’s the modern tool for building individual component packages.

Primary Use Cases

  1. Software distribution: Package applications for distribution
  2. Custom installers: Create packages for proprietary software
  3. System configuration: Package configuration files and scripts
  4. Automated builds: Integrate package creation into build systems

Common Command-Line Options

# Create a basic package from a directory
pkgbuild --root /path/to/payload \
  --identifier com.example.myapp \
  --version 1.0 \
  MyApp.pkg

# Include installation scripts
pkgbuild --root /path/to/payload \
  --scripts /path/to/scripts \
  --identifier com.example.myapp \
  --version 1.0 \
  MyApp.pkg

# Analyze an existing application to determine components
pkgbuild --analyze --root /Applications/MyApp.app components.plist

# Create package with custom install location
pkgbuild --root /path/to/payload \
  --install-location /Applications \
  --identifier com.example.myapp \
  --version 1.0 \
  MyApp.pkg

# Create a package without a payload (scripts only)
pkgbuild --nopayload \
  --scripts /path/to/scripts \
  --identifier com.example.config \
  --version 1.0 \
  Config.pkg

# Sign the package during creation
pkgbuild --root /path/to/payload \
  --identifier com.example.myapp \
  --version 1.0 \
  --sign "Developer ID Installer: Your Name (TEAMID)" \
  MyApp.pkg

Key Options Explained

  • --root <path>: Source directory containing files to package
  • --identifier <id>: Unique reverse-DNS identifier for the package (required)
  • --version <version>: Package version string (required)
  • --install-location <path>: Where files should be installed (default is /)
  • --scripts <dir>: Directory containing pre/post-install scripts
  • --nopayload: Creates a scripts-only package
  • --component <path>: Packages a bundle component (app, framework, etc.)
  • --analyze: Generates a component property list from existing bundles
  • --sign <identity>: Signs the package with specified Developer ID
  • --timestamp: Includes secure timestamp when signing (recommended)
  • --ownership <option>: Sets ownership mode (recommended, preserve, preserve-other)

Script Requirements

Installation scripts must follow specific naming conventions:

  • preinstall: Runs before installation
  • postinstall: Runs after installation

Scripts must be executable (chmod +x) and should include appropriate shebangs.

Practical Examples

# Create a simple application package
pkgbuild --root /tmp/MyApp_Payload \
  --identifier com.example.myapp \
  --version 1.0.0 \
  --install-location /Applications \
  MyApp-1.0.0.pkg

# Package with pre/post installation scripts
pkgbuild --root /tmp/payload \
  --scripts /tmp/scripts \
  --identifier com.example.myapp \
  --version 1.0.0 \
  --install-location /Library/Application\ Support/MyApp \
  MyApp.pkg

# Analyze an app bundle and create component package
pkgbuild --analyze --root /Applications/TextEdit.app components.plist
pkgbuild --root /Applications/TextEdit.app \
  --component-plist components.plist \
  --identifier com.example.textedit \
  --version 1.0 \
  TextEdit.pkg

# Create a configuration-only package (no payload, just scripts)
pkgbuild --nopayload \
  --scripts /tmp/config-scripts \
  --identifier com.example.system-config \
  --version 1.0 \
  SystemConfig.pkg

# Build and sign in one step
pkgbuild --root /tmp/payload \
  --identifier com.example.myapp \
  --version 2.0.0 \
  --sign "Developer ID Installer: My Company (ABC123DEF4)" \
  --timestamp \
  MyApp-signed.pkg

productbuild: Creating Product Archives

productbuild creates product archives that can contain multiple packages, custom distribution logic, and rich installation interfaces. It’s the tool for creating complex, multi-component installers.

Primary Use Cases

  1. Multi-component installers: Combine multiple packages into one installer
  2. Custom installation UI: Add welcome screens, license agreements, and readme files
  3. Conditional installation: Implement JavaScript-based installation logic
  4. Distribution packages: Create installers for Mac App Store or direct distribution
  5. Volume selection: Allow users to choose installation destination

Common Command-Line Options

# Create a product from a single package
productbuild --package MyApp.pkg MyAppInstaller.pkg

# Combine multiple packages
productbuild --package Component1.pkg \
  --package Component2.pkg \
  FinalProduct.pkg

# Use a distribution definition file
productbuild --distribution Distribution.plist \
  --package-path /path/to/packages \
  FinalProduct.pkg

# Add resources (welcome, license, readme)
productbuild --distribution Distribution.plist \
  --package-path ./packages \
  --resources ./resources \
  FinalProduct.pkg

# Synthesize a distribution file
productbuild --synthesize \
  --package Component1.pkg \
  --package Component2.pkg \
  Distribution.plist

# Sign the final product
productbuild --distribution Distribution.plist \
  --package-path ./packages \
  --sign "Developer ID Installer: Your Name (TEAMID)" \
  --timestamp \
  SignedProduct.pkg

# Specify minimum macOS version
productbuild --distribution Distribution.plist \
  --package-path ./packages \
  --min-os-version 10.15 \
  Product.pkg

Key Options Explained

  • --package <pkg>: Adds a component package to the product
  • --distribution <file>: Uses a custom distribution definition XML file
  • --package-path <dir>: Directory containing referenced component packages
  • --resources <dir>: Directory containing UI resources (HTML, RTF, images)
  • --synthesize: Generates a template distribution XML file
  • --sign <identity>: Signs the product with a Developer ID
  • --timestamp: Adds a secure timestamp to signature
  • --min-os-version <version>: Sets minimum required macOS version
  • --version <version>: Sets the product version
  • --scripts <dir>: Directory containing installation scripts

Distribution XML Basics

The distribution XML file controls the installation experience:

<?xml version="1.0" encoding="utf-8"?>
<installer-gui-script minSpecVersion="2">
    <title>My Application</title>
    <welcome file="Welcome.html"/>
    <license file="License.rtf"/>
    <readme file="ReadMe.html"/>
    
    <options customize="always" require-scripts="false"/>
    
    <choices-outline>
        <line choice="com.example.app"/>
        <line choice="com.example.plugins"/>
    </choices-outline>
    
    <choice id="com.example.app" title="Main Application">
        <pkg-ref id="com.example.myapp"/>
    </choice>
    
    <choice id="com.example.plugins" title="Plugins" start_selected="false">
        <pkg-ref id="com.example.plugins"/>
    </choice>
    
    <pkg-ref id="com.example.myapp" version="1.0">
        <bundle-version/>
    </pkg-ref>
</installer-gui-script>

Practical Examples

# Simple product from one component
productbuild --package MyApp.pkg MyAppInstaller.pkg

# Generate distribution template
productbuild --synthesize \
  --package Core.pkg \
  --package Extras.pkg \
  Distribution.plist

# Build product with custom distribution and resources
productbuild --distribution Distribution.xml \
  --package-path ./components \
  --resources ./installer-resources \
  MyProduct.pkg

# Complete signed product for distribution
productbuild --distribution Distribution.xml \
  --package-path ./packages \
  --resources ./resources \
  --sign "Developer ID Installer: My Company (ABC123DEF4)" \
  --timestamp \
  --min-os-version 11.0 \
  MyApp-v1.0-Installer.pkg

# Product with JavaScript requirements checking
# (Distribution.xml includes JavaScript for system checks)
productbuild --distribution DistributionWithRequirements.xml \
  --package-path ./packages \
  SmartInstaller.pkg

productsign: Signing and Verifying Products

productsign signs flat packages and product archives with a Developer ID, ensuring authenticity and enabling Gatekeeper verification. It can also verify existing signatures.

Primary Use Cases

  1. Code signing: Sign packages for distribution outside the Mac App Store
  2. Security compliance: Meet Gatekeeper requirements for installation
  3. Verification: Check signature validity on packages
  4. Re-signing: Sign packages built by other tools
  5. Certificate management: Inspect available signing identities

Common Command-Line Options

# Sign a package
productsign --sign "Developer ID Installer: Your Name (TEAMID)" \
  UnsignedPackage.pkg \
  SignedPackage.pkg

# Sign with timestamp (recommended)
productsign --sign "Developer ID Installer: Your Name (TEAMID)" \
  --timestamp \
  UnsignedPackage.pkg \
  SignedPackage.pkg

# Verify a package signature
pkgutil --check-signature SignedPackage.pkg

# Alternative verification with spctl
spctl --assess --verbose --type install SignedPackage.pkg

# List available signing identities
security find-identity -v -p basic

# Sign with specific keychain
productsign --sign "Developer ID Installer" \
  --keychain /path/to/keychain \
  UnsignedPackage.pkg \
  SignedPackage.pkg

Key Options Explained

  • --sign <identity>: Certificate identity to use for signing (required)
  • --timestamp: Adds secure timestamp from Apple’s timestamp server (highly recommended)
  • --keychain <path>: Uses specific keychain instead of default
  • Output filename: Third argument specifies the signed output file

Certificate Requirements

To sign packages, you need:

  1. Developer ID Installer certificate: Obtained from Apple Developer Program
  2. Keychain access: Certificate must be in your keychain
  3. Private key: Associated private key must be available

Signature Verification

# Detailed verification with pkgutil
pkgutil --check-signature MyPackage.pkg

# Example output:
# Package "MyPackage.pkg":
#    Status: signed by a developer certificate issued by Apple for distribution
#    Certificate Chain:
#     1. Developer ID Installer: Your Name (TEAMID)
#        Expires: 2025-01-01
#     2. Developer ID Certification Authority
#        Expires: 2027-02-01
#     3. Apple Root CA
#        Expires: 2035-02-09

# Gatekeeper assessment
spctl --assess --verbose=4 --type install MyPackage.pkg
# Output: MyPackage.pkg: accepted
#         source=Developer ID

# Check if package is notarized
spctl --assess -vv --type install MyPackage.pkg 2>&1 | grep origin

Practical Examples

# Sign a package for distribution
productsign --sign "Developer ID Installer: My Company (ABC123DEF4)" \
  --timestamp \
  MyApp-unsigned.pkg \
  MyApp-signed.pkg

# Verify signature details
pkgutil --check-signature MyApp-signed.pkg

# Check Gatekeeper acceptance
spctl --assess --verbose --type install MyApp-signed.pkg

# List all available signing identities
security find-identity -v -p basic

# Sign using a specific identity when multiple are available
productsign --sign "Developer ID Installer: My Company (ABC123DEF4)" \
  --timestamp \
  Component.pkg \
  SignedComponent.pkg

# Verify and display certificate chain
pkgutil --check-signature SignedComponent.pkg | grep -A 10 "Certificate Chain"

Complete Workflow Example

Here’s a complete example of creating, signing, and verifying a macOS package:

#!/bin/bash
set -euo pipefail

# Step 1: Prepare the payload
mkdir -p /tmp/MyApp_Payload/Applications
cp -R /path/to/MyApp.app /tmp/MyApp_Payload/Applications/

# Step 2: Prepare installation scripts
mkdir -p /tmp/MyApp_Scripts
cat > /tmp/MyApp_Scripts/postinstall << 'EOF'
#!/bin/bash
set -e
# Post-installation tasks
/usr/bin/logger "MyApp installation completed"
exit 0
EOF
chmod +x /tmp/MyApp_Scripts/postinstall

# Step 3: Build component package
pkgbuild --root /tmp/MyApp_Payload \
  --scripts /tmp/MyApp_Scripts \
  --identifier com.example.myapp \
  --version 1.0.0 \
  --install-location / \
  /tmp/MyApp-component.pkg

# Step 4: Create distribution definition
productbuild --synthesize \
  --package /tmp/MyApp-component.pkg \
  /tmp/Distribution.plist

# Edit Distribution.plist to customize (optional)

# Step 5: Build product archive
productbuild --distribution /tmp/Distribution.plist \
  --package-path /tmp \
  --resources /path/to/resources \
  /tmp/MyApp-unsigned.pkg

# Step 6: Sign the product
productsign --sign "Developer ID Installer: My Company (ABC123DEF4)" \
  --timestamp \
  /tmp/MyApp-unsigned.pkg \
  ./MyApp-1.0.0-signed.pkg

# Step 7: Verify the signature
echo "Verifying signature..."
pkgutil --check-signature ./MyApp-1.0.0-signed.pkg

echo "Checking Gatekeeper..."
spctl --assess --verbose --type install ./MyApp-1.0.0-signed.pkg

# Step 8: Test installation (on a test system)
# sudo installer -pkg ./MyApp-1.0.0-signed.pkg -target / -verbose

# Step 9: Verify installation
# pkgutil --pkg-info com.example.myapp
# pkgutil --files com.example.myapp

echo "Package created and verified successfully!"

Best Practices

For Package Creation

  1. Use unique identifiers: Use reverse-DNS notation (com.company.product)
  2. Version appropriately: Use semantic versioning (major.minor.patch)
  3. Test before signing: Verify package behavior before creating signed versions
  4. Minimize payload: Only include necessary files
  5. Use scripts wisely: Keep installation scripts simple and idempotent
  6. Set proper permissions: Ensure file ownership and permissions are correct in payload

For Signing

  1. Always timestamp: Use --timestamp to ensure long-term signature validity
  2. Protect certificates: Secure your Developer ID certificate and private key
  3. Consider notarization: For public distribution, submit packages for notarization
  4. Verify after signing: Always check signatures before distribution
  5. Keep certificates current: Renew certificates before expiration

For Testing

  1. Test on clean systems: Verify installation on systems without development tools
  2. Test different macOS versions: Ensure compatibility across supported versions
  3. Verify receipts: Check that pkgutil properly registers the installation
  4. Test uninstallation: Ensure packages can be cleanly removed if needed
  5. Check Gatekeeper: Verify packages pass Gatekeeper checks

For Distribution

  1. Sign all components: Sign both component packages and final product
  2. Document requirements: Clearly specify minimum macOS version and dependencies
  3. Provide uninstall instructions: Document how to remove the package
  4. Test over network: Verify packages work when downloaded from the internet
  5. Consider updates: Plan for package update mechanisms

Troubleshooting Common Issues

Installation Failures

# Check installation logs
cat /var/log/install.log | tail -100

# Verify package integrity
pkgutil --verify com.example.package

# Check target volume
diskutil info / | grep "Volume Name"

Signing Issues

# Verify certificate is in keychain
security find-identity -v -p basic

# Check certificate validity
security find-certificate -c "Developer ID Installer" -p | \
  openssl x509 -text | grep -A 2 "Validity"

# Unlock keychain if needed
security unlock-keychain ~/Library/Keychains/login.keychain-db

Permission Problems

# Check payload permissions before packaging
ls -lR /tmp/MyApp_Payload

# Fix ownership in payload
sudo chown -R root:wheel /tmp/MyApp_Payload/Applications

# Set proper script permissions
chmod +x /tmp/scripts/postinstall

Advanced Topics

Custom JavaScript Requirements

Distribution XML can include JavaScript for dynamic requirement checking:

<installation-check script="checkRequirements()"/>

<script>
function checkRequirements() {
    // Check for minimum macOS version (example: 10.15 Catalina)
    if (system.compareVersions(system.version.ProductVersion, '10.15') &lt; 0) {
        my.result.type = 'Fatal';
        my.result.message = 'Requires macOS 10.15 or later';
        return false;
    }
    return true;
}
</script>

Component Properties

Use --analyze to generate component property lists that preserve bundle attributes:

pkgbuild --analyze --root /Applications/MyApp.app components.plist

# Edit components.plist to set BundleIsRelocatable, BundleIsVersionChecked, etc.

pkgbuild --root /Applications/MyApp.app \
  --component-plist components.plist \
  --identifier com.example.myapp \
  --version 1.0 \
  MyApp.pkg

Package Payload Inspection

# Extract and examine package contents
pkgutil --expand MyPackage.pkg /tmp/expanded
cd /tmp/expanded
ls -la

# View package info
cat PackageInfo

# Extract payload
cat Payload | gunzip -dc | cpio -i

Conclusion

The macOS package management utilities provide powerful capabilities for creating, distributing, and managing software installations. By understanding these tools:

  • installer handles package installation with automation support
  • pkgutil inspects, verifies, and manages installed packages
  • pkgbuild creates component packages from payloads and scripts
  • productbuild combines components into feature-rich installers
  • productsign ensures package authenticity through code signing

Whether you’re distributing a simple application or building complex multi-component installers, these utilities give you complete control over the package lifecycle. Start with simple packages using pkgbuild, combine them with productbuild when needed, sign everything with productsign, and use pkgutil to verify and troubleshoot installations.

For further learning, consult the manual pages (man pkgbuild, man productbuild, etc.) and Apple’s Software Delivery Guide for detailed specifications and advanced features.

References

  • man installer - Manual page for the installer command
  • man pkgutil - Manual page for the pkgutil command
  • man pkgbuild - Manual page for the pkgbuild command
  • man productbuild - Manual page for the productbuild command
  • man productsign - Manual page for the productsign command
  • Apple Developer Documentation
  • Software Delivery Guide