cake 0.2.6 → 0.2.7

Sign up to get free protection for your applications and to get access to all the features.
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