= Portfile Recipes = [[PageOutline(2-3,Table of Contents,inline)]] == Branch versions and version segments == #branch Often times, when a port's version is x.y.z, you want to be able to refer to just the x.y part, for a download URL or for other reasons. The de facto standard way to do this is {{{ set branch [join [lrange [split ${version} .] 0 1] .] }}} This splits the version string into an array, takes the first two elements of the array, and glues them back together again. This example is from [browser:trunk/dports/devel/glib2 glib2]. If you need to refer to just one part of the version, you can similarly use: {{{ set major [lindex [split ${version} .] 0] set minor [lindex [split ${version} .] 1] set patch [lindex [split ${version} .] 2] }}} These split the version string into an array and return the desired element. These examples are based on [browser:trunk/dports/lang/php5 php5]. == Compare Darwin/macOS versions == #compare-osx-darwin-version ||||||= macOS Version Info =|| ||= Darwin =||= macOS =||= Name =|| || 8.x.y || 10.4.x || Tiger || || 9.x.y || 10.5.x || Leopard || || 10.x.y || 10.6.x || Snow Leopard || || 11.x.y || 10.7.x || Lion || || 12.x.y || 10.8.x || Mountain Lion || || 13.x.y || 10.9.x || Mavericks || || 14.x.y || 10.10.x || Yosemite || || 15.x.y || 10.11.x || El Capitan || || 16.x.y || 10.12.x || Sierra || || 17.x.y || 10.13.x || High Sierra || || 18.x.y || 10.14.x || Mojave || || 19.x.y || 10.15.x || Catalina || || 20.x.y || 11.x.y || Big Sur || || 21.x.y || 12.x.y || Monterey || || 22.x.y || 13.x.y || Ventura || || 23.x.y || 14.x.y || Sonoma || Note: os.major is the left most dot separated number of the darwin version. {{{ set check.os.major 13 if {${check.os.major} < ${os.major}} { puts "Yosemite or newer" } elseif {${check.os.major} == ${os.major}]} { puts "Mavericks" } } elseif {${check.os.major} > ${os.major}} { puts "Mountain Lion or older" } }}} == Fetching from a URL that uses GET parameters == #fetchwithgetparams Usually you set master_sites to the URL to the directory you want to download from, and at fetch time MacPorts appends a slash (if necessary) and the distfiles variable to this to create the complete URL. For example, [browser:trunk/dports/archivers/bzip2 bzip2] says {{{ name bzip2 version 1.0.5 ... homepage http://www.bzip.org/ master_sites ${homepage}${version} }}} so master_sites evaluates to !http://www.bzip.org/1.0.5, distfiles is the default bzip2-1.0.5.tar.gz, so the final URL MacPorts will download from is !http://www.bzip.org/1.0.5/bzip2-1.0.5.tar.gz. But sometimes the only download URL available is one that requires GET parameters, for example if you want to download an attachment from a Trac wiki. In this case, list the entire download URL in master_sites, and at the end, leave an empty dummy parameter. MacPorts will still append the slash and the distname, but since it'll be assigned to the dummy GET parameter, the server won't mind. This example is from [browser:trunk/dports/devel/gtkimageview gtkimageview]: {{{ master_sites ${homepage}attachment/wiki/WikiStart/${distfiles}?format=raw&dummy= }}} == Fetching a file that is not in a tarball == #fetchnotarball Usually a port will be installing a package from a proper distribution source (e.g. tarball, zip, etc.). Following [http://comments.gmane.org/gmane.os.apple.macports.devel/21460 advice] given on the macports-dev mailing list, If you eliminate the extract phase, there will be nothing in the workpath. You could technically install straight from distpath to destroot, but that's a little confusing. This would probably be better: {{{ distfiles name_of_script use_configure no extract { file copy ${distpath}/${distfiles} ${workpath} } build {} … }}} == Using glob results in "Cannot stat: " == #glob glob returns a list which is not handled by some commands (e.g. xinstall); instead, use argument expansion, introduced with Tcl 8.5. Instead of {{{ xinstall -m 0644 [glob ${worksrcpath}/docs/*.html] ${destroot}${prefix}/share/doc/${subport} }}} which fails with the "Cannot stat..." error, instead use {{{ xinstall -m 0644 {*}[glob ${worksrcpath}/docs/*.html] ${destroot}${prefix}/share/doc/${subport} }}} == variant_isset == #variant_isset Normally you write the code for a variant inside the variant declaration: {{{ variant my_variant description {Variant description} { ... post-destroot { [post-destroot code for when the variant is set] } } }}} Sometimes it's more natural to include the variant's code in other sections of the Portfile by checking if the variant is (or is not) set: {{{ post-destroot { ... if {![variant_isset my_variant]} { [post-destroot code for when the variant is not set] } } }}} This is fine, but if you do it this way, you must ensure the variant is still defined in your Portfile, even if it is empty: {{{ variant my_variant description {Variant description} {} }}} == default_variants == #default_variants When some users may want to turn a feature off but most will want it on, you can use a variant that is on by default. Define the variant as usual, and then use the default_variants keyword like this (to make the `foo` variant on by default): {{{ default_variants +foo }}} The port will then be installed with `+foo` unless the user specifies `-foo` on the command line or in variants.conf. You can also use default_variants when one of a set of mutually exclusive variants is needed; e.g. from [browser:trunk/dports/graphics/ImageMagick ImageMagick], when selecting the pixel quantum: {{{ if {![variant_isset q8] && ![variant_isset q32]} { default_variants +q16 } }}} Formerly, use of negatively-named variants (like `no_x11`) was preferred to having a default positively-named variant (like `x11`), due to a deficiency in the port registry (ticket #2377). Now that this is fixed, use of negatively-named variants should be avoided. == require_active_variants == #require_active_variants Depending upon a specific port variant is not possible in MacPorts: The quartz variant of a graphic library cannot require automatically that the quartz variant of gtk is picked. It's up to the user to issue the correct command and to the port maintainer to check for the installed variant. There was a common practice in Portfiles to check for the presence of a file known to be provided by the wanted variant and to display a message requesting the user to install the correct variant if it's missing. example ([source:trunk/dports/devel/gtk-osx-application/Portfile@109119 gtk-osx-application]) {{{ pre-configure { if {![file exists ${prefix}/lib/pkgconfig/gdk-quartz-3.0.pc]} { ui_error " **** **** gtk-osx-application is meant to be used only in a GTK3 quartz **** development environment but your version of GTK3 does not **** support quartz. Please make sure that port gtk3 and all its **** dependencies are built with variants +no_x11 +quartz and try again. **** " error "gtk3 +no_x11 +quartz not installed." } } }}} A PortGroup has been created to simplify this: [source:trunk/dports/_resources/port1.0/group/active_variants-1.1.tcl active_variants]. To use it: 1. add {{{ PortGroup active_variants 1.1 }}} to the top of your Portfile 2. add {{{ require_active_variants $depspec $required $forbidden}}} where $depspec:: is the name of the port you're trying to check (required), which can be specified as either just the port, or via "(bin:lib:path):FOO:port" as accepted by the dependency parser. $required:: is a list of variants that must be enabled for the test to succeed (required; remember this can also be a space-separated string or just a string for a single value. It's interpreted as list, though.) $forbidden:: is a list of variants that may not be enabled for the test to succeed (default is empty list, see description of $required for values that can be interpreted as list by Tcl) Example with gtk-osx-application requiring gtk2+quartz or gtk3+quartz: {{{ PortSystem 1.0 PortGroup active_variants 1.1 (...) variant gtk3 conflicts python25 description {Use gtk3} { require_active_variants gtk3 quartz (...) } if {![variant_isset gtk3]} { require_active_variants gtk2 quartz (...) } }}} {{{sudo port install gtk-osx-application +gtk3}}} results in {{{ ---> Configuring gtk-osx-application Error: org.macports.configure for port gtk-osx-application returned: gtk3 must be installed with +quartz. To report a bug, follow the instructions in the guide: http://guide.macports.org/#project.tickets Error: Processing of port gtk-osx-application failed }}} Example with py-cairo +x11 requiring /cairo +x11 -quartz/ ([source:trunk/dports/python/py-cairo/Portfile@106520 Portfile]) and selecting a default variant based on the variants of a cairo: {{{ variant x11 { require_active_variants path:lib/pkgconfig/cairo.pc:cairo x11 quartz } if {![catch {set result [active_variants path:lib/pkgconfig/cairo.pc:cairo x11 quartz]}]} { if {$result} { default_variants +x11 } } else { default_variants +x11 } }}} == cvs/svn/git tag for consistency == #checkout_tag When using one of the checkout-based fetch types (cvs, svn, git, etc) it is best to also use the accompanying tag or date to make sure everyone gets the same checkout as you originally did when creating the port. For example, [browser:trunk/dports/lang/slime slime] does the following for cvs: {{{ cvs.date ${version} }}} [browser:trunk/dports/irc/irssi-devel irssi-devel] does the following for svn: {{{ svn.revision ${version} }}} == Don't hardcode /opt/local == #hardcode_opt_local Make sure to never hardcode /opt/local anywhere as that is only the default prefix for MacPorts; a different prefix can be used. Many ports will either use a simple reinplace like {{{ reinplace "s|/usr/local|${prefix}|g" ${worksrcpath}/Makefile }}} to replace instances of /usr/local with the right MacPorts prefix when a Makefile simply assumes /usr/local (see for example [browser:trunk/dports/audio/id3v2 id3v2]). Others, when a patch being applied needs to reference the prefix, will use a template for the prefix like @@PREFIX@@, then a similar reinplace: {{{ reinplace "s|@@PREFIX@@|${prefix}|g" ${worksrcpath}/configure }}} that is then run in post-patch (see for example [browser:trunk/dports/devel/glib2 glib2]). == Xcode version checking == #xcode_version MacPorts [ticket:12794 checks the version of Xcode] being used at runtime and warns if it is an old version known to cause problems, but for specific ports that are known to fail to build with older versions of Xcode, you may want to actively prevent the installation from continuing, rather than just printing a warning. If you discover that your port requires a particular minimum version of Xcode, use the xcodeversion portgroup to print an error and exit if the user's Xcode is too old. At the top of the portfile, after the PortSystem line and near any other PortGroup lines you may have, add this line: {{{ PortGroup xcodeversion 1.0 }}} Then somewhere in the body of the portfile you can write a line like this: {{{ minimum_xcodeversions {9 3.1} }}} This example is from [browser:trunk/dports/multimedia/x264 x264] and ensures that, on Darwin 9 (Mac OS X 10.5 Leopard), Xcode 3.1 or greater is used. If you need to check the minimum Xcode version of more than one OS version, simply add more pairs of arguments. For example, to check for a minimum of Xcode 3.1 on Darwin 9 and a minimum of Xcode 2.5 on Darwin 8 (Mac OS X 10.4 Tiger), you would write: {{{ minimum_xcodeversions {9 3.1 8 2.5} }}} If you don't know what minimum version of Xcode would work, it's ok to just put whatever version you found that the port worked with. For example if someone reports a problem building a port on Leopard with Xcode 3.0, and you verified it works fine with Xcode 3.1.4, it's ok to list 3.1.4 as the minimum Xcode version, even without testing whether earlier 3.1.x versions would have worked. It is better to make a user upgrade to a newer Xcode than for a port maintainer to spend time testing old versions. == Stealth updates == #stealth-updates Although they would be wise not to do this, some software developers occasionally update their distfile with new changes without changing the distfile's name (e.g., it stays example-1.2.tar.gz). The port's `checksums` then need to be updated, but the `version` stays the same (e.g. stays at 1.2). So that users will see the update, the `revision` is increased, but users who already have the previous distfile would then experience a checksum mismatch. To avoid this, you must change `dist_subdir`. By default, `dist_subdir` is ${name}; change it so that it includes a subdirectory named for the version number and the distfile revision number (start at 1 and increment by 1 each time this version's distfile is stealth-updated): {{{ dist_subdir ${name}/${version}_1 }}} This line should be removed when the next proper version of the software is released. It's a good idea to add a comment above the dist_subdir line reminding yourself (or the next committer) to do this. Using the ${revision} variable in the `dist_subdir` definition isn't usually a good idea, because the `revision` is supposed to allow the portfile author to offer the user an upgrade that is unrelated to the upstream software version. Compare the old distfile with the new one. (In most cases, the old distfile can be downloaded from the MacPorts distfiles mirror.) Sometimes the new distfile does not differ materially from the old one (e.g. the gz or bz2 files are different but the underlying tar files are identical, or there are only changes in files that the port does not install). If you can determine that this has happened, then you don't want to force users to rebuild since it would be of no benefit to them. In this case, do not increase the port's `revision`, but do update the `checksums` and add the `dist_subdir` line as above. For software whose developers habitually stealth-update their software, further measures may need to be taken, such as those employed by the dcraw and molden ports. But ideally, convince the developers to refrain from stealth-updating. Any time a distfile is released to their download area, they should consider it to be immutable. See also the next entry on [#unversioned-distfiles unversioned distfiles]. == Unversioned distfiles == #unversioned-distfiles Although they would be wise not to do this, some software developers may distribute their distfile with a filename that does not contain the version number (e.g. example.tar.gz); such a port will have its `distname` specified as ${name} rather than its default ${name}-${version}. But when updating the port to a new version, the old version's distfile will get in the way, since it has the same name, and users who had a previous version of the port installed will experience a checksum mismatch. To avoid this, you must change `dist_subdir`. By default, `dist_subdir` is ${name}; change it so that it includes a subdirectory named for the version: {{{ dist_subdir ${name}/${version} }}} Ideally, however, convince the developers to put the version number in their distfile names. Any time a distfile is released to their download area, they should consider it to be immutable. See also the previous entry on [#stealth-updates stealth updates]. == Handling configuration files == #configfiles Many ports install config files (usually in ${prefix}/etc) which the user is expected to edit as needed, after the port is installed. Until ticket #2365 is implemented, proper handling of these config files must be done for each port. If you simply install the config file as part of the port itself, it will then be deleted on uninstall, as well as overwritten on upgrade. These outcomes are bad since it loses what the user updated. The current solution is to rename any config files to append a ''.dist'' or ''.sample'' extension to them so that the proper name of the config file is not part of the port. Then, in a post-activate phase, the port can test for the file's existence and, if it doesn't exist yet, copy it (copied in post-activate directly in ${prefix} keeps the file from being recorded as part of the port). The [browser:trunk/dports/sysutils/bacula/Portfile bacula Portfile] and [browser:trunk/dports/security/stegdetect/Portfile stegdetect Portfile] are good examples. == Installing additional documentation files == #doc Many ports come with documentation files, like "README" and "ChangeLog", which are part of the source but not installed anywhere. If these files are of value to the end user, they should be installed by the port in the post-destroot phase. (Files such as "INSTALL", which contain only installation instructions, are not of value to the end user and should not be installed.) {{{ post-destroot { set docdir ${prefix}/share/doc/${subport} xinstall -d ${destroot}${docdir} xinstall -m 0644 -W ${worksrcpath} \ AUTHORS \ COPYING \ NEWS \ README \ ${destroot}${docdir} } }}} Note: Ensure you use the correct capitalization for each filename. For example, many projects typically use all-caps for most of the documentation files, except for ChangeLog, which is often written in camel-case. If you fail to use the correct capitalization, the port may still install on your system, but will fail for users with case-sensitive filesystems. Note: This recipe used to recommend using ${destroot}${prefix}/share/doc/${name}, which is fine if used in a standalone port, but this will cause conflicts if used in a port with subports, such as one using the unified python portgroup. == Removing -arch flags from *-config scripts, *.pc files, etc. == #archflags MacPorts provides `-arch` flags to ports' configure scripts in CFLAGS, LDFLAGS and similar variables, which tell the port what architecture(s) to build for, but these `-arch` flags should not appear in *-config scripts or pkg-config *.pc files or the like because they will influence the build of dependent ports. Consider: [browser:trunk/dports/mail/libetpan/Portfile libetpan] @0.58_0+universal is installed. Its [changeset:66602 libetpan-config script contains -arch flags]. I now want to build etpan which depends on libetpan. [changeset:65910 etpan cannot be built universal] so I do not request the +universal variant when building etpan. Yet it still tries to build universal because it got the universal `-arch` flags from libetpan's config script, so the build fails. Port maintainers should therefore ensure that any `-arch` flags are removed from such scripts and files prior to installation, e.g. like this: {{{ post-configure { reinplace -E {s|-arch [a-z0-9_]+||g} \ ${worksrcpath}/redland-src-config \ ${worksrcpath}/redland.pc } }}} This example is taken from [browser:trunk/dports/www/redland/Portfile redland]. If you're using the muniversal portgroup, it's a bit more complicated because you have to reinplace in each architecture's source directory, and you have to still accommodate non-universal builds: {{{ post-configure { if {[variant_isset universal]} { set dirs {} foreach arch ${universal_archs_to_use} { lappend dirs ${worksrcpath}-${arch} } } else { set dirs ${worksrcpath} } foreach dir ${dirs} { reinplace -E {s|-arch [a-z0-9_]+||g} \ ${dir}/curl-config \ ${dir}/libcurl.pc } } }}} This example is taken from [browser:trunk/dports/www/curl/Portfile curl]. If you modify a port to remove `-arch` flags from its *-config scripts and/or *.pc files, don't forget to also increase the port's revision (unless you're already increasing its version). == Specifying which compiler to use based on the version of Xcode == #compiler As of MacPorts 2, the default value of configure.compiler is chosen based on Xcode version instead of macOS version. If a certain compiler won't build your port, blacklist it, and MacPorts will select the next-best compiler. If your port does not build with LLVM-GCC, blacklist it: {{{ compiler.blacklist *llvm-gcc-4.2 }}} This will prevent MacPorts from using either Xcode’s LLVM-GCC or MacPorts’. Similarly, if your port does not build with Clang: {{{ compiler.blacklist *clang* }}} As of MacPorts 2.2, the elements of `compiler.blacklist` are treated as patterns for [http://www.tcl.tk/man/tcl8.4/TclCmd/string.htm#M34 string matching]. See UsingTheRightCompiler for a list of available compiler values. If your port works with neither clang nor llvm-gcc-4.2, the fallback will be to gcc-4.2 (provided by the apple-gcc-4.2 port if it's not available). It is becoming increasingly important that ports build successfully with clang since it has been the default compiler since Xcode 4.2, and Apple no longer supports the use of gcc. If your port fails to build with the most recent current versions of clang, please file bugs upstream to fix issues when building with clang, and reference these bug reports in the Portfile, so other developers can followup. == Providing compiler variants == #gcc '''This recipe is not recommended. Please do not use it in new ports. Use the compilers PortGroup instead.''' Use of the C++ and ObjC compilers provided by macports-gcc-X.Y compiler options (and by extension, dragonegg compiler options) is strongly discouraged because it will lead to different C++ and ObjC runtimes being used by different ports (which can make such ports incompatible with each other). The general rule of thumb is that if the C++ or ObjC usage is wholly contained within the port, it is safe to use the gcc compilers. If the port uses C++ or ObjC APIs provided by the host or other ports, then you must use an Apple provided compiler or one of the macports-clang compilers. Similarly, if the port is providing ObjC or C++ APIs to other ports, then it must use an Apple provided compiler or one of the macports-clang ports. See the next section below for instructions on how to choose just the fortran compiler based on gcc variants. ---- By default, a port will compile using Apple's Clang or GCC compiler. For most ports this is fine, but some require a recent version of GCC, or are for some reason incompatible with Apple's version. In these cases you can use `configure.compiler` to specify an alternate compiler, for example one provided by a MacPorts GCC port. More commonly, a port specifies such a compiler because it needs GCJ or GFortran, which Apple does not provide. The software may also install a library or use a library built with a different GCC; subtle and difficult-to-find errors can occur if a library and the program using it are not both compiled with the same compiler. So on the one hand, all such ports should default to using a particular common version of GCC; the version we are currently using as the default version in MacPorts is `gcc48`. On the other hand, a user may want to choose their GCC version. Therefore, ports that need to use a GCC port, but aren't picky about exactly which one, are encouraged to offer variants: {{{ variant gcc45 conflicts gcc46 gcc47 gcc48 gcc49 description {Compile with gcc 4.5} { configure.compiler macports-gcc-4.5 } variant gcc46 conflicts gcc45 gcc47 gcc48 gcc49 description {Compile with gcc 4.6} { configure.compiler macports-gcc-4.6 } variant gcc47 conflicts gcc45 gcc46 gcc48 gcc49 description {Compile with gcc 4.7} { configure.compiler macports-gcc-4.7 } variant gcc48 conflicts gcc45 gcc46 gcc47 gcc49 description {Compile with gcc 4.8} { configure.compiler macports-gcc-4.8 } variant gcc49 conflicts gcc45 gcc46 gcc47 gcc48 description {Compile with gcc 4.9} { configure.compiler macports-gcc-4.9 } if {![variant_isset gcc45] && ![variant_isset gcc46] && ![variant_isset gcc47] && ![variant_isset gcc48] && ![variant_isset gcc49]} { default_variants +gcc48 } }}} Note that the variants are all marked as conflicting with one another, and that one is chosen by default if the user has not picked one. Note also that the dependencies are library dependencies because programs compiled using these compilers will generally end up linked to at least one of the compiler's libraries (i.e. `libgcc_s.dylib`, `libgfortran.dylib`, etc.). Setting `configure.compiler` changes the values MacPorts puts in variables like `configure.cc`, `configure.cxx`, `configure.f77`, etc., which MacPorts automatically sets as environment variables during the configure phase. If the software in question doesn't use the configure phase, and you therefore need to pass these variables to the build phase, you must do so in a `pre-build` block; if you try to do so directly in the portfile body, you'll pick up the original values, before the variant changed them. {{{ pre-build { build.args CC=${configure.cc} \ CXX=${configure.cxx} } }}} == Selecting a Fortran Compiler == #fortran '''This recipe is not recommended. Please do not use it in new ports. Use the compilers PortGroup instead.''' The default Apple-provided compilers (and the clang compilers from MacPorts) do not support as many languages as the gcc ports. The Portfile recipe below will allow you to compile fortran code while still using the default compiler for C, ObjC, C++, and ObjC++ code. If your port uses gcj, the same process can be used to select a gcj compiler. {{{ set gcc_versions {4.3 4.4 4.5 4.6 4.7 4.8 4.9} set default_fortran_variant +gcc48 set g95_conflicts {} foreach ver ${gcc_versions} { set ver_no_dot [join [split ${ver} "."] ""] set variant_line {variant gcc${ver_no_dot} description "build with gfortran from gcc${ver_no_dot}" conflicts g95} foreach over ${gcc_versions} { if {${ver} == ${over}} { continue } set over_no_dot [join [split ${over} "."] ""] append variant_line " conflicts gcc${over_no_dot}" } append variant_line { {}} eval $variant_line append g95_conflicts " conflicts gcc${ver_no_dot}" if {[variant_isset gcc${ver_no_dot}]} { if {${default_fortran_variant} != "+gcc${ver_no_dot}"} { set default_fortran_variant "" } } } eval [concat {variant g95 description {build with g95}} $g95_conflicts {{}}] if {[variant_isset g95]} { if {${default_fortran_variant} != "+g95"} { set default_fortran_variant "" } } if {${default_fortran_variant} != ""} { default_variants-append "${default_fortran_variant}" } foreach ver ${gcc_versions} { set ver_no_dot [join [split ${ver} "."] ""] if {[variant_isset gcc${ver_no_dot}]} { depends_lib-append path:lib/libgcc/libgcc_s.1.dylib:libgcc depends_build-append port:gcc${ver_no_dot} configure.fc ${prefix}/bin/gfortran-mp-${ver} configure.f77 ${prefix}/bin/gfortran-mp-${ver} configure.f90 ${prefix}/bin/gfortran-mp-${ver} } } if {[variant_isset g95]} { depends_lib-append path:lib/libgcc/libgcc_s.1.dylib:libgcc depends_build-append port:g95 configure.fc ${prefix}/bin/g95 configure.f77 ${prefix}/bin/g95 configure.f90 ${prefix}/bin/g95 } }}} == Replacing and renaming ports == #replaced-by When a software project is renamed, a new port is created with the project's new name. The new port should usually not be created from scratch; but be based on the old port. For example, when the developers of the OSXvnc project renamed it to Vine Server, the new vineserver port was created by copying the old osxvnc port: {{{ cd $(port dir osxvnc)/../ cp -R osxvnc vineserver }}} Then the new port (vineserver, in this example) is edited, including changing the `name` field to the project's new name, most likely changing the `version`, possibly changing the `homepage`, `master_sites` and `livecheck`, and anything else related to the name change. It's also a good idea to include the project's old name in the new port's `description` and `long_description` so that users searching for it by its old name can find it. The old port (osxvnc) should be kept around for a year, and converted into a stub port marked as having been replaced by the new port, so that users who already had the old port installed will learn about the new port and will be prompted (via `port outdated`) to upgrade to it. There are several steps to this process: 1. Add the `replaced_by` line, indicating the name of the port by which this port has been replaced, for example `replaced_by vineserver`. This causes MacPorts, when running `sudo port upgrade osxvnc`, to deactivate osxvnc and to install vineserver in its place. 1. Use the obsolete 1.0 PortGroup. This will take care of things like removing livecheck and giving the user a message stating that the port is indeed obsolete. 1. In a comment, mention the date, one year in the future, that the port can be removed. 1. In order for osxvnc to appear in `port outdated`, increase the port's `version`, `revision` or `epoch`. Customarily, the old port's version is set to the version of the replacement port. So, when osxvnc @3.0_1 was replaced by vineserver @3.1_0, osxvnc's `version` was set to 3.1 and its `revision` dropped to 0 to match the new vineserver port. If the software was only renamed, without changing its version number, then increase the old port's `revision`. 1. Remove all directives related to fetching and verifying files: `master_sites`, `patch_sites`, `checksums`, `fetch.*`, `cvs.*`, `svn.*`, `hg.*`, `git.*`, or `bzr.*` directives. 1. Remove any existing directives and blocks for any of the phases: fetch, extract, patch, configure, build, destroot, install, archive, activate and deactivate. 1. Remove any `depends_*`, `PortGroup` and `notes` directives and any `variant` and `platform` blocks. 1. Remove the `files` directory if present since those files are no longer needed. Here is a complete example stub port: {{{ # -*- 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 # This port can be removed on May 8, 2015. replaced_by mysql55-server PortGroup obsolete 1.0 name mysql5-server-devel version 5.5.2-m2 revision 3 homepage http://www.mysql.com/ categories databases license GPL-2 }}} The reason for leaving a stub port (rather than deleting the old port immediately) is to provide a seamless upgrade process for people who installed the port under its old name. So the stub port should not be deleted until after most users can be expected to have used `sudo port selfupdate` and `sudo port upgrade outdated`. Since some users do not update frequently, it is recommended that the old port remain for no less than one year after the `replaced_by` line is added. Sometimes a port is replaced because a [ticket:14540 -devel port] was created but will no longer be updated and is being replaced by a corresponding non-devel port. (For example, the xz-devel port was replaced by the xz port.) Ensure that the replacement port does not declare a conflict with the replaced port, as that would prevent the upgrade from succeeding. == Specifying a license == #license The list of possible licenses is quite exhaustive and somewhat of a moving target; see the [https://github.com/macports/macports-infrastructure/blob/master/jobs/distributable_lib.tcl source file] for the most up-to-date list. Briefly, the expected format `A {B C}` means the license is A plus either B or C. Notes: * 'Restrictive/Distributable' means a non-free license that nonetheless allows distributing binaries. * 'Restrictive' means a non-free license that does not allow distributing binaries, and is thus not in the list. * 'Permissive' is a catchall for other licenses that allow modification and distribution of source and binaries. * 'Copyleft' means a license that requires source code to be made available, and derivative works to be licensed the same as the original. * 'GPLConflict' should be added if the license conflicts with the GPL (and its variants like CeCILL and the AGPL) and is not in the list of licenses known to do so below. * 'Noncommercial' means a license that prohibits commercial use. * Public domain code, while technically not having or needing a license, should list the license as 'public-domain'. * The Mozilla Public License version 2.0 behaves differently depending on whether the software is declared by the authors to be "Incompatible With Secondary Licenses". If that declaration is not made, then the software can optionally be distributed under the terms of several other licenses including the GPL, the AGPL, and the LGPL, as well as the MPL itself. If an MPL-2 port is Incompatible With Secondary Licenses, put `MPL-2` in the license field. If it is not Incompatible With Secondary Licenses, use `{MPL-2 LGPL-2.1+}`, since the LGPL is the most permissive of the secondary licenses, and the other options do not have any advantages over it for our purposes. == Using the “deactivate hack” == #deactivatehack Using the so-called “deactivate hack” becomes necessary when a file that used to be provided by port A is going to be provided by port B from now on and we need a seamless update path. Without the deactivate hack the activation phase of port B would fail because one of the files it tries to install is already there. The deactivate hack works around this problem, by deactivating A if it is active. It is often combined with a check for a specific version of A. For example, let's say the `kerberos5` port used to install the file `${prefix}/bin/compile_et`, but we're trying to move this part to a library called `libcomerr`. Since the `kerberos5` port will still need `compile_et` to build, it will add a dependency on `libcomerr`. This will cause MacPorts to install `libcomerr` before upgrading `kerberos5`. When MacPorts tries to activate `libcomerr`, the old version of `kerberos5` is still active and the conflicting file is still installed. Usually, MacPorts would bail out at this point and require the user to solve this problem manually. However, since this would affect all users that had `kerberos5` installed, this is undesirable. In this particular case, we know `kerberos5` stopped providing `compile_et` in version 1.11. We will add the deactivate hack to `libcomerr`, where it will check whether any `kerberos5` < 1.11 port is active, and if there is, it will deactivate it before activating `libcomerr`. After activating `libcomerr`, MacPorts will then happily continue with the upgrade and install the new (and non-conflicting) version of `kerberos5`. The following block is taken from the `libcomerr` Portfile: {{{#!tcl pre-activate { # both kerberos5 and e2fsprogs previously conflicted because they installed files now provided by libcomerr if {![catch {lindex [registry_active kerberos5] 0} installed]} { set _version [lindex $installed 1] if {[vercmp $_version 1.11] < 0} { # kerberos5 used to install some files now provided by libcomerr in versions < 1.11 registry_deactivate_composite kerberos5 "" [list ports_nodepcheck 1] } } } }}} * `registry_active` returns a list containing information about active ports. Given a particular name, only one active port can match, so the returning list has at most one element, which we save as `installed`. (If we had not provided an argument, the list would have had one element for every active port.) * `installed` is now a list containing information on the active port. The elements relevant to version checking are the port version, revision, and epoch, which are at indices 1, 2, and 5, respectively. * If the active port is not recent enough, we deactivate it using `registry_deactivate_composite`. The second argument is a version number, which in this case we leave empty. The third argument is a list of options; setting `ports_nodepcheck` to 1 forces deactivation regardless of whether there are active dependents. This is fine, since `kerberos5` will be reinstalled shortly. == Current list of license keyword values in Portfiles == #licensekeyword {{{ TZ=UTC date && port -q info --license --index all | grep -ve ^-- -e '^$' | tr ' ' '\n' | tr -d '{}()' | grep -vxe and -e or -e License: -e '^$' -e '^--.*$' | sort -fu | sed 's/^ */* /' }}} Tue 15 Sep 2020 17:11:05 UTC * AFL-2.1 * AFL-3 * AGPL-3 * AGPL-3+ * AMPAS * Apache * Apache-1 * Apache-1.1 * Apache-2 * Apache-2+ * Apache-3 * APSL * APSL-1.1 * APSL-1.2 * APSL-2 * Artistic * Artistic-1 * Artistic-2 * Autoconf * Beerware * BitstreamVera * Boost * Boost-1 * BSD * BSD-old * BSDL * CC-BY * CC-BY-NC-SA-3 * CC-BY-SA * CC-BY-SA-3 * CC-BY-SA-4 * CC-SA-1 * CCBY-3 * cdcill * CDDL * CDDL-1 * CDDL-1.1 * CeCILL * CeCILL-2 * CeCILL-B * CeCILL-C * CNRI * Commercial * Copyleft * cpl * CPL-1 * Curl * custom * EPL-1 * EPL-2 * fontconfig * FPLL-1.1 * FreeBSD * FreeImage-1 * FreeType * gd * GFDL * GFDL-1.1 * GFDL-1.1+ * GFDL-1.2 * GFDL-1.3+ * GPL * GPL-1 * GPL-1+ * GPL-2 * GPL-2+ * GPL-2.1 * GPL-2.1+ * GPL-3 * GPL-3+ * GPLConflict * ibmpl * IBMPL-1 * IJG * ImageMagick * IPL-1 * ISC * ISC/BSD * ISCL * JasPer-2 * JSON * LBNL-BSD * LGPL * LGPL-2 * LGPL-2+ * LGPL-2.1 * LGPL-2.1+ * LGPL-3 * LGPL-3+ * libtool * LibXL * LICENSE * LPPL * LPPL-1.2 * MAME * MIT * MPL * MPL-1 * MPL-1.1 * MPL-1.1+ * MPL-1.1-AOLserver * MPL-2 * MPL-2+ * NCSA * netbsd * nethack * Nomirror * Noncommercial * none * NPL-1.1 * OFL-1.1 * openldap * OpenLDAP-2.8+ * OpenSSL * OpenSSLException * OPL-0.4 * OSL-2 * OSL-3 * PDL-1 * Permissive * PHP * PHP-2.02 * PHP-3 * PHP-3.01 * Pine * PopCap * PSF * PSF-2.1.1 * public-domain * PyNGL * PyNIO * QPL * QPL-1 * RASLIC * Restrictive * Restrictive/Distributable * RPL-1.1 * Ruby * SIL * Sleepycat * SPIN * SSLeay * SSPL * Tcl/Tk * UCAR-Unidata * unknown * unknown/none * unrestricted * UoI-NCSA * UW's * Vim * W3C * WTFPL * WTFPL-2 * wxwidgets * wxWidgets-3 * wxwidgets-3.1 * X11 * Xdebug-1.01 * zlib * ZPL * ZPL-2 * ZPL-2.1 == Checking for a symlink's existence == #symlink-exists Checking for a file's existence is easy: {{{ if {[file exists ${f}]} { ... } }}} But if `${f}` is a symlink, `file exists` will check for the existence of the file the symlink points to. If you want to check for the existence of the symlink itself, use: {{{ if {![catch {file type ${f}}]} { ... } }}}