about summary refs log tree commit diff
path: root/pkgs/development/interpreters/ruby/gem_nix_command.patch
blob: 79b6fe024c4ac9095f9fb4f1fef326bfdfe3f176 (plain) (blame)
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
diff --git a/lib/rubygems/command_manager.rb b/lib/rubygems/command_manager.rb
index 0a19016..ef66d30 100644
--- a/lib/rubygems/command_manager.rb
+++ b/lib/rubygems/command_manager.rb
@@ -70,6 +70,7 @@ class Gem::CommandManager
     register_command :unpack
     register_command :update
     register_command :which
+    register_command :nix
   end
 
   ##
diff --git a/lib/rubygems/commands/nix_command.rb b/lib/rubygems/commands/nix_command.rb
new file mode 100644
index 0000000..005d5a9
--- /dev/null
+++ b/lib/rubygems/commands/nix_command.rb
@@ -0,0 +1,226 @@
+require 'net/http'
+require 'rubygems/command'
+require 'rubygems/doc_manager'
+require 'rubygems/install_update_options'
+require 'rubygems/dependency_installer'
+require 'rubygems/local_remote_options'
+require 'rubygems/validator'
+require 'rubygems/exceptions'
+require 'rubygems/version_option'
+require 'rubygems/version'
+require 'open3'
+
+
+def nixname(gem)
+  s = "#{gem}" == gem ? gem : gem.full_name
+  s.gsub(/[.-]/,'_')
+end
+
+def nixdescription(spec)
+  desc_from_spec = spec.description
+  desc = desc_from_spec.sub(/[.].*/,'') # only keep first sentence
+  desc = desc.length > 120 \
+    ? "description = \"#{ desc[0..120] }\"; # cut to 120 chars" \
+    : "description = \"#{ desc }\";"
+  desc = desc.sub(/";$/,"[...]\";") if desc != desc_from_spec
+  desc.gsub("\n"," ") # no \ns in description
+end
+
+##
+# tool creating nix expression to install gems (from ruby forge etc)
+#
+# this is work in progress
+
+class Gem::Commands::NixCommand < Gem::Command
+
+  include Gem::VersionOption
+  include Gem::LocalRemoteOptions
+  include Gem::InstallUpdateOptions
+
+  def initialize
+    defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
+    })
+    super 'nix', 'create a nix file containing expressions of the gems', defaults
+  end
+
+  def description # :nodoc:
+    <<-EOF
+      create a nix file containing expressions of the gems
+      There are many gems. So it's best to only specify some target gems and
+      take them into acocunt with their deps
+      TODO more details
+    EOF
+  end
+
+  def usage # :nodoc:
+    "#{program_name} GEMNAME [GEMNAME ...] [options] -- --build-flags"
+  end
+
+  def arguments # :nodoc:
+    "GEMNAME       name of gem to be added to the expressions file"
+  end
+
+  def defaults_str # :nodoc:
+    # what to put in here ? TODO (probably nothing is ok)
+    ""
+  end
+
+  def execute
+
+    begin
+      @prerelease = false;
+
+      args = options[:args];
+
+      @gems_with_deps = {}
+      @seen = {}
+
+      # args to dep informations
+      args.map { |arg|
+        if arg =~ /(.+)-?(.*)?/ then
+          gem_name = $1
+          version =  $2.empty? ?  Gem::Requirement.default : Gem::Version.new($2)
+        else
+          raise Gem::CommandLineError, "could'nt parse arg. expected: name or name-version"
+        end
+
+        print "adding gem_name\n"
+
+        adddep(Gem::Dependency.new gem_name, version)
+      }
+
+      print " total: #{@gems_with_deps.length}\n"
+
+
+      out = "
+         # WARNING: automatically generated CODE
+         # This section has been generated by gem nix #{args.join(" ")}
+         # the gem nix command has been added by a nix patch to ruby gems
+      "
+      # define aliases
+      aliases = {}
+      @gems_with_deps.each{ |key, (spec, src, deps)|
+        aliases[spec.name] = spec \
+          if aliases[spec.name].nil? || aliases[spec.name].version < spec.version
+
+           src_url = "http://gems.rubyforge.org/gems/#{spec.full_name}.gem"
+
+         # [> get true mirror url reading redirect contents
+         #  h = Net::HTTP.new('gems.rubyforge.org', 80)
+         #  resp, data = h.get("/gems/#{spec.full_name}.gem", nil)
+         #  if resp.code == "200" then
+         #    src_url = "http://gems.rubyforge.org/gems/#{spec.full_name}.gem"
+         #  else if resp.code == "302" then
+         #      src_url = resp['location']
+         #      print "redirection: http://gems.rubyforge.org/gems/#{spec.full_name}.gem -> #{src_url}\n"
+         #    else
+         #      raise Gem::DependencyError.new("unkown http return code #{resp} #{data}")
+         #    end
+         #  end
+
+         #raise Gem::DependencyError("src_url is nil, 302 redirection failed?") if src_url.nil?
+
+         out = "
+         #{out}
+  #{nixname spec} = rubyDerivation {
+     name = \"ruby-#{spec.full_name}\"; # full_name
+     nameNoVersion = \"#{nixname spec.name}\";
+     propagatedBuildInputs = [ #{deps.map {|n| n.nil? ? "" : (nixname n) }.join(" ")} ];
+     src = fetchurl {
+       url = \"#{src_url}\";
+       sha256 = \"#{nixhashof src_url}\";
+     };
+     meta = {
+       homepage = \"#{spec.homepage}\";
+       license = [#{spec.licenses.map{|l| "\"#{l}\""}.join(" ") }]; # one of ?
+       #{nixdescription spec}
+       longDescription = \"#{ spec.description }\";
+     };
+  };\n"
+      }
+
+      out = "#{out}\n# aliases\n"
+      aliases.each { |key, spec |
+        out = "#{out}#{nixname key}=#{nixname spec};\n"
+      }
+
+      print out
+      exit_code = 0
+
+    rescue => e
+      puts e.inspect
+      puts e.backtrace
+    end
+
+
+  end
+
+  # helper funtions ================
+  
+  def adddep(dep)
+    gem = find_gem_with_source(dep)
+    raise Gem::CommandLineError, "couldn't find #{dep}" if gem.nil?
+    full_name = gem[0].full_name
+
+    return if @seen[full_name]
+    @seen[full_name] = true # there maybe circular dependencies. thus mark this gem seen as early as possible
+
+    # distinguish runtime / buildtime deps? (TODO)
+    deps = gem[0].dependencies
+
+    print " total deps of #{full_name}: #{deps.length}\n"
+
+    dep_specs = []
+    # recurse while collecting deps
+    deps.each {|dep_var| dep_specs.push(adddep(dep_var)) }
+    
+
+    @gems_with_deps[full_name] = [
+      gem[0], # spec
+      gem[1], # src
+      dep_specs # deps
+    ]
+    gem[0] # only return spec, no source for dep list
+  end
+
+
+  # copied from dependency_installer, stripped
+  def find_gem_with_source(dep)
+    gems_and_sources = []
+
+    # no local 
+
+      requirements = dep.version_requirements.requirements.map do |req, ver|
+        req
+      end
+
+      all = true
+      found = Gem::SpecFetcher.fetcher.fetch dep, all, true, @prerelease
+      found.reverse[0]
+  end
+
+
+  def nixhashof(src)
+    cashfile="#{ENV['HOME']}/.nix-ruby-gems-cache"
+    cash = {}
+    if FileTest.exists?(cashfile)
+      File.open(cashfile,'r') do |f| Marshal.load(f) end
+    end
+
+    if cash[src].nil? then
+      tmp="/tmp/ruby-gems-nix-tmp-file"
+      raise Gem::DependencyError("could'nt nix-prefetch  #{src}") \
+	if (not system("nix-prefetch-url #{src.gsub(/([:= `$;])/,'\\\\\1')} > #{tmp} 2>/dev/null")) || $? != 0
+      file = File.new(tmp)
+      hash = file.readlines().first().split("\n")[0] # remove trailing \n
+      file.close()
+      File.delete(tmp)
+      cash[src] = hash
+
+      File.open(cashfile, "w+") do |f| Marshal.dump(cash, f) end
+    end
+
+    return cash[src]
+  end
+
+end