Building Command Line Apps With Gradle

Over the course of the last years, the build automation tool gradle has established itself as the favourite kid on the block regarding build tools. It is used in many great open source projects, e.g., hibernate and grails, and is now even the default build system for Android apps.

In betsy, one major open source project of the distributed systems group, we also use gradle. I want to elaborate one particular use case we had in mind when leveraging gradle for our project.

Our focus was to have one simple command that can do the following:

  • download all dependencies (including the build tool itself)
  • compile the software
  • run the software passing it arguments from the command line

Most build tools today can download all dependencies via a repository like central maven and compile the software. Downloading itself as part of the build process, however, is not an easy task. The same holds true for starting the built software with passed parameters as part of the build process. I want to show you how we achieved it with the help of gradle.

Download all dependencies (including the build tool itself)

Gradle ships with the amazing feature of the Gradle Wrapper which consists of a shell and a batch script that can automatically download gradle and start the gradle build. Its only dependencies are that Java has to be installed. These scripts can be automatically generated with gradle. To achieve this, you need to define the wrapper task in the build.gradle script like that:

task wrapper(type: Wrapper) {
    gradleVersion = '1.7'
}

Then, by running the command gradle wrapper on the command line, we generate the gradlew and the gradlew.bat files. Thus, no local gradle installation is required anymore, as we can use these scripts instead. This allows developers interested in our project to try it out with much less hassle.

Run the software passing it arguments from the command line

To be able to run the software directly from gradle, we use the application plugin which lets us define a Main class which can be started using the run command. To achieve this, we extend the build.gradle file as follows:

apply plugin: "application"
mainClassName = "some.package.MyMainClass"

With this extension, we can run our application with gradlew run. However, it is not possible to pass in arguments from the command line, yet. For this, we use the following extension of the run task:

run {
    if(project.hasProperty('args')){
        args project.args.split('\\s+')
    }
}

Then, we can pass in arguments using the args property via gradlew run -Pargs=”–my args for the system”. This works really well, however, we always have to type in gradlew run -Pargs=”” when we want to pass in custom parameters. Thus, we introduced one shell and one batch script to circumvent that problem. We named these scripts after our project, in our case betsy and betsy.bat.

gradlew run -Pargs="$*" # contents of the betsy shell script
gradlew run -Pargs="%*" REM contents of the betsy batch script

Now, we are able to download gradle, run gradle with our build script, download all dependencies, compile our tool and start the tool with our parameters in an easy way by only typing in betsy -o bpelg Sequence.

Advertisements

Find the most used first word (and every word) in your git commit messages

Via Twitter, I found this interesting gist (https://gist.github.com/2140115) which contains a one-line bash command for “Find[ing] the most used verbs in your git commit messages” in a git repo.

$ git log --pretty=format:'%s' | cut -d " " -f 1 | sort | uniq -c | sort -nr
      2 Added
      1 Improved

In the following, I have extracted the neccessary information from the help/man pages to understand, how this is achieved. I also preserved the intermediate states which are printed after a command is explained.

git log # "Show commit logs"(git log --help)
git log --pretty=format:'%s' # print each commit log as a line containing solely its subject

$ git log --pretty=format:'%s'
Improved README
Added CHANGELOG
Added README

cut # "print selected parts of lines from each FILE to standard output."(cut --help)
cut -d, --delimiter=DELIM # "use DELIM instead of TAB for field delimiter"(cut --help)
cut -f, --fields=LIST # "output only these fields"(cut --help)
cut -d " " -f 1 # split line by " " and output only the first field

$ git log --pretty=format:'%s' | cut -d " " -f 1
Improved
Added
Added

sort # "Write sorted concatenation of all FILE(s) to standard output."(sort --help)

$ git log --pretty=format:'%s' | cut -d " " -f 1 | sort
Added
Added
Improved

uniq # "Discard all but one of successive identical lines from INPUT (or standard input), wirting to OUTPUT (or standard output)"(uniq --help)
uniq -c, --count # "prefix lines by the number of occurrences"(uniq --help)

$ git log --pretty=format:'%s' | cut -d " " -f 1 | sort | uniq -c
      2 Added
      1 Improved

sort # "Write sorted concatenation of all FILE(s) to standard output."(sort --help)
sort -r # "reverse the result of comparisons"(sort --help)
sort -n # "compare according to string numerical value, imply -b"(sort --help)
sort -b # "ignore leading blanks in sort fields or keys"(sort --help)

$ git log --pretty=format:'%s' | cut -d " " -f 1 | sort | uniq -c | sort -nr
      2 Added
      1 Improved

If you want all the words (and not only the first words of each commit), the cut command does not suffice. In that case, sed, a “stream editor for filtering and transforming text”(sed man page) can be leveraged. It is shown in the following.

$ git log --pretty=format:'%s'
Improved README
Added CHANGELOG
Added README

sed # "stream editor for filtering and transforming text"(sed man page)
sed s/regexp/replacement/ # "Attempt to match regexp against the pattern space. If successful, replace that portion matched with replacement."(sed --help)
\s # whitespace character
\n # new line
sed 's/\s/\n/g' # replace all whitespace characters by new lines

$ git log --pretty=format:'%s' | sed 's/\s/\n/g'
Improved
README
Added
CHANGELOG
Added
README

$ git log --pretty=format:'%s' | sed 's/\s/\n/g' | sort
Added
Added
CHANGELOG
Improved
README
README

$ git log --pretty=format:'%s' | sed 's/\s/\n/g' | sort | uniq -c
      2 Added
      1 CHANGELOG
      1 Improved
      2 README

$ git log --pretty=format:'%s' | sed 's/\s/\n/g' | sort | uniq -c | sort -nr
      2 README
      2 Added
      1 Improved
      1 CHANGELOG

But beware, as this can be too much for your command line buffer to handle. This can be solved by piping the result to less.

$ git log --pretty=format:'%s' | sed 's/\s/\n/g' | sort | uniq -c | sort -nr | less
      2 README
      2 Added
      1 Improved
      1 CHANGELOG

It can still be improved, e.g., to convert all upper case letters to lower case ones. This is possible using tr '[:upper:]' '[:lower:]' which uses the tr tool that is able to “translate, squeeze, and/or delete characters from standard input, writing to standard output”(tr --help). In that case it translates every upper case letter to a lower case one while copying the other characters.

Starting Sublime Text 2 from the Command Line

My all-new-favorite text editor is Sublime Text 2. This editor is blazingly fast, has a project view for simple navigation, supports syntax highlighting for allmost all formats out of the box and can be extended. I often have to work in the command line, however, I do not want to use vim. Thus, I have to start and navigate to my project in the command line as well as in my editor. Additionally, opening files in my editor instead of vim has to be done using my windows explorer.

Scripting to the rescue!

I wrote a small bash script, which can be used in cygwin/git bash environments for starting sublime:

#!/bin/sh
/c/Program\ Files/Sublime\ Text\ 2/sublime_text.exe $1 &

A few comments to this script:

  • The first line indicates that this is a script which is executed using /bin/sh (the standard shell)
  • The second line executes the sublime_text.exe. Note, that spaces in the folders had to be escaped using the backslash character (\).
  • $1 refers to the first parameter passed to this script. This can be a file or a folder which is passed on to Sublime Text 2 which in turn automatically opens the file or in case of a folder adds it to the current project.
  • The & sign at the end moves this process into the background. This allows to use the current command line further while Sublime Text 2 is running (as it is NOT running in the foreground of the command line. )

Of course, you have to chmod +x this file to make it executable and place it into a folder which is within the PATH. Perhaps renaming the script to sl would be nice in order to reduce typing.

But then, you can use the script as follows to open the current directory:

$> sublime .

Or open the test.txt file:

$> sublime test.txt

TextMate (solely available on the Mac) already has such a feature named mate.