You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@buildr.apache.org by as...@apache.org on 2008/02/28 09:31:39 UTC
svn commit: r631890 - in /incubator/buildr/trunk: lib/core.rb
lib/core/addon.rb lib/tasks/zip.rb spec/addon_spec.rb spec/sandbox.rb
Author: assaf
Date: Thu Feb 28 00:31:38 2008
New Revision: 631890
URL: http://svn.apache.org/viewvc?rev=631890&view=rev
Log:
Addons
Added:
incubator/buildr/trunk/lib/core/addon.rb
incubator/buildr/trunk/spec/addon_spec.rb
Modified:
incubator/buildr/trunk/lib/core.rb
incubator/buildr/trunk/lib/tasks/zip.rb
incubator/buildr/trunk/spec/sandbox.rb
Modified: incubator/buildr/trunk/lib/core.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/lib/core.rb?rev=631890&r1=631889&r2=631890&view=diff
==============================================================================
--- incubator/buildr/trunk/lib/core.rb (original)
+++ incubator/buildr/trunk/lib/core.rb Thu Feb 28 00:31:38 2008
@@ -24,3 +24,4 @@
require 'core/test'
require 'core/checks'
require 'core/generate'
+require 'core/addon'
Added: incubator/buildr/trunk/lib/core/addon.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/lib/core/addon.rb?rev=631890&view=auto
==============================================================================
--- incubator/buildr/trunk/lib/core/addon.rb (added)
+++ incubator/buildr/trunk/lib/core/addon.rb Thu Feb 28 00:31:38 2008
@@ -0,0 +1,145 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with this
+# work for additional information regarding copyright ownership. The ASF
+# licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+
+require 'java/artifact'
+
+
+module Buildr
+
+ # Addons are a mechanicm for sharing extensions, tasks and common code across builds,
+ # using remote and local repositories.
+ #
+ # An addon is a collection of files, a ZIP archive when distributed and an exploded
+ # directory when used locally. They are installed and expanded into the local repository.
+ #
+ # Addons provide functionality in three different ways:
+ # * The addon directory is added to the $LOAD_PATH, and its files can be accessed from
+ # the build tasks, typically using +require+.
+ # * The +init.rb+ file is required by default, if present. An addon can use this to
+ # install project extensions, introduce new tasks, install libraries, etc.
+ # * Task files that go in the +tasks+ sub-directory with the extension +.rake+ are
+ # automatically loaded after the buildfile. Use these to introduce addition tasks.
+ #
+ # The addon method supports options that are set on the addon on first use. The +init.rb+
+ # file can access these options through the global variable $ADDON.
+ #
+ # Addons are referenced by a qualified name. For local and remote repositories, the
+ # last part of the qualified name maps to the artifact identifier, the rest is the group
+ # identifier. For example, 'org.apache.buildr.openjpa:1.0' becomes
+ # 'org.apache.buildr:openjpa:zip:1.0'.
+ class Addon
+
+ class << self
+
+ # Returns all the loaded addons.
+ def list
+ @addons.values
+ end
+
+ def load(from, options = nil) #:nodoc:
+ options ||= {}
+ case from
+ when Rake::FileTask
+ target = from.to_s
+ name = target.pathmap('%n')
+ when String
+ name, version, *rest = from.split(':')
+ fail "Expecting <name>:<version>, found #{from}." unless name && version && rest.empty?
+ group = name.split('.')
+ id = group.pop
+ fail "Addon name is qualified, like foo.bar or foo.bar.baz, but not foo." if group.empty?
+ artifact = Buildr.artifact("#{group.join('.')}:#{id}:zip:#{version}")
+ target = artifact.to_s.ext
+ Buildr.unzip(target=>artifact)
+ else
+ fail "Can only load addon from repository (name:version) or file task."
+ end
+ if addon = @addons[name]
+ fail "Attempt to load addon #{name} with two different version numbers, first (#{addon.version}) and now (#{version})." unless
+ addon.version == version
+ false
+ else
+ @addons[name] = new(name, version, target, options)
+ true
+ end
+ end
+
+ end
+
+ @addons = {}
+
+ # Addon name.
+ attr_reader :name
+ # Version number (may be nil).
+ attr_reader :version
+ # The path for the addon directory.
+ attr_reader :path
+
+ include Enumerable
+
+ def initialize(name, version, path, options) #:nodoc:
+ @name, @version, @options = name, version, options
+ @path = File.expand_path(path)
+ file(@path).invoke
+ raise "#{@path} is not a directory." unless File.directory?(@path)
+ begin
+ $ADDON = self
+ $LOAD_PATH << @path unless $LOAD_PATH.include?(@path)
+ init_file = File.join(@path, 'init.rb')
+ require init_file if File.exist?(init_file)
+ import *FileList[File.join(@path, 'tasks/*.rake')]
+ rescue
+ $LOAD_PATH.delete @path
+ ensure
+ $ADDON = nil
+ end
+ end
+
+ # Returns the value of the option.
+ def [](name)
+ @options[name]
+ end
+
+ # Sets the value of the option.
+ def []=(name, value)
+ @options[name] = value
+ end
+
+ def each(&block) #:nodoc:
+ @options.each(&block)
+ end
+
+ def to_s #:nodoc:
+ version ? "#{name}:#{version}" : name
+ end
+
+ end
+
+ # :call-seq:
+ # addon(id, options?)
+ # addon(task, options?)
+ #
+ # Use this to download and install an addon. The first form takes the addon identifier,
+ # a string that contains the qualified name, colon and version number. For example:
+ # addon 'org.apache.buildr.openjpa:1.0'
+ # Some addons accept options passed as a hash argument.
+ #
+ # The second form takes a file task that points to the directory containing the addon.
+ def addon(from, options = nil)
+ Addon.load(from, options)
+ end
+
+end
Modified: incubator/buildr/trunk/lib/tasks/zip.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/lib/tasks/zip.rb?rev=631890&r1=631889&r2=631890&view=diff
==============================================================================
--- incubator/buildr/trunk/lib/tasks/zip.rb (original)
+++ incubator/buildr/trunk/lib/tasks/zip.rb Thu Feb 28 00:31:38 2008
@@ -537,7 +537,6 @@
# specified. Nothing will happen unless we include all files.
if @paths.empty?
@paths[nil] = FromPath.new(self, nil)
- @paths[nil].include '*'
end
# Otherwise, empty unzip creates target as a file when touching.
@@ -652,7 +651,7 @@
end
def map(entries)
- includes = @include || ['*']
+ includes = @include || ['**/*']
excludes = @exclude || []
entries.inject({}) do |map, entry|
short = entry.name.sub(@path, '')
Added: incubator/buildr/trunk/spec/addon_spec.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/spec/addon_spec.rb?rev=631890&view=auto
==============================================================================
--- incubator/buildr/trunk/spec/addon_spec.rb (added)
+++ incubator/buildr/trunk/spec/addon_spec.rb Thu Feb 28 00:31:38 2008
@@ -0,0 +1,170 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with this
+# work for additional information regarding copyright ownership. The ASF
+# licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+
+require File.join(File.dirname(__FILE__), 'spec_helpers')
+
+
+describe Addon do
+ before { $loaded = false }
+
+ it 'should list all loaded addons' do
+ write 'foobar/init.rb'
+ addon file('foobar')
+ Addon.list.each { |addon| addon.should be_kind_of(Addon) }.
+ map(&:name).should include('foobar')
+ end
+
+ it 'should return true when loading addon for first time' do
+ write 'foobar/init.rb'
+ addon(file('foobar')).should be(true)
+ end
+
+ it 'should allow loading same addon twice' do
+ write 'foobar/init.rb'
+ addon file('foobar')
+ lambda { addon file('foobar') }.should_not raise_error
+ end
+
+ it 'should return false when loading addon second time' do
+ write 'foobar/init.rb'
+ addon(file('foobar'))
+ addon(file('foobar')).should be(false)
+ end
+
+ it 'should instantiate addon once even if loaded twice' do
+ write 'foobar/init.rb', <<-RUBY
+ $loaded = !$loaded
+ RUBY
+ lambda { addon file('foobar') }.should change { $loaded }
+ lambda { addon file('foobar') }.should_not change { $loaded }
+ end
+end
+
+
+describe Addon, 'from directory' do
+ before { $loaded = false }
+
+ it 'should have no version number' do
+ end
+
+ it 'should add directory to LOAD_PATH' do
+ mkpath 'foobar'
+ lambda { addon file('foobar') }.should change { $LOAD_PATH.clone }
+ $LOAD_PATH.should include(File.expand_path('foobar'))
+ end
+
+ it 'should load init.rb file if found' do
+ write 'foobar/init.rb', '$loaded = true'
+ lambda { addon file('foobar') }.should change { $loaded }.to(true)
+ end
+
+ it 'should add init.rb to LOADED_FEATURES' do
+ write 'foobar/init.rb'
+ lambda { addon file('foobar') }.should change { $LOADED_FEATURES.clone }
+ $LOADED_FEATURES.should include(File.expand_path('foobar/init.rb'))
+ end
+
+ it 'should pass options to addon' do
+ write 'foobar/init.rb', '$loaded = $ADDON[:loaded]'
+ lambda { addon file('foobar'), :loaded=>5 }.should change { $loaded }.to(5)
+ end
+
+ it 'should import any tasks present in tasks sub-directory' do
+ write 'foobar/tasks/foo.rake', "$loaded = 'foo'"
+ addon file('foobar')
+ lambda { Rake.application.load_imports }.should change { $loaded }.to('foo')
+ end
+
+ it 'should fail if directory doesn\'t exist' do
+ lambda { addon file('missing') }.should raise_error(RuntimeError, /not a directory/i)
+ end
+
+ it 'should fail if path is not a directory' do
+ write 'wrong'
+ lambda { addon file('wrong') }.should raise_error(RuntimeError, /not a directory/i)
+ end
+end
+
+
+describe Addon, 'from artifact' do
+ before { $loaded = false }
+
+ def load_addon(options = nil)
+ write 'source/init.rb', "require 'extra'"
+ write 'source/extra.rb', '$loaded = $ADDON[:loaded] || true'
+ zip('repository/org/apache/buildr/foobar/1.0/foobar-1.0.zip').include(:from=>'source').invoke
+ addon 'org.apache.buildr.foobar:1.0', options
+ end
+
+ it 'should figure out addon group from name:version' do
+ artifact('fizz.buzz:foobar:zip:1.0').should_receive(:execute)
+ addon 'fizz.buzz.foobar:1.0' rescue nil
+ end
+
+ it 'should pick name from prefix' do
+ load_addon
+ Addon.list.map(&:name).should include('org.apache.buildr.foobar')
+ end
+
+ it 'should pick version from suffix' do
+ load_addon
+ Addon.list.map(&:version).should include('1.0')
+ end
+
+ it 'should download artifact from remote repository' do
+ lambda { addon 'org.apache.buildr.foobar:1.0' }.should raise_error(Exception, /no remote repositories/i)
+ end
+
+ it 'should install artifact in local repository' do
+ load_addon
+ file('repository/org/apache/buildr/foobar/1.0/foobar-1.0.zip').should exist
+ end
+
+ it 'should expand ZIP addon into local repository' do
+ load_addon
+ file('repository/org/apache/buildr/foobar/1.0/foobar-1.0').should exist
+ file('repository/org/apache/buildr/foobar/1.0/foobar-1.0').should contain('init.rb', 'extra.rb')
+ end
+
+ it 'should add directory to LOAD_PATH' do
+ lambda { load_addon }.should change { $LOAD_PATH.clone }
+ $LOAD_PATH.should include(File.expand_path('repository/org/apache/buildr/foobar/1.0/foobar-1.0'))
+ end
+
+ it 'should load init.rb file if found' do
+ lambda { load_addon }.should change { $loaded }.to(true)
+ end
+
+ it 'should add init.rb to LOADED_FEATURES' do
+ lambda { load_addon }.should change { $LOADED_FEATURES.clone }
+ $LOADED_FEATURES.should include(File.expand_path('repository/org/apache/buildr/foobar/1.0/foobar-1.0/init.rb'))
+ end
+
+ it 'should pass options to addon' do
+ lambda { load_addon :loaded=>5 }.should change { $loaded }.to(5)
+ end
+
+ it 'should fail if loading same addon with two different versions' do
+ load_addon
+ lambda { addon 'org.apache.buildr.foobar:2.0' }.should raise_error(RuntimeError, /two different version numbers/)
+ end
+
+ it 'should import any tasks present in tasks sub-directory' do
+ write 'source/tasks/foo.rake', "$loaded = 'foo'"
+ load_addon
+ lambda { Rake.application.load_imports }.should change { $loaded }.to('foo')
+ end
+end
Modified: incubator/buildr/trunk/spec/sandbox.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/spec/sandbox.rb?rev=631890&r1=631889&r2=631890&view=diff
==============================================================================
--- incubator/buildr/trunk/spec/sandbox.rb (original)
+++ incubator/buildr/trunk/spec/sandbox.rb Thu Feb 28 00:31:38 2008
@@ -56,6 +56,8 @@
# Move to the work directory and make sure Rake thinks of it as the Rakefile directory.
@sandbox[:pwd] = Dir.pwd
Dir.chdir @test_dir
+ @sandbox[:load_path] = $LOAD_PATH.clone
+ @sandbox[:loaded_features] = $LOADED_FEATURES.clone
@sandbox[:original_dir] = Rake.application.original_dir
Rake.application.instance_eval { @original_dir = Dir.pwd }
Rake.application.instance_eval { @rakefile = File.expand_path('buildfile') }
@@ -96,6 +98,8 @@
# Switch back Rake directory.
Dir.chdir @sandbox[:pwd]
original_dir = @sandbox[:original_dir]
+ $LOAD_PATH.replace @sandbox[:load_path]
+ $LOADED_FEATURES.replace @sandbox[:loaded_features]
Rake.application.instance_eval { @original_dir = original_dir }
FileUtils.rm_rf @test_dir
@@ -104,8 +108,9 @@
@sandbox[:tasks].each { |block| block.call }
Rake.application.instance_variable_set :@rules, @sandbox[:rules]
- # Get rid of all artifacts and out test directory.
+ # Get rid of all artifacts and addons.
@sandbox[:artifacts].tap { |artifacts| Artifact.class_eval { @artifacts = artifacts } }
+ Addon.instance_eval { @addons.clear }
# Restore options.
Buildr.options.test = nil