"url" now has same features as "head"

Specifically, it can accept all the VCS tag specs that head could,
making it more useful for defining stable versions that come from
VCS instead of tarballs.

A new "SoftwareSpecification" class was added to implement this.

This new class holds a "spec" for downloading a software package.
It combines the url (or head url) with the "specs" [1] that head has
been able to take.

This allows both the stable (url) and unstable (head) specification
for a software package to co-exist without stomping on each others
"specs".

[1] "specs" contain instructions on which branch/tag/revision/etc. to use
    from the source repository URL.
This commit is contained in:
Adam Vandenberg 2010-07-04 14:17:03 -07:00
parent a56466a4d3
commit bbaac45e3e

View File

@ -11,6 +11,50 @@ class FormulaUnavailableError <RuntimeError
end end
class SoftwareSpecification
attr_reader :url, :specs, :using
VCS_SYMBOLS = {
:bzr, BazaarDownloadStrategy,
:curl, CurlDownloadStrategy,
:cvs, CVSDownloadStrategy,
:git, GitDownloadStrategy,
:hg, MercurialDownloadStrategy,
:nounzip, NoUnzipCurlDownloadStrategy,
:post, CurlPostDownloadStrategy,
:svn, SubversionDownloadStrategy,
}
def initialize url, specs=nil
raise "No url provided" if url.nil?
@url = url
unless specs.nil?
# Get download strategy hint, if any
@using = specs.delete :using
# The rest of the specs are for source control
@specs = specs
end
end
# Returns a suitable DownloadStrategy class that can be
# used to retreive this software package.
def download_strategy
return detect_download_strategy @url if @using.nil?
# If a class is passed, assume it is a download strategy
return @using if @using.kind_of? Class
detected = VCS_SYMBOLS[@using]
raise "Unknown strategy #{@using} was requested." unless detected
return detected
end
def detect_version
Pathname.new(@url).version
end
end
# Derive and define at least @url, see Library/Formula for examples # Derive and define at least @url, see Library/Formula for examples
class Formula class Formula
include FileUtils include FileUtils
@ -19,13 +63,24 @@ class Formula
# Homebrew determines the name # Homebrew determines the name
def initialize name='__UNKNOWN__' def initialize name='__UNKNOWN__'
set_instance_variable 'homepage'
set_instance_variable 'url' set_instance_variable 'url'
set_instance_variable 'head' set_instance_variable 'head'
set_instance_variable 'specs' set_instance_variable 'specs'
set_instance_variable 'stable'
set_instance_variable 'unstable'
if @head and (not @url or ARGV.build_head?) if @head and (not @url or ARGV.build_head?)
@url=@head @url = @head
@version='HEAD' @version = 'HEAD'
@spec_to_use = @unstable
else
if @stable.nil?
@spec_to_use = SoftwareSpecification.new(@url, @specs)
else
@spec_to_use = @stable
end
end end
raise "No url provided for formula #{name}" if @url.nil? raise "No url provided for formula #{name}" if @url.nil?
@ -33,16 +88,12 @@ class Formula
validate_variable :name validate_variable :name
set_instance_variable 'version' set_instance_variable 'version'
@version ||= Pathname.new(@url).version @version ||= @spec_to_use.detect_version
validate_variable :version if @version validate_variable :version if @version
set_instance_variable 'homepage' CHECKSUM_TYPES.each { |type| set_instance_variable type }
CHECKSUM_TYPES.each do |type| @downloader=download_strategy.new @spec_to_use.url, name, version, @spec_to_use.specs
set_instance_variable type
end
@downloader=download_strategy.new url, name, version, specs
end end
# if the dir is there, but it's empty we consider it not installed # if the dir is there, but it's empty we consider it not installed
@ -52,18 +103,14 @@ class Formula
return false return false
end end
def prefix
validate_variable :name
validate_variable :version
HOMEBREW_CELLAR+@name+@version
end
def path def path
self.class.path name self.class.path name
end end
def cached_download def prefix
@downloader.cached_location validate_variable :name
validate_variable :version
HOMEBREW_CELLAR+@name+@version
end end
def bin; prefix+'bin' end def bin; prefix+'bin' end
@ -77,33 +124,19 @@ class Formula
def include; prefix+'include' end def include; prefix+'include' end
def share; prefix+'share' end def share; prefix+'share' end
# generally we don't want var stuff inside the keg
def var; HOMEBREW_PREFIX+'var' end
# configuration needs to be preserved past upgrades # configuration needs to be preserved past upgrades
def etc; HOMEBREW_PREFIX+'etc' end def etc; HOMEBREW_PREFIX+'etc' end
# generally we don't want var stuff inside the keg
def var; HOMEBREW_PREFIX+'var' end
# reimplement if we don't autodetect the download strategy you require # Use the @spec_to_use to detect the download strategy.
# Can be overriden to force a custom download strategy
def download_strategy def download_strategy
if @specs and @url == @head @spec_to_use.download_strategy
vcs = @specs.delete :using
if vcs != nil
# If a class is passed, assume it is a download strategy
return vcs if vcs.kind_of? Class
case vcs
when :bzr then return BazaarDownloadStrategy
when :curl then return CurlDownloadStrategy
when :cvs then return CVSDownloadStrategy
when :git then return GitDownloadStrategy
when :hg then return MercurialDownloadStrategy
when :svn then return SubversionDownloadStrategy
end end
raise "Unknown strategy #{vcs} was requested." def cached_download
end @downloader.cached_location
end
detect_download_strategy url
end end
# tell the user about any caveats regarding this package, return a string # tell the user about any caveats regarding this package, return a string
@ -213,7 +246,7 @@ class Formula
require self.path(name) require self.path(name)
end end
begin begin
klass_name =self.class_s(name) klass_name = self.class_s(name)
klass = eval(klass_name) klass = eval(klass_name)
rescue NameError rescue NameError
# TODO really this text should be encoded into the exception # TODO really this text should be encoded into the exception
@ -284,10 +317,10 @@ protected
end end
private private
# creates a temporary directory then yields, when the block returns it # Create a temporary directory then yield. When the block returns,
# recursively deletes the temporary directory # recursively delete the temporary directory.
def mktemp def mktemp
# I used /tmp rather than mktemp -td because that generates a directory # I used /tmp rather than `mktemp -td` because that generates a directory
# name with exotic characters like + in it, and these break badly written # name with exotic characters like + in it, and these break badly written
# scripts that don't escape strings before trying to regexp them :( # scripts that don't escape strings before trying to regexp them :(
@ -336,11 +369,8 @@ EOF
def stage def stage
HOMEBREW_CACHE.mkpath HOMEBREW_CACHE.mkpath
fetched = @downloader.fetch
downloaded_tarball = @downloader.fetch verify_download_integrity fetched if fetched.kind_of? Pathname
if downloaded_tarball.kind_of? Pathname
verify_download_integrity downloaded_tarball
end
mktemp do mktemp do
@downloader.stage @downloader.stage
@ -427,6 +457,8 @@ EOF
end end
class << self class << self
# The methods below define the formula DSL.
attr_reader :stable, :unstable
def self.attr_rw(*attrs) def self.attr_rw(*attrs)
attrs.each do |attr| attrs.each do |attr|
@ -438,13 +470,21 @@ EOF
end end
end end
attr_rw :url, :version, :homepage, :specs, :deps, :external_deps, *CHECKSUM_TYPES attr_rw :version, :homepage, :specs, :deps, :external_deps
attr_rw *CHECKSUM_TYPES
def head val=nil, specs=nil def head val=nil, specs=nil
if specs return @head if val.nil?
@unstable = SoftwareSpecification.new(val, specs)
@head = val
@specs = specs @specs = specs
end end
val.nil? ? @head : @head = val
def url val=nil, specs=nil
return @url if val.nil?
@stable = SoftwareSpecification.new(val, specs)
@url = val
@specs = specs
end end
def depends_on name def depends_on name