1. Introduction
2. Installing MacPorts
2.1. Install Xcode
2.2. Install MacPorts
2.3. Upgrade MacPorts
2.4. Uninstall MacPorts
2.5. MacPorts and the Shell
3. Using MacPorts
3.1. The port Command
3.2. Port Variants
3.3. Common Tasks
3.4. Port Binaries
4. Portfile Development
4.1. Portfile Introduction
4.2. Creating a Portfile
4.3. Example Portfiles
4.4. Port Variants
4.5. Patch Files
4.6. Local Portfile Repositories
4.7. Portfile Best Practices
4.8. MacPorts' buildbot
5. Portfile Reference
5.1. Global Keywords
5.2. Global Variables
5.3. Port Phases
5.4. Dependencies
5.5. Variants
5.6. Tcl Extensions & Useful Tcl Commands
5.7. StartupItems
5.8. Livecheck / Distcheck
5.9. PortGroups
6. MacPorts Internals
6.1. File Hierarchy
6.2. Configuration Files
6.3. Port Images
6.4. APIs and Libs
6.5. The MacPorts Registry
6.6. Tests
7. MacPorts Project
7.1. Using Trac for Tickets
7.2. Using Git and GitHub
7.3. Contributing to MacPorts
7.4. Port Update Policies
7.5. Updating Documentation
7.6. MacPorts Membership
7.7. The PortMgr Team
8. MacPorts Guide Glossary
Glossary

4.7. Portfile Best Practices

This section contains practical guidelines for creating Portfiles that install smoothly and provide consistency between ports. The following sections are on the TODO list.

4.7.1. Port Style

Portfiles may be thought of as a set of declarations rather than a piece of code. It is best to format the port file is if it were a table consisting of keys and values. In fact, the simplest of ports will only contain a small block of values. Nicely formatted compact tables will result in more values being visible at the same time.

The two columns should be separated by spaces (not tabs), so you should set your editor to use soft tabs, which are tabs emulated by spaces. By default, the top line of all Portfiles should use a modeline that defines soft tabs for the vim and emacs editors as shown.

# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4

The left column should consist of single words, and will be separated from the more complex right side by spaces in multiples of four. Variable assignments and variant declarations are exceptions, and may be considered a single word on the left side, with a single space between words.

set libver "8.5"

When items require multiple lines with line continuation, they can be separated from the previous and next items with a blank line. Indent the additional lines to the same column that the right side begins on in the first line.

checksums               rmd160  7bbfce4fecc2a8e1ca081169e70c1a298ab1b75a \
                        sha256  2829fcb7393bac85925090b286b1f9c3cd3fbbf8e7f35796ef4131322509aa53 \
                        size    1061530

Should a key item such as a phase or variant require braces, the opening brace should appear on the same line and the closing brace should be on its own line. The block formed by the braces is indented for visual clearance. Braces merely quoting strings, for example the description of variants, are placed on the same line without line breaks.

variant mysql5 description {Enable support for MySQL 5} {
    depends_lib-append        port:mysql5
    configure.args-replace    --without-mysql5 --with-mysql5
}

Frequently multiple items are necessary in the second column. For example, to set multiple source download locations, multiple master_sites must be defined. Unless the second column items are few and short you should place each additional item on a new line and separate lines with a backslash. Indent the lines after the first line to make it clear the items are second column values and also to emphasize the unity of the block.

destroot.keepdirs    ${destroot}${prefix}/var/run \
                     ${destroot}${prefix}/var/log \
                     ${destroot}${prefix}/var/cache/mrtg

4.7.2. Don't Overwrite Config Files

For packages that use a configuration file, it's generally desirable to not overwrite user-changes in the config file when performing an upgrade or reinstall.

post-destroot {
    # Move conf file to sample so it does not get overwritten
    file rename ${destroot}${prefix}/etc/apcupsd/apcupsd.conf \
                ${destroot}${prefix}/etc/apcupsd/apcupsd.conf.sample
}

post-activate {
    # Create initial conf file if needed
    if {![file exists ${prefix}/etc/apcupsd/apcupsd.conf]} {
        file copy ${prefix}/etc/apcupsd/apcupsd.conf.sample \
                  ${prefix}/etc/apcupsd/apcupsd.conf
    }
}

4.7.3. Install Docs and Examples

TODO:

4.7.4. Provide User Messages

TODO:

4.7.5. Use Variables

TODO: Set variables so changing paths may be done in one place; use them anytime it makes updates simpler: distname ${name}-src-${version}

4.7.6. Renaming or replacing a port

If there is the need to replace a port with another port or a renaming is necessary for some reason, the port should be marked as replaced_by.

As an illustration of a typical workflow the port skrooge-devel shall be taken. This port had been used for testing new versions of skrooge, but it turned out to have become unnecessary due to the fact that skrooge's developers currently prefer a distribution via port skrooge instead.

At the end of this section the use of the obsolete PortGroup is suggested as an even shorter approach to the below described workflow.

4.7.6.1. The long way

Skrooge's original devel port file looked like this:

# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4

PortSystem          1.0
PortGroup           kde4    1.1

fetch.type          svn
svn.url             svn://anonsvn.kde.org/home/kde/trunk/extragear/office/skrooge
svn.revision        1215845

name                skrooge-devel
version             0.8.0-${svn.revision}

categories          kde finance
maintainers         mk pixilla openmaintainer
description         Skrooge
long_description    Personal finance management tool for KDE4, with the aim of being highly intuitive, while \
                    providing powerful functions such as reporting (including graphics), persistent \
                    Undo/Redo, encryption, and much more...

conflicts           skrooge

platforms           darwin
license             GPL-3

homepage            https://skrooge.org
master_sites        https://skrooge.org/files/

livecheck.type      none

distname            skrooge

depends_lib-append  port:kdelibs4 \
                    port:libofx \
                    port:qca-ossl \
                    port:kdebase4-runtime \
                    port:oxygen-icons

The following steps have to be taken to ensure a smooth transition for a MacPorts user updating his local installation using sudo port upgrade:

  1. add the line replaced_by foo where foo is the port this one is replaced by; when a user upgrades this port, MacPorts will instead install the replacement port

    replaced_by         skrooge
  2. increase the version, revision, or epoch, so that users who have this port installed will get notice in port outdated that they should upgrade it and trigger the above process

    revision            1
  3. clear distfiles (have a line reading only distfiles) so that no distfile is downloaded for this stub port

    distfiles
  4. delete master_sites since there aren't any distfiles to download

  5. disable livecheck

    livecheck.type      none
  6. add a pre-configure block with a ui_error and return -code error explaining to users who try to install this port that the port has been replaced

    pre-configure {
        ui_error "Please do not install this port since it has been replaced by 'skrooge'."
        return -code error
    }

With above modifications the port file eventually looks like this:

# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4

PortSystem          1.0

name                skrooge-devel
svn.revision        1215845
version             0.8.0-${svn.revision}
revision            1

replaced_by         skrooge

categories          kde finance
maintainers         mk pixilla openmaintainer
description         Skrooge
long_description    Personal finance management tool for KDE4, with the aim of being highly intuitive, while \
                    providing powerful functions such as reporting (including graphics), persistent \
                    Undo/Redo, encryption, and much more...

platforms           darwin
license             GPL-3

homepage            https://skrooge.org

livecheck.type      none

pre-configure {
    ui_error "Please do not install this port since it has been replaced by 'skrooge'."
    return -code error
}

distfiles

A user upgrading ports will experience the following for port skrooge-devel:

%% sudo port upgrade skrooge-devel
--->  skrooge-devel is replaced by skrooge
--->  Computing dependencies for skrooge
--->  Fetching skrooge
--->  Verifying checksum(s) for skrooge
--->  Extracting skrooge
--->  Configuring skrooge
--->  Building skrooge
--->  Staging skrooge into destroot
--->  Deactivating skrooge-devel @0.8.0-1215845_0
--->  Cleaning skrooge-devel
--->  Computing dependencies for skrooge
--->  Installing skrooge @0.8.0.6_0
--->  Activating skrooge @0.8.0.6_0
##########################################################
# Don't forget that dbus needs to be started as the local 
# user (not with sudo) before any KDE programs will launch
# To start it run the following command:                  
# launchctl load /Library/LaunchAgents/org.freedesktop.dbus-session.plist
##########################################################

######################################################
#  Programs will not start until you run the command 
#  'sudo chown -R $USER ~/Library/Preferences/KDE'  
#  replacing $USER with your username.              
######################################################
--->  Cleaning skrooge

In case a user actually tries to install the obsolete port skrooge-devel it would be pointed out by an error message that this is impossible now:

%% sudo port install skrooge-devel
--->  Fetching skrooge-devel
--->  Verifying checksum(s) for skrooge-devel
--->  Extracting skrooge-devel
--->  Configuring skrooge-devel
Error: Please do not install this port since it has been replaced by 'skrooge'.
Error: Target org.macports.configure returned: 
Log for skrooge-devel is at: /opt/local/var/macports/logs/_opt_local_var_macports_sources_rsync.macports.org_release_ports_kde_skrooge-devel/main.log
Error: Status 1 encountered during processing.
To report a bug, see <https://guide.macports.org/#project.tickets>

4.7.6.2. The shortcut: PortGroup obsolete

Using the PortGroup obsolete makes the task described in the previous subsection much easier:

# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4

PortSystem          1.0
PortGroup           obsolete 1.0

name                skrooge-devel
replaced_by         skrooge
svn.revision        1215845
version             0.8.0-${svn.revision}
revision            2
categories          kde finance
        

The PortGroup defines a number of reasonable defaults for a port that is only there to inform users that they should uninstall it and install something else instead. You might want to override some of the defaults though. For details have a look at the PortGroup's source code in ${prefix}/var/macports/sources/rsync.macports.org/macports/release/tarballs/ports/_resources/port1.0/group/obsolete-1.0.tcl.

Note

replaced_by can be specified before or after the PortGroup line.

4.7.7. Removing a port

If a port has to be removed from MacPorts one should consider the hints concerning replacing it by some alternative port given above. It is recommended to wait one year before the port directory is actually removed from the MacPorts ports tree.

If there is no replacement for a port, it can simply be deleted immediately.