We are in the process of open sourcing a lot of the general purpose libraries we’ve built at Silk under the BSD3 license. We’re planning to write a series of blog posts introducing them so please stay tuned!
fay-builder lets you store options needed to compile Fay code along
with an executable. This is done by using custom cabal flags that can
be read either by
Setup.hs (but see caveats below) or from within the
application at startup, on request, or at some other point.
If you want to try it out you can install it by running
Join our team if you enjoy this stuff too, we’re hiring!
The Cabal file
Here’s how you can define a Cabal file that uses fay-builder for a project:
name: my-program version: 0.1
Use a custom build type
extra-source-files specifies files what should be included with a
source distribution. The fay files are added here so they don’t get
lost when running
data-files are also included, with the addition that Cabal will
generate a paths module that you can use to fetch the resources during
runtime. We probably want to precompile the fay files so the program
can just serve them.
data-files: my-program.cabal, js/*.js
x-foo specifies custom flags that Cabal itself ignores, but we can
use it for extra configuration options.
x-fay-packages: fay-jquery, fay-text x-fay-root-modules: Index, Misc x-fay-include-paths: src x-fay-output-dir: data/js x-fay-source-dir: fay executable my-program main-is: Main.hs hs-source-dirs: src default-language: Haskell2010 ghc-options: -Wall
This is the haskell module Cabal generates to let us access data-files.
To make sure we can build the Fay code we add all the dependencies it has here along with the GHC libraries dependencies.
One thing to note is that we can’t depend on base and fay-base at the same time since they have clashing module names. But if we depend on some other fay package we get a transitive dependency to fay-base and we know it will be available.
build-depends: base, Cabal, fay, fay-builder, fay-jquery, fay-text
We have a few options for when to build the Fay code.
Building as a Cabal hook
With a custom Setup.hs we can add a postBuildHook that will compile the Fay code after the executable has been compiled, but before an sdist is created. This is nice because it will generate the needed .js files before the tarball is created. The disadvantage is that Setup.hs cannot have explicit dependencies so you won’t be able to sdist your program unless you already have all dependencies installed.
It is possible to do this, and fay-builder includes a
that you can use in Setup.hs like this:
import Fay.Builder (defaultFayHook) main = defaultFayHook
It’s pretty cool, but maybe not very useful because of the dependency
issue. Just make sure to have fay-builder installed before running
Building inside the program
For one application I started out with the above Setup.hs approach,
but soon realized that this would not work out since sdisting wasn’t
possible in a clean package DB. I instead chose to add a
--compile-fay flag that would compile all Fay sources when the
process starts when developing. The live setup won’t do this and
instead read the data-files.
The trick here is that we added the .cabal file as a data-file to let us access it from other modules.
import qualified Paths_my_program as Paths import qualified Fay.Builder as Builder compileFay :: IO () compileFay = do pkgDb <- getPackageDb packageDesc <- Paths.getDataFileName "my-program.cabal" >>= Builder.readPackageDescription Builder.build packageDesc pkgDb where -- Some clever way to fetch the current package DB -- if you are using a sandbox. getPackageDb :: IO (Maybe FilePath)
If we had added the settings elsewhere, such as in a custom
does, we wouldn’t be able to read it from within
Setup.hs so the cabal
file might be the only place to put this configuration if we want both
features at once.
One of the biggest advantages in using fay-builder is that all dependencies can be listed in one cabal file instead of having to split the client and server code into multiple packages (which would turn into three different packages if there’s shared code).