Saturday, 7 July 2012

Scripting in Installer Packages


In my previous post, I discussed about creating packages to deploy your application bundles. However, installation of some applications is more complex and requires many actions other than copying the payload. For example, you want to check if a particular application is already installed on the system before installing your application.  For all these tasks, you can use the preinstall, postinstall and Distribution scripts which are included in the package and run by the installer during the installation. After the Installer application finishes checking installation requirements, it performs an install through distinct operations, known as install operations. You can define all but one of these operations, which copy payloads to their installation destinations. You should not use install operations to fix install problems, such as incorrect ownership and access permissions. You should use install operations only when other managed-install features, such as installation requirements, are not adequate for the chore you need to perform as part of installing a package.
Install operations allow you to configure the destination environment before the payload is copied to the file system and to perform additional processing afterward. Install operation executables must be named according to the install operation you want to define. The files can be binary files or text files containing shell scripts. All install operations are optional.

You can use following arguments and environment variables which are available to install operation executables.
  • $1: Full path to the installation package the Installer application is processing. For example: /Volumes/Users/Vikrams/Desktop/TestPkg.pkg
  • $2: Full path to the installation destination. For example: /Applications
  • $3: Installation volume (or mount point) to receive the payload. For example: /Volumes/Tools
  • $4: The root directory for the system: /
  • $SCRIPT_NAME: Filename of the operation executable. For example: preflight
  • $PACKAGE_PATH: Full path to the installation package. Same as $1.
  • $INSTALLER_TEMP: Scratch directory used by Installer to place its temporary work files. Install operations may use this area for their temporary work, too, but must not overwrite any Installer files. The Installer application erases this directory at the end of the install. For example: /private/tmp/.vikrams.pkg.234.install

     Your script must return 0 on success and any other number to denote failure. The name of the scripts must be “preinstall” and “postinstall”. You can see some sample scripts below-
--------------------------------------------------------------
#!/bin/sh
#This is a sample preinstall or postinstall script.
#Your logic goes here.
if [ your_logic_returns_false ]
then
     exit -1
fi
exit 0
--------------------------------------------------------------

Lines starting with # are comments. First line #!/bin/sh is called “Shebang” and denotes that it is a shell script. Any return value other than 0 denotes failure. If you return anything other than 0, your installation will stop with a failure message.


Scripting Quickies 

Let’s discuss about some common and some complex scenarios where a lot of people can get stuck:

  • I have some resources which need to be accessible from the preinstall and postinstall scripts.
    •  In older style packages (bundles), since the path inside the package was accessible, we kept the resources in the resources directory and had access to them using PACKAGE_PATH/Contents/Resources/ path. In case of flat packages, the installer extracts the scripts directory to some temporary directory while running. So, we can keep any resource with the scripts while packaging and when the installer is run, these resources will be present in the same directory from where the scripts are run. In this way, we can access any resource from preinstall and postinstall scripts.
  • How can I perform installation requirements check?
    • Bundle packages supported installation check using InstallationCheck script. However, flat packages no longer support InstallationCheck script. In flat packages, this installation check phase has moved to the Distribution file. You can write Javascript functions and assign them to be called during installation-check phase. Your distribution file should contain<installation-check script=”your_function()” /> and if your function returns false, the installation will not proceed.
  • Can I perform installation requirements check for component packages?
    • No, you must use a Distribution package if you intend to perform InstallationCheck or VolumeCheck.



Apart from preinstall and postinstall scripts, a lot of functionality goes in the Distribution script. I will cover all the aspects of a typical Distribution file in the next post.


Thursday, 7 June 2012

Creating Packages for Mac


So, our great application is now ready for deployment after all the hard work that has gone in development and testing. But how are we going to distribute our applications to all the users in the world? Well, you need not worry, you can package your application and these packages can be deployed on any Mac systems.
Creating packages is an easy and standard way to distribute your applications for Mac. Along with ease, you get a lot of options through which you can carry out a full-fledged installation including requirements check, pre-install and post-install actions. So, even if you need a complex environment for your application to run like changing permission or whatever, packages can still do it for you.
Let’s move on to how to create packages. Again, you have got many options: Do it using GUI mode of PackageMaker or use its command-line mode or use the productbuild command to create the packages. It’s all up to you what you end up using. However, all of the above mentioned methods have their pros and cons. We will go through them one by one:

PackageMaker GUI mode:

Well, it’s a no-brainer. If you follow the on-screen messages, you will know what options to use for what purpose and you will end up with a decent package ready for distribution. In case you need more detailed steps:

  • Launch the PackageMaker application.

  • It will ask you which version of OS you are targeting and an identifier for your package. Select the minimum version which you have to support. If you select 10.4, bundle packages will be created since 10.4 does not support flat packages.
  • You can use the options provided in the 3 tabs:
    • Configuration: Allows you to set general properties of components like title, identifier, whether you require restart after installation, etc.
    • Requirements: Allows you to set the requirements which must be met before your package can be installed on the system. See the screenshot below.
    • Actions: Allows you to add your own scripts- preinstall and postinstall which will be run before and after your payload is dumped in the system.
  • Drag and drop the components to be installed to the left-side pane. You can add different sub-components as choices. Choices provide a lot of flexibility to your package. For example, if you have some optional sub-packages, the user will be able to choose whether to install that package or not.
     
  • You can select properties of individual components as well. If you want to allow relocation of your .app files or to allow downgrade of packages, select the corresponding checkbox.
    • Relocation allows you to replace the .app file in-place if the application is already installed on your system.
    • Downgrade allows you to install older versions on top of newer versions.
  • And then you have got options to modify the background image of the installer and add the required resource files like License, Read Me, etc.
  • You can add localization and provide localized resources as well if required.

    And that’s it. Build the package and you have your Mac Installer ready for distribution.
.

PackageMaker command-line mode:

You can also achieve most of the functionality provided by GUI mode from the command-line mode of PackageMaker. If you have to create packages very frequently or for large number of products, then it’s a good idea to automate that process. You can use the command-line mode of packagemaker for all you automated package creation. See manual page for Packagemaker for details on various options available.

However, there are several issues in command-line version of Packagemaker. I have given solution to some of the common problems you may face.

Problems and Solutions:

  1. I have bundle packages. Will they work on MacOS 10.8 (Mountain Lion)?
    • Mountain Lion introduces “Gatekeeper” which allows only signed apps to run on the system. We CAN NOT sign bundle packages and hence if the Gatekeeper is turned ON, bundle packages will not install on 10.8 if they have been download via web. However, if these packages have not been downloaded via web or the Gatekeeper is turned OFF, then bundle packages will install as expected.
  2. Why is the Finder not showing the version of the flat packages?
    • Finder gets the version information of an archive from the info.plist file present inside the archive. So, in old-style bundle packages, we were able to see the version of the package in the Finder. However, flat packages behave as files. They do not behave like an archive. So, Finder will not be able to show the version.
  3. I am not able to sign my packages using packagemaker. Why?
    • It is possibly a bug with packagemaker. Packages signed using packagemaker don’t work. You can use “productsign” command-line tool to sign your packages. See manual page for productsign  for more information.
  4. I am creating a distribution package from command-line. I am providing localized license files to --resources flag but when I run the package, I don’t get the License page during installation. Why?
    • Again, it is possibly a bug with packagemaker. It does not include the license file information in the Distribution file of the created package. Open your package using Flat Package Editor and check if the Distribution file has the attribute <license file=“License.rtf”/>.
    • You can fix this issue in your package even without rebuilding the package.Follow the instructions below:o   Expand the package using pkgutil command.
                 pkgutil   --expand   ABC.pkg   ABC_expanded.pkg
      o   Open the ABC_expanded.pkg using Show Package Contents.
      o   Open the Distribution file and add this line at same hierarchy level as <title> </title>
                  <license file="License.rtf"/>
      o   Save and close the file. Make sure the resources directory is present and contains the necessary resources (License files in this case).
      o   Flatten the package using pkgutil.
                  pkgutil   --flatten   ABC_expanded.pkg   FinalPkg.pkg
      o   Run the FinalPkg.pkg and it should show the License files.
    • Alternatively, check “productbuild” command. If you use this command, you can specify your own Distribution.dist file while creating the package. Check manual page for productbuild for more information.
  5. How can  I check if my installer is signed?
    • Use  pkgutil   --check-signature   pkg-path


Productbuild command:

You can also build packages using productbuild. This command provides you many different options to create a package than those in packagemaker. However, you can only create flat packages using this. It has three different modes:
·        You can create a product archive for your application bundles.
o   productbuild    [--product definition-plist]    {--component component-path [install-path]}   product-output-path

·        Create a product archive using a distribution file.
o   productbuild    [options]    --distribution    dist-path    [--package-path    search-path]    product-output-path

·        Synthesize a distribution for one or more component packages.
o   productbuild    --synthesize    [--product   definition-plist]    {--package    pkg-path}    distribution-output-path

 For more details about this command, see manual page for productbuild.


So, this was a little explanation about packaging in Mac. There are a lot of details which have not been covered here. However, I will try to post more articles about this. For now, AdiĆ³s!

Tuesday, 6 March 2012

Packagemaker 3: An Introduction

Packagemaker 3 - An Introduction

Packagemaker is Apple's official packaging tool which is used for packaging the applications before distributing them. It is part of XCode developer software suite. Earlier, it was installed as part of XCode installation, however, with the release of XCode 4.3, it is a part of separate package (Auxiliary Tools for XCode) which has to be downloaded separately. You need an Apple Id to download these tools.

At the time of writing, the latest Packagemaker version is 3.0.5. This version offers great improvements over the previous versions in terms of GUI mode of creating the installers. Talking about the GUI mode, most of the things are self-explanatory, so it is pretty easy to end up with a decent, working installer. However, if you are planning to use the command line mode for creating the installers, it is highly likely that you are going to have some trouble as the documentation is just not good enough.
Before moving on, lets have a look at the supported package formats-
There are two types of packages- Bundles and Flat Packages.

Bundle vs Flat Package
  • Bundle contains data in a hierarchical manner. You can  do RightClick --> Show Package Contents and browse through all the items present inside as if they were part of some folder structure and modify it. For some, it was good to be able to see what is inside the package, tweak it according to their will and requirements and then use it. However, for others, it was a security issue because users had the ability to modify the contents of the packages.
    So Apple came up with concept of Flat packages. These packages behave as files instead of folders. With these, you cannot browse through the contents easily though it is possible if you are curious enough to peek inside the package.
  • If your application distribution target is MacOS 10.5 and above, PM3 will create flat packages. For MacOS 10.4 and below, PM3 also supports Backwards-Compatibility mode that creates older packages(Bundles).

Here, we will be talking more about the flat packages, though we will be pointing out the significant differences from the Bundles if any.

Want to peek inside a flat package?
 There are some tools available for opening/modifying flat packages as well. If you want to see inside the package, open it using Flat Package Editor.
This tool allows you to see the basic structure of the package which in most cases will be one of the following-

Component Package

Distribution Package(Left) and Component Package(Right)
                              

After getting the above information, some of you won't be satisfied as it just shows the high level structure and doesn't allow to see what is inside the Payload or Scripts. 
So, what next!. 
Is it not possible to see the contents of the flat package? 
Well! Everything is possible in this world if you are curious enough to know and do it. 
Let's take a look at a command-line tool - pkgutil.

We can use this utility to perform a number of operations in our packages. I will be mentioning only some of the operations here. You can get more detailed information at the man pages of pkgutil.

For those of you who were dying to see the contents of the flat package, we can expand the flat package using pkgutil.

pkgutil     --expand     FlatPackage.pkg     NewPackage.pkg

Now, newly created package is older-style package which we can browse through by doing RightClick --> Show Package Contents. After going inside, double click the Payload file to extract the contents! So finally you got what you were looking for. Here you can modify files at your will. 
But will the package work after we modify the files?
Yes! As far as we don't change the basic structure of the package.

Suppose we modified some script inside the Scripts directory to bypass some settings that were not allowing installation in my machine. Now we have to flatten the package again. Use

pkgutil    --flatten    OlderStylePkg.pkg    NewFlatPackage.pkg

And you get your modified package back in flat format ready for installation.

Types of Flat Packages:

So in the images above, you see two different types of packages. One contains only compressed files inside while the other one contains packages inside as well.
So what's the difference between the two?
The one with only compressed files inside is Component Package. Their primary purpose is to dump the payload in the system where they are installed and of course perform the preinstall and postinstall actions.
Well, you must be wondering that this is exactly what a package is supposed to do when installed. So what is it that  they can not do during the installation?
They can't show any License, ReadMe, etc to the user. They cannot perform InstallationCheck as well.
If we want to show all these things to the user before installation, we use Distribution Package for this purpose. Distribution Packages contain the component packages to be installed, a Distribution file that has the information about the location and name of license files, background images and javascript functions for InstallationCheck actions. It also contains the ususal Resources directory that contains required localization directories which then contain the localized resources (License, background image, etc). 

When you will build the package using GUI mode, you will notice that whenever you try to add license, background, etc or InstallationCheck actions to your package, the type of your package changes to Distribution.


Conclusion:
This was a little introduction about the Packagemaker 3, package format and types and about some useful tools to interact with those packages.
With PM3, building a package using its GUI is fairly simple, however, for people who need to create packages everyday and use some sort of automation for creating packages, its command line mode can be of great help. I will be talking about command line mode for building packages in my next post.
Adios!

Links to External Resources: