diff mbox series

[meta-darwin,1/1] Add DMG and PKG SDK installers

Message ID 20260202165146.3727-3-dev@qinc.tv
State New
Headers show
Series macOS DMG/PKG | expand

Commit Message

Eric L. Hernes Feb. 2, 2026, 4:51 p.m. UTC
This adds support for generating both .dmg and .pkg installers.

The .dmg will require running the `finalize-dmg` to relocate and sign
the executables.

The .pkg automates the process and integrates the relocation and
signage into the installer.

Signed-off-by: Eric L. Hernes <dev@qinc.tv>
---
 README                                      |  19 ++
 conf/files/darwin-toolchain-shar-extract.sh | 316 ++++++++++++++++++++
 conf/files/finalize-sdk.sh                  |  48 +++
 conf/files/mk-macos-dmg.sh                  |  49 +++
 conf/files/mk-macos-pkg.sh                  | 161 ++++++++++
 conf/files/post-install.in                  |   9 +
 conf/files/pre-install.in                   |  84 ++++++
 conf/layer.conf                             |   3 +
 conf/machine-sdk/darwin-common.inc          |  54 +++-
 9 files changed, 741 insertions(+), 2 deletions(-)
 create mode 100644 conf/files/darwin-toolchain-shar-extract.sh
 create mode 100644 conf/files/finalize-sdk.sh
 create mode 100755 conf/files/mk-macos-dmg.sh
 create mode 100755 conf/files/mk-macos-pkg.sh
 create mode 100644 conf/files/post-install.in
 create mode 100644 conf/files/pre-install.in
diff mbox series

Patch

diff --git a/README b/README
index 89845b5..97d176d 100644
--- a/README
+++ b/README
@@ -30,3 +30,22 @@  https://lists.yoctoproject.org/g/yocto-patches before being able to post.
 When sending single patches, please use something like:
 'git send-email -M -1 --to yocto-patches@lists.yoctoproject.org --subject-prefix="meta-darwin][PATCH"'
 
+The follwing variables can be set in the local.conf or elsewhere:
+
+To enable building the .pkg installer, set:
+DARWIN_INSTALLER_PKG = "1"
+
+To enable building a .dmg installer, set:
+DARWIN_INSTALLER_DMG = "1"
+
+To save some time during builds, you can disable the shell script installer with:
+DARWIN_INSTALLER_SH = "0"
+
+To specify the path in the .dmg and path that the .pkg will install to, set
+DARWIN_INSTALLER_PATH = "/Library/Developer/org.openembedded.sdk"
+
+The .dmg will require running the `finalize-dmg` to relocate and sign
+the executables.
+
+The .pkg automates the process and integrates the relocation and
+signage into the installer.
diff --git a/conf/files/darwin-toolchain-shar-extract.sh b/conf/files/darwin-toolchain-shar-extract.sh
new file mode 100644
index 0000000..4417010
--- /dev/null
+++ b/conf/files/darwin-toolchain-shar-extract.sh
@@ -0,0 +1,316 @@ 
+#!/bin/sh
+
+export LC_ALL=en_US.UTF-8
+
+# The pipefail option is now part of POSIX (POSIX.1-2024) and available in more
+# and more shells. Enable it if available to make the SDK installer more robust.
+(set -o pipefail 2> /dev/null) && set -o pipefail
+
+#Make sure at least one python is installed
+INIT_PYTHON=$(which python3 2>/dev/null )
+[ -z "$INIT_PYTHON" ] && INIT_PYTHON=$(which python2 2>/dev/null)
+[ -z "$INIT_PYTHON" ] && echo "Error: The SDK needs a python installed" && exit 1
+
+# Remove invalid PATH elements first (maybe from a previously setup toolchain now deleted
+PATH=`$INIT_PYTHON -c 'import os; print(":".join(e for e in os.environ["PATH"].split(":") if os.path.exists(e)))'`
+
+tweakpath () {
+    case ":${PATH}:" in
+        *:"$1":*)
+            ;;
+        *)
+            PATH=$PATH:$1
+    esac
+}
+
+# Some systems don't have /usr/sbin or /sbin in the cleaned environment PATH but we make need it 
+# for the system's host tooling checks
+tweakpath /usr/sbin
+tweakpath /sbin
+
+# linux calls it aarch64; Apple calls it arm64
+if [ @SDK_ARCH@ == aarch64 ]; then
+    OSX_SDK_ARCH=arm64
+else
+    OSX_SDK_ARCH=@SDK_ARCH@
+fi
+
+INST_ARCH=$(uname -m | gsed -e "s/i[3-6]86/ix86/" -e "s/x86[-_]64/x86_64/")
+SDK_ARCH=$(echo ${OSX_SDK_ARCH} | gsed -e "s/i[3-6]86/ix86/" -e "s/x86[-_]64/x86_64/")
+
+INST_GCC_VER=$(gcc --version 2>/dev/null | gsed -ne 's/.* \([0-9]\+\.[0-9]\+\)\.[0-9]\+.*/\1/p')
+SDK_GCC_VER='@SDK_GCC_VER@'
+
+verlte () {
+	[  "$1" = "`printf "$1\n$2" | sort -V | head -n1`" ]
+}
+
+verlt() {
+	[ "$1" = "$2" ] && return 1 || verlte $1 $2
+}
+
+verlt `uname -r` @OLDEST_KERNEL@
+if [ $? = 0 ]; then
+	echo "Error: The SDK needs a kernel > @OLDEST_KERNEL@"
+	exit 1
+fi
+
+if [ "$INST_ARCH" != "$SDK_ARCH" ]; then
+	# Allow for installation of ix86 SDK on x86_64 host
+	if [ "$INST_ARCH" != x86_64 -o "$SDK_ARCH" != ix86 ]; then
+		echo "Error: Incompatible SDK installer! Your host is $INST_ARCH and this SDK was built for $SDK_ARCH hosts."
+		exit 1
+	fi
+fi
+
+if ! xz -V > /dev/null 2>&1; then
+	echo "Error: xz is required for installation of this SDK, please install it first"
+	exit 1
+fi
+
+SDK_BUILD_PATH="@SDKPATH@"
+DEFAULT_INSTALL_DIR="@SDKPATHINSTALL@"
+SUDO_EXEC=""
+EXTRA_TAR_OPTIONS=""
+target_sdk_dir=""
+answer=""
+relocate=1
+savescripts=0
+verbose=0
+publish=0
+listcontents=0
+while getopts ":yd:npDRSl" OPT; do
+	case $OPT in
+	y)
+		answer="Y"
+		;;
+	d)
+		target_sdk_dir=$OPTARG
+		;;
+	n)
+		prepare_buildsystem="no"
+		;;
+	p)
+		prepare_buildsystem="no"
+		publish=1
+		;;
+	D)
+		verbose=1
+		;;
+	R)
+		relocate=0
+		savescripts=1
+		;;
+	S)
+		savescripts=1
+		;;
+	l)
+		listcontents=1
+		;;
+	*)
+		echo "Usage: $(basename "$0") [-y] [-d <dir>]"
+		echo "  -y         Automatic yes to all prompts"
+		echo "  -d <dir>   Install the SDK to <dir>"
+		echo "======== Extensible SDK only options ============"
+		echo "  -n         Do not prepare the build system"
+		echo "  -p         Publish mode (implies -n)"
+		echo "======== Advanced DEBUGGING ONLY OPTIONS ========"
+		echo "  -S         Save relocation scripts"
+		echo "  -R         Do not relocate executables"
+		echo "  -D         use set -x to see what is going on"
+		echo "  -l         list files that will be extracted"
+		exit 1
+		;;
+	esac
+done
+
+payload_offset=$(($(grep -na -m1 "^MARKER:$" "$0"|cut -d':' -f1) + 1))
+if [ "$listcontents" = "1" ] ; then
+    if [ @SDK_ARCHIVE_TYPE@ = "zip" ]; then
+        tail -n +$payload_offset "$0" > sdk.zip
+        if unzip -l sdk.zip;then
+            rm sdk.zip
+        else
+            rm sdk.zip && exit 1
+        fi
+    else
+        tail -n +$payload_offset "$0"| gtar tvJ || exit 1
+    fi
+    exit
+fi
+
+titlestr="@SDK_TITLE@ installer version @SDK_VERSION@"
+printf "%s\n" "$titlestr"
+printf "%${#titlestr}s\n" | tr " " "="
+
+if [ $verbose = 1 ] ; then
+	set -x
+fi
+
+@SDK_PRE_INSTALL_COMMAND@
+
+# SDK_EXTENSIBLE is exposed from the SDK_PRE_INSTALL_COMMAND above
+if [ "$SDK_EXTENSIBLE" = "1" ]; then
+	DEFAULT_INSTALL_DIR="@SDKEXTPATH@"
+	if [ "$INST_GCC_VER" = '4.8' -a "$SDK_GCC_VER" = '4.9' ] || [ "$INST_GCC_VER" = '4.8' -a "$SDK_GCC_VER" = '' ] || \
+		[ "$INST_GCC_VER" = '4.9' -a "$SDK_GCC_VER" = '' ]; then
+		echo "Error: Incompatible SDK installer! Your host gcc version is $INST_GCC_VER and this SDK was built by gcc higher version."
+		exit 1
+	fi
+fi
+
+if [ "$target_sdk_dir" = "" ]; then
+	if [ "$answer" = "Y" ]; then
+		target_sdk_dir="$DEFAULT_INSTALL_DIR"
+	else
+		read -p "Enter target directory for SDK (default: $DEFAULT_INSTALL_DIR): " target_sdk_dir
+		[ "$target_sdk_dir" = "" ] && target_sdk_dir=$DEFAULT_INSTALL_DIR
+	fi
+fi
+
+eval target_sdk_dir=$(echo "$target_sdk_dir"|gsed 's/ /\\ /g')
+if [ -d "$target_sdk_dir" ]; then
+	target_sdk_dir=$(cd "$target_sdk_dir"; pwd)
+else
+	target_sdk_dir=$(greadlink -m "$target_sdk_dir")
+fi
+
+# limit the length for target_sdk_dir, ensure the relocation behaviour in relocate_sdk.py has right result.
+# This is due to ELF interpreter being set to 'a'*1024 in
+# meta/recipes-core/meta/uninative-tarball.bb
+if [ ${#target_sdk_dir} -gt 1024 ]; then
+	echo "Error: The target directory path is too long!!!"
+	exit 1
+fi
+
+if [ "$SDK_EXTENSIBLE" = "1" ]; then
+	# We're going to be running the build system, additional restrictions apply
+	if echo "$target_sdk_dir" | grep -q '[+\ @$]'; then
+		echo "The target directory path ($target_sdk_dir) contains illegal" \
+		     "characters such as spaces, @, \$ or +. Abort!"
+		exit 1
+	fi
+	# The build system doesn't work well with /tmp on NFS
+	fs_dev_path="$target_sdk_dir"
+	while [ ! -d "$fs_dev_path" ] ; do
+		fs_dev_path=`dirname $fs_dev_path`
+        done
+	fs_dev_type=`stat -f -c '%t' "$fs_dev_path"`
+	if [ "$fsdevtype" = "6969" ] ; then
+		echo "The target directory path $target_sdk_dir is on NFS, this is not possible. Abort!"
+		exit 1
+	fi
+else
+	if [ -n "$(echo $target_sdk_dir|grep ' ')" ]; then
+		echo "The target directory path ($target_sdk_dir) contains spaces. Abort!"
+		exit 1
+	fi
+fi
+
+if [ -e "$target_sdk_dir/environment-setup-@REAL_MULTIMACH_TARGET_SYS@" ]; then
+	echo "The directory \"$target_sdk_dir\" already contains a SDK for this architecture."
+	printf "If you continue, existing files will be overwritten! Proceed [y/N]? "
+
+	default_answer="n"
+else
+	printf "You are about to install the SDK to \"$target_sdk_dir\". Proceed [Y/n]? "
+
+	default_answer="y"
+fi
+
+if [ "$answer" = "" ]; then
+	read answer
+	[ "$answer" = "" ] && answer="$default_answer"
+else
+	echo $answer
+fi
+
+if [ "$answer" != "Y" -a "$answer" != "y" ]; then
+	echo "Installation aborted!"
+	exit 1
+fi
+
+# Try to create the directory (this will not succeed if user doesn't have rights)
+mkdir -p $target_sdk_dir >/dev/null 2>&1
+
+# if don't have the right to access dir, gain by sudo 
+if [ ! -x $target_sdk_dir -o ! -w $target_sdk_dir -o ! -r $target_sdk_dir ]; then 
+	if [ "$SDK_EXTENSIBLE" = "1" ]; then
+		echo "Unable to access \"$target_sdk_dir\", will not attempt to use" \
+		     "sudo as as extensible SDK cannot be used as root."
+		exit 1
+	fi
+
+	SUDO_EXEC=$(which "sudo")
+	if [ -z $SUDO_EXEC ]; then
+		echo "No command 'sudo' found, please install sudo first. Abort!"
+		exit 1
+	fi
+
+	# test sudo could gain root right
+	$SUDO_EXEC pwd >/dev/null 2>&1
+	[ $? -ne 0 ] && echo "Sorry, you are not allowed to execute as root." && exit 1
+
+	# now that we have sudo rights, create the directory
+	$SUDO_EXEC mkdir -p $target_sdk_dir >/dev/null 2>&1
+fi
+
+printf "Extracting SDK..."
+if [ @SDK_ARCHIVE_TYPE@ = "zip" ]; then
+    tail -n +$payload_offset "$0" > sdk.zip
+    if $SUDO_EXEC unzip $EXTRA_TAR_OPTIONS sdk.zip -d $target_sdk_dir;then
+        rm sdk.zip
+    else
+        rm sdk.zip && exit 1
+    fi
+else
+    tail -n +$payload_offset "$0"| $SUDO_EXEC gtar mxJ -C $target_sdk_dir --checkpoint=.2500 $EXTRA_TAR_OPTIONS || exit 1
+fi
+echo "done"
+
+printf "Setting it up..."
+# fix environment paths
+real_env_setup_script=""
+for env_setup_script in `ls $target_sdk_dir/environment-setup-*`; do
+	if grep -q 'OECORE_NATIVE_SYSROOT=' $env_setup_script; then
+		# Handle custom env setup scripts that are only named
+		# environment-setup-* so that they have relocation
+		# applied - what we want beyond here is the main one
+		# rather than the one that simply sorts last
+		real_env_setup_script="$env_setup_script"
+	fi
+	$SUDO_EXEC gsed -e "s:@SDKPATH@:$target_sdk_dir:g" -i $env_setup_script
+done
+if [ -n "$real_env_setup_script" ] ; then
+	env_setup_script="$real_env_setup_script"
+fi
+
+@SDK_POST_INSTALL_COMMAND@
+
+# delete the relocating script, so that user is forced to re-run the installer
+# if he/she wants another location for the sdk
+if [ $savescripts = 0 ] ; then
+	$SUDO_EXEC rm -f ${env_setup_script%/*}/relocate_sdk.py ${env_setup_script%/*}/relocate_sdk.sh
+fi
+
+# Execute post-relocation script
+post_relocate="$target_sdk_dir/post-relocate-setup.sh"
+if [ -e "$post_relocate" ]; then
+	$SUDO_EXEC gsed -e "s:@SDKPATH@:$target_sdk_dir:g" -i $post_relocate
+	$SUDO_EXEC /bin/sh $post_relocate "$target_sdk_dir" "@SDKPATH@"
+	if [ $? -ne 0 ]; then
+		echo "Executing $post_relocate failed"
+		exit 1
+	fi
+	$SUDO_EXEC rm -f $post_relocate
+fi
+
+echo "SDK has been successfully set up and is ready to be used."
+echo "Each time you wish to use the SDK in a new shell session, you need to source the environment setup script e.g."
+for env_setup_script in `ls $target_sdk_dir/environment-setup-*`; do
+	echo " \$ . $env_setup_script"
+done
+
+exit 0
+
+MARKER:
diff --git a/conf/files/finalize-sdk.sh b/conf/files/finalize-sdk.sh
new file mode 100644
index 0000000..bd4f313
--- /dev/null
+++ b/conf/files/finalize-sdk.sh
@@ -0,0 +1,48 @@ 
+#!/bin/sh
+##################################################
+## file: finalize-sdk.sh
+##
+##
+## /bin/sh script to finalize oesdk files
+##
+## 1. change pathing from hardcoded path to install path
+## 2. codesign binaries and dylibs
+##
+## This can be run on the final installed files; or during install in a staging area if we know
+## what the final destination path will be.
+##
+
+x_path=$(realpath $(dirname "${0}"))
+first_csid=$(security -qq find-identity -v -p codesigning |cut -c47- |head -1 | tr -d '"')
+
+staging=${1:-"${x_path}"}
+new_path=${2:-"${x_path}"}
+codesign_id=${3:-"${first_csid}"}
+
+native_sysroot="@native_sysroot@"
+
+do_relocate=yes
+do_codesign=yes
+
+if [ "${do_relocate}" = yes ]; then
+    old_path="/usr/local/oe-sdk-hardcoded-buildpath"
+
+    find "${staging}" -type f -exec file {} +| grep -i ":.*\(ASCII\|script\|source\).*text" |cut -d: -f1  |
+        while read fn; do
+            sed -i "" "s,${old_path},${new_path},g" "${fn}" || echo "${fn}"
+        done
+fi
+
+if [ "${do_codesign}" = yes ]; then
+    if [ -z "${codesign_id}" ]; then
+        echo "requested codesigning, but did not find a codesigning identity"
+    else
+        find "${staging}/sysroots/${native_sysroot}" -type f -exec file {} + |
+            grep Mach-O | 
+            cut -d: -f1 |
+            sed -e "s,^,'," -e "s,$,'," |
+            xargs codesign --continue --force --timestamp --sign "${codesign_id}"
+    fi
+fi
+
+exit 0
diff --git a/conf/files/mk-macos-dmg.sh b/conf/files/mk-macos-dmg.sh
new file mode 100755
index 0000000..ebf95d0
--- /dev/null
+++ b/conf/files/mk-macos-dmg.sh
@@ -0,0 +1,49 @@ 
+#!/bin/bash
+##################################################
+## file: meta-darwin/conf/files/mk-macos-dmg.sh
+##
+## Original Author: Eric L. Hernes <eric@qinc.tv>
+##
+## /bin/sh script to do create macos .dmg image for openembedded sdk
+##
+
+# $1 = SDK Title
+# $2 = SDK Name
+# $3 = SDK Architecture
+# $4 = SDK Version
+# $5 = path to SDK path files
+# $6 = SDK dmg output filename
+# $7 = path to finalize script
+# $8 = destination path within the DMG
+
+pkg_title=${1:-"OpenEmbedded SDK"}
+pkg_name=${2:-"oesdk-darwin21"}
+pkg_arch=${3:-"aarch64"}
+pkg_vers=${4:-"x.x.x.y"}
+sdk_path=${5:-".../tmp/work/armv8a-oe-linux/meta-toolchain/1.0/sdk/image"}
+dmg_path=${6:-".../tmp/deploy/sdk/macos-test-sdk.dmg"}
+finalize_dmg=${7:-"finalize-dmg"}
+dst_path=${8-"/Library/Developer/org.openembedded.sdk/${pkg_name}"}
+
+dmg_finalize="${dst_path}/"$(basename "${finalize_dmg}")
+
+set $(du -sm ${sdk_path})
+
+# add 10% for overhead
+size_mb=$((${1} * 11 / 10))
+
+dd if=/dev/zero of="${dmg_path}" bs=1M count=${size_mb}
+mkfs.hfsplus -s -v "${pkg_name}" "${dmg_path}"
+
+hfsplus "${dmg_path}" mkdir-p "${dst_path}"
+
+hfsplus "${dmg_path}" addall "${sdk_path}" "${dst_path}"
+
+hfsplus "${dmg_path}" add "${finalize_dmg}" "${dmg_finalize}"
+hfsplus "${dmg_path}" chmod 755 "${dmg_finalize}"
+
+hfsplus "${dmg_path}" rm "${dst_path}/relocate_sdk.py"
+
+hfsplus "${dmg_path}" rm "${dst_path}/post-relocate-setup.sh"
+
+
diff --git a/conf/files/mk-macos-pkg.sh b/conf/files/mk-macos-pkg.sh
new file mode 100755
index 0000000..283a0e3
--- /dev/null
+++ b/conf/files/mk-macos-pkg.sh
@@ -0,0 +1,161 @@ 
+#!/bin/bash
+##################################################
+## file: meta-darwin/conf/files/mk-macos-pkg.sh
+##
+## Original Author: Eric L. Hernes <eric@qinc.tv>
+##
+## /bin/sh script to do create macos .pkg installer for openembedded sdk
+##
+
+# $1 = SDK Title
+# $2 = SDK Name
+# $3 = SDK Architecture
+# $4 = SDK Version
+# $5 = path to SDK path files
+# $6 = SDK dmg output filename
+# $7 = path to finalize script
+# $8 = destination path relative to the user's selected volume
+
+pkg_title=${1:-"OpenEmbedded SDK"}
+pkg_name=${2:-"oesdk-darwin21"}
+pkg_arch=${3:-"aarch64"}
+pkg_vers=${4:-"x.x.x.y"}
+sdk_path=${5:-".../tmp/work/armv8a-oe-linux/meta-toolchain/1.0/sdk/image"}
+pkg_path=${6:-".../tmp/deploy/sdk/macos-test-sdk.pkg"}
+finalize_pkg=${7:-"finalize-pkg"}
+dst_path=${8-"/Library/Developer/org.openembedded.sdk/${pkg_name}/x"}
+
+echo "pkg_title: ${pkg_title}"
+echo "pkg_name: ${pkg_name}"
+echo "pkg_arch: ${pkg_arch}"
+echo "pkg_vers: ${pkg_vers}"
+echo "sdk_path: ${sdk_path}"
+echo "pkg_path: ${pkg_path}"
+echo "dst_path: ${dst_path}"
+
+count=$(find ${sdk_path} -type f | wc -l)
+set $(du -sk ${sdk_path})
+kbytes=$1
+
+echo "SDK will contain ${count} files and consume ${kbytes}K bytes"
+
+org_id=org.openembedded.sdk
+
+pkg_id="${org_id}.${pkg_name}.pkg"
+
+flat=dist/Package.pkg
+
+build=build
+mkbom=mkbom
+xar=xar
+
+files=$(realpath $(dirname $0))
+
+rm -rf ${build}
+rm -f "${pkg_path}"
+
+mkdir ${build}
+cd ${build}
+
+mkdir -p ${flat}
+mkdir scripts
+
+( cd ${sdk_path} && find . | cpio -o --format odc --owner 0:80 | gzip -c ) > ${flat}/Payload
+
+#
+# Swift installs stuff to /Library/org.swift.swiftpm;
+#
+# So let's do something similar
+# Install files to /Library/Developer/org.openembedded.sdk
+#
+cat > ${flat}/PackageInfo <<EOF
+<pkg-info format-version="2" identifier="${pkg_id}" version="${pkg_version}" relocatable="false" overwrite-permissions="false" followSymLinks="false" install-location="${dst_path}" auth="root">
+  <payload installKBytes="${kbytes}" numberOfFiles="${count}"/>
+    <bundle-version/>
+    <upgrade-bundle/>
+    <update-bundle/>
+    <atomic-update-bundle/>
+    <strict-identifier/>
+    <relocate/>
+    <scripts>
+      <preinstall file="./pre-install"/>
+      <postinstall file="./post-install"/>
+  </scripts>
+</pkg-info>
+EOF
+
+sed -e "s#@dst_path@#${dst_path}#g" \
+    ${files}/pre-install.in >scripts/pre-install
+
+sed -e 's,$,&,' ${files}/post-install.in >scripts/post-install
+cp ${finalize_pkg} scripts/finalize-pkg
+
+chmod +x scripts/finalize-pkg
+chmod +x scripts/pre-install
+chmod +x scripts/post-install
+
+( cd scripts && find . | cpio -o --format odc --owner 0:80 | gzip -c ) > ${flat}/Scripts
+${mkbom} -u 0 -g 80 ${sdk_path} ${flat}/Bom
+
+#
+# linux calls it aarch64; apple calls it arm64
+#
+
+declare -A arch_map
+arch_map["aarch64"]="arm64"
+arch_map["x86_64"]="x86_64"
+
+p_id=$(tr -dc 'a-z0-9' < /dev/urandom | head -c 16)
+pki_id="org.openembedded.oesdk.installer.${p_id}"
+
+
+cat >dist/Distribution <<EOF
+<?xml version="1.0" encoding="utf-8"?>
+<installer-gui-script minSpecVersion="2">
+    <title>${pkg_title}</title>
+    <options customize="allow" hostArchitectures="${arch_map[${pkg_arch}]}"/>
+    <domains enable_localSystem="true" enable_anywhere="true" enable_currentUserHome="true"/>
+    <installation-check script="canInstall()"/>
+    <script><![CDATA[
+	function canInstall() {
+		var version;
+		var major;
+
+		// ProductVersion is usually "major.minor.patch"
+		if (!system || !system.version || !system.version.ProductVersion) {
+			return false;
+		}
+
+		version = system.version.ProductVersion.split(".");
+		major = parseInt(version[0], 10);
+
+		if (isNaN(major)) {
+			return false;
+		}
+
+		// Darwin21 ==  macOS 12+
+		if (major < 12) {
+			return false;
+		}
+
+		return true;
+	}
+
+]]></script>
+    <pkg-ref id="${p_id}" version="1" onConclusion="none">#Package.pkg</pkg-ref>
+    <pkg-ref id="${p_id}" packageIdentifier="${pki_id}" installKBytes="0" updateKBytes="0">
+        <must-close/>
+        <bundle-version/>
+    </pkg-ref>
+    <choices-outline>
+        <line choice="${p_id}"/>
+    </choices-outline>
+    <choice id="${p_id}" title="Package" start_selected="true">
+        <pkg-ref id="${p_id}"/>
+    </choice>
+    <product id="${pkg_id}" version="${pkg_vers}"/>
+</installer-gui-script>
+EOF
+
+( cd dist && ${xar} --compression none -cf "${pkg_path}" * )
+
diff --git a/conf/files/post-install.in b/conf/files/post-install.in
new file mode 100644
index 0000000..9321f79
--- /dev/null
+++ b/conf/files/post-install.in
@@ -0,0 +1,9 @@ 
+#!/bin/bash
+#########################################
+#  file: meta-darwin/conf/files/post-install.in
+#
+#  @file    post-install
+#  @version V1.0
+#
+# post install script for meta-darwin SDK
+#
diff --git a/conf/files/pre-install.in b/conf/files/pre-install.in
new file mode 100644
index 0000000..0c488c4
--- /dev/null
+++ b/conf/files/pre-install.in
@@ -0,0 +1,84 @@ 
+#!/usr/bin/osascript -l JavaScript
+/***************************************************
+ * file: meta-darwin/conf/files/pre-install.in
+ *
+ * @file    pre-install.jxa
+ * @version V1.0
+ *
+ * pre-install script for meta-darwin macOS SDK
+ *
+ * this script gets user options and sets up various things
+ * so that the files are installed properly in their final
+ * destination
+ *
+ */
+
+ObjC.import("stdlib");
+
+var seApp = Application("System Events");
+var oProcess = seApp.processes.whose({frontmost: true})[0];
+var appName = oProcess.displayedName();
+
+app = Application.currentApplication();
+
+app.includeStandardAdditions = true;
+
+retval = []
+
+function run(args) {
+
+  codesign_id=""
+  if (true) {
+    idstr = app.doShellScript("security -qq find-identity -v -p codesigning |cut -c47- |tr '\n' ';'")
+    idList = idstr.split(';').filter(element => element);
+    switch (idList.length) {
+      case 0: // can't sign
+        let dialogText = "Could not find any code signing identities, binaries will not be signed"
+        app.displayDialog(dialogText)
+      break;
+
+      case 1: // no decisions
+        codesign_id = idList[0]
+      break;
+
+      default:
+        codesign_id = app.chooseFromList(idList, {
+          withTitle: "Signing ID",
+          withPrompt: "Please select an ID to sign binaries",
+          defaultItems: [idList[0]],
+          okButtonName: "OK",
+          cancelButtonName: "Cancel",
+          multipleSelectionsAllowed: false,
+          emptySelectionAllowed: false
+        })
+      if (codesign_id == false) {
+        let dialogText = "Cancelled code signing"
+        app.displayDialog(dialogText)
+        return false
+      }
+    }
+  }
+
+  // finalize script
+  // finalize {staging} {new_path} {code_sign}
+  pwd = ObjC.unwrap($.NSFileManager.defaultManager.currentDirectoryPath)
+  finalize = pwd + "/finalize-pkg";
+  staging = $.getenv("INSTALLER_PAYLOAD_DIR") + "@dst_path@"
+  new_path = args[1]
+  command = `${finalize} "${staging}" "${new_path}" ${codesign_id}`
+
+  var dialogText = "Will run a script to relocate and codesign\n\n"
+  dialogText += "\n\n"
+  dialogText += "This may take a few minutes depending on the size of the SDK"
+
+  app.displayDialog(dialogText)
+
+  finalize_data = app.doShellScript(command + " >/dev/null 2>&1")
+
+  app.displayDialog(finalize_data)
+
+  retval.push("ok")
+  return retval.join("\n")
+}
+
+/* end of pre-install.in */
diff --git a/conf/layer.conf b/conf/layer.conf
index 2f14754..6730f3b 100644
--- a/conf/layer.conf
+++ b/conf/layer.conf
@@ -9,3 +9,6 @@  BBFILES += "${LAYERDIR}/recipes*/*/*.bb ${LAYERDIR}/recipes*/*/*.bbappend"
 BBFILE_COLLECTIONS += "meta-darwin"
 BBFILE_PATTERN_meta-darwin := "^${LAYERDIR}/"
 BBFILE_PRIORITY_meta-darwin = "8"
+
+DARWINBASE = '${@os.path.normpath("${LAYERDIR}")}'
+BB_BASEHASH_IGNORE_VARS:append = " DARWINBASE"
diff --git a/conf/machine-sdk/darwin-common.inc b/conf/machine-sdk/darwin-common.inc
index cec76a5..2f1aec9 100644
--- a/conf/machine-sdk/darwin-common.inc
+++ b/conf/machine-sdk/darwin-common.inc
@@ -14,8 +14,13 @@  SDKUSE_NLS = "no"
 SDKIMAGE_LINGUAS = ""
 SDK_DEPENDS:remove = "nativesdk-glibc-locale nativesdk-qemuwrapper-cross"
 
+SDK_DEPENDS:append = "${@bb.utils.contains('DARWIN_INSTALLER_PKG', '1', ' bomutils-native xar-native', '', d)}"
+SDK_DEPENDS:append = "${@bb.utils.contains('DARWIN_INSTALLER_DMG', '1', ' hfsprogs-native libdmg-hfsplus-native', '', d)}"
+
 SDKPKGSUFFIX = "nativesdk-darwin"
 
+DARWIN_INSTALL_LOC = "${@d.getVar('DARWIN_INSTALLER_PATH') or '/Library/Developer/org.openembedded.sdk'}"
+
 OSX_TOOLCHAIN_OPTIONS = " \
     -mmacosx-version-min=12.3 \
     -L${STAGING_DIR_TARGET}${SDKPATHNATIVE}/usr/lib \
@@ -24,9 +29,12 @@  OSX_TOOLCHAIN_OPTIONS = " \
 "
 
 TOOLCHAIN_OPTIONS:append:darwin21 = " \
-    ${OSX_TOOLCHAIN_OPTIONS}"
+    ${OSX_TOOLCHAIN_OPTIONS} \
+"
+
 TOOLCHAIN_OPTIONS:append:class-cross-canadian = " \
-    ${OSX_TOOLCHAIN_OPTIONS}"
+    ${OSX_TOOLCHAIN_OPTIONS} \
+"
 
 # Remove -rpath-link
 BUILDSDK_LDFLAGS = " \
@@ -35,3 +43,45 @@  BUILDSDK_LDFLAGS = " \
 "
 
 MACHINEOVERRIDES .= ":darwinsdk"
+
+TOOLCHAIN_SHAR_EXT_TMPL ?= "${DARWINBASE}/conf/files/darwin-toolchain-shar-extract.sh"
+
+SDK_POSTPROCESS_COMMAND:append = "${@bb.utils.contains('DARWIN_INSTALLER_PKG', '1', ' create_macos_pkg', '', d)}"
+SDK_POSTPROCESS_COMMAND:append = "${@bb.utils.contains('DARWIN_INSTALLER_DMG', '1', ' create_macos_dmg', '', d)}"
+
+#
+# remove the .xz and .sh creation if we want to save a bit of time
+SDK_POSTPROCESS_COMMAND:remove = "${@bb.utils.contains('DARWIN_INSTALLER_SH', '0', ' archive_sdk', '', d)}"
+SDK_POSTPROCESS_COMMAND:remove = "${@bb.utils.contains('DARWIN_INSTALLER_SH', '0', ' create_shar', '', d)}"
+
+fakeroot create_macos_pkg() {
+    sdk_pkg="${SDKDEPLOYDIR}/${TOOLCHAIN_OUTPUTNAME}.pkg"
+    native_sysroot=$(basename ${SDKPATHNATIVE})
+
+    sed -e "s#@target_prefix@#${TARGET_PREFIX}#g" \
+        -e "s#@native_sysroot@#${native_sysroot}#g" \
+         ${DARWINBASE}/conf/files/finalize-sdk.sh \
+         > "${B}/finalize-pkg"
+
+    (cd ${B}; \
+        ${DARWINBASE}/conf/files/mk-macos-pkg.sh "${SDK_TITLE}" "${SDK_NAME}" "${SDK_ARCH}" "${DISTRO_VERSION}.${SDK_VERSION}" "${SDK_OUTPUT}/${SDKPATH}" "${sdk_pkg}" "${B}/finalize-pkg" ${DARWIN_INSTALL_LOC}/${SDK_NAME} \
+    )
+
+   ls -lh "${sdk_pkg}"
+}
+
+fakeroot create_macos_dmg() {
+    sdk_dmg="${SDKDEPLOYDIR}/${TOOLCHAIN_OUTPUTNAME}.dmg"
+    native_sysroot=$(basename ${SDKPATHNATIVE})
+    sed -e "s#@target_prefix@#${TARGET_PREFIX}#g" \
+        -e "s#@native_sysroot@#${native_sysroot}#g" \
+         ${DARWINBASE}/conf/files/finalize-sdk.sh \
+         > "${B}/finalize-dmg"
+
+    echo 'rm ${0}' >> "${B}/finalize-dmg"
+
+    ( cd ${B}; \
+        ${DARWINBASE}/conf/files/mk-macos-dmg.sh "${SDK_TITLE}" "${SDK_NAME}" "${SDK_ARCH}" "${DISTRO_VERSION}.${SDK_VERSION}" "${SDK_OUTPUT}/${SDKPATH}" "${sdk_dmg}" "${B}/finalize-dmg" "${DARWIN_INSTALL_LOC}/${SDK_NAME}" \
+    )
+   ls -lh "${sdk_dmg}"
+}