9a20f794f59c7ed0445e40ccd9e13c67766b0961
[ruby-vorbistagger.git] / rake / configuretask.rb
1 #
2 # Copyright (c) 2005, 2006 Tilman Sauerbeck (tilman at code-monkey de)
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be
13 # included in all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23 require "rake/tasklib"
24 require "rake/clean"
25 require "yaml"
26 require "tempfile"
27 require "fileutils"
28
29 module Rake
30         class ConfigureTask < TaskLib
31                 CACHE_FILE = ".configure_state.yaml"
32
33                 attr_reader :tests
34
35                 def initialize # :yield: self
36                         @tests = TestList.new(load_tests || [])
37
38                         yield self if block_given?
39
40                         define
41                 end
42
43                 # returns the test with the specified name
44                 def [](name)
45                         @tests.find { |t| t.name == name }
46                 end
47
48                 def method_missing(m)
49                         self[m.to_s]
50                 end
51
52                 private
53                 def load_tests
54                         r = YAML.load(File.read(CACHE_FILE)) rescue nil
55
56                         r.is_a?(TestList) ? r : nil
57                 end
58
59                 def define
60                         desc "Remove configure results"
61                         task :clobber_configure do
62                                 FileUtils::Verbose.rm_f(CACHE_FILE)
63                         end
64
65                         task :clobber => :clobber_configure
66
67                         desc "Configure this package"
68                         task :configure => [CACHE_FILE]
69
70                         file CACHE_FILE do
71                                 @tests.each do |t|
72                                         t.on_checking.reverse_each { |b| b.call }
73
74                                         if t.invoke
75                                                 t.on_success.reverse_each { |b| b.call }
76                                         else
77                                                 t.on_failure.reverse_each { |b| b.call }
78                                         end
79                                 end
80
81                                 # store the test results in CACHE_FILE
82                                 File.open(CACHE_FILE, "w") { |f| YAML.dump(@tests, f) }
83                         end
84                 end
85
86                 class TestList < Array
87                         def initialize(stored_tests)
88                                 @stored_tests = stored_tests
89                         end
90
91                         def <<(arg)
92                                 assign_result(arg)
93                                 super
94                         end
95
96                         def push(*args)
97                                 args.each { |a| assign_result(a) }
98                                 super
99                         end
100
101                         def unshift(arg)
102                                 assign_result(arg)
103                                 super
104                         end
105
106                         private
107                         def assign_result(test)
108                                 st = @stored_tests.find { |st| st.name == test.name }
109                                 test.result = st.result unless st.nil?
110                         end
111                 end
112
113                 class Test
114                         attr_reader :name, :on_checking, :on_success, :on_failure
115                         attr_accessor :result
116
117                         def initialize(name, opts = {}) # :yield: self
118                                 @name = name
119                                 @opts = opts
120
121                                 @result = nil
122                                 @on_checking = []
123                                 @on_success = []
124                                 @on_failure = []
125
126                                 if opts[:is_critical]
127                                         @on_failure << lambda { raise }
128                                 end
129
130                                 yield self if block_given?
131                         end
132
133                         def to_yaml_properties
134                                 ["@name", "@result"]
135                         end
136
137                         def invoke
138                         end
139
140                         protected
141                         def can_exec_binary?(bin)
142                                 fork do
143                                         tf = Tempfile.open("configuretask")
144                                         STDOUT.reopen(tf)
145                                         STDERR.reopen(tf)
146
147                                         begin
148                                                 exec(bin)
149                                         rescue SystemCallError
150                                                 exit 0xb00bface
151                                         end
152                                 end
153
154                                 Process.wait
155
156                                 $?.exitstatus != 0xb00bface
157                         end
158                 end
159
160                 class FooConfigTest < Test
161                         def initialize(name, opts = {})
162                                 super
163
164                                 @result = {}
165                                 @command = "#{name}-config"
166
167                                 @on_checking << lambda do
168                                         print "checking for #{name}... "
169                                         STDOUT.flush
170                                 end
171
172                                 @on_success << lambda { puts "yes (#{version})" }
173                                 @on_failure << lambda { puts "no" }
174                         end
175
176                         def method_missing(m)
177                                 @result[m]
178                         end
179
180                         def invoke
181                                 return false unless can_exec_command?
182
183                                 [:version, :cflags, :libs].each do |f|
184                                         @result[f] = lookup_flags(f)
185                                 end
186
187                                 true
188                         end
189
190                         protected
191                         def lookup_flags(f)
192                                 tmp = `#{@command} --#{f}`.strip
193                                 $?.exitstatus.zero? ? tmp : nil
194                         end
195
196                         private
197                         def can_exec_command?
198                                 can_exec_binary?(@command)
199                         end
200                 end
201
202                 class PkgConfigTest < FooConfigTest
203                         def initialize(name, opts = {})
204                                 super
205
206                                 @command = "pkg-config --silence-errors"
207                         end
208
209                         protected
210                         def lookup_flags(f)
211                                 f = :modversion if f == :version
212
213                                 tmp = `#{@command} --#{f} #{@name}`.strip.tr("\n", "/")
214                                 $?.exitstatus.zero? ? tmp : nil
215                         end
216                 end
217
218                 class CompileTest < Test
219                         TMP_FILE = ".compile_test"
220
221                         def CompileTest.cflags
222                                 @@cflags
223                         end
224
225                         def CompileTest.cflags=(f)
226                                 @@cflags = f
227                         end
228
229                         def initialize(name, code, opts = {})
230                                 super(name, opts)
231
232                                 @code = code
233                         end
234
235                         def invoke
236                                 @result = false
237
238                                 cc = ENV["CC"] || "cc"
239                                 flags = (ENV["CFLAGS"] || "").dup
240                                 flags << " -I" + Config::CONFIG["archdir"]
241
242                                 unless @opts[:try_link]
243                                         flags << " -c"
244                                 end
245
246                                 File.open(TMP_FILE + ".c", "w") do |f|
247                                         f << @code << "\n"
248                                 end
249
250                                 `#{cc} #{flags} #{TMP_FILE}.c -o #{TMP_FILE}.o > /dev/null 2>&1`
251                                 @result = $?.exitstatus.zero?
252                         ensure
253                                 FileUtils.rm_f("#{TMP_FILE}.c")
254                                 FileUtils.rm_f("#{TMP_FILE}.o")
255                         end
256                 end
257
258                 class HaveFuncTest < CompileTest
259                         def initialize(name, includes = [], opts = {})
260                                 super(name, assemble_code(name, includes), opts)
261
262                                 @on_checking << lambda do
263                                         print "checking for #{name}... "
264                                         STDOUT.flush
265                                 end
266
267                                 @on_success << lambda { puts "yes" }
268                                 @on_failure << lambda { puts "no" }
269                         end
270
271                         private
272                         def assemble_code(func, includes)
273                                 header = includes.inject("") do |a, h|
274                                         a << "#include <#{h}>\n"
275                                 end
276
277                                 body =<<EOF
278 int main () {
279         void *foo = (void *) #{func};
280         foo = (void *) 0;
281         return 0;
282 }
283 EOF
284
285                                 header + body
286                         end
287                 end
288         end
289 end