Use a configure task and extension tasks to build ruby-ecore.
[ruby-ecore.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 "fileutils"
27
28 module Rake
29         class ConfigureTask < TaskLib
30                 CACHE_FILE = ".configure_state.yaml"
31
32                 attr_reader :tests
33
34                 def initialize # :yield: self
35                         @tests = TestList.new(load_tests || [])
36
37                         yield self if block_given?
38
39                         define
40                 end
41
42                 # returns the test with the specified name
43                 def [](name)
44                         @tests.find { |t| t.name == name }
45                 end
46
47                 def method_missing(m)
48                         self[m.to_s]
49                 end
50
51                 private
52                 def load_tests
53                         r = YAML.load(File.read(CACHE_FILE)) rescue nil
54
55                         r.is_a?(TestList) ? r : nil
56                 end
57
58                 def define
59                         desc "Remove configure results"
60                         task :clobber_configure do
61                                 FileUtils::Verbose.rm_f(CACHE_FILE)
62                         end
63
64                         task :clobber => :clobber_configure
65
66                         desc "Configure this package"
67                         task :configure => [CACHE_FILE]
68
69                         file CACHE_FILE do
70                                 @tests.each do |t|
71                                         t.on_checking.reverse_each { |b| b.call }
72
73                                         if t.invoke
74                                                 t.on_success.reverse_each { |b| b.call }
75                                         else
76                                                 t.on_failure.reverse_each { |b| b.call }
77                                         end
78                                 end
79
80                                 # store the test results in CACHE_FILE
81                                 File.open(CACHE_FILE, "w") { |f| YAML.dump(@tests, f) }
82                         end
83                 end
84
85                 class TestList < Array
86                         def initialize(stored_tests)
87                                 @stored_tests = stored_tests
88                         end
89
90                         def <<(arg)
91                                 assign_result(arg)
92                                 super
93                         end
94
95                         def push(*args)
96                                 args.each { |a| assign_result(a) }
97                                 super
98                         end
99
100                         def unshift(arg)
101                                 assign_result(arg)
102                                 super
103                         end
104
105                         private
106                         def assign_result(test)
107                                 st = @stored_tests.find { |st| st.name == test.name }
108                                 test.result = st.result unless st.nil?
109                         end
110                 end
111
112                 class Test
113                         attr_reader :name, :on_checking, :on_success, :on_failure
114                         attr_accessor :result
115
116                         def initialize(name, opts = {}) # :yield: self
117                                 @name = name
118                                 @opts = opts
119
120                                 @result = nil
121                                 @on_checking = []
122                                 @on_success = []
123                                 @on_failure = []
124
125                                 if opts[:is_critical]
126                                         @on_failure << lambda { raise }
127                                 end
128
129                                 yield self if block_given?
130                         end
131
132                         def to_yaml_properties
133                                 ["@name", "@result"]
134                         end
135
136                         def invoke
137                         end
138
139                         protected
140                         def can_exec_binary?(bin)
141                                 fork do
142                                         STDOUT.reopen("/dev/null")
143                                         STDERR.reopen("/dev/null")
144
145                                         begin
146                                                 exec(bin)
147                                         rescue SystemCallError
148                                                 exit 255
149                                         end
150                                 end
151
152                                 Process.wait
153
154                                 $?.exitstatus != 255
155                         end
156                 end
157
158                 class FooConfigTest < Test
159                         def initialize(name, opts = {})
160                                 super
161
162                                 @result = {}
163                                 @command = "#{name}-config"
164
165                                 @on_checking << lambda do
166                                         print "checking for #{name}... "
167                                         STDOUT.flush
168                                 end
169
170                                 @on_success << lambda { puts "yes (#{version})" }
171                                 @on_failure << lambda { puts "no" }
172                         end
173
174                         def method_missing(m)
175                                 @result[m]
176                         end
177
178                         def invoke
179                                 return false unless can_exec_command?
180
181                                 begin
182                                         [:version, :cflags, :libs].each do |f|
183                                                 @result[f] = lookup_flags(f)
184                                         end
185                                 rescue Exception
186                                         @result.clear
187                                 end
188
189                                 !@result.empty?
190                         end
191
192                         protected
193                         def lookup_flags(f)
194                                 tmp = `#{@command} --#{f}`.strip
195
196                                 raise unless $?.exitstatus.zero?
197                                 tmp
198                         end
199
200                         private
201                         def can_exec_command?
202                                 can_exec_binary?(@command)
203                         end
204                 end
205
206                 class PkgConfigTest < FooConfigTest
207                         def initialize(name, opts = {})
208                                 super
209
210                                 @command = "pkg-config"
211                         end
212
213                         protected
214                         def lookup_flags(f)
215                                 f = :modversion if f == :version
216
217                                 tmp = `#{@command} --silence-errors --#{f} #{@name}`.
218                                       strip.tr("\n", "/")
219
220                                 raise unless $?.exitstatus.zero?
221                                 tmp
222                         end
223                 end
224
225                 class CompileTest < Test
226                         TMP_FILE = ".compile_test"
227
228                         def CompileTest.cflags
229                                 @@cflags
230                         end
231
232                         def CompileTest.cflags=(f)
233                                 @@cflags = f
234                         end
235
236                         def initialize(name, code, opts = {})
237                                 super(name, opts)
238
239                                 @code = code
240                         end
241
242                         def invoke
243                                 @result = false
244
245                                 cc = ENV["CC"] || "cc"
246                                 flags = (ENV["CFLAGS"] || "").dup
247                                 flags << " -I" + Config::CONFIG["archdir"]
248
249                                 unless @opts[:try_link]
250                                         flags << " -c"
251                                 end
252
253                                 File.open(TMP_FILE + ".c", "w") do |f|
254                                         f << @code << "\n"
255                                 end
256
257                                 `#{cc} #{flags} #{TMP_FILE}.c -o #{TMP_FILE}.o > /dev/null 2>&1`
258                                 @result = $?.exitstatus.zero?
259                         ensure
260                                 FileUtils.rm_f("#{TMP_FILE}.c")
261                                 FileUtils.rm_f("#{TMP_FILE}.o")
262                         end
263                 end
264
265                 class HaveFuncTest < CompileTest
266                         def initialize(name, includes = [], opts = {})
267                                 super(name, assemble_code(name, includes), opts)
268
269                                 @on_checking << lambda do
270                                         print "checking for #{name}... "
271                                         STDOUT.flush
272                                 end
273
274                                 @on_success << lambda { puts "yes" }
275                                 @on_failure << lambda { puts "no" }
276                         end
277
278                         private
279                         def assemble_code(func, includes)
280                                 header = includes.inject("") do |a, h|
281                                         a << "#include <#{h}>\n"
282                                 end
283
284                                 body =<<EOF
285 int main () {
286         void *foo = (void *) #{func};
287         foo = (void *) 0;
288         return 0;
289 }
290 EOF
291
292                                 header + body
293                         end
294                 end
295         end
296 end