Crew is a tool to rewrite commands on the fly depending on the current directory or project. Basic use cases are :
- selecting the correct version of a tool , ex
ghc-7.8.4orghc-7.10.3 - integrating
cabal new-buildwith current editor plugin. - using
stackfor tool designed to usecabal - etc …
Crew allows to change the executable, modify or even drop arguments, and set environment variables. Crew is written in Haskell and has been designed to solve some problems specific to Haskell tooling (mainly cabal new-build and ghc-mod), can be used to anything which needs a aliasing per directory.
However, it is at the moment still experimental and I’m taking any responsibilities if anything happen using it. Crew has been designed for Linux. It should compile on Windows, but I don’t know if it is usable or not.
When Crew is executed, it tries locate a .crew file an located in the current directory or above.
It applies then the transformation corresponding to the name it’s been invocated with (.i.e argv[0]).
In order to execute Crew instead of the command to rewritten, the trick is to create of copy of Crew with the name of the command to rewrite. Of course, this new command needs to be in the PATH in a directory with a higher priority.
I recommend to create a .crew-bin directory in your home directory and setup your path to use it first.
Add PATH=~/.crew-bin:$PATH at the end of your usual <shell>rc. However to make it work with program launched from the desktop,
you might need to add in your .profile as well.
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.
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.
Now, you’ll need to tell were the real cabal is, otherwise Crew will just loop, trying to call cabal but only finding itself.
For this, create a .crew in your home directory with the following :
cabal:
cmd: /home/<user>/.cabal/bin/cabalAlternatively 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.
cabal:
path:
prepend: "/home/<user>:"Note at the moment, path needs to be absolute (and shell expansion like ~ doesn’t work yet).
To check that you are calling crewed and not the original cabal, you can add a message in the configuration file
cabal:
cmd: /home/<user>/.cabal/bin/cabal
msg: This is a crewed version of cabalAny invocation of cabal should display This a crewed version of cabal before any real cabal messages.
Now, we want to replace cabal build with cabal new-build but only for the test-newbuild project.
Create a new .crew file in the test-newbuild directory with the following
cabal:
msg: CREW: build => new-build
sub:
build: new-buildsub if for subcommand. It only replace build by new-build if build is the first argument of cabal.
We don’t need to specify the cmd. They will be inherited from the ~/.crew.
If you type cabal build, you should see the CREW: build => new-build (or new message and the result new-build).
That’s not really exciting, you could have typed cabal new-build yourself, but it’s handy if you use a text editor with
some shortcut bound to call cabal build. For example, I use Spacemacs, and pressing <SPC> c c will now result in a new-build.
I have other projects using stack. I can configure Crew to call stack build when Spacemacs try to call cabal build (see examples).
The configuration file is a simple YAML file, which each entry corresponding to a command and different sections.
cmd specifies the command to execute.
sub specifies a list of mapping for subcommands, i.e. the first argument of a command.
sub can be either a value, or a mappings. A mapping allows to specify a different set of arguments mappings,
environment variables and message for each subcommand. Use smdcmd to specify a different sub-command within a mapping.
The following example redirect cabal build to cabal new-build
cabal:
sub:
build: new-buildWe can add a message specify to the build command, this way :
cabal:
cmd: stack
sub:
build:
subcmd: new-build
msg: "Cabal new-build"Note, the need to add subcmd: new-build, to specify the sub-command mapping.
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.
An argument can be skipped by replacing with an empty string.
msg specifies a message to display before executing the command.
Can be specified at a command or sub-command level.
env specifies a list of environment variables to set or modify.
Variable can be either set with a new value or modified by prepending and/or appending a value to the existing value.
Examples;
PATH: /home/user/.local/binSet PATH to /home/user/.local/bin
PATH:
prepend: "/home/user/.local/bin:"
append: :/home/user/.other/binset is equivalent to /home/user/.local/bin:$PATH:/home/user.other/bin. Note the prepend value needs to be between quote.
This is due to yaml not liking value ending with :.
At the moment, environment variable in the value are not expended. Can be specified at a command or sub-command level.
All files name .crew in or above the current directory are loaded with children version overriding parent one.
For example, in our example above, cmd is defined in ~/.crew but msg is defined in ~/.crew AND ~/test-newbuild/.crew.
cmd will be inherited from ~/.crew but msg will use msg from ~/test-newbuild
Values can be overridden with environment variable using the _env:VAR:default syntax (taken from Yesod).
Example :
cabal:
msg: _env:CABAL_MESSAGE: CREW: build => new-buildTyping cabal will result in
> cabal
CREW: build => new build
cabal: no command given (try --help)But if CABAL_MESSAGE is set if will be used instead of the default message.
> CABAL_MESSAGE="env message" cabal
env message
cabal: no command given (try --help)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.
cabal:
cmd: <path>/cabal
sub:
build: new-build
configure: new-configure
repl: new-repl
old-build: build
old-configure: configure
old-repl: replIn case you need, the old commands, they are mapped as old-.
Another use of Crew is to redirect <SPC> c c in Spacemacs to stack. This can be achieved with the following
Here we don’t need to map the sub-command are they are both called build. However, the argument to pass some options
to GHC has a different name, so we remap it.
cabal:
cmd: <path>/stack
args:
--ghc-option: --ghc-optionsThe stack build --file-watch --fast automatically builds your project on change. We can alias it to stack watch :
stack:
watch: build --file-watch --fastghc-mod needs to be compiled with the same version of GHC than the code your use ghc-mod for.
This is a problem when working with projects using a different version of GHC, as you can only have
one version of ghc-mod installed globally at the same time.
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.
In a directory using GHC-7.8.4
ghc-mod:
cmd: ghc-mod-7.8.4In a directory using GHC-7.10.3
ghc-mod:
cmd: ghc-mod-7.10.3This a work in progress, pull requests are welcomes !
allow things similar PATH = newpath:$PATHexample -mpatter => –test-arguments -mpatter
execute command by removing .crew-bin from the path