FFI imports packaging utility
The Haskell Cabal  is a framework which defines a common interface for authors to more easily build their applications in a portable way. The Haskell Foreign Functions Import Generator (hsffig)  is a tool to convert a C header file (.h) into Haskell code containing FFI  import statements for all entities whose declarations are found in the header file. The FFI Packaging Utility (ffipkg) is a tool that integrates the functionality of hsffig with the Cabal framework allowing for building and installation of packages entirely consisting of foreign functions imports.
2 Benefits of Packaging FFI Imports
To build a Haskell application linked to a foreign library, it is necessary to specify the locations of certain files (C headers and static or shared library files) for the Haskell compiler, and this information must be remembered for every application using the library. Building a FFI package means that all such information is contained within the package descriptor, and all that needs to be remembered is just name of the package.
The ffipkg utility accepts locations of C header and foreign library files as command line arguments and produces Haskell source files with FFI declarations, a Makefile, a Cabal package descriptor file, and a Setup.hs file suitable for running the Cabal package setup program. The utility acts as a "driver" running the C preprocessor, the equivalent of the hsffig program, and the source splitter. The Makefile created allows for compilation of Haskell source files into split object files: a feature provided by GHC. This technique is discussed in .
4 Command Line Options
Usage: ffipkg [OPTION...] include-file... -v --verbose provide verbose output -n --new-hooks use newer userHooks interface -i --header stop after writing package include file -?, -h --help print this help message -I include files location (may be multiple) -L library files location (may be multiple) -l library file to link (may be multiple) -V --version show program version number -w 0.0 --package-version=0.0 specify version of the package -p --package-name= name the package (will be uppercased) --with-make=make path to make --with-awk=awk path to awk --with-ar=ar path to ar --with-ghc=ghc path to ghc --with-gcc=gcc path to gcc --with-hsc2hs=hsc2hs path to hsc2hs
4.2 Package Naming and Versioning
Per the Cabal specification, the two fields are mandatory for a package descriptor file: Name and Version. The -p option sets the name of the package into its argument uppercased. If omitted, name of the first include file found on the command line will be used for package name, uppercased, with directory part and file name suffix stripped. The -w option sets the version field of the package descriptor file to its argument. The version supplied is checked for correctness using the same parser Cabal itself uses. If the syntax of the version is incorrect, or if the option is omitted, the default version string "0.0" will be used.
For FFI packages, versioning does not carry as much sense as it does for native library packages. In some cases, as shown in the Berkeley DB Binding example, it may be set to the version of the library used, but this is totally up to the FFI package creator. It is generally safe to omit this option unless there are separate packages created for different versions of the same library.
4.3 Location of Libraries and Include (Header) Files
Similarly to GCC, the -I option is used to specify location(s) where header files will be searched for, and the -L option is used to specify location(s) where library files will be searched for. The -l option is to specify name(s) of library files to link the resulting executable against, and all non-option command line arguments regardless of their position will be treated as include file names (although it is advisable to place all the option arguments on the command line first, and then all non-option arguments).
The ffipkg utility itself does not check for validity or existence of directories and files supplied this way; it only places this information in appropriate fields of the Cabal package descriptor file created by the utility.
Include file names may or may not include the directory part. If included, that may be either relative or absolute paths. The utility creates a small include file which in turn contains #include directives for all include files found on the command line.
For example, if the command line contains:
then the include file will look like:
/* File is generated automatically: do not edit */ #include "db.h" #include "sys/stat.h"
Library file names may be anything a particular linker would accept. It is recommended though to keep with the standard practice not to include directory part into library file names, but use the -L option instead.
Number of -I, -L, -l options, and non-option command line arguments is not limited by the logic of the utility.
4.4 External Programs
The --with-XXX options may be used to specify paths to certain programs (list of programs may vary between the versions of ffipkg) used during the course of action, and referred to in the Makefile. This may be necessary if a program is not on the default PATH, or it is desired to use a specific version of a program other than installed in "standard" way.
Only absolute paths to executable programs are accepted with the --with-XXX command line options.
The ffipkg utility checks for existence and executability of these programs (full list in the Synopsis), and also of several other programs (such as echo, rm, find, etc.) to make sure that the Makefile produced is valid.
If any of these programs is not possible to execute, the utility aborts with a diagnostic message. Users are advised to check their PATH environment variable in this case.
The specialized Cabal setup program created by the ffipkg utility uses the userHooks interface to alter Cabal's behavior while building the package. This interface has been evolving with time. Around the version 1.1.3 of Cabal, this interface underwent a minor change resulting however in the change of buildHook 's type signature. By default, the Setup.hs file generated is for "older" version of buildHook signature. If the -n option is specified, the file will be generated for "newer" type signature of buildHook.
If version of Cabal installed is less than 1.1.3, the -n option should not be used. If it is greater than 1.1.3, the option should be used. Otherwise, it is recommended to try to run ffipkg without this option; if buiding of the package fails, and the source of failure is compilation of Setup.hs then ffipkg must be rerun with the -n option.
4.6 Other Options
- The -v option sets verbosity level: if specified, the 'ffipkg utility will output intermediate information about intermediate steps.
- The -i option causes the utility to stop after the package include file has been created. No hsc code is produced, and neither Makefile nor Setup.hs files are created. This option may be used to just check that all the external programs are available or specified correctly on the utility command line.
- The -V option prints version number of the utility.
- The -h option prints the command line options synopsis.
5 Creating a FFI Import Package
5.1 Preparatory Steps
Create an empty directory and change there. There are no source files specific to a FFI package.
Determine which include and library files will be used in the package, locate them. Keep in mind that the more is the summary length of all include files, the longer it takes to run the utility, and this dependency may be nonlinear.
5.2 Creating Haskell Sources, Makefile, Cabal File, Setup Program
Execute the ffipkg utility supplying all information about locations of include and library files, and package name and version if applicable.
After the utility finishes, the following files will be created in the directory:
- the package include file
- Haskell sources which are result of running hsc2hs, and the splitter
- the Cabal package descriptor file
- the Makefile
- the Setup.hs file