Last update: 2015-10-09

Quick Start

What is Bee?

Bee is a build tool. This means that its purpose is to automate a build process. Its design goals are:

  • Clean and simple build file syntax. While XML is too verbose and Ruby too complicated, YAML is perfect with its simple yet powerful syntax.
  • Speed. Slow startup are irritating while working with a build tool the whole day. Bee happens to be as fast as Make, according to my tests on Make builds ported to Bee.
  • System and language independent. Bee is written in Ruby, which is platform independent, and is not limited to building Ruby projects. Furthermore, using Bee tasks (we'll see later) instead of shell commands is a way to write build files that would run on any platform.
  • Run Ruby scripts. Because Ruby is so cool, nobody wants to write anymore intricate shell scripts to code complicated tasks involving sed, grep with loads of pipes! Just write it in a Ruby script! Nevertheless, you are free to write shell scripts if you build your project on a single platform.

To demonstrate these features, let's see what hello world example looks like using Bee:

- target: hello
  script:
  - print: "Hello World!"

This is that simple!

Build properties

A build file can embed properties that are like variables. For instance, we can put user name in a property named user_name like this:

- properties:
    user_name: "World"

- target: hello
  script:
  - print: "Hello #{user_name}!"

We access the value of this property with a Ruby syntax. Thus, to get value for property user_name, we must write #{user_name}.

In this example, the property value is a string "World". In fact, this is a Ruby expression that is evaluated to get the value of the property. Thus, we can write Ruby expressions as value. For instance, to get the user name, we could write:

- properties:
    user_name: "#{ENV['USER']}"

- target: hello
  script:
  - print: "Hello #{user_name}!"

Which could print:

----------------------------------------------------------------------- hello --
Hello casa!
OK

Properties may also have integer, list or hash values. You must then use a YAML syntax to write them, but fortunately, this syntax is very close to Ruby one:

- properties:
    string:  "string"
    integer: 1
    list:    [1, "two"]
    ahash:   {one: 1, two: 2}

- target: hello
  script:
  - print: "string: #{string.inspect}"
  - print: "integer: #{integer.inspect}"
  - print: "list: #{list.inspect}"
  - print: "ahash: #{hash.inspect}"

This outputs:

----------------------------------------------------------------------- hello --
string: "string"
integer: 1
list: [1, "two"]
ahash: 70057795067980
OK

Bee defines a special property named base which contains absolute path for build file directory. This is useful for building absolute file path, such as license: "#{base}/LICENSE". Bee also stores directory where it was started in property here. These two properties might differ in some circumstances, for instance when starting Bee searching recursively build file with command line argument -r. These two property names are reserved for Bee usage and you can't name your properties base or here.

It is also possible to load properties from a YAML file that contains a list of properties to set. Just write the file name (absolute or relative to the directory of the build file) instead of the list of properties. For instance, to load properties in file numbers.yml:

one: 1
two: 2

Into a build file, you might write:

- properties: numbers.yml

- target: count
  script:
  - print: "one: #{one}"
  - print: "two: #{two}"

Targets

If we see build files as programs, we could see targets as functions that you can call to achieve a given goal. A build file can define more that one target that may depend on each other. For instance:

- properties:
    user_name: "#{ENV['USER']}"

- target: hello
  depends: capitalize
  script:
  - print: "Hello #{user_name}!"

- target: capitalize
  script:
  - rb: "user_name.capitalize!"

Which produces following output:

------------------------------------------------------------------ capitalize --
----------------------------------------------------------------------- hello --
Hello Casa!
OK

Target hello now depends on target capitalize which capitalizes user_name property. Thus, Bee runs first target capitalize, then target hello.

You can tell Bee to run a given target by passing its name on command line. For instance, running build file above typing bee capitalize would output:

------------------------------------------------------------------ capitalize --
OK

Which is not much, because target capitalize doesn't output anything.

When you don't pass any target on command line, Bee selects the first one as default target. There is a way to force default target (even if not the first one) by adding build information entry that indicates default build target:

- build: hello
  default: hello
  description: Example Hello World build file

- properties:
    user_name: "#{ENV['USER']}"

- target: capitalize
  description: Capitalize user name
  script:
  - rb: "user_name.capitalize!"

- target: hello
  depends: capitalize
  description: Say hello to user
  script:
  - print: "Hello #{user_name}!"

Which, when launched without specifying any target on command line, outputs:

------------------------------------------------------------------ capitalize --
----------------------------------------------------------------------- hello --
Hello Casa!
OK

Tasks

If targets are functions, tasks can be seen as instructions. They can be shell scripts, Ruby code or Bee tasks:

Shell scripts

To run a shell script, you just have to put it in a string. Thus, to print the user's name, we could write:

- 'echo "Hello $USER!"'

Note that we can surround YAML strings with simple or double quotes. We choose simple quotes here so that we can use double for the Shell one. We could also escape double quotes inside YAML string as follows:

- "echo \"Hello $USER!\""

If return value of the script is not 0, which denotes an error running the script, the build is interrupted and an error message is printed on the console. For instance, this script:

- target: broken
  script:
  - "foo"

Will produce this output on the console:

---------------------------------------------------------------------- broken --
ERROR: Script exited with value 32512
In target 'broken', in task:
- foo

There are convenient ways to write shell scripts:

- target: first
  script: "ls"

- target: second
  script: |
    cd tmp
    ls

First, when a target embeds a single shell script, we can write it just after the script key. Second, a multi line shell scripts can be written using pipe character (|).

Ruby scripts

A Ruby script must be preceded with rb key, such as in example bellow:

- target: ruby
  script:
  - rb: 'puts "We are running Ruby #{RUBY_VERSION}"'

Which outputs:

------------------------------------------------------------------------ ruby --
We are running Ruby 1.8.7
OK

It is also possible to use YAML pipe syntax for multi line Ruby scripts.

Bee tasks

These tasks are written in Ruby and can be called with pure YAML syntax, as follows:

- target: hello
  script:
  - print: "Hello #{ENV['USER'].capitalize}"

As you might guess, print Bee task prints the string passed as parameter on the console, and this build file outputs as expected:

----------------------------------------------------------------------- hello --
Hello Casa
OK

Control structures

Bee is not a programming language, but it enables you to control the tasks execution with control structures. There is a if-then-else construct that works this way:

- if: RUBY_PLATFORM =~ /darwin/
  then:
  - print: Hello, I'm a Mac
  else:
  - print: Hello, I'm a PC

You can also loop while a condition is met, using the while-do construct:

- while: nb_sarah_connor > 0
  do:
  - terminate:

The last construct if for-in-do which will iterate over a list (a plain list or a Ruby expression evaluated as an Enumerable). For instance, to iterate over the files in the current directory, you might write:

- for: file
  in:  "Dir.glob('*')"
  do:
  - print: :file

Finally, try-catch consruct helps manage errors. For instance, if you want to know if a given command exists, you might write:

- try:
  - "which foo"
  catch:
  - print: "Command 'foo' not found"

Note that while not tasks, you can get help about these constructs the same way you get help about tasks (as seen hereafter).

Getting help

To get help on Bee command line arguments, type bee -h, which will print:

Usage: bee [options] [targets]
-V             Print version and exit.
-h             Print help about usage and exit.
-b             Print help about build and exit.
-n             Don't actually run any commands; just print them.
-k task        Print help about tasks in a package (writing "foo.?") or a 
               given one (writing "foo.bar") and exit.
-e egg         Print help about templates in a given package (writing 
               "foo.?") or a given one (writing "foo.bar") and exit.
-p name=value  Set a named property with a given value.
-t egg         Run a given egg to generate a template project.
-v             Enable verbose mode.
-s style       Define style for output (see documentation).
-c             Use color scheme for output (if running in color terminal).
-w             Use black and white output (default).
-f file        Build file to run (defaults to "build.yml").
-r             Look for build file recursively up in file system.
-l             Print bee logo on console.
-R resource    Print given resource (such as ':bee:clean.yml') on console.
-a             Print list of available targets.
-o             Print list of available options.
-x             Print list of available tasks.
-y             Print list of available templates.
targets        Targets to run (default target if omitted).

To get help about a build file, type bee -b. This will output the build name and description, property names and values, target names and description and default target:

build: hello
description: Example Hello World build file
properties:
- base: "/home/casa/tmp/bee/doc/exp"
- here: "/home/casa/tmp/bee"
- user_name: "casa"
targets:
- capitalize: Capitalize user name
- hello: Say hello to user [capitalize]
default: hello

To get help on a given task, use -k option. For instance, to get help about print task, type bee -k print, which would output:

----------------------------------------------------------------------- print --
Alias for echo.

Print a message on console. If message is not a string, this task
outputs the inspected value of the object.

- message: message to print.

Example

 - echo: "Hello World!"

You can find documentation about all standard tasks on this page.

Go further

Bee has much more to offer, including build inheritance, scaffoldings and many more features detailed in the User Guide. After playing a while with Bee, you should read it to get more from it.

Enjoy!