]> icculus.org git repositories - duncan/rpmer.git/blob - lib/rpmer/rpmspec.rb
Initial commit
[duncan/rpmer.git] / lib / rpmer / rpmspec.rb
1 # This file can be distributed under the same terms as ruby itself.  
2 # Author: Marek Gilbert <gil (at) fooplanet (dot) com>
3
4 # RpmSpecWriter has formatting primitives that simplify generating RPM spec
5 # files.
6 class RpmSpecWriter
7   def initialize(fd)
8     @fd = fd
9   end
10
11   # Write out the contents of +line+ and a newline, just like
12   # +Kernel::puts+.
13   def puts(line = '')
14     @fd.puts(line)
15   end
16
17   # Write out an RPM header.  If the header is a multi-valued header then
18   # the value is joined with ", ".  If the value is nil, nothing is written.
19   def header(name, value)
20     return if value.nil?
21     if value.kind_of?(Array)
22       return if value.length == 0
23
24       value = value.join(', ')
25     end
26     puts("#{name}: #{value}")
27   end
28
29   # Declare a section and yield.
30   def section(name, rest = nil)
31     out = '%' + name
32     if !rest.nil?
33       out += ' ' + rest
34     end
35
36     puts()
37     puts(out)
38     yield
39   end
40 end
41
42 # A simple abstraction for a %name section in an rpm file.  Every section
43 # has a %-sign, a name, possibly some text following the %name (e.g.
44 # %files -f %{name}.files), and a block of lines that follow.
45 class RpmSection
46   # The name of the section
47   attr_accessor :name
48
49   # The rest of the line following the section declaration, if any.
50   attr_accessor :rest
51
52   # The block of lines that follow the section declaration after a newline.
53   attr_accessor :text
54
55   def initialize(name, rest = nil)
56     @name = name
57     @rest = rest
58     @text = ''
59   end
60
61   # Add a line or lines to +text+.
62   def <<(line)
63     @text << "\n" if @text.length > 0
64     @text << line
65   end
66 end
67
68 # A generic interface to an RPM specification, consisiting of headers,
69 # sections, and a little bit of glue to connect to RpmSpecWriter.
70 class RpmSpec
71   @@headers = []
72   @@sections = []
73
74   # RPM header names aren't suitable method names because Ruby wants at
75   # least the initial character of the method to be lower case.  Generate
76   # good ruby names from the RPM names by converting from CamelCase to
77   # camel_case.
78   def self.sym_name(name)
79     case name
80     when 'URL'
81       return name.downcase
82
83     else
84       sym = name.dup
85       sym.sub!(/^[A-Z]/) { |x| x.downcase }
86       sym.gsub!(/[A-Z]/) { |x| '_' + x.downcase }
87       return sym
88     end
89   end
90
91
92   # Declares an RPM header.  +default_value+ can be lambda in which case a
93   # new value is taken from the result of calling the lambda with no
94   # arguments.
95   def self.rpm_header(name, default_value = nil)
96     get = sym_name(name)
97     set = get + '='
98
99     @@headers.push([name, get.intern, set.intern, default_value])
100     attr_accessor(get.intern)
101   end
102
103   # Declares an RPM header whose value consists of a list of things.
104   def self.rpm_list_header(name)
105     default_value = lambda do
106       Array.new()
107     end
108     rpm_header(name, default_value)
109   end
110
111   # Declares an RPM section.
112   def self.rpm_section(name)
113     get = sym_name(name)
114     set = get + '='
115     @@sections.push([name, get.intern, set.intern])
116     attr_accessor(get.intern)
117   end
118
119   attr_accessor :spec_filename
120
121   rpm_header('Name')
122   rpm_header('Version')
123   rpm_header('Release')
124   rpm_header('Summary')
125   rpm_header('License')
126   rpm_header('Group')
127   rpm_header('URL')
128
129   #rpm_header('AutoReqProv')
130   rpm_list_header('Conflicts')
131   rpm_list_header('Provides')
132   rpm_list_header('Requires')
133   rpm_list_header('Obsoletes')
134
135   rpm_list_header('Prereq')
136
137   rpm_header('BuildArch')
138   rpm_header('BuildRoot')
139   rpm_list_header('BuildConflicts')
140   rpm_list_header('BuildRequires')
141
142   # The +sources+ array generates the Source0, Source1, etc headers.
143   attr_reader :sources
144
145   # The +patches+ array generates the Patch0, Patch1, etc headers.
146   attr_reader :patches
147
148   rpm_section('description')
149   rpm_section('prep')
150   rpm_section('build')
151   rpm_section('install')
152   rpm_section('clean')
153   rpm_section('files')
154   rpm_section('changelog')
155
156   # Create a new RpmSpec that is intended to be written to the file
157   # at +filename+.
158   def initialize(filename)
159     @spec_filename = filename
160
161     @@headers.each do |name, get, set, default_value|
162       if default_value.kind_of?(Proc)
163         default_value = default_value.call()
164       end
165       self.send(set, default_value)
166     end
167
168     @sources = []
169     @patches = []
170
171     @@sections.each do |name, get, set|
172       self.send(set, RpmSection.new(name))
173     end
174   end
175
176   # Write the RpmSpec out to the +io+ which is already open for write.
177   def write(io)
178     @@headers.each do |name, get, set, |
179       io.header(name, self.send(get))
180     end
181
182     @sources.each_with_index do |src, i|
183       io.header('Source' + i.to_s, src)
184     end
185
186     @patches.each_with_index do |src, i|
187       io.header('Source' + i.to_s, src)
188     end
189
190     @@sections.each do |name, get, set|
191       sec = self.send(get)
192       io.section(sec.name, sec.rest) do
193         io.puts(sec.text)
194       end
195     end
196   end
197 end