├── Setup.hs ├── ChangeLog.md ├── Crew.cabal ├── stack.yaml ├── LICENSE ├── cabal.config ├── Main.hs └── README.org /Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | # Revision history for CSW 2 | 3 | ## 0.1.0.0 -- YYYY-mm-dd 4 | 5 | * First version. Released on an unsuspecting world. 6 | -------------------------------------------------------------------------------- /Crew.cabal: -------------------------------------------------------------------------------- 1 | -- Initial CSW.cabal generated by cabal init. For further documentation, 2 | -- see http://haskell.org/cabal/users-guide/ 3 | 4 | name: Crew 5 | version: 0.1.0.2 6 | synopsis: Command rewriter 7 | -- description: 8 | license: BSD3 9 | license-file: LICENSE 10 | author: Maxime Bourget 11 | maintainer: bmx007@gmail.com 12 | -- copyright: 13 | -- category: 14 | build-type: Simple 15 | extra-source-files: ChangeLog.md 16 | cabal-version: >=1.10 17 | 18 | executable crew 19 | main-is: Main.hs 20 | -- other-modules: 21 | -- other-extensions: 22 | build-depends: base >=4.7 && < 4.9 23 | , directory >= 1.2 && < 1.3 24 | , filepath >= 1.3 && < 1.5 25 | , process >= 1.2 && < 1.3 26 | , yaml >= 0.8 && < 0.9 27 | , aeson >= 0.11 && < 0.12 28 | , unordered-containers >= 0.2 && < 0.3 29 | , text >= 1.2 && < 1.3 30 | -- hs-source-dirs: 31 | default-language: Haskell2010 32 | -------------------------------------------------------------------------------- /stack.yaml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by stack init 2 | # For more information, see: http://docs.haskellstack.org/en/stable/yaml_configuration/ 3 | 4 | # Specifies the GHC version and set of packages available (e.g., lts-3.5, nightly-2015-09-21, ghc-7.10.2) 5 | resolver: lts-6.16 6 | 7 | # Local packages, usually specified by relative directory name 8 | packages: 9 | - '.' 10 | # Packages to be pulled from upstream that are not in the resolver (e.g., acme-missiles-0.3) 11 | extra-deps: [] 12 | 13 | # Override default flag values for local packages and extra-deps 14 | flags: {} 15 | 16 | # Extra package databases containing global packages 17 | extra-package-dbs: [] 18 | 19 | # Control whether we use the GHC we find on the path 20 | # system-ghc: true 21 | 22 | # Require a specific version of stack, using version ranges 23 | # require-stack-version: -any # Default 24 | # require-stack-version: >= 1.0.0 25 | 26 | # Override the architecture used by stack, especially useful on Windows 27 | # arch: i386 28 | # arch: x86_64 29 | 30 | # Extra directories used by stack for building 31 | # extra-include-dirs: [/path/to/dir] 32 | # extra-lib-dirs: [/path/to/dir] 33 | 34 | # Allow a newer minor version of GHC than the snapshot specifies 35 | # compiler-check: newer-minor 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Maxime Bourget 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Maxime Bourget nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /cabal.config: -------------------------------------------------------------------------------- 1 | constraints: aeson ==0.11.2.0, 2 | attoparsec ==0.13.0.2, 3 | array ==0.5.0.0, 4 | base ==4.7.0.2, 5 | ghc-prim ==0.3.1.0, 6 | rts ==1.0, 7 | integer-gmp ==0.5.1.0, 8 | bytestring ==0.10.4.0, 9 | deepseq ==1.3.0.2, 10 | containers ==0.5.5.1, 11 | scientific ==0.3.4.9, 12 | binary ==0.7.1.0, 13 | hashable ==1.2.4.0, 14 | text ==1.2.2.1, 15 | vector ==0.11.0.0, 16 | primitive ==0.6.1.0, 17 | transformers ==0.3.0.0, 18 | dlist ==0.7.1.1, 19 | fail ==4.9.0.0, 20 | mtl ==2.1.3.1, 21 | nats ==1.1.1, 22 | template-haskell ==2.9.0.0, 23 | pretty ==1.1.1.1, 24 | old-locale ==1.0.0.6, 25 | semigroups ==0.18.2, 26 | tagged ==0.8.4, 27 | unordered-containers ==0.2.7.1, 28 | syb ==0.6, 29 | time ==1.4.2, 30 | directory ==1.2.1.0, 31 | filepath ==1.3.0.2, 32 | unix ==2.7.0.1, 33 | process ==1.2.0.0, 34 | yaml ==0.8.18, 35 | conduit ==1.2.6.6, 36 | exceptions ==0.8.2.1, 37 | stm ==2.4.4.1, 38 | transformers-compat ==0.5.1.3, 39 | lifted-base ==0.2.3.6, 40 | monad-control ==1.0.1.0, 41 | transformers-base ==0.4.4, 42 | mmorph ==1.0.6, 43 | resourcet ==1.1.7.4, 44 | void ==0.7.1, 45 | enclosed-exceptions ==1.0.2 46 | -------------------------------------------------------------------------------- /Main.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Main where 3 | 4 | import Data.Maybe (maybeToList, fromMaybe, catMaybes) 5 | import Data.Yaml.Config (loadYamlSettings, useEnv) 6 | import System.Environment (getArgs, getProgName, setEnv, lookupEnv) 7 | import System.Directory (doesFileExist, getCurrentDirectory) 8 | import System.FilePath (splitDirectories, joinPath, normalise) 9 | import System.Process (callProcess) 10 | import GHC.Generics 11 | import Data.Aeson 12 | import qualified Data.HashMap.Strict as H 13 | import Data.Text (unpack) 14 | import Data.Traversable (traverse) 15 | import Data.List (inits) 16 | 17 | -- | find path in the current directory or above 18 | findUp :: FilePath -> FilePath -> IO [FilePath] 19 | findUp path cwd = fmap catMaybes $ mapM exists (reverse $ inits (splitDirectories (normalise cwd))) 20 | where exists dirs = do 21 | let path' = joinPath (dirs ++ [path]) 22 | found <- doesFileExist path' 23 | return $ if found 24 | then Just path' 25 | else Nothing 26 | 27 | -- * Configuration 28 | -- | Operation on String 29 | data StringOperation = Replace String | Concat (Maybe String) (Maybe String) deriving (Read, Show, Eq) 30 | 31 | -- | Top level configuration 32 | data Config = Config { progName :: String -- ^ program name 33 | , transConfig :: TransConfig 34 | , subcommands :: [(String, SubConfig)] 35 | } deriving (Read, Show) 36 | 37 | -- For JSON only 38 | data ConfigJ = ConfigJ TransConfig [(String, SubConfig)] 39 | 40 | -- | Sub command configuration 41 | data SubConfig = SubConfig 42 | { subCommand :: Maybe String 43 | , subTransConfig :: TransConfig 44 | } deriving (Read, Show) 45 | 46 | -- | transformation configuration. Can relate to a commmand (ex stac,) or a subcommand (stack build) 47 | data TransConfig = TransConfig 48 | { cmd :: Maybe String -- ^ command to substitue with 49 | , translations :: [(String, String)] 50 | , message :: Maybe String -- ^ to display before launching the command 51 | , environments :: [(String, StringOperation)] 52 | } deriving (Read, Show) 53 | 54 | emptyTransConfig = TransConfig Nothing [] Nothing [] 55 | 56 | -- * From Json 57 | 58 | invalidYamlObject v = error $ "Invalid configuration :" ++ show v 59 | objectToPairs _ Nothing = return [] 60 | objectToPairs f (Just (Object cs)) = mapM (unpackPair f) (H.toList cs) 61 | objectToPairs _ val = invalidYamlObject val 62 | unpackPair f (key, v') = do 63 | val <- f v' 64 | return (unpack key, val) 65 | 66 | unpack' (String v') = return $ unpack v' 67 | unpack' Null = return $ "" 68 | unpack' o = invalidYamlObject o 69 | 70 | instance FromJSON StringOperation where 71 | parseJSON (String v) = return $ Replace (unpack v) 72 | parseJSON (Object v) = do 73 | prepend <- v .:? "prepend" 74 | append <- v .:? "append" 75 | return $ Concat (fmap unpack prepend) (fmap unpack append) 76 | parseJSON v = invalidYamlObject v 77 | 78 | instance FromJSON TransConfig where 79 | parseJSON Null = return emptyTransConfig 80 | parseJSON (Object v) = do 81 | cmd <- v .:? "cmd" 82 | translations <- objectToPairs unpack' =<< v .:? "args" 83 | message <- v .:? "msg" 84 | environments <- objectToPairs parseJSON =<< v .:? "env" 85 | 86 | return $ TransConfig cmd 87 | translations 88 | message 89 | environments 90 | 91 | parseJSON v = invalidYamlObject v 92 | 93 | instance FromJSON SubConfig where 94 | parseJSON (String subCmd) = return $ SubConfig (Just $ unpack subCmd) emptyTransConfig 95 | parseJSON o@(Object v) = do 96 | subCommandName <- v .:? "subcmd" 97 | trans <- parseJSON o 98 | 99 | return $ SubConfig (fmap unpack subCommandName) trans 100 | 101 | parseJSON v = invalidYamlObject v 102 | 103 | instance FromJSON ConfigJ where 104 | parseJSON o@(Object v) = do 105 | subs <- objectToPairs parseJSON =<< v .:? "sub" 106 | trans <- parseJSON o 107 | 108 | return $ ConfigJ trans subs 109 | 110 | parseJSON v = invalidYamlObject v 111 | 112 | 113 | 114 | 115 | 116 | 117 | -- | find an load the configuration for the given command 118 | -- lookup for the ".cws" file in current file or up (recursively) 119 | -- and find the corresponding entry 120 | loadConfig :: String -> IO (Maybe Config) 121 | loadConfig command = do 122 | cwd <- getCurrentDirectory 123 | paths <- findUp ".crew" cwd 124 | case paths of 125 | [] -> return Nothing 126 | _ -> do 127 | configs <- loadYamlSettings paths [] useEnv 128 | return $ do 129 | ConfigJ trans sub <- (H.lookup command configs) 130 | return $ Config command trans sub 131 | 132 | -- | Split a string on space, unless 133 | -- there spaces are escaped 134 | splitOnSpace :: String -> [String] 135 | splitOnSpace xs = go [] [] xs where 136 | go result current [] = result ++ [current] 137 | go result current ('\\':' ':xs) = go result (current++" ") xs 138 | go result current (' ':xs) = go (result ++ [current]) [] xs 139 | go result current (x:xs) = go result (current++[x]) xs 140 | 141 | 142 | translate :: Config -> String -> [String] -> (String, [String], TransConfig) 143 | -- current transConfig can be overriden by subcommand one 144 | -- we need first to try to find if the first argument is a registered subcommand 145 | translate config command args@(subCmd:_) | Just subConf <- lookup subCmd (subcommands config) = 146 | let globalTrans = transConfig config 147 | subTrans = subTransConfig subConf 148 | merge f | f subTrans == Nothing = f globalTrans 149 | merge f = f subTrans 150 | mergeL f | f subTrans == [] = f globalTrans 151 | mergeL f = f subTrans 152 | trans = TransConfig (merge cmd) (mergeL translations) (merge message) (mergeL environments) 153 | 154 | in translate' config { transConfig = trans } command args 155 | 156 | -- | Can probably be rewritten to only use TransConfig but we are using 157 | -- the old code 158 | translate config command args = translate' (config) command args 159 | 160 | translate' config command args = let 161 | subc = transConfig config 162 | (command:extra) = maybe [progName config] splitOnSpace (cmd subc) 163 | (sub, args') = translateSubcommand config args 164 | in ( command 165 | , extra ++ sub ++ concatMap (translateArgument subc) args' 166 | , subc) 167 | 168 | -- | only translate the full word ending to an == 169 | translateArgument :: TransConfig -> String -> [String] 170 | translateArgument config cs = 171 | let (arg, value) = span (/= '=') cs 172 | in case lookup arg (translations config) of 173 | Nothing -> [cs] 174 | Just "" -> [] 175 | Just new -> splitOnSpace (new ++ value) 176 | 177 | -- | check if the first argument is a subcommand and translate it 178 | translateSubcommand :: Config -> [String] -> ([String], [String]) 179 | translateSubcommand _ [] = ([], []) 180 | translateSubcommand config all@(sub:args) = 181 | case lookup sub (subcommands config) of 182 | Just subC | Just new <- subCommand subC -> (splitOnSpace new, args) 183 | Nothing -> ([], all) 184 | 185 | execute :: String -> [String] -> IO () 186 | execute command args = callProcess command args 187 | 188 | setEnvironment :: TransConfig -> IO () 189 | setEnvironment config = mapM_ set (environments config) where 190 | set (var, op) = do 191 | new <- case op of 192 | Replace value -> return value 193 | Concat prepend append -> do 194 | value <- lookupEnv var 195 | return . concat $ catMaybes [prepend , value , append] 196 | 197 | -- print (var, new) 198 | setEnv var new 199 | main :: IO () 200 | main = do 201 | command <- getProgName 202 | args <- getArgs 203 | 204 | 205 | configM <- loadConfig command 206 | -- print configM 207 | case configM of 208 | Nothing -> error $ "No configuration found for command " ++ command 209 | Just config -> do 210 | let (command', args', transc) = translate config command args 211 | -- print command' 212 | -- print args' 213 | -- print transc 214 | setEnvironment transc 215 | _ <- traverse putStrLn (message transc) 216 | execute command' args' 217 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * CREW the command line rewriter 2 | *Crew* is a tool to rewrite commands on the fly depending on the current directory or project. 3 | Basic use cases are : 4 | - selecting the correct version of a tool , ex =ghc-7.8.4= or =ghc-7.10.3= 5 | - integrating =cabal new-build= with current editor plugin. 6 | - using =stack= for tool designed to use =cabal= 7 | - etc ... 8 | 9 | *Crew* allows to change the executable, modify or even drop arguments, and set environment variables. 10 | *Crew* is written in Haskell and has been designed to solve some problems specific to Haskell tooling (mainly cabal new-build and ghc-mod), 11 | can be used to anything which needs a *aliasing* per directory. 12 | 13 | However, it is at the moment still experimental and I'm taking any responsibilities if anything happen using it. 14 | *Crew* has been designed for Linux. It should compile on Windows, but I don't know if it is usable or not. 15 | 16 | * How it works 17 | When *Crew* is executed, it tries locate a =.crew= file an located in the current directory or above. 18 | It applies then the transformation corresponding to the name it's been invocated with (.i.e =argv[0]=). 19 | 20 | ** Setup 21 | In order to execute *Crew* instead of the command to rewritten, the trick is to create of copy of *Crew* with the 22 | name of the command to rewrite. Of course, this new command needs to be in the PATH in a directory with a higher priority. 23 | 24 | I recommend to create a =.crew-bin= directory in your home directory and setup your path to use it first. 25 | Add =PATH=~/.crew-bin:$PATH= at the end of your usual rc. However to make it work with program launched from the desktop, 26 | you might need to add in your =.profile= as well. 27 | 28 | Let's say you want to tweak =cabal= to call =new-build= whenever =build= is called. You'll need to link *crew* as *cabal* in your=.crew-bin= directory. 29 | If you have installed *crew* using =cabal= it should be in =~/.cabal/bin=. So you'll need =cd .crew-bin; ln -s ~/.cabal/bin/crew cabal=. 30 | 31 | Now, you'll need to tell were the real =cabal= is, otherwise *Crew* will just loop, trying to call =cabal= but only finding itself. 32 | For this, create a =.crew= in your home directory with the following : 33 | 34 | 35 | #+BEGIN_SRC yaml 36 | cabal: 37 | cmd: /home//.cabal/bin/cabal 38 | #+END_SRC 39 | 40 | Alternatively you can alter the path to find the real cabal. You don't need them to specify the command. *Crew* will use the orginal command. 41 | 42 | #+BEGIN_SRC yaml 43 | cabal: 44 | path: 45 | prepend: "/home/:" 46 | #+END_SRC 47 | 48 | Note at the moment, path needs to be absolute (and shell expansion like =~= doesn't work yet). 49 | To check that you are calling crewed and not the original cabal, you can add a message in the configuration file 50 | 51 | #+BEGIN_SRC yaml 52 | cabal: 53 | cmd: /home//.cabal/bin/cabal 54 | msg: This is a crewed version of cabal 55 | #+END_SRC 56 | 57 | Any invocation of =cabal= should display =This a crewed version of cabal= before any real cabal messages. 58 | 59 | Now, we want to replace =cabal build= with =cabal new-build= but only for the =test-newbuild= project. 60 | Create a new =.crew= file in the =test-newbuild= directory with the following 61 | 62 | #+BEGIN_SRC yaml 63 | cabal: 64 | msg: CREW: build => new-build 65 | sub: 66 | build: new-build 67 | #+END_SRC 68 | 69 | =sub= if for subcommand. It only replace =build= by =new-build= if build is the first argument of cabal. 70 | We don't need to specify the =cmd=. They will be inherited from the =~/.crew=. 71 | 72 | If you type =cabal build=, you should see the =CREW: build => new-build= (or new message and the result new-build). 73 | That's not really exciting, you could have typed =cabal new-build= yourself, but it's handy if you use a text editor with 74 | some shortcut bound to call =cabal build=. For example, I use Spacemacs, and pressing = c c= will now result in a new-build. 75 | 76 | I have other projects using stack. I can configure *Crew* to call =stack build= when Spacemacs try to call =cabal build= (see examples). 77 | 78 | 79 | ** Configuration file 80 | The configuration file is a simple YAML file, which each entry corresponding to a command and different sections. 81 | *** Command 82 | =cmd= specifies the command to execute. 83 | *** Subcommand 84 | =sub= specifies a list of mapping for subcommands, i.e. the first argument of a command. 85 | =sub= can be either a value, or a mappings. A mapping allows to specify a different set of arguments mappings, 86 | environment variables and message for each subcommand. Use =smdcmd= to specify a different sub-command within a mapping. 87 | 88 | The following example redirect =cabal build= to =cabal new-build= 89 | #+BEGIN_SRC yaml 90 | cabal: 91 | sub: 92 | build: new-build 93 | #+END_SRC 94 | 95 | We can add a message specify to the build command, this way : 96 | 97 | #+BEGIN_SRC yaml 98 | cabal: 99 | cmd: stack 100 | sub: 101 | build: 102 | subcmd: new-build 103 | msg: "Cabal new-build" 104 | #+END_SRC 105 | 106 | Note, the need to add =subcmd: new-build=, to specify the sub-command mapping. 107 | *** Arguments 108 | =args= specifies a list of mapping for all arguments. At the moment only long argument of flag are replaced. Can be specified at a command or sub-command level. 109 | *** Skiping arguments 110 | An argument can be skipped by replacing with an empty string. 111 | *** Message 112 | =msg= specifies a message to display before executing the command. 113 | Can be specified at a command or sub-command level. 114 | *** Setting environment variables 115 | =env= specifies a list of environment variables to set or modify. 116 | Variable can be either set with a new value or modified by prepending and/or appending a value to the existing value. 117 | Examples; 118 | 119 | #+BEGIN_SRC yaml 120 | PATH: /home/user/.local/bin 121 | #+END_SRC 122 | Set =PATH= to =/home/user/.local/bin= 123 | 124 | #+BEGIN_SRC yaml 125 | PATH: 126 | prepend: "/home/user/.local/bin:" 127 | append: :/home/user/.other/bin 128 | #+END_SRC 129 | 130 | set is equivalent to =/home/user/.local/bin:$PATH:/home/user.other/bin=. Note the =prepend= value needs to be between quote. 131 | This is due to yaml not liking value ending with =:=. 132 | 133 | 134 | At the moment, environment variable in the value are not expended. 135 | Can be specified at a command or sub-command level. 136 | *** Inheritence 137 | All files name =.crew= in or above the current directory are loaded with children version overriding parent one. 138 | For example, in our example above, =cmd= is defined in =~/.crew= but =msg= is defined in =~/.crew= AND =~/test-newbuild/.crew=. 139 | =cmd= will be inherited from =~/.crew= but =msg= will use =msg= from =~/test-newbuild= 140 | *** Reading environment variables 141 | Values can be overridden with environment variable using the =_env:VAR:default= syntax (taken from =Yesod=). 142 | Example : 143 | 144 | #+BEGIN_SRC yaml 145 | cabal: 146 | msg: _env:CABAL_MESSAGE: CREW: build => new-build 147 | #+END_SRC 148 | 149 | Typing =cabal= will result in 150 | 151 | #+BEGIN_SRC shell 152 | > cabal 153 | CREW: build => new build 154 | cabal: no command given (try --help) 155 | #+END_SRC 156 | 157 | But if CABAL_MESSAGE is set if will be used instead of the default message. 158 | 159 | 160 | #+BEGIN_SRC shell 161 | > CABAL_MESSAGE="env message" cabal 162 | env message 163 | cabal: no command given (try --help) 164 | #+END_SRC 165 | 166 | * Examples 167 | ** cabal new-build 168 | Cabal-1.24 introduce a NIX-style build. This is a great feature but it requires some new commands instead. We can use crew to map the old command to the new ne. 169 | 170 | #+BEGIN_SRC yaml 171 | cabal: 172 | cmd: /cabal 173 | sub: 174 | build: new-build 175 | configure: new-configure 176 | repl: new-repl 177 | old-build: build 178 | old-configure: configure 179 | old-repl: repl 180 | #+END_SRC 181 | 182 | In case you need, the old commands, they are mapped as =old-=. 183 | 184 | 185 | ** redirect cabal build in Spacemacs to use stack 186 | Another use of *Crew* is to redirect = c c= in Spacemacs to stack. This can be achieved with the following 187 | Here we don't need to map the sub-command are they are both called build. However, the argument to pass some options 188 | to GHC has a different name, so we remap it. 189 | 190 | #+BEGIN_SRC yaml 191 | cabal: 192 | cmd: /stack 193 | args: 194 | --ghc-option: --ghc-options 195 | #+END_SRC 196 | 197 | ** Add watch command to stack 198 | The =stack build --file-watch --fast= automatically builds your project on change. We can alias it to =stack watch= : 199 | 200 | #+BEGIN_SRC yaml 201 | stack: 202 | watch: build --file-watch --fast 203 | #+END_SRC 204 | ** ghc-mod 205 | =ghc-mod= needs to be compiled with the same version of GHC than the code your use =ghc-mod= for. 206 | This is a problem when working with projects using a different version of GHC, as you can only have 207 | one version of ghc-mod installed globally at the same time. 208 | A solution to this problem is to rename ghc-mod with it's version number and use crew to select the appropriate version depending on the project. 209 | 210 | In a directory using GHC-7.8.4 211 | 212 | #+BEGIN_SRC yaml 213 | ghc-mod: 214 | cmd: ghc-mod-7.8.4 215 | #+END_SRC 216 | 217 | In a directory using GHC-7.10.3 218 | #+BEGIN_SRC yaml 219 | ghc-mod: 220 | cmd: ghc-mod-7.10.3 221 | #+END_SRC 222 | 223 | 224 | * Todo 225 | This a work in progress, pull requests are welcomes ! 226 | 227 | ** TODO add global configuration 228 | ** TODO expand home variable 229 | ** DONE allows multiple arguments expansion 230 | ** DONE prepend append value to env variable 231 | CLOSED: [2016-09-14 Wed 21:51] 232 | allow things similar PATH = newpath:$PATH 233 | ** TODO add log options 234 | ** TODO display message to sderr 235 | ** TODO add section 236 | example -mpatter => --test-arguments -mpatter 237 | ** TODO crew command to generate links and default config 238 | ** TODO options to bypass crew 239 | execute command by removing =.crew-bin= from the path 240 | 241 | ** TODO use regexp 242 | --------------------------------------------------------------------------------