Robot Has No Heart

Xavier Shay blogs here

A robot that does not have a heart

Rant 0.5.7 Released

Recently I have had the need to do some C# work. Not wanting to stray too far from ruby, I used Rant to build my project. Initially, Rant wasn’t quite up to the task, so I rolled up my sleeves and did something about it. The culmination of my efforts comprise the primary component of today’s 0.5.7 release, so check it out for some white hot C# build file action.

Hack the Planet

  1. Vegetarian – battery farms lose!
  2. Buy organic, fairtrade and/or local where possible
  3. No car, use public transport and feet, except where not possible (Geelong)
  4. Refuse plastic bags, although I think perversely our excessive number of green bags at home is soon to become an environmental risk
  5. I plan to vote, haven’t had the opportunity yet
  6. Spread the love. Bring politics into conversations. Getting people talking and thinking is the first step.

The last one is important. Preaching at people will never work – global awareness must come from within. We must provide the support and encouragement. Lead by example. It can be tough sometimes. I almost hit intolerable despair last night. Startling, raw, realisations: The pope – the most important man in Christianity – is a political retard, the most powerful man in the world is widely regarded an idiot, and you couldn’t have pulled the recent Naomi Robson story from Frontline… Politics, Religion, Media, the triple crown. The world is loco.

In other news, I’ve just commited some C Sharp tools to Ruby Rant , if you’re interested in a sweet build tool that lets you use ruby (XML loses!). I’m using it for a fairly decent project at work (multiple projects, resources, unit tests, etc) and find it a pleasure to work with. Note I’m talking about a replacement for the deprecated method described in the current documentation. I’m going to get that updated, but for now check the mailing list for info.

Building Firefox Extensions

This article will introduce the basics of Ruby Rant by creating a Rantfile to build Firefox extensions. You don’t actually need to know anything about extensions to follow along, but if you are interested may I recommend this tutorial by roachfiend. You will note that that article (and many others on the same topic) use a batch file to build their extensions. While this is quick to set up for simple development, a build file saves time and effort in the long run, and gives more flexibility.

I assume you at least know what Rant is – a replacement for Rake – and have it installed and working. Please visit their website for more information on this topic. This is also not a build file tutorial – you should know what a task and a dependency are.

Table of Contents

  1. Extension Basics
  2. Rant
  3. Making the JAR
  4. Cleaning
  5. Making the XPI
  6. Final Touches
  7. The Completed Rakefile

Extension Basics

The first step is to decide on directory structure for your project. Firefox extensions are comprised of two main portions – the install instructions, and the actual content of the extension. A Firefox extension (an XPI file) is really just a zip file with a different extension. You can open it up using your favourite archive manager and see the following structure:

1
2
3
4
5
6
myextension.xpi/
  install.js
  install.rdf
  chrome/
    myextension.jar/
      ... myextension content ...

Likewise, the JAR file is also a zip file with an alternate extension. We can see that there are two major portions of the extension that need building, the JAR and the XPI (which contains the JAR). As such, we will use a source structure that looks like this (download the source code):

1
2
3
4
5
myextension/
  Rantfile
  src/
    install/
    jar/

Clearly, the install folder will only contain our install.js and install.rdf files, and the jar folder will contain the contents of our jar.

Rant

Enough introduction, let’s get started with Rant. Rant is a replacement for Rake. I won’t go into detail here, but one of the advantages for our purposes is portable zip creation without the need for external libraries. Rant is similar to Rake in that you define all your build tasks in a file in your root directory – the Rantfile. We will create 3 tasks – package, clean, and clobber. The first obviously packages up our extension into a zip file and gives it a .xpi extension. “clean” removes temporary files used to package the extension, and “clobber” removes all generated artefacts (basically the same as clean but also removes the XPI file).

Making the JAR

Baby steps steps though – first of all we want to create the JAR file for our extension. We can do this using the Archive::Zip generator provided by Rant:

1
2
3
4
5
6
7
import "archive/zip"
require "archive_rootdir_fix"

gen Archive::Zip, "build/helloworld", 
                  :files     => sys["src/jar/**/*"],
                  :rootdir   => "src/jar",
                  :extension => ".jar"

This generator creates a task called “build/helloworld.jar” that creates exactly that archive, containing all the files from src/jar. “**/*” tells rant to recursively add all files. The rootdir parameter is necessary so that the generator knows where to start adding files. Without it, the created JAR will have the “src/jar” folders inside it, which is undesirable.

I draw your attention to the archive_rootdir_fix file that is being required. Support for the rootdir parameter is currently not in Rant. I’ve submitted a patch, but until it is accepted, you need this particular file. It is included in the example source code for you convenience.

The generated task name is quite cumbersome, but it is quite trivial to create an alias to it using a blank task with a sole dependency. But what happens when we change our extension name or build directory? We also have to recode our alias task. Thankfully, the generator returns an object with information about the generated task, so that we can use it later in our Rantfile:

1
2
3
4
5
6
7
8
import "archive/zip"

jar_t = gen Archive::Zip, "build/helloworld", 
                  :files     => sys["src/jar/**/*"],
                  :rootdir   => "src/jar",
                  :extension => ".jar"

task :build_jar => jar_t.path

Cleaning

Before we proceed, let us quickly set up our clean and clobber tasks, as they are required for the next section. Rant makes this trivially easy, so I’m just going to show you some code and move on.

1
2
3
4
5
6
7
8
import "clean"

gen Clean, :clean
var[:clean] << "build"

gen Clean, :clobber
var[:clobber] << "build"
var[:clobber] << "bin"

Making the XPI

As you can imagine, the next step – packaging up the XPI file – is more of the same. A small amount of trickery is required to get the JAR file into the chrome directory – we actually move files around and prepare the XPI file in the build directory, so that our zip task only has to zip the single directory. You can do this using methods of the sys object. Since it uses standard shell commands it is fairly self explanatory, as you’ll see in the following example. See that we can keep using the jar_t object through out build file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
xpitask = gen Archive::Zip, "bin/helloworld",
                            :version   => "1.0.0",
                            :files     => sys["build/**/*"],
                            :rootdir   => "build",
                            :extension => ".xpi"
task :build_xpi => xpitask.path           

task :prepare => [:build_jar] do |t|
  sys.mkdir_p "build/chrome"
  sys.mv jar_t.path, "build/chrome/helloworld.jar"
  sys.cp sys["src/install/**/*"], "build"
end

task :package => [:prepare, :build_xpi]

Note that we’ve added a version parameter to the zip task – this automatically appends a version string to our output file.

Final Touches

Now we just need to add the finishing touches to our build file. For maintainability, we will extract common names (such as the “helloworld” title and the “build” directory) into variables, so that changing them once will change them throughout the entire buildfile. You can use normal ruby variables for this, but it is preferable to use the “var” construct since it means you have the option of using them in Command generators later on (maybe I will cover it in another tutorial). It is more verbose, however, so you may choose not to use it in your own projects.

Finally, we move our public tasks to the top of file for readability and give them descriptions so they are displayed when executing “rant -T”. And there you have it folks, an automated build script for firefox extensions. Please download the source code to peruse at your leisure.

The Completed Rantfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# Rantfile for building Firefox Extension
# Xavier Shay (xshay@rhnh.net), July 2006

import "archive/zip"
require "archive_rootdir_fix"
import "clean"

# Configuration
var :title   => "helloworld"
var :version => "1.0.0"
var :build_dir => "build"
var :bin_dir => "bin"
var :src_dir => "src"

# Primary tasks
desc "Package up the XPI file for release"
task :package => [:prepare, :build_xpi]

desc "Cleanup temporary files"
gen Clean, :clean
var[:clean] << "build"

desc "Cleanup all generated artifacts"
gen Clean, :clobber
var[:clobber] << "build"
var[:clobber] << "bin"

# Support tasks
jar_t = gen Archive::Zip, "#{var :build_dir}/#{var :title}", 
                  :files     => sys["#{var :src_dir}/jar/**/*"],
                  :rootdir   => "#{var :src_dir}/jar",
                  :extension => ".jar"
task :build_jar => jar_t.path

xpi_t = gen Archive::Zip, "#{var :bin_dir}/#{var :title}",
                  :version   => "#{var :version}",
                  :files     => sys["#{var :build_dir}/**/*"],
                  :rootdir   => "#{var :build_dir}",
                  :extension => ".xpi"
task :build_xpi => xpi_t.path           

task :prepare => [:clean, :build_jar] do |t|
  sys.mkdir_p "#{var :build_dir}/chrome"
  sys.mv jar_t.path, "#{var :build_dir}/chrome/#{var :title}.jar"
  sys.cp sys["#{var :src_dir}/install/**/*"], "#{var :build_dir}"
end

Lecture Etiquette

I love enterprise .NET, but nothing annoys me more than people slagging off microsoft in the lectures. Today on security “There’s no such thing as 100% security?” “Linux?”. Aaaargh. And it only got worse. SHUTUP AND LEARN. The lecture didn’t even have any MS specific material in it. People not liking the subject because they “don’t like the way microsoft does things”. 3-tier design? Security? Welcome to the enterprise world. THESE ARE BEST PRACTICES. We are just using microsoft products as a context for those practices. We can’t possibly cover all the options – this is a 12 week course. You knew we were going to be using microsoft products before you started the course.

Of course, this goes for all lectures.

There is rarely a reason to speak in a lecture

Save it for the tutes. No one else is interested in your opinion/question.

A pretty flower Another pretty flower