Skip to content


Folders and files

Last commit message
Last commit date

Latest commit



31 Commits

Repository files navigation

A Shake C++ Framework

This project is a practical example of using shake against a fairly small C++ build problem case. The design concepts of shake-cpp can likely be applied to non-C++ build problems as well, so this is being made public for educational purposes to the Shake/Haskell community.

The following is an experimental abstraction layer for the Shake build system , specifically for abstracting away concerns of:

  • C++ toolchain, e.g. linker and compiler
  • testing tools
  • path conventions, e.g. ./src vs ./sources

Main design approach:

  • shake-cpp moves *.cpp <-> *.o and *.o <-> *.exe etc isomorphisms to a single data-structure, and generalizes operations on that. This approach works very well in managing the complexity of shake rule patterns:
buildPaths :: FilePath -> BuildPaths 
buildPaths build_par =  
  BuildPaths {  
    outputPfx   = build_par, -- E.g. .build/ or build_ or dist etc
    testLib     = "test-lib",
    -- An isomorphism, the second member is prefixed with outputPfx,
    -- so ".build/bin/a/b/last.o" when going from left to right,
    --   for input "src/a/b/"
    sourceObj   = Iso "src"   "bin", 
    testObj     = Iso "tests" "tests", 
    testExec    = Iso (build_par </> "tests") "test-bin",
    testStates  = Iso (build_par </> "test-bin") "test-state",
    archives    = "lib"
  • shake-cpp enhances shake rules with a monad, BuildM that passes an environment Env to build targets and tool-chain calls.
srcRules :: BuildM Rules ()
srcRules = do 
  -- rule for an archive, named "./lib/project-lib.o", comprised of sources
  -- under the src/foo/ directory
  Rule.archive "project-lib" [Leaves "foo" False False]

  -- rule for objects built from sources in "src/"
  Rule.object sourceObj [] Deps.llvmObj 

testRules :: BuildM Rules ()
testRules = do 
  src_root <- inputDir sourceObj
  test_root <- inputDir testObj

  Rule.object testObj []
    (Deps.llvmObj <> Deps.boostTestObj src_root test_root) 

    (Deps.boostTestExec projectName <> Deps.clang <> Deps.llvmExec ) 

  -- Rule for test pass states, this makes "./shake .build/test-state/test_example.pass" 
  -- as a test runner case possible
  -- Bind source rules into this monad (this is a super-set of those)
  -- clean rule, derived from BuildPaths

and in the module Deps example:

module Deps where

import Development.Shake.Cpp.Build (Def)
import Development.Shake.Cpp.ObjectDeps
import Development.Shake.Cpp.ExecDeps
import Development.Shake.Cpp.Obj
import Data.Monoid
import qualified Data.Set as S 
import qualified Data.Map as M
import qualified Data.List as L

boostTestExec :: FilePath -> ExecDeps
boostTestExec subject_archive = 
    (M.singleton subject_archive Archive)
    $ Lib
        ["boost_system", "boost_thread", "boost_unit_test_framework"]

boostTestObj :: FilePath -> FilePath -> ObjectDeps     
boostTestObj src_root test_root = 
    (S.fromList [src_root, test_root])

llvmInc :: FilePath
llvmInc = "/usr/lib/llvm-3.5/include"

llvmDefs :: S.Set Def 
llvmDefs = 

llvmObj :: ObjectDeps
llvmObj = 
  ObjectDeps llvmDefs (S.fromList ["/usr/lib/llvm-3.5/include"]) 

llvmExec :: ExecDeps 
llvmExec = ExecDeps mempty [Evaluated "llvm-config --libs all --ldflags"] 

clang :: ExecDeps
clang = mempty { 
  exeLinked = Lib


A Shake C++ Framework






No releases published


No packages published