Skip to content

maxigit/crew

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CREW the command line rewriter

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.4 or ghc-7.10.3
  • integrating cabal new-build with current editor plugin.
  • using stack for tool designed to use cabal
  • 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.

How it works

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]).

Setup

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/cabal

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.

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 cabal

Any 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-build

sub 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).

Configuration file

The configuration file is a simple YAML file, which each entry corresponding to a command and different sections.

Command

cmd specifies the command to execute.

Subcommand

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-build

We 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.

Arguments

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.

Skiping arguments

An argument can be skipped by replacing with an empty string.

Message

msg specifies a message to display before executing the command. Can be specified at a command or sub-command level.

Setting environment variables

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/bin

Set PATH to /home/user/.local/bin

PATH:
   prepend: "/home/user/.local/bin:"
   append: :/home/user/.other/bin

set 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.

Inheritence

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

Reading environment variables

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-build

Typing 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)

Examples

cabal new-build

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: repl

In case you need, the old commands, they are mapped as old-.

redirect cabal build in Spacemacs to use stack

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-options

Add watch command to stack

The stack build --file-watch --fast automatically builds your project on change. We can alias it to stack watch :

stack:
   watch: build --file-watch --fast

ghc-mod

ghc-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.4

In a directory using GHC-7.10.3

ghc-mod:
   cmd: ghc-mod-7.10.3

Todo

This a work in progress, pull requests are welcomes !

add global configuration

expand home variable

allows multiple arguments expansion

prepend append value to env variable

allow things similar PATH = newpath:$PATH

add log options

display message to sderr

add section

example -mpatter => –test-arguments -mpatter

crew command to generate links and default config

options to bypass crew

execute command by removing .crew-bin from the path

use regexp

Releases

No releases published

Packages