Building Cabal Package¶
To build a cabal package with Finkel, make a cabal configuration file as in the Haskell cabal package, but with custom setup script and some build tool and package dependencies.
Note
This documentation assumes the readers are using the stack build tool for building cabal packages. Those who prefer other tools such as cabal-install may translate the invoked commands and modify the file contents as necessary.
Building My First Package¶
Make a directory named my-first-package
, and create a file named
package.yaml
under the directory with following
contents:
# File: my-first-package/package.yaml
name: my-first-package
version: 0.1.0.0
extra-source-files:
- src/**.fnk
custom-setup:
dependencies:
- base
- Cabal
- finkel-setup
library:
source-dirs: src
exposed-modules: MyFirstPackage
build-tools:
- finkel:finkel
dependencies:
- base
And a custom Setup.hs
script:
-- File: my-first-package/Setup.hs
import Distribution.Simple.Finkel (finkelMakeMain)
main :: IO ()
main = finkelMakeMain
And a Finkel source code src/MyFirstPackage.fnk
for exposed
module:
;;;; File: my-first-package/src/MyFirstPackage.fnk
(defmodule MyFirstPackage
(export factorial))
(defn (:: factorial (-> Integer Integer))
"Compute factorial of the given number.
This function does not support negative numbers. If the argument was
negative, constantly returns @-1@.
==== __Example__
>>> (factorial 10)
3628800
>>> (factorial -42)
1
"
[n]
(if (<= n 1)
1
(* n (factorial (- n 1)))))
And a stack.yaml
:
resolver: lts-16.4
packages:
- .
At this point the files under the my-first-project
directory
should look like below:
my-first-package
├── package.yaml
├── Setup.hs
├── src
│ └── MyFirstPackage.fnk
└── stack.yaml
Now one can build the my-first-package
package with stack
:
$ stack build my-first-package
... Output messages omitted ...
[1 of 2] Compiling Main
[2 of 2] Compiling StacksetupShim
... More output messages ...
[1 of 1] Compiling MyFirstPackage
Note
While building packages with stack
, one may see a warning
message saying “Unable to find a known candidate for the Cabal
entry”. Although the message says it cannot find the candidate, the
compilation process does work. This is a known issue, and hoping to
fix in not so far future.
Tip
To build a package containing Finkel source codes with the latest
finkel
built from source, one can specify the packages from
finkel git repository as
extra dependencies.
For example, the following stack.yaml
is set to build a package
in the current directory with finkel
from the git repository:
resolver: lts-16.4
packages:
- .
extra-deps:
- git: https://github.com/finkel-lang/finkel
commit: c0ce56cc220d533dc2e46e0bcf6387fed238abc7
subdirs:
- finkel-kernel
- fkc
- finkel-setup
- finkel-core
- finkel-tool
- finkel
See the stack documentation and the Cabal User Guide for more information about using remote git repository for extra dependencies.
Mixing Finkel And Haskell Source Codes¶
One can mix Finkel source codes and Haskell source codes in a package.
This time, making a package my-second-package
from Finkel specific
template:
$ stack new my-second-package https://raw.githubusercontent.com/finkel-lang/finkel/master/finkel-tool/finkel.hsfiles
Warning
At the time of writing, one may encounter messages similar to the
following when running stack new
with the above template:
Selecting the best among 17 snapshots...
* Partially matches lts-15.7
finkel-setup not found
- my-second-pkg requires -any
* Partially matches nightly-2020-03-04
finkel-setup not found
- my-second-pkg requires -any
...
Selected resolver: lts-15.7
Resolver 'lts-15.7' does not have all the packages to match your requirements.
finkel-setup not found
- my-second-pkg requires -any
This may be resolved by:
- Using '--omit-packages' to exclude mismatching package(s).
- Using '--resolver' to specify a matching snapshot/resolver
This is because the packages for finkel
is not yet uploaded to
stackage.
As the message indicates, one can pass --omit-packages
option
or --resolver
option to stack new
until the finkel
dependency packages are uploaded to the upstream, and add the git
repository to stack.yaml
.
The above command will make a directory named my-second-package
with a cabal configuration file, Setup.hs
script, and a stub
Finkel source code file. Directory contents of my-second-package
should look like below:
my-second-package
├── app
│ └── Main.hs
├── LICENSE
├── my-second-package.cabal
├── README.md
├── Setup.hs
├── src
│ └── Lib.fnk
└── test
└── Spec.hs
Note
As of cabal version 3.0.0, the file extension of an executable in a
cabal package needs to end with .hs
or .c
file
extension. From this restriction, one needs to make a wrapper file
to run an executable written in Finkel. This is why the executable
and test stanzas in cabal configuration file generated from
template contains dummy Main.hs
and Spec.hs
files instead
of .fnk
files.
Add a new file named my-second-package/src/FnkCodes.fnk
, with
Finkel source codes:
;;;; File: my-second-package/src/FnkCodes.fnk
(defmodule FnkCodes
(export fnkfactorial))
(defn (:: fnkfactorial (-> Int Int))
[n]
(if (<= n 1)
n
(* n (fnkfactorial (- n 1)))))
And another new file named my-second-package/src/HsCodes.hs
, with
Haskell source codes:
-- File: my-second-package/src/HsCodes.hs
module HsCodes
( hsfactorial
, fnkfactorial
) where
import FnkCodes
hsfactorial :: Int -> Int
hsfactorial = fnkfactorial
Modify the library
stanza of the file my-second-package.cabal
and add HsCodes
and FnkCodes
modules as shown below:
library
hs-source-dirs: src
exposed-modules: Lib
HsCodes
FnkCodes
build-depends: base >= 4.7 && < 5
build-tool-depends: finkel:finkel >= 0.0 && < 1
default-language: Haskell2010
The functions exported from HsCodes
module could be used from
Lib
module, as in compilation of cabal package without Finkel
codes. Modify the file my-second-package/src/Lib.fnk
to import
hsfactorial
and fnkfactorial
functions from HsCodes
:
;;;; File: my-second-package/src/Lib.fnk
(defmodule Lib
(export someFunc)
(import (HsCodes [hsfactorial fnkfactorial])))
(defn (:: someFunc (IO ()))
(putStrLn
(++ "From `Lib.someFunc':\n"
" hsfactorial 10 : " (show (hsfactorial 10)) "\n"
" fnkfactorial 10 : " (show (fnkfactorial 10)))))
One can build the my-second-package
with stack build
command, as
before:
$ stack build my-second-package
Note
It is also possible to use a library package containing Finkel code
from other Haskell packages as a build dependency since the
resulting object codes are compiled by compatible ghc
version.
Executable, Test, Coverage, And Haddock¶
The my-second-package
cabal package contains an executable named
my-second-package
. The executable simply invokes the
Lib.someFunc
function. To compile and run the executable:
$ stack run my-second-package:my-second-package
From `Lib.someFunc':
hsfactorial 10 : 3628800
fnkfactorial 10 : 3628800
To run tests, invoke stack test
or stack build --test
:
$ stack build --test my-second-package
To generate code coverage report, add --coverage
option when running
test:
$ stack build --test --coverage my-second-package
And, to generate haddock documentation of the package, add --haddock
option to stack build
command:
$ stack build --haddock my-second-package