Command-line interface: attention to details
About the Ceylon command-line interface
We’re programmers, so let’s face it, we spend lots of time in those small obscure windows with tiny text, preferably with a green or black background, that Hollywood movies often display scrolling a lot, really fast, you know? Terminals.
While good IDEs are required, and the Ceylon IDE is a must-have for Ceylon developers, they can never replace a good terminal session with the proper command-line interface (CLI) tools.
Oh, I know Eclipse has good Git support and I know a few colleagues who use it for committing and branching and other daily, nay, hourly push/pull command. But I really love my CLI when it comes to using Git, I never use Eclipse for that, so I spend a lot of time in my terminal.
Also, it’s essential to be able to build a Ceylon project from the command-line, and not just from the IDE. Imagine trying to build your project in a remote machine using an IDE with a remote X session? No, that doesn’t cut it, sorry.
So from the start, Ceylon had a CLI. Actually, we had a CLI before we had an IDE, but that didn’t last long. And in the
beginning we copied Java and had a few commands such as
ceylonc to compile, and
ceylon to run. Then we added the API
generator tool called
ceylondoc, and very soon after that we figured we were doing something horribly wrong.
Back when Java came out in 1995 it was sorta cute and useful that all its commands started with the
java prefix, but
after you got around to remembering what
javadoc stood for, you started to wonder what the hell
javah could possibly mean.
Then Git came around and revolutionised not only distributed version-control, it also revolutionised command-line
interfaces. Suddenly you could start with remembering a single command called
git and find out from here what
the full list of subcommands would be. You add
--help and it spits out a man page. You have completion out of the
box. And everyone can write plugins very easily in just a few lines of shell script. Not only that, but most options
can be stored and retrieved in a really straightforward config file, with useful and logical lookup in your project,
It’s really a treasure trove of good ideas, and much more modern than the Java CLI we started copying. So we stopped and rewrote our CLI to copy the Git CLI, and I have to say I’m really glad we did, because we really have a great CLI now.
One Command to rule them all, One Command to find them, One Command to bring them all and in the CLI bind them In the Land of Terminal where the Shell lie.
We have a single command called
ceylon, and if you do
ceylon help or
ceylon --help you will get the following:
Yes it’s a man page, this is what is useful and will help you find your way out of the many
ceylon subcommands we
A similar thing will happen if you type
ceylon compile --help or
ceylon help compile.
We ship with completion support for
$ . /usr/share/ceylon/1.0.0/contrib/scripts/ceylon-completion.bash $ ceylon [TAB] all compile-js doc help info run src version compile config doc-tool import-jar new run-js test $ ceylon compile[TAB] compile compile-js $ ceylon compile --re[TAB] --rep\= --resource\=
As you can see, it’s quite useful, and again, that’s what you expect in this day and age.
The tool system that we wrote (well, mostly that Tom Bentley wrote) even allows us to generate HTML, XML and text documentation automatically, for example, the tool documentation pages are entirely generated, and the man pages that we ship in the Ceylon CLI distribution are also generated.
You may not have noticed it but if you type
ceylon help on your system, it will likely not include that line:
[...] DESCRIPTION [...] * 'all' - Compiles ceylon modules for JVM and JS and documents them
And that’s because, like Git, we support CLI plugins, and
ceylon all is a plugin I wrote for myself, that
aggregates a bunch of subcommands in a single one.
Ceylon CLI plugins are as straightforward as Git CLI plugins: any executable in your path which starts with
will be picked up to be a plugin. It will be listed and documented too. If you add documentation in your plugin
it will be used.
ceylon-all shell script for example:
#!/bin/sh USAGE='[generic options] module...' DESCRIPTION='Compiles ceylon modules for JVM and JS and documents them' LONG_USAGE='ceylon-all allows you to build the specified ceylon modules for the JVM and JS backends, and generates the API documentation in a single command.' . $CEYLON_HOME/bin/ceylon-sh-setup $CEYLON_HOME/bin/ceylon compile $@ $CEYLON_HOME/bin/ceylon compile-js $@ $CEYLON_HOME/bin/ceylon doc $@
As you can see, our CLI will use the
LONG_USAGE environment variables for completion
and documentation. You only have to source the provided
$CEYLON_HOME/bin/ceylon-sh-setup shell script to benefit
from this. Then you can run whatever you want.
There are a lot of options the CLI and IDE have good defaults for, such as the source path, or the output module
repository, or the list of module repositories. But when you need something different it gets tiring really fast
to have to specify them for every command, so we support configuration files just like Git, in the form of INI
files located in your project at
.ceylon/config. If that file does not exist or a particular configuration
option is not defined in it, we will also try the user’s config in
$HOME/.ceylon/config as a fallback.
The syntax is straightforward, take for example the
.ceylon/config for the Ceylon SDK:
[repositories] output=./modules lookup=./test-deps [defaults] encoding=UTF-8
It just specifies that the output repository is
modules, the additional lookup repository is
(for test dependencies) and the source encoding is
ceylon config command which allows you to edit this file if you don’t want to fire up
that, and the best part is that this config file is actually used and edited by the IDE too! So no more
hassle to keep the CLI and IDE settings in sync.
Welcome to the future! :)