Skip to content

Packaging Fpm

Sebastian Ehlert edited this page Nov 13, 2022 · 9 revisions

Non-comprehensive guide on packaging fpm itself and packaging with fpm.

Packaging status

MSYS2 (Windows)

The MSYS2 project is a packaging ecosystem build around compiling against the MinGW library. The toolchain is based on the Cygwin fork MSYS2, hence its name, for providing difficult to port Unix tools on Windows. It uses pacman as package manager, which is based on Arch Linux' package manager with the same name. Package files use the PKGBUILD format with MSYS2 specific extensions. PKGBUILD files are written in bash and provide entry points for the makepkg program to build a package with can be installed by pacman.

Important: before starting to use any PKGBUILD install the respective toolchains:

pacman -S base-devel mingw-w64-<arch>-toolchain

Where <arch> is one of i686 (mingw32), x86_64 (mingw64), ucrt-x86_64 (ucrt64), clang-i686 (clang32), or clang-x86_64 (clang64).

Package file for fpm

This package file will create a binary package for fpm 0.4.0, a few specialties of MSYS2's toolchain should be noted:

  • All packages build against MinGW, i.e. for native Windows, are prefixed with mingw-w64-. Therefore, we define the variable _realname before with the actual package name.
  • The variables MINGW_PACKAGE_PREFIX, MINGW_PREFIX and CARCH are defined by MSYS2's makepkg.
  • The arch entry is always 'any', instead the mingw_arch is used to specify the MinGW backend. Available mingw_arch entries are 'mingw32', 'mingw64', 'ucrt64', 'clang32' and 'clang64'.
  • The pkgver entry reflects the actual package version, while pkgrel is the build number (start counting from 1, reset at each release)
  • The pkgdesc entry should contain (mingw-w64) for packages build against MinGW.
  • The build() and package() function are the most important hooks to define for creating a package.
  • Always add a comment line with yourself as maintainer to the PKGBUILD
  • For building Fortran packages you have to have "${MINGW_PACKAGE_PREFIX}-gcc-fortran" in your makedepends entries and both "${MINGW_PACKAGE_PREFIX}-gcc-libs" "${MINGW_PACKAGE_PREFIX}-gcc-libgfortran" in depends entries
# Maintainer: Sebastian Ehlert <awvwgk@disroot.org>

_realname=fpm
pkgbase=mingw-w64-${_realname}
pkgname="${MINGW_PACKAGE_PREFIX}-${_realname}"
pkgver=0.4.0
pkgrel=1
arch=('any')
mingw_arch=('mingw32' 'mingw64' 'ucrt64')
pkgdesc="Fortran package manager (mingw-w64)"
url="https://github.com/fortran-lang/fpm"
depends=("${MINGW_PACKAGE_PREFIX}-gcc-libs" "${MINGW_PACKAGE_PREFIX}-gcc-libgfortran")
makedepends=("${MINGW_PACKAGE_PREFIX}-gcc-fortran" "${MINGW_PACKAGE_PREFIX}-curl" "git")
options=('strip')
license=('MIT')
source=(${_realname}-${pkgver}.tar.gz::"https://github.com/fortran-lang/fpm/archive/refs/tags/v${pkgver}.tar.gz")
sha256sums=('6c1574273164f859591547b7aae3b2d39471cf8a1d901cf1e453e607fbe9757a')

build() {
  cd "${srcdir}/${_realname}-${pkgver}"
  local _build="build_${CARCH}"
  local _fc="${MINGW_PREFIX}/bin/gfortran"

  FC="${_fc}" ./install.sh --prefix="${_build}"
}

package() {
  cd "${srcdir}/${_realname}-${pkgver}/build_${CARCH}"
  install -Dm755 bin/fpm "${pkgdir}"${MINGW_PREFIX}/bin/fpm
}

Save the above file as PKGBUILD. In the same directory you can now start makepkg to build the package:

❯ MINGW_ARCH=mingw64 makepkg-mingw --syncdeps --cleanbuild --log --force

The final package will be placed in the same directory and can be installed with pacman using:

❯ pacman -U mingw-w64-x86_64-fpm-0.4.0-1-any.pkg.tar.zst

Package fpm from git

It is possible to package directly from version controlled repositories, while discouraged in the actual distribution, using git packaged version can be practical for personal use.

A few notes about packages build from git repositories:

  • The -git suffix is used for the package name
  • Additional provides and conflicts entries ensure that the git package cannot be installed when a normal version of the same package is already installed
  • There is no checksum to test the integrity of the repository, using 'SKIP' disables the check
  • The pkgver is determined by a function rather than a static entry
# Maintainer: Sebastian Ehlert <awvwgk@disroot.org>

_realname=fpm
pkgbase=mingw-w64-${_realname}
pkgname="${MINGW_PACKAGE_PREFIX}-${_realname}-git"
pkgver=0.4.0
pkgrel=1
arch=('any')
mingw_arch=('mingw32' 'mingw64' 'ucrt64')
pkgdesc="Fortran package manager (mingw-w64)"
url="https://github.com/fortran-lang/fpm"
provides=("${MINGW_PACKAGE_PREFIX}-${_realname}")
conflicts=("${MINGW_PACKAGE_PREFIX}-${_realname}")
depends=("${MINGW_PACKAGE_PREFIX}-gcc-libs" "${MINGW_PACKAGE_PREFIX}-gcc-libgfortran")
makedepends=("${MINGW_PACKAGE_PREFIX}-gcc-fortran" "${MINGW_PACKAGE_PREFIX}-curl" "git")
options=('strip')
license=('MIT')
source=(${_realname}-git::"git+https://github.com/fortran-lang/fpm.git#branch=main")
sha256sums=('SKIP')

pkgver() {
  cd "${_realname}-git"
  _parent_ver=$(git describe --tags --abbrev=0 | sed 's/\([^-]*-\)g/r\1/;s/-//g' | tr -d '[:alpha:]' )
  printf "%s.r%s.%s" \
         "${_parent_ver}" \
         "$(git rev-list --count HEAD)" \
         "$(git rev-parse --short HEAD)"
}

build() {
  cd "${_realname}-git"
  local _build="build_${CARCH}"
  local _fc="${MINGW_PREFIX}/bin/gfortran"

  FC="${_fc}" ./install.sh --prefix="${_build}"
}

package() {
  cd "${_realname}-git/build_${CARCH}"
  install -Dm755 bin/fpm "${pkgdir}"${MINGW_PREFIX}/bin/fpm
}

As before save the above file as PKGBUILD. In the same directory you can now start makepkg to build the package:

❯ MINGW_ARCH=mingw64 makepkg-mingw --syncdeps --cleanbuild --log --force

The final package will be placed in the same directory and can be installed with pacman using:

❯ pacman -U mingw-w64-x86_64-fpm-git-0.4.0-r*-g*-1-any.pkg.tar.zst

Homebrew (MacOS)

brew is a package manager for MacOS. The nomenclature for all components in brew is oriented at home brewing. Package files are called formulas and are written as Ruby classes. The final package is called a bottle, individual channels are called taps, the main tap for brew is homebrew/homebrew-core.

Package file for fpm

This package file will create a binary package for fpm 0.4.0, a few specialties of brew toolchain are noted.

  • to set a lower bound on the supported GCC version all lower versions must be marked using fails_with.
  • there is no easy way to get a correct Fortran compiler version, a hacky solution seems to produce it from the C compiler
class Fpm < Formula
  desc "Fortran Package Manager (fpm)"
  homepage "https://fpm.fortran-lang.org"
  url "https://github.com/fortran-lang/fpm/archive/refs/tags/v0.4.0.tar.gz"
  sha256 "6c1574273164f859591547b7aae3b2d39471cf8a1d901cf1e453e607fbe9757a"
  license "MIT"

  depends_on "curl" => :build
  depends_on "gcc" # for gfortran
  fails_with gcc: "4"
  fails_with gcc: "5"
  fails_with gcc: "6"
  fails_with :clang

  def install
    # ENV.fc is not defined and setting it up with ENV.fortran will yield default gfortran
    ENV["FC"] = ENV.cc.gsub(/gcc/, "gfortran")
    # Compile arguments need some tweaking
    system "./install.sh", "--prefix=#{prefix}"
  end

  test do
    system "#{bin}/fpm", "--version"
  end
end

A comprehensive guide to get started with packaging for brew can be found here.