A few build script recipes for the list

I just worked on some build scripts. I thought I found some neat
solutions, and I wanted to share them.

I don’t know if this is really enough for a project. If there is
interest, I’ll rewrite the code with unit tests, address cross
platforms more elegantly, and in general make it a proper library.

~ Patrick

So here goes:

— Targets —

I wrote a Target class that lets you declare new Targets:

Target.new( "name", "long description used for help text" ) {
    # ... do something here
}

It keeps track of its instances, so at the end of the build script you
have a bit that does:

if (__FILE__ == $0 )
    Target.run( ARGV[0] )
end

It will automatically build the help text, so if it can’t find a given
target it will output something like:

% ruby build.rb
Usage:   ruby build.rb [ doc | install | install_depends | test ]
Options: doc              -- build rdocs
         install          -- install narf in site ruby
         install_depends  -- install dependencies into site_ruby
         test             -- run narf unit tests
%

To run a particular target, you would do:

% ruby build.rb test
Target: test | run narf unit tests
........................................................
........................................................
...................
124 runs, 218 assertions, 0 failures, 0 errors
%

— Dependencies —

The other bit I wrote was a Lib class to help me install dependencies.
You declare each of your dependencies with a package to check, a url
to use to obtain the package, and the commands to install it:

Lib.new('test/unit',
    'http://www.talbott.ws/testunit/packages/testunit-0.1.4.tar.gz'){
    ['config', 'setup', 'install'].each{ |target|
        system("ruby setup.rb #{ target }")
    }
}

To show the status of dependencies, you can do

Lib.check

which outputs something like:

% ruby build.rb depends
Target: depends | check dependencies
Checking for test/unit -- yes.
Checking for html-parser -- yes.
Checking for html/tree -- yes.
Checking for strscan -- yes.
%

Then, to install the libraries you:

 Lib.install

This will download each tar.gz, unpack it, Dir.chdir into the new
directory, run the install script, cd back out, and delete the archive
and the directory.

— Ugly Things About The Code —

  • There are no unit tests (won’t be a good base for long term project)
  • On narf, I’m including the cygwin dll and the cygwin rm and tar
    utils. This functionality might be able to be implemented in pure
    ruby, which could be cleaner
  • It sure would be nice if Target.run passed arguments to the running
    command, wouldn’t it?

— No More Excuses, Here’s The Code —

class Target
@@targets = {}
def Target.run(name="")
if (keys.include? name)
@@targets[name].run
else
help_text
end
end

def Target.keys
@@targets.keys.sort
end

def Target.first
keys.first
end

def Target.justify
justify = 0

keys.each { |key|
    justify = key.length if (key.length > justify)
}

justify + 1
end

def Target.help_text
text  = "Usage:   ruby build.rb [ #{ keys.join( \" | \") } ]" + $/
text += @@targets[keys.first].help_text( "Options: ", justify )
keys.slice(1...keys.length).each{ |key|
    text += @@targets[key].help_text( "         ", justify )
}
puts text
end

attr_reader :name, :description
def initialize( name, description, &run )
@name = name
@description = description
@run = run || Target.method(:help_text)
@@targets[name] = self
end

def run
$stderr.puts "Target: " + name + " | " + description
@run.call
end

def help_text( prefix, justify )
prefix + name.ljust(justify) + " -- " + description + $/
end

end

class Lib
def Lib.install
@@dependencies.each{ |lib|
lib.install
}
end

def Lib.check
@@dependencies.each{ |lib|
    lib.check
}
end

@@dependencies = []

def initialize( package, location, dirname=nil, &install_script )
@package = package
@location = location
@dirname = dirname
@install_script = install_script || proc {}

@@dependencies.push self
end

def installed?
lib = @package.dup
lib += ".rb" unless lib =~ /\.rb\Z/
begin
    require lib
rescue LoadError
end
$".include? lib
end

def check
output = ["Checking for #{ @package } -- "]
if (installed?)
    output << "yes."
else
    output << "no." + $/
    output << "  You can get it from #{ @location }"
end
output << $/
print output 
end

def uri
$: << Lib.dir
require 'uri'
URI::create(@location)
end

def filename
Lib.dir + File.basename(uri.path)
end

def dirname
if (@dirname)
    Lib.dir + @dirname
else
    filename.sub(/((\.tar\.gz)||(\.tgz))\Z/, "")
end
end

def path_and_query
uri.path + if (uri.query) then "?" + uri.query else "" end
end

def install
unless( installed? )
    download
    extract
    Dir.chdir( dirname )
    @install_script.call
    Dir.chdir( "../.." )
    clean
    puts "Installed #{ @package }"
end
end

def download
require "net/http"

puts( "Downloading #{ @package }...",
      "    contacting #{ @location.slice(0..50) }..." )

open( filename, "wb" ){ |local_file|
    Net::HTTP.new( uri.host, 80 ).get( path_and_query ) { |download|
	local_file.write download
    }
}

puts "      ...download saved."

end

def extract
system(bin + "tar -xzf #{ filename } -C lib")
end

def clean
system(bin + "rm -Rf #{ dirname }*" )
end

def bin
if /mswin32/ =~ RUBY_PLATFORM
    Lib.dir
else
    ""
end
end

def Lib.dir
"scripts/"
end

end

Nice. I’m working on dependency handling for rpkg right now (see next
msg) and am getting real crazy with multiple levels, conflicting
packages, and ensuring against cyclic dependencies (see next msg ;-)).
I know it is not polite to assume insanity in someone else, but are
you going to deal with these, too? What, trying to steal code? Who?
Me? Naaaaaah… and by the way, under what license do you release?
;->

Massimiliano

···

On Mon, Sep 16, 2002 at 02:48:32PM +0900, Patrick May wrote:

         install_depends  -- install dependencies into site_ruby

Massimiliano Mirra list@NOSPAMchromatic-harp.com wrote in message news:20020916153522.GA29288@newton.rcost.unisannio.it

         install_depends  -- install dependencies into site_ruby

Nice. I’m working on dependency handling for rpkg right now (see next
msg) and am getting real crazy with multiple levels, conflicting
packages, and ensuring against cyclic dependencies (see next msg ;-)).
I know it is not polite to assume insanity in someone else, but are
you going to deal with these, too? What, trying to steal code? Who?
Me? Naaaaaah… and by the way, under what license do you release?
;->

Ruby’s license. Looks like you’ve been pulling an all nighter.

I didn’t try to track dependencies b/w libraries, I tried to keep
things simple. I came up with this by automatting the entire Narf
install process. The only crazy bit is patching the html-parser
library, which is what drove me to use a block to run off the actual
installation.

I did checkout rpkg before I installed it, and I had some
philosophical differences. Mainly I was wondering if dpkg was
necessarily a good model for our CPAN / RAA.suc tool. I’m going think
about my hunches, and try to write some usable feedback later on.
(aka, right now its late and I need sleep)

Good luck with your dependencies,

~ Patrick

···

On Mon, Sep 16, 2002 at 02:48:32PM +0900, Patrick May wrote: