Changes between Initial Version and Version 1 of howto/CreateInstallers


Ignore:
Timestamp:
Feb 16, 2017, 5:35:37 PM (8 years ago)
Author:
ctreleaven (Craig Treleaven)
Comment:

Brain dump

Legend:

Unmodified
Added
Removed
Modified
  • howto/CreateInstallers

    v1 v1  
     1= How to create standalone installers with MacPorts =
     2
     3== Audience ==
     4
     5This page summarizes some experience from building an installer with MacPorts facilities.  The reader is assumed to be relatively comfortable using MacPorts and the command line. 
     6
     7Building an installer package may unearth dependency issues or the need for packaging scripts (see following sections).  You may need to work with the maintainer of the port you wish to package (if you are not that port's maintainer).  You may also need to work with the maintainers of ports that your target port depends on.
     8
     9== Non-default prefix ==
     10
     11As the official documentation says, "make sure you do not use a standard installation of MacPorts in /opt/local."
     12
     13https://guide.macports.org/#using.binaries.binary-packages
     14
     15The reason is simple.  An installer can easily overwrite newer software with older--possibly incompatible--versions creating havoc on the end user's system. 
     16
     17Even with a custom prefix, software installed by a MacPorts-created installer may still conflict with software installed directly by MacPorts due to competing use of the `/Applications` or `/Library/Launchdaemons` folders.  For example, if your software package needs a daemon, your installer will need a launchd plist in `/Library/Launchdaemons`.  The installer may therefore overwrite a plist installed during a `sudo port install foo`, or vice versa.
     18
     19== Virtual machines for development and testing ==
     20
     21I have found using virtual machines to be essential for developing and testing an installer due to the issues with conflicting launchd plists, etc, discussed in the previous section.  Further, using VM's makes it possible to build under one Mac OS version and test deployment in a different version or even into multiple versions.
     22
     23The environment for building the installer will need MacPorts installed in a non-default prefix and thus, of course, XCode and the XCode command line tools. 
     24
     25The environment for testing deployment of the packaged software likely should not have MacPorts installed.  This will help identify missing dependencies.
     26
     27I've used Parallels as, at the time I started, it seemed to have the best support for running a variety of Mac OS guest operating systems.  It appears that Virtual Box may have improved in this regard recently and could therefore be a less-expensive option.
     28
     29== Variants ==
     30
     31Variants can have a significant impact on the dependencies that incorporated into your installer.  For example, I found (early on) that both python26 and python27 were being incorporated into my installer.  By requesting a `+python27` variant, there was a substantial savings in the size of the installer created.
     32
     33You have to specify your desired variants each time you run mpkg or mdmg.  Otherwise, the default variants will be used.  For example, to produce an installer for mythtv.28, I use:
     34
     35{{{#!sh
     36sudo port mpkg mythtv.28 -x11+mariadb+mariadb55+python27+perl5_24+startupitem
     37}}}
     38
     39== No post-activate ==
     40
     41Some ports define steps to be run at the time the port is activated, usually in a `post-activate { ... }` block.  An installer needs another way to achieve the same result since it has no way to run the post-activate code.  The normal method is to either use a postinstall script or a pre-pkg action depending on whether the steps can be taken before the installer is created or after the payload has been copied in place.  See the following sections on scripts and startupitems.
     42
     43If you have a significant number of dependencies, it can be difficult to know which, if any, actually utilize post-activate steps.  My approach is as follows:
     44
     45{{{#!sh
     46port cat rdepof:foo +somevariant-someother |edit
     47}}}
     48
     49The above command concatenates all the Portfiles of all the dependencies of port 'foo' and pipes the output into TextWrangler's edit.  Now, I simply search for `-activate` to locate any such blocks of code.  You can then determine the appropriate way to handle the required steps. 
     50
     51For example, fontconfig has the following post-activate block:
     52
     53{{{#!tcl
     54post-activate {
     55    # fc-cache can fail due to /Network/Library/Fonts being unavailable, so force success.
     56    system "${prefix}/bin/fc-cache -sv || true"
     57    system "${prefix}/bin/fc-cache -v || true"
     58}
     59}}}
     60
     61The `fc-cache` utility can't be run at the time the package is created since the user may have some fonts installed on their system (outside the installer).  Thus we should use a postinstall script to achieve.  See the example in the following section.
     62
     63
     64== preinstall / postinstall scripts ==
     65
     66MacPorts supports including preinstall and/or postinstall scripts for each component package. 
     67
     68These scripts are run at the obvious times before and after the payload is copied into place.  The scripts run with superuser privileges.    What is less obvious is that they are run in a sandbox where certain locations are inaccessible, such as `/tmp`.  If your script works in testing but fails when run by the installer, the sandbox may be the cause.
     69
     70Also, if a preinstall or postinstall script returns a non-zero result code, the installer will report that the install failed.
     71
     72To include a preinstall or postinstall script in the package, it needs to be copied to a particular folder in a pre-pkg phase.  The following example is from fontconfig:
     73
     74{{{#!tcl
     75pre-pkg {
     76    xinstall -m 0755 ${filespath}/postinstall ${package.scripts}/
     77    reinplace -locale C "s|@PREFIX@|${prefix}|g" ${package.scripts}/postinstall
     78    long_description-append  Install prefix: ${prefix}
     79}
     80}}}
     81
     82Keep in mind that the installer may be:
     83- running on a virgin system, or
     84- re-installing over top of a previous install, or
     85- upgrading from an older version to a newer one. 
     86
     87If your script copies a default configuration file into place on a virgin system, you probably want to check if the file already exists before replacing it with the default version.  Otherwise you may undo your user's modifications.
     88
     89Another issue, at least currently, is that MacPorts-created installers seem to execute the scripts twice.  Again, your scripts should avoid repeating actions that may already be done.  In the case of fontconfig, it doesn't hurt anything if the `fc-cache` utility is run multiple times.  Thus the postinstall script is simply:
     90
     91{{{#!sh
     92#!/bin/sh
     93
     94# fontconfig installer support, postinstall script
     95# runs as root after installer successfully copies payload to destination
     96# thus picks up _any_ fonts that were delivered with this installer
     97
     98# fc-cache can fail due to /Network/Library/Fonts being unavailable, so force success.
     99@PREFIX@/bin/fc-cache --system-only --verbose || true
     100}}}
     101
     102When debugging preinstall / postinstall scripts, you can find output from the script in the installer log.  In the Installer app, got to Window > Installer Log.
     103
     104== Startupitems ==
     105
     106If the software you are packaging relies on daemons set up with 'startupitem.create  yes', they need a MacPorts-provided utility (daemondo) that will not necessarily be present in your package.  The hack to get around this problem is to force a copy of the `daemondo` binary into the package.  The following is an example from the apache2 port:
     107
     108{{{#!tcl
     109if {[info exists pkg.asroot]} {
     110        pkg.asroot      yes
     111}
     112
     113pre-pkg {
     114    if {![info exists pkg.asroot]} {
     115        ui_error "Packaging ${name} ${version} requires MacPorts 2.3.5 or greater (pkg.asroot support)"
     116        return -code error "Incompatible MacPorts version"
     117    }
     118
     119    # at this point the destroot has been pruned and bin doesn't exist
     120    xinstall -d -m 0755 ${destroot}${prefix}/bin
     121    # apache2 needs daemondo; ram a copy into the destroot so it will be packaged
     122    xinstall -m 0755 ${prefix}/bin/daemondo ${destroot}${prefix}/bin/
     123}
     124}}}
     125
     126We need root permissions to carry out this step.  Before MacPorts version 2.3.5, the pre-pkg step wasn't able to run as root.  'pkg.asroot' was added for 2.3.5 to fix this problem.
     127
     128
     129== Work directories ==
     130
     131The first time `sudo port mpkg foo +variant1-variant2` is run will take extra time and disk space.  Each dependency is processed to the destroot stage and therefore nearly doubles the disk space consumed.  Subsequent runs will proceed much more quickly.
     132
     133The installer for 'foo' (when it is successfully created) will be in the work directory for foo.  `port work foo` will give you the path; `open $(port work foo)` will open a Finder window for that directory.
     134
     135If you wish to clean up or 'start fresh' for any reason, `sudo port clean rdepof:foo` to delete all the work directories of the dependencies.  Of course, `sudo port clean foo` will clean that work directory and delete any installer that might be in there.
     136
     137== Useful utilities ==
     138
     139You may wish to look at the contents of an installer.  One choice is Pacifist (free to try but nags for registration).  A free alternative is Suspicious Package:
     140
     141http://www.mothersruin.com/software/SuspiciousPackage/
     142
     143----
     144
     145[wiki:howto <- Back to the HOWTO section]