Monday, February 5, 2024

[SOLVED] Install 3rd party macOS installer package

Issue

I am using CPack module in CMake to generate the product installer on macOS. I am trying to install a 3rd party macOS installer package as a prerequisite to our product installer. I tried -

add_executable(
    XYZ
    IMPORTED
)

set_property(
    TARGET XYZ PROPERTY
    IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/import/redist/XYZ.pkg 
)

install(
    IMPORTED_RUNTIME_ARTIFACTS
        XYZ
    COMPONENT
        XYZ
) 

However, this doesn't install the XYZ.pkg though it is part of my final product installer pkg file. Is this the correct way to embed and install 3rd party installer package from my product package installer ?

Edit: Instead of executing the XYZ.pkg installer to install it's components, my product installer is copying it to my product's destination folder. I was expecting to chain the install instead.


Solution

Looking at CPack IMPORTED_RUNTIME_ARTIFACTS , I see it is designed for copying files, not executing them. That means the approach you have taken will not execute the XYZ.pkg package installer, but will package it into your product's .pkg installer.

If you wish to chain installers, you would typically use a post-install script that is executed once your package is installed. That script would then run the XYZ.pkg installer.

You can specify a post-install script (for instance, postflight.sh + chmod 755) using the CPACK_POSTFLIGHT_SCRIPT variable in CMake, as seen in this thread. Below is a simplified example.

#!/bin/bash
installer -pkg $PWD/XYZ.pkg -target /

In your CMakeLists.txt, set the CPACK_POSTFLIGHT_SCRIPT variable to point to your script.

...
include(CPack)

# specify your postflight script
set(CPACK_POSTFLIGHT_SCRIPT "/path/to/postflight.sh")
...

Do copy the XYZ.pkg into a location that the postflight.sh script can reference, or modify the postflight.sh script to locate XYZ.pkg appropriately.

Once your package is installed, the postflight.sh script should be executed, and it will run the XYZ.pkg installer. That should achieve the chained installation behavior you are looking for.


I had a slightly different solution where post install script of my product installs the dependency using the exact command you mentioned. Unfortunately, our team was looking for a more standard solution.

We ended up doing:

  1. Modify our package distribution.xml to include XYZ.pkg info.
  2. CPack builds our package (i.e., PRODUCT.pkg).
  3. From post build script (CPACK_POST_BUILD_SCRIPTS), expand the PRODUCT.pkg using pkgutil. 4. Using the productbuild, include the expanded PRODUCT.pkg, XYZ.pkg.

Indeed, manipulating the package directly provides a more "native" approach for handling macOS installer packages.

By altering the distribution.xml and rebuilding the package using productbuild, you are essentially creating a "meta-package" that encompasses both your own product and the third-party dependency (XYZ.pkg). That ensures a more streamlined installation process and is generally more robust than relying on post-install scripts to install additional packages.

The use of pkgutil to expand the PRODUCT.pkg and productbuild to reassemble it, along with the third-party package, is a particularly effective way to achieve this. That approach has the advantage of integrating tightly with the macOS installer system, making it more predictable and potentially less prone to errors compared to a script-based approach.



Answered By - VonC
Answer Checked By - Willingham (WPSolving Volunteer)