-
Notifications
You must be signed in to change notification settings - Fork 16
/
hubris.rb
executable file
·134 lines (112 loc) · 3.75 KB
/
hubris.rb
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
require 'tmpdir'
require 'rubygems'
require 'open4'
require 'digest/md5'
require 'rbconfig'
$:.unshift File.dirname(__FILE__) + "/../ext/stub"
require 'stub'
# require 'file/temp'
# require 'libHaskell'
# TODO delete old files
class HaskellError < RuntimeError
end
class File
# takes block
@@used = 0
def self.withTempFile(template)
# put the code in a temporary file, set opt[:source]
filename="/tmp/foo_#{$$}_#{@@used}.hs" # File::Temp.new(false)
@@used += 1
handle=File.open(filename, 'w')
yield(filename, handle)
File::delete(filename)
end
end
module Hubris
HUBRIS_DIR = ENV['HUBRIS_DIR'] || "/var/hubris"
SO_CACHE = File.expand_path(HUBRIS_DIR + "/cache")
HS_CACHE = File.expand_path(HUBRIS_DIR + "/source")
# require 'stub'
[SO_CACHE,HS_CACHE].each {|dir| FileUtils.mkdir_p(dir)}
$:.push(SO_CACHE)
@always_rebuild=false
@@basepackages = []
def self.add_packages(packages)
@@basepackages.concat packages
end
# load the new functions into target
def hubris(options = { })
# :inline beats :source beats :mod
immediate, source, mod,packages = [:inline,:source,:module,:packages].collect {|x| options[x]}
build_args = options[:no_strict] ? [] : ["--strict"]
# puts packages
# leaves generated source lying around - might want to clean up, but
# useful for debugging too...
source = anonymous_module(immediate, build_args) if immediate
mod = gen_modname(source) if source
raise "code error, should never happen" unless mod
require(hubrify(mod,build_args,source||"",packages||[])) # let it crash
# bind the functions from the Exports namespace
include(eval("Hubris::Exports::#{zencode(mod)}"))
end
private
def anonymous_module(source,build_args)
mod = "Inline_#{Digest::MD5.hexdigest(source + build_args.to_s)}"
filename = "#{HS_CACHE}/#{zencode(mod)}.hs"
File.open(filename, "w") {|h| h.write "module #{mod} where\nimport Language.Ruby.Hubris.Binding\n#{source}\n"}
return filename
end
def gen_modname(filename)
# find the code, compile into haskell module in namespace, set
l = File.open(filename).read
l =~ /^ *module *([^ \t\n]*)/
return $1
end
def zencode(name)
name.gsub(/Z/, 'ZZ').gsub(/z/, 'zz').gsub(/\./,'zi').gsub(/_/,'zu').gsub(/'/, 'zq')
end
def hubrify(mod, args, src,packages=[])
libFile = "#{SO_CACHE}/#{zencode(mod)}.#{dylib_suffix}"
headers = ""
libraries = ""
if @always_rebuild or !File.exists?(libFile)
status,msg = Hubris.noisy("Hubrify #{headers} #{libraries} -v --module #{mod} --output #{libFile} #{args.join(' ')} " +
(packages+@@basepackages).collect{|x| "--package #{x}"}.join(' ') + ' ' + src)
# if Hubrify's not installed, we throw an exception. just as
# good as explicitly checking a flag.
# puts msg
raise HaskellError.new("Hubrify error:\n#{msg + status.exitstatus.to_s}") unless status.exitstatus == 0
end
return libFile
end
def dylib_suffix
case RbConfig::CONFIG['target_os']
when /darwin/; "bundle"
when /linux/; "so"
else; "so" #take a punt
end
end
def self.noisy(str)
pid, stdin, stdout, stderr = Open4.popen4 str
# puts "running #{str}\n"
# puts "Status: #{status.exitstatus}"
# puts "#{pid} done"
msg =<<-"EOF"
ran |#{str}|
output|#{stdout.read}|
error |#{stderr.read}|
EOF
ignored, status = Process.waitpid2 pid
msg += "status |#{status}|"
puts msg unless status == 0
return status, msg
end
end
# this may be sketchy :)
class Class
include Hubris
end
# rescue LoadError
# raise HaskellError, "loading #{libFile} failed:\n#{$!.to_s}" +
# `nm #{libFile} 2>/dev/null` + "\n"
# end