cake 0.2.6 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. data/bin/cake +358 -31
  2. data/lib/bake.jar +0 -0
  3. data/lib/cake.jar +0 -0
  4. metadata +4 -4
data/bin/cake CHANGED
@@ -1,20 +1,28 @@
1
1
  #!/usr/bin/env ruby
2
2
  # Save your fork, there's cake!"
3
3
  require 'ftools'
4
+ require 'find'
4
5
  require 'open-uri'
5
6
  require 'rexml/document'
7
+ require 'socket'
8
+ require 'timeout'
9
+ require 'readline'
10
+ require 'fileutils'
11
+ require 'pp'
6
12
 
7
- $repo = "http://clojars.org/repo/cake/cake"
8
- $file = File.readlink(__FILE__) rescue __FILE__
9
- $root = File.dirname(File.dirname($file))
13
+ file = File.readlink(__FILE__) rescue __FILE__
14
+ $cakedir = File.dirname(File.dirname(file))
15
+ $confdir = ".cake"
16
+ $repo = "http://clojars.org/repo/cake/cake"
17
+ File.makedirs($confdir)
10
18
 
11
- def have_cake(version, dest)
19
+ def get_cake(version, dest)
12
20
  jar = version =~ /(.*)-SNAPSHOT/ ? "cake-#{$1}-#{snapshot(version)}.jar" : "cake-#{version}.jar"
13
21
  dest = File.expand_path(dest)
14
22
  path = "#{dest}/#{jar}"
15
23
 
16
24
  if not File.exists?(path)
17
- puts "fetching cake libraries. this may take a moment..."
25
+ log(:deps, "fetching cake libraries. this may take a moment...")
18
26
  url = "#{$repo}/#{version}/#{jar}"
19
27
  File.makedirs(dest)
20
28
  open(url) do |jar|
@@ -27,16 +35,11 @@ def have_cake(version, dest)
27
35
  end
28
36
  path
29
37
  rescue OpenURI::HTTPError => e
30
- puts "unable to find cake version #{version} on clojars."
31
- puts "please check http://github.com/ninjudd/cake for latest install instructions."
38
+ log(:deps, "unable to find cake version #{version} on clojars.")
39
+ log(:deps, "please check http://github.com/ninjudd/cake for latest install instructions.")
32
40
  exit
33
41
  end
34
42
 
35
- def eat_cake(*args)
36
- opts = %{ -d32 -cp #{$classpath} -Dbakepath="#{$bakepath}" }
37
- system %{ java #{opts} clojure.main -e "(use 'cake)(-main)" /dev/null #{args.join(' ')} }
38
- end
39
-
40
43
  def current_version
41
44
  open("#{$repo}/maven-metadata.xml") do |file|
42
45
  doc = REXML::Document.new file
@@ -60,34 +63,358 @@ def extract(jar, file)
60
63
  "#{dest}/#{file}"
61
64
  end
62
65
 
63
- # Bootstap cake dependencies.
64
- lib = "#{$root}/lib"
65
- if File.exists?("#{$root}/.gitignore")
66
+ def newer?(file1, file2)
67
+ not File.exists?(file2) or test(?>, file1, file2)
68
+ end
69
+
70
+ def add_opt!(key, *vals)
71
+ ($opts[key.to_sym] ||= []).concat vals
72
+ end
73
+
74
+ def parse_opts!
75
+ ARGV.unshift('default') if ARGV.empty? or ARGV.first[0,1] == '-'
76
+ $command = ARGV.shift.to_sym
77
+ $opts = {}
78
+ ARGV.each do |opt|
79
+ case opt
80
+ when /^-(\w*)$/ then $1.chars.each {|c| add_opt!(c, "")}
81
+ when /^--?([-\w]*)=([-,\w]+)$/ then add_opt!($1, *$2.split(','))
82
+ when /^--?([-\w]*)$/ then add_opt!($1, "")
83
+ else add_opt!($command, opt)
84
+ end
85
+ end
86
+ $opts.freeze
87
+ end
88
+ parse_opts!
89
+
90
+ def debug?
91
+ ENV['CAKE_DEBUG'] or $opts[:d] or $opts[:debug]
92
+ end
93
+
94
+ def verbose?
95
+ debug? or $opts[:v] or $opts[:verbose]
96
+ end
97
+
98
+ def restart?
99
+ $opts[:r] or $opts[:restart] or [:stop, :restart].include?($command)
100
+ end
101
+
102
+ def admin_command?
103
+ [:start, :stop, :reload, :restart].include?($command)
104
+ end
105
+
106
+ def log(command, *message)
107
+ message.each do |line|
108
+ printf("%11s %s\n", "[#{command}]", line)
109
+ end
110
+ end
111
+
112
+ class JVM
113
+ attr_reader :type, :classpath, :port, :pid, :pidfile, :start_time
114
+
115
+ def initialize(classpath)
116
+ @type = self.class.name.downcase
117
+ @classpath = classpath
118
+ @pidfile = "#{$confdir}/#{type}.pid"
119
+ @start_time = File.exists?(pidfile) ? File.mtime(pidfile) : Time.now
120
+
121
+ @pid, @port = IO.read(pidfile).map {|l| l.to_i}
122
+ Process.kill(0, @pid) # make sure pid is valid
123
+
124
+ rescue Errno::ESRCH, Errno::ENOENT => e
125
+ reset! # no pidfile or invalid pid
126
+ end
127
+
128
+ def running?
129
+ not pid.nil?
130
+ end
131
+
132
+ def init
133
+ reload_stale_files if running?
134
+ start
135
+ end
136
+
137
+ def reload_stale_files
138
+ stale_files = files.select {|f| stale?(f)}
139
+
140
+ if stale_files.empty?
141
+ log(:reload, "no stale #{type} files found") if verbose? and admin_command?
142
+ else
143
+ log(:reload, "the following #{type} files have changed: #{stale_files.join(', ')}") if debug?
144
+
145
+ if stale_files.any? {|f| File.extname(f) != ".clj"}
146
+ log(:reload, "non-clojure #{type} files have changed, restarting") if verbose?
147
+ stop(:reload)
148
+ else
149
+ log(:reload, "clojure #{type} files have changed, reloading") if verbose?
150
+ with_socket(0) do |socket|
151
+ stale_files = stale_files.collect {|f| %{"#{f}"} }
152
+ socket.write ":reload [#{stale_files.join(" ")}]\n"
153
+ if socket.eof?
154
+ FileUtils.touch(pidfile)
155
+ @start_time = Time.now
156
+ else
157
+ inspect(socket) if debug?
158
+ log(:reload, "unable to reload all #{type} files, restarting") if verbose?
159
+ stop(:reload)
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ MIN_PORT = 2**13
167
+ MAX_PORT = 2**16
168
+
169
+ def start
170
+ return if running?
171
+ @port = rand(MAX_PORT - MIN_PORT) + MIN_PORT
172
+ log(:start, "starting #{type} jvm on port #{port}") if debug?
173
+ @pid = daemon %{ java -d32 -cp #{classpath} clojure.main -e "(use '#{type})(start-server #{port})" /dev/null }
174
+ File.open(pidfile, 'w') {|f| f.write("#{pid}\n#{port}\n") }
175
+ log(:start, "#{type} jvm started") if verbose?
176
+ rescue Errno::EADDRNOTAVAIL => e
177
+ retry
178
+ end
179
+
180
+ def stop(mode = :stop)
181
+ with_socket(0) do |socket|
182
+ action = mode == :reload ? 'quit' : 'force-quit'
183
+ log(mode, "sending #{action} to #{type} jvm on port #{port}") if debug?
184
+ socket.write(":#{action}\n")
185
+ if socket.eof?
186
+ log(mode, "#{type} jvm stopped") if verbose?
187
+ reset!
188
+ else
189
+ inspect(socket) if debug?
190
+ if mode == :stop
191
+ log(mode, "error stopping #{type} jvm, try 'cake kill' or 'cake kill -9'")
192
+ else
193
+ log(mode, "warning: classes have changed but can't restart #{type} jvm with active connections")
194
+ log(mode, " close active connections or use 'cake stop' to force quit")
195
+ end
196
+ end
197
+ end || (log(mode, "#{type} jvm not running") if verbose?)
198
+ end
199
+
200
+ def kill
201
+ Process.kill($opts[:"9"] ? "KILL" : "TERM", pid) if pid
202
+ end
203
+
204
+ def files
205
+ files = []
206
+ classpath.split(':').uniq.each do |path|
207
+ if path =~ /(.*)\/\*$/
208
+ files.concat Dir["#{path}.jar"] << "#{$1}/." # include the directory itself so deletions will be detected
209
+ else
210
+ Find.find(path) do |f|
211
+ files << f if File.exists?(f) and not File.directory?(f)
212
+ end
213
+ end
214
+ end
215
+ files
216
+ end
217
+
218
+ private
219
+
220
+ def inspect(socket)
221
+ while line = socket.gets
222
+ puts line
223
+ end
224
+ end
225
+
226
+ def reset!
227
+ File.unlink(pidfile) if File.exists?(pidfile)
228
+ @pid, @port = []
229
+ @start_time = Time.now
230
+ end
231
+
232
+ def stale?(file)
233
+ File.exists?(file) and File.mtime(file) > start_time
234
+ end
235
+
236
+ def daemon(cmd)
237
+ puts cmd if debug?
238
+ pid = fork do
239
+ Process.setsid
240
+ exec(cmd)
241
+ end
242
+ Process.detach(pid)
243
+ pid
244
+ end
245
+
246
+ def with_socket(retries = 5)
247
+ return unless port
248
+ socket = TCPSocket.new("localhost", port)
249
+ result = yield(socket)
250
+ result
251
+ rescue Errno::ECONNREFUSED => e
252
+ sleep 1
253
+ retry if (retries -= 1) >= 0
254
+ exit
255
+ ensure
256
+ socket.close if socket
257
+ end
258
+ end
259
+
260
+ class Cake < JVM
261
+ attr_accessor :bakeport
262
+
263
+ def files
264
+ super.concat ["project.clj", "build.clj", ".cake/swank"]
265
+ end
266
+
267
+ def send_command(command, *args)
268
+ with_socket do |socket|
269
+ socket.write("(#{command} [#{args.join(' ')}] #{bakeport})\n")
270
+ while(line = socket.gets)
271
+ puts line
272
+ end
273
+ command
274
+ end
275
+ end
276
+ end
277
+
278
+ class Bake < JVM
279
+ MARKER = rand.to_s
280
+ PROMPT = /^(.*)#{MARKER}$/
281
+ START_REPL = <<-END
282
+ (clojure.main/repl
283
+ :init #(in-ns 'user)
284
+ :prompt #(printf "%s=> #{MARKER}\n" (ns-name *ns*)))
285
+ END
286
+
287
+ def repl
288
+ load_history
289
+ loop do
290
+ with_socket do |socket|
291
+ socket.write START_REPL
292
+ socket.gets # burn extra prompt
293
+
294
+ while prompt = read_until_prompt(socket)
295
+ line = readline(prompt)
296
+ return unless line
297
+ socket.write(line + "\n")
298
+ end
299
+ end
300
+ end
301
+ ensure
302
+ save_history
303
+ end
304
+
305
+ private
306
+
307
+ HISTORY_NUM = 500
308
+ HISTORY_FILE = ".cake/history"
309
+
310
+ def load_history
311
+ open(HISTORY_FILE) do |file|
312
+ file.each {|line| Readline::HISTORY << line.chomp}
313
+ end if File.exists?(HISTORY_FILE)
314
+ end
315
+
316
+ def save_history
317
+ open(HISTORY_FILE, 'w') do |file|
318
+ history = Readline::HISTORY.to_a
319
+ file.puts(history[-HISTORY_NUM..-1] || history)
320
+ end
321
+ end
322
+
323
+ def read_until_prompt(socket)
324
+ while line = socket.gets
325
+ return $1 if line =~ PROMPT
326
+ puts line
327
+ end
328
+ end
329
+
330
+ def complete?(input)
331
+ return true if input.empty?
332
+ with_socket do |socket|
333
+ socket.write(":validate #{input.join("\n").strip}")
334
+ socket.close_write # send eof
335
+ socket.gets != "incomplete\n"
336
+ end
337
+ end
338
+
339
+ def readline(initial_prompt)
340
+ input = []
341
+ prompt = initial_prompt.clone
342
+ while line = Readline.readline(prompt)
343
+ input << line
344
+ if complete?(input)
345
+ Readline::HISTORY.push(input.join(' ').strip)
346
+ return input.join("\n").strip
347
+ end
348
+ prompt[-2] = ?*
349
+ end
350
+ rescue Interrupt => e
351
+ return nil if input.empty?
352
+ Readline::HISTORY.push(input)
353
+ retry
354
+ end
355
+ end
356
+
357
+ # Bootstrap cake dependencies.
358
+ lib = "#{$cakedir}/lib"
359
+ if File.exists?("#{$cakedir}/.gitignore")
66
360
  if Dir["#{lib}/*.jar"].empty?
67
361
  # In a new git checkout, need to fetch dependencies.
68
- version = IO.read("#{$root}/project.clj").first.match(/defproject cake \"(.*)\"/)[1]
69
- have_cake(version, lib)
362
+ version = IO.read("#{$cakedir}/project.clj").first.match(/defproject cake \"(.*)\"/)[1]
363
+ get_cake(version, lib)
70
364
  end
71
- $classpath = "#{$root}/src/:#{lib}/*"
72
- $bakepath = "#{$root}/bake"
365
+ cakepath = "#{$cakedir}/src:#{lib}/*"
366
+ bakepath = "#{$cakedir}/bake"
73
367
  else
74
- cake_jar = "#{lib}/cake.jar"
75
- bake_jar = "#{lib}/bake.jar"
76
- if File.exists?(cake_jar) and File.exists?(bake_jar)
368
+ cakejar = "#{lib}/cake.jar"
369
+ bakejar = "#{lib}/bake.jar"
370
+ if File.exists?(cakejar) and File.exists?(bakejar)
77
371
  # Inside a gem install.
78
- $classpath = cake_jar
79
- $bakepath = bake_jar
372
+ cakepath = cakejar
373
+ bakepath = bakejar
80
374
  else
81
375
  # Naked script.
82
- version = current_version
83
- dest = "~/.m2/repository/cake/cake/#{version}"
84
- $classpath = have_cake(version, dest)
85
- $bakepath = extract($classpath, "bake-#{version}.jar")
376
+ version = current_version
377
+ dest = "~/.m2/repository/cake/cake/#{version}"
378
+ cakepath = get_cake(version, dest)
379
+ bakepath = extract(classpath, "bake-#{version}.jar")
86
380
  end
87
381
  end
88
382
 
89
- if not File.exists?("pom.xml") or test(?>, "project.clj", "pom.xml") and ARGV.first != "deps"
90
- eat_cake("deps")
383
+ pwd = Dir.getwd
384
+ srcpath = "#{pwd}/src:#{pwd}/src/clj"
385
+
386
+ cake = Cake.new("#{cakepath}:#{srcpath}:#{pwd}/lib/dev/*")
387
+ bake = Bake.new("#{bakepath}:#{srcpath}:#{pwd}/classes:#{pwd}/test:#{pwd}/lib/*:#{pwd}/lib/dev/*")
388
+
389
+ if $command == :kill
390
+ cake.kill
391
+ bake.kill
392
+ exit
393
+ elsif restart?
394
+ cake.stop
395
+ bake.stop
396
+ exit if $command == :stop
91
397
  end
92
398
 
93
- eat_cake(*ARGV)
399
+ cake.init
400
+ if not [:deps, :clean].include?($command) and File.exists?('project.clj')
401
+ if newer?('project.clj', 'pom.xml')
402
+ deps = cake.send_command(:deps)
403
+ elsif File.exist?('.cake/swank')
404
+ deps = cake.send_command(:"swank-deps")
405
+ end
406
+
407
+ bake.init
408
+ cake.init if deps # must check if dev dependencies have changed
409
+ end
410
+
411
+ if $command == :repl
412
+ if File.exists?('project.clj')
413
+ bake.repl
414
+ else
415
+ log(:repl, "cannot start repl without project.clj")
416
+ end
417
+ elsif not [:start, :reload, :restart].include?($command)
418
+ cake.bakeport = bake.port
419
+ cake.send_command($command, ARGV)
420
+ end
Binary file
Binary file
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cake
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 6
10
- version: 0.2.6
9
+ - 7
10
+ version: 0.2.7
11
11
  platform: ruby
12
12
  authors:
13
13
  - Justin Balthrop
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-06-05 00:00:00 -07:00
19
+ date: 2010-06-22 00:00:00 -07:00
20
20
  default_executable: cake
21
21
  dependencies: []
22
22