forked from OpenACD/OpenACD
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Rakefile
359 lines (325 loc) · 11.2 KB
/
Rakefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
require 'rake/clean'
def percent_to_color(per)
if ENV['COLORTERM'].to_s.downcase == 'yes' or ENV['TERM'] =~ /-color$/
if per >= 90.0
colorstart = "\e[1;32m"
elsif per >= 75.0
colorstart = "\e[0;32m"
elsif per >= 50.0
colorstart = "\e[0;33m"
elsif per >= 25.0
colorstart = "\e[0;31m"
else
colorstart = "\e[1;31m"
end
return [colorstart, "\e[0m"]
else
return ["", ""]
end
end
if RUBY_PLATFORM =~ /win32/i
win32 = true
else
win32 = false
end
INCLUDE = "include"
vertest = `erl -noshell -eval "timer:sleep(10), io:format(\\"~n~s~n\\", [erlang:system_info(otp_release)])." -s init stop`.chomp.split("\n")[-1]
if vertest =~ /(R(\d\d)[AB])(\d*)/
OTPVERSION = $1
unless $2.to_i >= 13 and not ($2 == '13' and $3 == '')
STDERR.puts "I'm sorry, but your version of erlang (#{$1}#{$3}) is too old. Please upgrade to R13B01 or later."
exit -1
end
else
STDERR.puts "unable to determine OTP version! (I got #{vertest})"
exit -1
end
ERLC_FLAGS = "-I#{INCLUDE} -D #{OTPVERSION} +warn_unused_vars +warn_unused_import +warn_exported_vars +warn_untyped_record"
SRC = FileList['src/*.erl']
HEADERS = FileList['include/*.hrl']
OBJ = SRC.pathmap("%{src,ebin}X.beam")
CONTRIB = FileList['contrib/*']
DEBUGOBJ = SRC.pathmap("%{src,debug_ebin}X.beam")
COVERAGE = SRC.pathmap("%{src,coverage}X.txt")
RELEASE = FileList['src/*.rel.src'].pathmap("%{src,ebin}X")
# check to see if gmake is available, if not fall back on the system make
if (not win32) and res = `which gmake` and $?.exitstatus.zero? and not res =~ /no gmake in/
MAKE = File.basename(res.chomp)
else
MAKE = 'make'
end
@maxwidth = SRC.map{|x| File.basename(x, 'erl').length}.max
CLEAN.include("ebin/*.beam")
CLEAN.include("ebin/*.script")
CLEAN.include("ebin/*.boot")
CLEAN.include("ebin/*.rel")
CLEAN.include("debug_ebin/*.beam")
CLEAN.include("debug_ebin/*.script")
CLEAN.include("debug_ebin/*.boot")
CLEAN.include("coverage/*.txt")
CLEAN.include("coverage/*.txt.failed")
CLEAN.include("coverage/*.html")
CLEAN.include("doc/*.html")
verbose(true) unless ENV['quiet']
directory 'ebin'
directory 'debug_ebin'
directory 'coverage'
#directory 'doc'
rule ".beam" => ["%{ebin,src}X.erl"] + HEADERS do |t|
sh "erlc -pa ebin -W #{ERLC_FLAGS} +warn_missing_spec -o ebin #{t.source} "
end
rule ".beam" => ["%{debug_ebin,src}X.erl"] + HEADERS do |t|
sh "erlc +debug_info -D TEST -pa debug_ebin -W #{ERLC_FLAGS} -o debug_ebin #{t.source} "
end
rule ".rel" => ["%{ebin,src}X.rel.src"] do |t|
contents = File.read(t.source)
#p contents
while contents =~ /^[\s\t]*([-a-zA-Z0-9_]+),[\s\t]*$/
app = $1
if app == "erts"
version = `erl -noshell -eval "timer:sleep(10), io:format(\\"~n~s~n\\", [erlang:system_info(version)])." -s init stop`.chomp.split("\n")[-1]
else
version = `erl -noshell -eval "timer:sleep(10), application:load(#{app}), io:format(\\"~n~s~n\\", [proplists:get_value(#{app}, lists:map(fun({Name, Desc, Vsn}) -> {Name, Vsn} end, application:loaded_applications()))])." -s init stop`.chomp.split("\n")[-1]
end
if md = /(\d+\.\d+(\.\d+(\.\d+|)|))/.match(version)
contents.sub!(app, "{#{app}, \"#{md[1]}\"}")
else
STDERR.puts "Cannot find application #{app} mentioned in release file!"
exit 1
end
end
File.open(t.name, 'w') do |f|
f.puts contents
end
end
rule ".txt" => ["%{coverage,debug_ebin}X.beam"] do |t|
mod = File.basename(t.source, '.beam')
if ENV['modules'] and not ENV['modules'].split(',').include? mod
puts "skipping tests for #{mod}"
next
end
print " #{mod.ljust(@maxwidth - 1)} : "
STDOUT.flush
test_output = `erl -noshell -pa debug_ebin -sname testpx -eval "cover:start(), cover:compile_beam(\\"#{t.source}\\"), try eunit:test(#{mod}, [verbose]) of _Any -> cover:analyse_to_file(#{mod}, \\"coverage/#{mod}.txt\\"), cover:analyse_to_file(#{mod}, \\"coverage/#{mod}.html\\", [html]) catch _:_ -> io:format(\\"This module does not provide a test() function~n\\"), ok end." -s init stop`
if /(All \d+ tests (successful|passed)|There were no tests to run|This module does not provide a test\(\) function|Test (successful|passed))/ =~ test_output
File.delete(t.to_s+'.failed') if File.exists?(t.to_s+'.failed')
if ENV['verbose']
puts test_output.split("\n")[1..-1].map{|x| x.include?('1>') ? x.gsub(/\([a-zA-Z0-9\-@]+\)1>/, '') : x}.join("\n")
else
out = $1
if /(All \d+ tests (successful|passed)|Test (successful|passed))/ =~ test_output
colorstart, colorend = percent_to_color(80)
#elsif /This module does not provide a test\(\) function/ =~ test_output
#colorstart, colorend = percent_to_color(50)
else
colorstart, colorend = percent_to_color(50)
#colorstart, colorend = ["", ""]
end
puts "#{colorstart}#{out}#{colorend}"
#puts " #{mod.ljust(@maxwidth - 1)} : #{out}"
end
else
if ENV['COLORTERM'].to_s.downcase == 'yes' or ENV['TERM'] =~ /-color$/
puts "\e[1;35mFAILED\e[0m"
else
puts "FAILED"
end
puts test_output.split("\n")[1..-1].map{|x| x.include?('1>') ? x.gsub(/\([a-zA-Z0-9\-@]+\)1>/, '') : x}.join("\n")
if ENV['COLORTERM'].to_s.downcase == 'yes' or ENV['TERM'] =~ /-color$/
puts " #{mod.ljust(@maxwidth - 1)} : \e[1;35mFAILED\e[0m"
else
puts " #{mod.ljust(@maxwidth - 1)} : FAILED"
end
File.delete(t.to_s) if File.exists?(t.to_s)
File.new(t.to_s+'.failed', 'w').close
end
end
task :compile => ['ebin', :contrib, :keygen] + HEADERS + OBJ + RELEASE do
Dir["ebin/*.rel"].each do |rel|
rel = File.basename(rel, '.rel')
sh "erl -noshell -eval \"systools:make_script(\\\"ebin/#{rel}\\\", [{outdir, \\\"ebin\\\"}]).\" -s init stop -pa ebin"
end
end
task :submodules do
if File.directory? ".git"
sh "git submodule init"
sh "git submodule update"
end
end
task :contrib => [:submodules] do
CONTRIB.each do |cont|
if File.exists? File.join(cont, 'Rakefile')
pwd = Dir.pwd
Dir.chdir(cont)
if win32
sh "ruby #{$0}"
else
sh "#{$0}"
end
Dir.chdir(pwd)
elsif File.exists? File.join(cont, 'Makefile')
sh "#{MAKE} -C #{cont}"
else
STDERR.puts "Don't know how to build for #{cont}"
end
unless Dir["#{cont}/ebin/*.beam"].length.zero?
if win32
sh "copy /Y \"#{cont}/ebin\\*.beam\" ebin"
else
sh "cp #{cont}/ebin/*.beam ebin"
end
end
end
end
task :default => :compile do
if File.exists?('success_message')
puts File.read('success_message')
end
end
task :release => :compile
desc "Alias for test:all"
task :test => "test:all"
desc "Generate Documentation"
task :doc do
sh('mkdir doc') unless File.directory? 'doc'
sh("rm -rf doc/*.html && cd doc && erl -noshell -run edoc files ../#{SRC.join(" ../")} -run init stop")
end
desc "Generate a RSA key to encrypt HTTP passwords"
task :keygen do
unless File.exists?("key")
puts "Generating RSA key..."
`ssh-keygen -t rsa -f key -N ""`
if $?.exitstatus != 0
STDERR.puts "RSA key generation FAILED!"
exit -1
end
end
end
task :roll_release => [:submodules] do
version = `git describe --exact-match`
puts $?.exitstatus
if $?.exitstatus != 0
version = "snapshot-#{Time.now.strftime('%Y-%m-%d')}"
end
sh "./git-archive-all.sh --prefix OpenACD-#{version}/ OpenACD-#{version}.tar"
sh "gzip -f OpenACD-#{version}.tar"
end
namespace :test do
desc "Compile .beam files with -D TEST and +debug_info => debug_ebin"
task :compile => ['debug_ebin', :keygen, :contrib] + HEADERS + DEBUGOBJ
task :contrib do
if File.directory? ".git"
sh "git submodule init"
sh "git submodule update"
end
CONTRIB.each do |cont|
if File.exists? File.join(cont, 'Rakefile')
pwd = Dir.pwd
Dir.chdir(cont)
if win32
sh "ruby #{$0}"
else
sh "#{$0}"
end
Dir.chdir(pwd)
elsif File.exists? File.join(cont, 'Makefile')
sh "#{MAKE} -C #{cont}"
else
STDERR.puts "Don't know how to build for #{cont}"
end
unless Dir["#{cont}/ebin/*.beam"].length.zero?
if win32
sh "copy /Y \"#{cont}/ebin\\*.beam\" debug_ebin"
else
sh "cp #{cont}/ebin/*.beam debug_ebin"
end
end
end
end
desc "run eunit tests and output coverage reports"
task :all => [:compile, :eunit, :report_coverage]
desc "run only the eunit tests"
task :eunit => [:compile, 'coverage'] + COVERAGE
desc "rerun any outstanding tests and report the coverage"
task :report_coverage => [:eunit, :report_current_coverage]
desc "report the percentage code coverage of the last test run"
task :report_current_coverage do
global_total = 0
files = (Dir['coverage/*.txt'] + Dir['coverage/*.txt.failed']).sort
maxwidth = files.map{|x| x = File.basename(x, '.failed'); File.basename(x, ".txt").length}.max
puts "Code coverage:"
files.each do |file|
if file =~ /\.txt\.failed$/
if ENV['COLORTERM'].to_s.downcase == 'yes' or ENV['TERM'] =~ /-color$/
puts " #{File.basename(file, ".txt.failed").ljust(maxwidth)} : \e[1;35mFAILED\e[0m"
else
puts " #{File.basename(file, ".txt.failed").ljust(maxwidth)} : FAILED"
end
else
total = 0
tally = 0
File.read(file).each do |line|
if line =~ /^\s+[1-9][0-9]*\.\./
total += 1
tally += 1
elsif line =~ /^\s+0\.\./ and not line =~ /^-module/
total += 1
end
end
per = tally/total.to_f * 100
colorstart, colorend = percent_to_color(per)
puts " #{File.basename(file, ".txt").ljust(maxwidth)} : #{colorstart}#{sprintf("%.2f%%", (tally/(total.to_f)) * 100)}#{colorend}"
global_total += (tally/(total.to_f)) * 100
end
end
colorstart, colorend = percent_to_color(global_total/files.length)
puts "Overall coverage: #{colorstart}#{sprintf("%.2f%%", global_total/files.length)}#{colorend}"
end
task :report_missing_specs do
unspecced = []
ignored = %w{handle_info handle_cast handle_call code_change terminate init}
puts "Functions missing specs:"
SRC.each do |src|
contents = File.read(src)
contents.each do |line|
if md = /^([a-z_]+)\(.*?\) ->/.match(line) and not ignored.include?(md[1]) and not md[1][-5..-1] == '_test' and not md[1][-6..-1] == '_test_'
unless /^-spec\(#{md[1]}\//.match(contents)
unspecced << File.basename(src, '.erl') + ':'+ md[1]
end
end
end
end
puts " "+unspecced.uniq.join("\n ")
end
desc "run the dialyzer"
task :dialyzer do
print "running dialyzer..."
`dialyzer --check_plt`
if $?.exitstatus != 0
puts 'no PLT'
puts "The dialyzer can't find the initial PLT, you can try building one using `rake test:build_plt`. This can take quite some time."
exit(1)
end
STDOUT.flush
# Add -DTEST=1 here to make dialyzer evaluate the code in the test cases. This generates some spurious warnings so
# it's not set normally but it can be very helpful occasionally.
dialyzer_flags = ""
dialyzer_flags += " -DTEST=1" if ENV['dialyzer_debug']
dialyzer_flags += " -Wunderspecs" if ENV['dialyzer_underspecced']
contribfiles = Dir['contrib/*/src/*.erl'].join(' ')
dialyzer_output = `dialyzer -D#{OTPVERSION}=1 #{dialyzer_flags} --src -I include -c #{SRC.join(' ')} #{contribfiles}`
#puts dialyzer_output
if $?.exitstatus.zero?
puts 'ok'
else
puts 'not ok'
puts dialyzer_output
end
end
desc "try to create the dialyzer's initial PLT"
task :build_plt do
out = `which erlc`
foo = out.split('/')[0..-3].join('/')+'/lib/erlang/lib'
sh "dialyzer --build_plt -r #{foo}/erts*/ebin #{foo}/kernel*/ebin #{foo}/stdlib*/ebin #{foo}/mnesia*/ebin #{foo}/crypto*/ebin #{foo}/eunit*/ebin #{foo}/compiler*/ebin #{foo}/ssl*/ebin #{foo}/public_key*/ebin"
end
end