Author: assaf
Date: Tue Apr  8 13:31:32 2008
New Revision: 646061

URL: http://svn.apache.org/viewvc?rev=646061&view=rev
Log:
I think it's about time we have a class to handle Manifests.


Modified:
    incubator/buildr/trunk/lib/buildr/java/packaging.rb
    incubator/buildr/trunk/spec/java_packaging_spec.rb

Modified: incubator/buildr/trunk/lib/buildr/java/packaging.rb
URL: 
http://svn.apache.org/viewvc/incubator/buildr/trunk/lib/buildr/java/packaging.rb?rev=646061&r1=646060&r2=646061&view=diff
==============================================================================
--- incubator/buildr/trunk/lib/buildr/java/packaging.rb (original)
+++ incubator/buildr/trunk/lib/buildr/java/packaging.rb Tue Apr  8 13:31:32 2008
@@ -23,81 +23,103 @@
     # Adds packaging for Java projects: JAR, WAR, AAR, EAR, Javadoc.
     module Java
 
-      # Adds support for MANIFEST.MF and other META-INF files.
-      module WithManifest #:nodoc:
+      class Manifest
 
-        MANIFEST_HEADER = ['Manifest-Version: 1.0', 'Created-By: Buildr']
-        MANIFEST_LINE_SEP = /\r\n|\n|\r[^\n]/
-        MANIFEST_SECTION_SEP = /(#{MANIFEST_LINE_SEP}){2}/
+        STANDARD_HEADER = "Manifest-Version: 1.0\nCreated-By: Buildr\n"
+        LINE_SEPARATOR = /\r\n|\n|\r[^\n]/ #:nodoc:
+        SECTION_SEPARATOR = /(#{LINE_SEPARATOR}){2}/ #:nodoc:
 
         class << self
-          def included(base)
-            base.class_eval do
-              alias :initialize_without_manifest :initialize
-              alias :initialize :initialize_with_manifest
-            end
+
+          def parse(str)
+            sections = str.split(SECTION_SEPARATOR).reject { |s| 
s.strip.empty? }
+            new sections.map { |section|
+              lines = section.split(LINE_SEPARATOR).inject([]) { |merged, line|
+                if line[0] == 32
+                  merged.last << line[1..-1]
+                else
+                  merged << line
+                end
+                merged
+              }
+              lines.map { |line| line.scan(/(.*?):\s*(.*)/).first }.
+                inject({}) { |map, (key, value)| map.merge(key=>value) }
+            }
           end
 
-          # from_file(path) => Array of hashes (sections of manifest)
-          #
-          # The array returned by this function can be feed to
-          #       WithManifest#manifest_lines_from
-          # to obtain the formatted manifest content.
-          def from_file(file)
+          def from_zip(file)
             Zip::ZipFile.open(file.to_s) do |zip|
-              return [{}] unless zip.get_entry('META-INF/MANIFEST.MF')
-              zip.read('META-INF/MANIFEST.MF').split(MANIFEST_SECTION_SEP).
-                reject { |s| s.chomp == "" }.map do |section|
-                section.split(MANIFEST_LINE_SEP).inject([]) { |merged, line|
-                  if line[0] == 32
-                    merged.last << line[1..-1]
-                  else
-                    merged << line
-                  end
-                  merged
-                }.map { |line| line.split(/:\s+/) }.
-                  inject({}) { |map, (name, value)| map.merge(name => value) }
-              end
-            end
-          end
-          
-          def manifest_lines_from(arg)
-            case arg
-            when Hash
-              arg.map { |name, value| "#{name}: #{value}" }.sort.
-                map { |line| manifest_wrap_at_72(line) }.flatten
-            when Array
-              arg.map { |section|
-                name = section.has_key?('Name') ? ["Name: #{section['Name']}"] 
: []
-                name + section.except('Name').map { |name, value| "#{name}: 
#{value}" }.sort + ['']
-              }.flatten.map { |line| manifest_wrap_at_72(line) }.flatten
-            when Proc, Method
-              manifest_lines_from(arg.call)
-            when String
-              arg.split("\n").map { |line| manifest_wrap_at_72(line) }.flatten
-            else
-              fail 'Invalid manifest, expecting Hash, Array, file name/task or 
proc/method.'
+              return Manifest.new unless zip.get_entry('META-INF/MANIFEST.MF')
+              Manifest.parse zip.read('META-INF/MANIFEST.MF')
             end
           end
 
-          # update_manifest(file) { |manifest_sections| ... }
+          # update_manifest(file) { |manifest| ... }
           def update_manifest(file)
-            sections = from_file(file.to_s)
-            result = yield sections
+            manifest = from_zip(file)
+            result = yield manifest
             Zip::ZipFile.open(file.to_s) do |zip|
               zip.get_output_stream('META-INF/MANIFEST.MF') do |out|
-                out.write WithManifest.manifest_lines_from(sections).join("\n")
+                out.write manifest.to_s
                 out.write "\n"
               end
             end
             result
           end
 
-         private
-          def manifest_wrap_at_72(arg)
-            #return arg.map { |line| manifest_wrap_at_72(line) 
}.flatten.join("\n") if Array === arg
-            return arg if arg.size < 72
-            [ arg[0..70], manifest_wrap_at_72(' ' + arg[71..-1]) ]
+        end
+
+        def initialize(arg = nil)
+          case arg
+          when nil, Hash then @sections = [arg || {}]
+          when Array then @sections = arg
+          when String then @sections = Manifest.parse(arg).sections
+          when Proc, Method then @sections = Manifest.parse(arg.call).sections
+          else
+            fail 'Invalid manifest, expecting Hash, Array, file name/task or 
proc/method.'
+          end
+        end
+
+        attr_reader :sections
+
+        def main
+          sections.first
+        end
+
+        include Enumerable
+
+        def each(&block)
+          @sections.each(&block)
+        end
+
+        def to_s
+          @sections.map { |section|
+            keys = section.keys
+            keys.unshift('Name') if keys.delete('Name')
+            lines = keys.map { |key| manifest_wrap_at_72("#{key}: 
#{section[key]}") }
+            lines + ['']
+          }.flatten.join("\n")
+        end
+
+      private
+
+        def manifest_wrap_at_72(line)
+          return [line] if line.size < 72
+          [ line[0..70] ] + manifest_wrap_at_72(' ' + line[71..-1])
+        end
+
+      end
+
+
+      # Adds support for MANIFEST.MF and other META-INF files.
+      module WithManifest #:nodoc:
+
+        class << self
+          def included(base)
+            base.class_eval do
+              alias :initialize_without_manifest :initialize
+              alias :initialize :initialize_with_manifest
+            end
           end
 
         end
@@ -124,9 +146,10 @@
               # Tempfiles gets deleted on garbage collection, so we're going 
to hold on to it
               # through instance variable not closure variable.
               Tempfile.open 'MANIFEST.MF' do |@manifest_tmp|
-                lines = String === manifest || Rake::Task === manifest ?
-                WithManifest.manifest_lines_from(File.read(manifest.to_s)) : 
WithManifest.manifest_lines_from(manifest)
-                @manifest_tmp.write((MANIFEST_HEADER + lines).join("\n"))
+                self.manifest = File.read(manifest.to_s) if String === 
manifest || Rake::Task === manifest
+                self.manifest = Manifest.new(manifest) unless Manifest === 
manifest
+                @manifest_tmp.write Manifest::STANDARD_HEADER
+                @manifest_tmp.write manifest.to_s
                 @manifest_tmp.write "\n"
                 path('META-INF').include @manifest_tmp.path, :as=>'MANIFEST.MF'
               end
@@ -409,15 +432,15 @@
           file(path_to(component[:path], 
component[:artifact].to_s.pathmap('%f')) => component[:artifact]) do |task|
             mkpath task.to_s.pathmap('%d'), :verbose => false
             cp component[:artifact].to_s, task.to_s, :verbose => false
-            WithManifest.update_manifest(task) do |sections|
-              class_path = sections.first['Class-Path'].to_s.split
+            Manifest.update_manifest(task) do |manifest|
+              class_path = manifest.main['Class-Path'].to_s.split
               included_libs = class_path.map { |fn| fn.pathmap('%f') }
               Zip::ZipFile.foreach(task.to_s) do |entry|
                 included_libs << entry.name.pathmap('%f') if entry.file? && 
entry.name =~ /^WEB-INF\/lib\/[^\/]+$/
               end
               # Include all other libraries in the classpath.
               class_path += libs_classpath(component).reject { |path| 
included_libs.include?(File.basename(path)) }
-              sections.first['Class-Path'] = class_path.join(' ')
+              manifest.main['Class-Path'] = class_path.join(' ')
             end
           end
         end

Modified: incubator/buildr/trunk/spec/java_packaging_spec.rb
URL: 
http://svn.apache.org/viewvc/incubator/buildr/trunk/spec/java_packaging_spec.rb?rev=646061&r1=646060&r2=646061&view=diff
==============================================================================
--- incubator/buildr/trunk/spec/java_packaging_spec.rb (original)
+++ incubator/buildr/trunk/spec/java_packaging_spec.rb Tue Apr  8 13:31:32 2008
@@ -54,7 +54,9 @@
 
 
 shared_examples_for 'package with manifest' do
-  @long_line = 'No line may be longer than 72 bytes (not characters), in its 
UTF8-encoded form. If a value would make the initial line longer than this, it 
should be continued on extra lines (each starting with a single SPACE).'
+  before do
+    @long_line = 'No line may be longer than 72 bytes (not characters), in its 
UTF8-encoded form. If a value would make the initial line longer than this, it 
should be continued on extra lines (each starting with a single SPACE).'
+  end
 
   def package_with_manifest(manifest = nil)
     packaging = @packaging
@@ -67,15 +69,15 @@
   def inspect_manifest
     package = project('foo').package(@packaging)
     package.invoke
-    yield Buildr::Packaging::Java::WithManifest.from_file(package)
+    yield Buildr::Packaging::Java::Manifest.from_zip(package)
   end
 
   it 'should include default header when no options specified' do
     ENV['USER'] = 'MysteriousJoe'
     package_with_manifest # Nothing for default.
-    inspect_manifest do |sections|
-      sections.size.should be(1)
-      sections.first.should == {
+    inspect_manifest do |manifest|
+      manifest.sections.size.should be(1)
+      manifest.main.should == {
         'Manifest-Version'        =>'1.0',
         'Created-By'              =>'Buildr',
         'Implementation-Title'    =>@project.name,
@@ -96,12 +98,12 @@
 
   it 'should map manifest from hash' do
     package_with_manifest 'Foo'=>1, :bar=>'Bar'
-    inspect_manifest do |sections|
-      sections.size.should be(1)
-      sections.first['Manifest-Version'].should eql('1.0')
-      sections.first['Created-By'].should eql('Buildr')
-      sections.first['Foo'].should eql('1')
-      sections.first['bar'].should eql('Bar')
+    inspect_manifest do |manifest|
+      manifest.sections.size.should be(1)
+      manifest.main['Manifest-Version'].should eql('1.0')
+      manifest.main['Created-By'].should eql('Buildr')
+      manifest.main['Foo'].should eql('1')
+      manifest.main['bar'].should eql('Bar')
     end
   end
 
@@ -109,24 +111,24 @@
     package_with_manifest 'Foo'=>1, :bar=>'Bar'
     package = project('foo').package(@packaging)
     package.invoke
-    Zip::ZipFile.open(package.to_s) { |zip| 
zip.file.read('META-INF/MANIFEST.MF').should =~ 
/#{Buildr::Packaging::Java::WithManifest::MANIFEST_LINE_SEP}$/ }
+    Zip::ZipFile.open(package.to_s) { |zip| 
zip.file.read('META-INF/MANIFEST.MF').should =~ 
/#{Buildr::Packaging::Java::Manifest::LINE_SEPARATOR}$/ }
   end
 
   it 'should break hash manifest lines longer than 72 characters using 
continuations' do
     package_with_manifest 'foo'=>@long_line
     package = project('foo').package(@packaging)
-    inspect_manifest do |sections|
-      sections.first['foo'].should == @long_line
+    inspect_manifest do |manifest|
+      manifest.main['foo'].should == @long_line
     end
   end
 
   it 'should map manifest from array' do
     package_with_manifest [ { :foo=>'first' }, { :bar=>'second' } ]
-    inspect_manifest do |sections|
-      sections.size.should be(2)
-      sections.first['Manifest-Version'].should eql('1.0')
-      sections.first['foo'].should eql('first')
-      sections.last['bar'].should eql('second')
+    inspect_manifest do |manifest|
+      manifest.sections.size.should be(2)
+      manifest.main['Manifest-Version'].should eql('1.0')
+      manifest.main['foo'].should eql('first')
+      manifest.sections.last['bar'].should eql('second')
     end
   end
 
@@ -140,8 +142,8 @@
   it 'should break array manifest lines longer than 72 characters using 
continuations' do
     package_with_manifest ['foo'=>@long_line]
     package = project('foo').package(@packaging)
-    inspect_manifest do |sections|
-      sections.first['foo'].should == @long_line
+    inspect_manifest do |manifest|
+      manifest.main['foo'].should == @long_line
     end
   end
 
@@ -149,27 +151,27 @@
     package_with_manifest [ {}, { 'Name'=>'first', :Foo=>'first', 
:bar=>'second' } ]
     package = project('foo').package(@packaging)
     package.invoke
-    inspect_manifest do |sections|
-      sections[1]["Name"].should == "first"
+    inspect_manifest do |manifest|
+      manifest.sections[1]["Name"].should == "first"
     end
   end
 
   it 'should create manifest from proc' do
     package_with_manifest lambda { 'Meta: data' }
-    inspect_manifest do |sections|
-      sections.size.should be(1)
-      sections.first['Manifest-Version'].should eql('1.0')
-      sections.first['Meta'].should eql('data')
+    inspect_manifest do |manifest|
+      manifest.sections.size.should be(1)
+      manifest.main['Manifest-Version'].should eql('1.0')
+      manifest.main['Meta'].should eql('data')
     end
   end
 
   it 'should create manifest from file' do
     write 'MANIFEST.MF', 'Meta: data'
     package_with_manifest 'MANIFEST.MF'
-    inspect_manifest do |sections|
-      sections.size.should be(1)
-      sections.first['Manifest-Version'].should eql('1.0')
-      sections.first['Meta'].should eql('data')
+    inspect_manifest do |manifest|
+      manifest.sections.size.should be(1)
+      manifest.main['Manifest-Version'].should eql('1.0')
+      manifest.main['Meta'].should eql('data')
     end
   end
 
@@ -178,10 +180,10 @@
       write task.to_s, 'Meta: data'
     end
     package_with_manifest 'MANIFEST.MF'
-    inspect_manifest do |sections|
-      sections.size.should be(1)
-      sections.first['Manifest-Version'].should eql('1.0')
-      sections.first['Meta'].should eql('data')
+    inspect_manifest do |manifest|
+      manifest.sections.size.should be(1)
+      manifest.main['Manifest-Version'].should eql('1.0')
+      manifest.main['Meta'].should eql('data')
     end
   end
 
@@ -190,7 +192,7 @@
     mkpath 'target/classes'
     packaging = @packaging
     define('foo', :version=>'1.0') { package(packaging).with 
:manifest=>{'Foo'=>'data'} }
-    inspect_manifest { |sections| sections.first['Foo'].should eql('data') }
+    inspect_manifest { |manifest| manifest.main['Foo'].should eql('data') }
   end
 
   it 'should include META-INF directory' do
@@ -564,8 +566,8 @@
       File.open('tmp.zip', 'wb') do |tmp|
         tmp.write ear.file.read(package)
       end
-      sections = Buildr::Packaging::Java::WithManifest.from_file('tmp.zip')
-      yield sections.first['Class-Path'].to_s.split(' ')
+      manifest = Buildr::Packaging::Java::Manifest.from_zip('tmp.zip')
+      yield manifest.main['Class-Path'].to_s.split(' ')
     end
   end
 
@@ -624,8 +626,8 @@
 
     inspect_ear { |files| files.should include('lib/one-1.0.jar', 
'ejb/two-1.0.jar') }
     
-    
Buildr::Packaging::Java::WithManifest.from_file(project('one').package(:jar)).first['Class-Path'].should
 be_nil
-    
Buildr::Packaging::Java::WithManifest.from_file(project('two').package(:jar)).first['Class-Path'].should
 be_nil
+    
Buildr::Packaging::Java::Manifest.from_zip(project('one').package(:jar)).main['Class-Path'].should
 be_nil
+    
Buildr::Packaging::Java::Manifest.from_zip(project('two').package(:jar)).main['Class-Path'].should
 be_nil
     
     inspect_classpath 'ejb/two-1.0.jar' do |classpath|
       classpath.should include('../lib/one-1.0.jar')


Reply via email to