How the Hallelujah Input Method Is Packaged as a .pkg Installer Using build-package.bash
The 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 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 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 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/.
pushd ${PROJECT_ROOT}
sh build.sh
popd
According to the source code in 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.
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:
--info: Points topackage/PackageInfo, a minimal XML manifest specifying package metadata--root: The temporary root directory containinghallelujah.app--identifier: Reverse-DNS stringgithub.dongyuwei.inputmethod.hallelujahInputMethodfor unique system identification--version: Timestamp generated viadate "+%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: Referencespackage/scripts/containing thepostinstallhook
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:
- Terminates existing instances: Kills any running
hallelujahprocess and removes previous user-level copies from~/Library/Input Methods - Registers the new input method: Launches the freshly installed app with the
--installflag, triggering macOS system registration - Refreshes system caches: Restarts
cfprefsdto 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:
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:
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:
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: Orchestrates the entire workflow from compilation to package generationbuild.sh: Executes the Xcode build process for the Release configurationpackage/PackageInfo: XML manifest providing metadata required bypkgbuildpackage/scripts/postinstall: Post-installation script that registers the input method and refreshes system cachessrc/: Contains Objective-C++ implementation files (e.g.,InputController.mm,ConversionEngine.mm) compiled into the final app bundle
Summary
- Compilation:
build-package.bashtriggersbuild.shto create a Release build ofhallelujah.appvia Xcode - Payload preparation: The script copies the compiled app into a temporary root directory at
/tmp/hallelujah/build/release/root/ - Package generation:
pkgbuildassembles the.pkgusing--identifier,--version(timestamp), and--install-locationtargeting/Library/Input Methods - System integration: The
postinstallscript 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, 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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →