# How the Hallelujah Input Method Is Packaged as a .pkg Installer Using build-package.bash

> Learn how build-package.bash creates a macOS .pkg installer for the Hallelujah Input Method. Automate compilation, payload prep, pkgbuild, and postinstall scripts for seamless registration.

- Repository: [dongyuwei/hallelujahim](https://github.com/dongyuwei/hallelujahim)
- Tags: how-to-guide
- Published: 2026-02-28

---

**The [`build-package.bash`](https://github.com/dongyuwei/hallelujahim/blob/main/build-package.bash) script automates a four-stage pipeline—compiling the Xcode project, preparing a payload root, invoking `pkgbuild` with specific metadata flags, and executing a `postinstall` script—to generate a versioned macOS installer that automatically registers the input method in `/Library/Input Methods`.**

The Hallelujah input method, hosted at `dongyuwei/hallelujahim`, requires installation into macOS system directories to function as a third-party keyboard input source. Rather than manual distribution of the `.app` bundle, the repository uses [`build-package.bash`](https://github.com/dongyuwei/hallelujahim/blob/main/build-package.bash) to orchestrate the entire build-to-package workflow. This script coordinates Xcode compilation, payload assembly, and installer generation while embedding a post-installation hook that handles system registration without user intervention.

## The Four-Stage Packaging Pipeline

The packaging process defined in [`package/build-package.bash`](https://github.com/dongyuwei/hallelujahim/blob/main/package/build-package.bash) follows a strict sequence to ensure the resulting `.pkg` meets macOS requirements for input method installation.

### Stage 1: Compiling the Xcode Project

The script initiates the process by invoking [`build.sh`](https://github.com/dongyuwei/hallelujahim/blob/main/build.sh) from the repository root. This companion script executes `xcodebuild` using the **hallelujah** scheme in **Release** configuration, producing `hallelujah.app` and placing it in `/tmp/hallelujah/build/release/`.

```bash
pushd ${PROJECT_ROOT}
sh build.sh
popd

```

According to the source code in [`build.sh`](https://github.com/dongyuwei/hallelujahim/blob/main/build.sh), this step cleans previous build artifacts and compiles the Objective-C++ source files (such as `src/InputController.mm` and `src/ConversionEngine.mm`) into the final application bundle.

### Stage 2: Preparing the Payload Root

Before invoking `pkgbuild`, the script establishes a temporary directory structure that serves as the installer payload. It removes any existing root directory to prevent stale files, then creates a fresh hierarchy and copies the compiled `.app` bundle into position.

```bash
rm -rf /tmp/hallelujah/build/release/root/
mkdir -p /tmp/hallelujah/build/release/root
cp -R /tmp/hallelujah/build/release/hallelujah.app \
      /tmp/hallelujah/build/release/root/

```

This root directory (`/tmp/hallelujah/build/release/root`) becomes the `--root` argument for `pkgbuild`, determining what files the installer places on the target system.

### Stage 3: Generating the .pkg with pkgbuild

The core packaging operation occurs via `pkgbuild` with six critical flags defined in lines 19–26 of [`build-package.bash`](https://github.com/dongyuwei/hallelujahim/blob/main/build-package.bash):

- **`--info`**: Points to `package/PackageInfo`, a minimal XML manifest specifying package metadata
- **`--root`**: The temporary root directory containing `hallelujah.app`
- **`--identifier`**: Reverse-DNS string `github.dongyuwei.inputmethod.hallelujahInputMethod` for unique system identification
- **`--version`**: Timestamp generated via `date "+%Y%m%d%H%M%S"` to ensure unique versioning
- **`--install-location`**: Hardcoded to `/Library/Input Methods`, the mandatory system directory for third-party input methods
- **`--scripts`**: References `package/scripts/` containing the `postinstall` hook

```bash
pkgbuild \
    --info "${PROJECT_ROOT}/package/PackageInfo" \
    --root "/tmp/hallelujah/build/release/root" \
    --identifier "github.dongyuwei.inputmethod.hallelujahInputMethod" \
    --version ${Version} \
    --install-location "/Library/Input Methods" \
    --scripts "${PROJECT_ROOT}/package/scripts" \
    /tmp/hallelujah-${Version}.pkg

```

The command outputs a versioned installer file at `/tmp/hallelujah-${Version}.pkg`, where `${Version}` expands to the current timestamp.

### Stage 4: Automated Registration with postinstall

The `postinstall` script (located at `package/scripts/postinstall`) executes automatically after file installation completes. This bash script performs three critical registration tasks:

1. **Terminates existing instances**: Kills any running `hallelujah` process and removes previous user-level copies from `~/Library/Input Methods`
2. **Registers the new input method**: Launches the freshly installed app with the `--install` flag, triggering macOS system registration
3. **Refreshes system caches**: Restarts `cfprefsd` to ensure the input method appears immediately in **System Preferences → Keyboard → Input Sources**

## Building and Installing Locally

To generate the installer from source, execute the packaging script from the `package` directory:

```bash
cd package
./build-package.bash

```

The script produces a timestamped package in `/tmp/` (e.g., `/tmp/hallelujah-20241015103045.pkg`). Install it using the macOS `installer` command:

```bash
sudo installer -pkg /tmp/hallelujah-20241015103045.pkg -target /

```

During installation, the bundled `postinstall` script automatically handles system registration. Verify successful installation by checking the system's enabled input sources:

```bash
defaults read /Library/Preferences/com.apple.HIToolbox.plist AppleEnabledInputSources

```

Look for the identifier `github.dongyuwei.inputmethod.hallelujahInputMethod` in the output array.

## Key Files in the Packaging Architecture

- **[`package/build-package.bash`](https://github.com/dongyuwei/hallelujahim/blob/main/package/build-package.bash)**: Orchestrates the entire workflow from compilation to package generation
- **[`build.sh`](https://github.com/dongyuwei/hallelujahim/blob/main/build.sh)**: Executes the Xcode build process for the Release configuration
- **`package/PackageInfo`**: XML manifest providing metadata required by `pkgbuild`
- **`package/scripts/postinstall`**: Post-installation script that registers the input method and refreshes system caches
- **`src/`**: Contains Objective-C++ implementation files (e.g., `InputController.mm`, `ConversionEngine.mm`) compiled into the final app bundle

## Summary

- **Compilation**: [`build-package.bash`](https://github.com/dongyuwei/hallelujahim/blob/main/build-package.bash) triggers [`build.sh`](https://github.com/dongyuwei/hallelujahim/blob/main/build.sh) to create a Release build of `hallelujah.app` via Xcode
- **Payload preparation**: The script copies the compiled app into a temporary root directory at `/tmp/hallelujah/build/release/root/`
- **Package generation**: `pkgbuild` assembles the `.pkg` using `--identifier`, `--version` (timestamp), and `--install-location` targeting `/Library/Input Methods`
- **System integration**: The `postinstall` script automatically registers the input method with macOS and refreshes preference caches without manual user steps

## Frequently Asked Questions

### What is the purpose of the postinstall script in the Hallelujah package?

The `postinstall` script ensures the input method becomes immediately available after installation. It kills any running instance of the app, removes outdated user-level copies from `~/Library/Input Methods`, launches the new version with the `--install` flag to register with macOS, and restarts the `cfprefsd` daemon to refresh the input source cache.

### Why does build-package.bash use a timestamp for the package version?

The script generates the version using `date "+%Y%m%d%H%M%S"` to create a unique, monotonically increasing version string for every build. This ensures that each `.pkg` file has a distinct identifier, preventing conflicts with previous installations and simplifying distribution tracking.

### Where does the installer place the Hallelujah input method?

According to the `--install-location` flag in [`build-package.bash`](https://github.com/dongyuwei/hallelujahim/blob/main/build-package.bash), the installer places `hallelujah.app` directly into `/Library/Input Methods`, the standard macOS system directory for third-party input methods. This system-wide location makes the input method available to all user accounts on the machine.

### Can I modify the package identifier or install location?

Yes, by editing [`package/build-package.bash`](https://github.com/dongyuwei/hallelujahim/blob/main/package/build-package.bash) before running the script. Change the `--identifier` flag to your own reverse-DNS string, or modify `--install-location` to target a different directory (though `/Library/Input Methods` is required for system-wide input method functionality). Re-run the script to generate a customized installer.