# File lib/echoe.rb, line 165
165: def initialize(name, _version = nil)
166: # Defaults
167:
168: self.name = name
169: self.project = name.downcase
170: self.changelog = "CHANGELOG"
171: self.url = ""
172: self.author = ""
173: self.email = ""
174: self.clean_pattern = ["pkg", "doc", 'build/*', '**/coverage', '**/*.o', '**/*.so', '**/*.a', '**/*.log', "{ext,lib}/*.{bundle,so,obj,pdb,lib,def,exp}", "ext/Makefile", "{ext,lib}/**/*.{bundle,so,obj,pdb,lib,def,exp}", "ext/**/Makefile", "pkg", "*.gem", ".config"]
175: self.test_pattern = File.exist?("test/test_all.rb") ? "test/test_all.rb" : ['test/**/test_*.rb', 'test/**/*_test.rb']
176: self.spec_pattern = "spec/**/*_spec.rb"
177:
178: self.ignore_pattern = /^(pkg|doc)|\.svn|CVS|\.bzr|\.DS|\.git/
179:
180: self.changelog_patterns = {
181: :version => [
182: /^\s*v([\d\w\.]+)(\.|\s|$)/,
183: /\s*\*\s*([\d\w\.]+)\s*\*\s*$/
184: ],
185: :changes => [
186: /^\s*v([\d\.]+\. .*)/,
187: /\*\s*[\d\.]+\s*\*\s*(.*)\*\s*[\d\.]+\s*\*$/
188: ]
189: }
190:
191: self.description = ""
192: self.summary = ""
193: self.install_message = nil
194: self.executable_pattern = /^bin\//
195: self.require_paths = nil
196: self.has_rdoc = true
197: self.use_sudo = !(Platform.windows? or ENV['GEM_HOME'].to_s.include?(ENV['USER'].to_s))
198: self.gem_bin = "gem#{Platform.suffix}"
199: self.rcov_options = []
200: self.rdoc_pattern = /^(lib|bin|tasks|ext)|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
201:
202: self.gemspec_format = :ruby
203:
204: title = (name.downcase == name ? name.capitalize : name)
205: self.rdoc_options = ['--line-numbers', '--inline-source', '--title', title]
206:
207: readme = Dir['*'].detect { |filename| filename =~ /^readme/ }
208: self.rdoc_options += ['--main', readme] if readme
209:
210: self.runtime_dependencies = []
211: self.development_dependencies = [] # These appear to not work at all
212: self.manifest_name = "Manifest"
213: self.extension_pattern = ["ext/**/extconf.rb", "ext/extconf.rb"]
214: self.private_key = ENV['GEM_PRIVATE_KEY']
215: self.require_signed = false
216: self.certificate_chain = ENV['GEM_CERTIFICATE_CHAIN'].to_s.split(/\,\s*/).compact
217:
218: self.need_gem = true
219: self.need_tar_gz = true
220: self.need_tgz = false
221: self.need_zip = false
222: self.platform = $platform
223:
224: self.include_rakefile = true
225: self.include_gemspec = true
226: self.gemspec_name = "#{name}.gemspec"
227: self.retain_gemspec = false
228: self.rakefile_name = "Rakefile"
229: self.rubygems_version = ">= 1.2"
230:
231: yield self if block_given?
232:
233: # legacy compatibility
234: self.runtime_dependencies = dependencies if dependencies and runtime_dependencies.empty?
235: self.runtime_dependencies = extra_deps if extra_deps and runtime_dependencies.empty?
236: self.project = rubyforge_name if rubyforge_name
237: self.rdoc_pattern = rdoc_files if rdoc_files
238: self.extension_pattern = extensions if extensions
239:
240: # read manifest
241: begin
242: self.files = File.readlines(manifest_name).map { |x| x.strip } +
243: [(gemspec_name if include_gemspec)] +
244: [(rakefile_name if include_rakefile)]
245: self.files = files.compact.uniq
246: rescue Errno::ENOENT
247: unless ARGV.include? "manifest"
248: puts "Missing manifest. You can build one with 'rake manifest'."
249: exit 1
250: else
251: self.files = []
252: end
253: end
254:
255: # snag version and changeset
256: self.version ||= _version
257: unless version
258: if File.exist? changelog
259: parsed = Array(changelog_patterns[:version]).map do |pattern|
260: open(changelog) do |log|
261: log.read[pattern, 1]
262: end
263: end.compact.first
264: raise "Could not parse version from #{changelog}" unless parsed
265: self.version = parsed.chomp(".").strip
266: else
267: raise "No #{changelog} found, and no version supplied in Rakefile."
268: end
269: end
270:
271: unless self.changes
272: self.changes = if File.exist? changelog
273: Array(changelog_patterns[:changes]).map do |pattern|
274: open(changelog) do |log|
275: log.read[pattern, 1]
276: end
277: end.compact.first or ""
278: else
279: ""
280: end
281: end
282:
283: # set some post-defaults
284: self.certificate_chain = Array(certificate_chain).map {|file| File.expand_path(file)}
285: self.private_key = File.expand_path(private_key) if private_key
286: self.description = summary if description.empty?
287: self.summary = description if summary.empty?
288: self.clean_pattern = apply_pattern(clean_pattern)
289: self.extension_pattern = apply_pattern(extension_pattern, files)
290:
291: self.ignore_pattern = apply_pattern(ignore_pattern)
292: honor_gitignore! if File.exist?(".git")
293:
294: self.rdoc_pattern = apply_pattern(rdoc_pattern, files) - [manifest_name]
295: self.executable_pattern = apply_pattern(executable_pattern, files)
296: self.test_pattern = apply_pattern(test_pattern)
297: self.spec_pattern = apply_pattern(spec_pattern)
298:
299: define_tasks
300: end
# File lib/echoe/extensions.rb, line 14
14: def self.silence
15: if !ENV['VERBOSE']
16: stdout, stderr = $stdout.clone, $stderr.clone
17: $stdout.reopen(File.new("#{Dir.tmpdir}/stdout.echoe", 'w'))
18: $stderr.reopen(File.new("#{Dir.tmpdir}/stderr.echoe", 'w'))
19: begin
20: yield
21: ensure
22: $stdout.reopen(stdout)
23: $stderr.reopen(stderr)
24: end
25: else
26: yield
27: end
28: end
# File lib/echoe.rb, line 317
317: def apply_pattern(pattern, files = nil)
318: files ||= Dir['**/**']
319: case pattern
320: when String, Array
321: files & (Array(pattern).map do |p|
322: Dir.glob(p)
323: end.flatten)
324: when Regexp
325: files.select do |file|
326: file =~ pattern
327: end
328: when FileList
329: pattern.each do |ignorefile|
330: ignorefiles = File.open(ignorefile).to_a.map(&:chomp)
331: files = files.select do |file|
332: ignorefiles.map { |i| File.fnmatch(i, file) }.include?(true)
333: end
334: end
335: files
336: else
337: []
338: end
339: end
# File lib/echoe.rb, line 341
341: def define_tasks
342:
343: ### Packaging and Installing
344:
345: self.spec = Gem::Specification.new do |s|
346: s.name = name
347: s.version = version
348: # s.specification_version = 3
349: s.summary = summary
350: s.author = Array(author).join(", ")
351: s.email = email
352: s.homepage = url
353: s.rubyforge_project = project
354: s.post_install_message = install_message if install_message
355: s.description = description
356: s.required_ruby_version = ruby_version
357: s.required_rubygems_version = rubygems_version if rubygems_version
358: s.platform = platform
359: s.rdoc_options = rdoc_options
360: s.extra_rdoc_files = rdoc_pattern
361:
362: if private_key and File.exist? private_key
363: s.signing_key = private_key
364: s.cert_chain = certificate_chain
365: end
366:
367: runtime_dependencies.each do |dep|
368: dep = dep.split(" ") if dep.is_a? String
369: s.add_runtime_dependency(*dep)
370: end
371:
372: development_dependencies.each do |dep|
373: dep = dep.split(" ") if dep.is_a? String
374: s.add_development_dependency(*dep)
375: end
376:
377: s.files = files
378:
379: s.bindir = if executable_pattern.any?
380: executable_pattern[0].split("/")[0]
381: else
382: "bin"
383: end
384:
385: s.executables = executable_pattern.map do |file|
386: file[(s.bindir.length + 1)..1]
387: end
388:
389: dirs = Dir['{lib,ext}']
390: s.extensions = extension_pattern if extension_pattern.any?
391: if require_paths
392: s.require_paths = require_paths
393: else
394: s.require_paths = dirs unless dirs.empty?
395: end
396: s.has_rdoc = has_rdoc
397:
398: if File.exist? "test/test_all.rb"
399: s.test_file = "test/test_all.rb"
400: else
401: s.test_files = test_pattern
402: end
403:
404: if eval
405: s.instance_eval &eval
406: end
407:
408: end
409:
410: self.lib_files = spec.files.grep(/^lib/)
411: self.bin_files = spec.files.grep(/^bin/)
412: self.test_files = spec.files.grep(/^test/)
413:
414: Rake::GemPackageTask.new(spec) do |pkg|
415: pkg.need_tar = @need_tgz
416: pkg.need_tar_gz = @need_tar_gz
417: pkg.need_zip = @need_zip
418: end
419:
420: desc "Display Echoe's knowledge of your system"
421: task :details do
422: (self.instance_variables.sort - ['@spec']).each do |var|
423: puts "#{var}: #{instance_variable_get(var).inspect}"
424: end
425: end
426:
427: desc "Builds the .gemspec"
428: task :build_gemspec do
429: # Construct the gemspec file, if needed.
430: if include_gemspec
431: File.open(gemspec_name, 'w') do |f|
432: case gemspec_format
433: when :yaml
434: spec.to_yaml.split("\n").each do |line|
435: # Don't publish any information about the private key or certificate chain
436: f.puts line unless line =~ /signing_key|cert_chain|\.pem/
437: end
438: when :ruby
439: f.puts spec.to_ruby
440: else
441: raise "Unknown gemspec format #{gemspec_format.inspect}. Supported formats: :ruby and :yaml"
442: end
443: end
444: end
445: puts "Gemspec generated"
446: end
447:
448: # Chain it to the gemspec task prerequisite
449: task gemspec_name.to_sym => [:build_gemspec]
450:
451: desc "Generates manifest & gemspec in one go"
452: task :build => [:manifest, :build_gemspec]
453:
454: task :package do
455: # Chain some cleanup tasks to the default :package task.
456: # Remove the gemfile if it wasn't actually requested.
457: unless @need_gem
458: puts " Gem file not requested. Removed."
459: system "rm pkg/*.gem"
460: end
461: # Remove the generated gemspec once the packaging is done, to discourage people from modifying it by hand.
462: if include_gemspec and File.exist? gemspec_name and not retain_gemspec
463: File.delete gemspec_name
464: end
465:
466: # Test signing status
467: if private_key and File.exist? private_key
468: puts "Signing gem."
469: else
470: raise "Key required, but not found. Maybe you forget to set ENV['GEM_PRIVATE_KEY']?" if require_signed
471: puts "Private key not found; gem will not be signed."
472: end
473: puts "Targeting \"#{platform}\" platform."
474: end
475:
476: desc 'Install the gem'
477: task :install => [:clean, :package, :uninstall] do
478: system "#{'sudo' if use_sudo} #{gem_bin} install pkg/*.gem --no-update-sources #{'-P MediumSecurity' if private_key and File.exist?(private_key)}"
479: end
480:
481: namespace :install do
482: desc 'Install the gem including development dependencies'
483: task :development => [:clean, :package, :uninstall] do
484: system "#{'sudo' if use_sudo} #{gem_bin} install pkg/*.gem -P MediumSecurity --no-update-sources --development"
485: end
486: end
487:
488: desc 'Uninstall the gem'
489: task :uninstall do
490: system "#{'sudo' if use_sudo} #{gem_bin} uninstall #{name} -a -I -x"
491: end
492:
493: desc 'Package and upload the release to Gemcutter'
494: task :release => [:clean, :package] do |t|
495: pkg = "pkg/#{name}-#{version}"
496: pkg_gem = pkg + ".gem"
497: pkg_tar = pkg + ".tgz"
498: pkg_tar_gz = pkg + ".tar.gz"
499: pkg_zip = pkg + ".zip"
500:
501: puts "Releasing #{name} v. #{version} to Gemcutter."
502: system("gem push #{pkg_gem.inspect}")
503: end
504:
505: ### Extension building
506:
507: task :lib do
508: directory "lib"
509: end
510:
511: if extension_pattern.any?
512:
513: desc "Compile the binary extension module"
514: task :compile => [:lib] do
515: extension_pattern.each do |extension|
516: ext_dir = File.dirname(extension)
517: lib_target = nil
518: Dir.chdir(ext_dir) do
519: ruby File.basename(extension)
520: system(RUBY_PLATFORM =~ /win32/ ? 'nmake' : 'make')
521: lib_target = open('Makefile').readlines.grep(/target_prefix = /).first.split('=').last.chomp("\n").strip
522: end
523: Dir["#{ext_dir}/*.#{Config::CONFIG['DLEXT']}"].each do |file|
524: dir = "lib/#{lib_target}/".gsub('//', '/')
525: mkdir_p dir
526: cp file, dir
527: end
528: end
529: end
530:
531: task :test => [:compile] if test_pattern.any?
532: task :spec => :compile if spec_pattern.any?
533:
534: end
535:
536: ### Cross-platform targets
537:
538: Gem::Specification::PLATFORM_CROSS_TARGETS.each do |target|
539: task target do
540: reset_target target
541: end
542: end
543:
544: ### Documentation
545:
546: Rake::RDocTask.new(:docs) do |rd|
547: # rd.main = Dir['*'].detect {|f| f =~ /^readme/i}
548: rd.options += Array(rdoc_options)
549:
550: rd.rdoc_dir = 'doc'
551: rd.rdoc_files.push(*rdoc_pattern)
552:
553: if rdoc_template
554: rd.template = rdoc_template
555: elsif ENV['RDOC_TEMPLATE']
556: rd.template = ENV['RDOC_TEMPLATE']
557: end
558: end
559:
560: task :doc => [:redocs]
561:
562: desc "Publish documentation to #{docs_host ? "'#{docs_host}'" : "rubyforge"}"
563: task :publish_docs => [:clean, :docs] do
564:
565: local_dir = 'doc'
566: remote_dir_name = project
567: remote_dir_name += "/#{name}" if project != name
568:
569: unless docs_host
570: config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml")))
571: pub = Rake::SshDirPublisher.new "#{config["username"]}@rubyforge.org",
572: "/var/www/gforge-projects/#{remote_dir_name}",
573: local_dir
574: if project != name then
575: def pub.upload
576: begin
577: super
578: rescue
579: # project directory probably doesn't exist, transfer as a whole
580: cmd = "scp -qr #{local_dir} #{host}:#{remote_dir}"
581: puts "Uploading: #{cmd}"
582: system(cmd)
583: end
584: end
585: end
586: pub.upload
587: else
588: # you may need ssh keys configured for this to work
589: host, dir = docs_host.split(":")
590: dir.chomp!("/")
591:
592: # XXX too dangerous?
593: cmd = "ssh #{host} 'rm -rf #{dir}/#{remote_dir_name}'"
594: puts "Deleting existing docs: #{cmd}"
595: system(cmd)
596:
597: cmd = "scp -qr #{local_dir} #{host}:#{dir}/#{remote_dir_name}"
598: puts "Uploading: #{cmd}"
599: system(cmd)
600: end
601: end
602:
603: desc 'Generate a release announcement, edit it, and post it to Rubyforge.'
604: task :announce do
605:
606: filename = "#{Dir.tmpdir}/#{name}_#{version}_announcement.txt"
607:
608: if File.exist?(filename)
609: puts "Announcement file already exists. Please delete #{filename.inspect} first."
610: exit(1)
611: end
612:
613: File.open(filename, 'w') do |f|
614: f.write "Subject: #{name.capitalize} #{version}\n\n"
615: f.write "#{name.capitalize} has been updated to #{version}. #{name.capitalize} is #{summary.uncapitalize}\n\n"
616: unless changes.empty?
617: f.write "Changes in this version: "
618: if changes.include?("\n")
619: f.write(changes)
620: else
621: f.write(changes.sub(/^\s*[\w\d\.]+\s+/, '').uncapitalize)
622: end
623: f.write("\n\n")
624: end
625: f.write "More information is available at #{url} .\n\n" unless url.empty?
626: end
627:
628: editor = ENV['EDITOR'] || 'nano'
629: system("#{editor} #{filename}") or raise "Editor '#{editor}' failed to start"
630: puts File.open(filename).read
631:
632: File.open(filename).readlines.detect { |line| line =~ /Subject: (.*)/ }
633: subject = $1 or raise "Subject line seems to have disappeared"
634:
635: body = File.open(filename).readlines.reject { |line| line =~ /Subject: / }.join.gsub("\n\n\n", "\n\n")
636:
637: rf = RubyForge.new.configure
638: rf.login
639: rf.post_news(project, subject, body)
640: puts "Published announcement to Rubyforge."
641: File.delete filename
642: end
643:
644: ### Clean
645:
646: desc 'Clean up auto-generated files'
647: task :clean do
648: puts "Cleaning"
649: clean_pattern.each do |file|
650: if File.exist?(file)
651: puts "- #{file}"
652: rm_rf file
653: end
654: end
655: end
656:
657: ### Manifest
658:
659: desc "Build a Manifest list"
660: task :manifest => [:clean] do
661: puts "Building Manifest"
662: old_files = files
663: files = []
664: Dir['**/**'].sort.each do |file|
665: next unless file
666: next if ignore_pattern.include?(file)
667: next if File.directory?(file)
668: next if !include_rakefile and file == rakefile_name
669: files << file
670: end
671:
672: files << rakefile_name if include_rakefile
673: files << manifest_name
674: files.uniq!
675:
676: File.open(manifest_name, 'w').puts(files)
677:
678: (files | old_files).sort.each do |file|
679: next if file == gemspec_name
680: sign = " "
681: if old_files.include?(file) and !files.include?(file)
682: sign = "-"
683: elsif files.include?(file) and !old_files.include?(file)
684: sign = "+"
685: end
686: puts "#{sign} #{file}"
687: end
688: end
689:
690: task :build_manifest => :manifest
691:
692: ### Testing
693:
694: if test_pattern.any?
695:
696: Rake::TestTask.new(:test_inner) do |t|
697: t.libs = ['lib', 'ext', 'bin', 'test']
698: t.test_files = test_pattern
699: t.verbose = true
700: end
701:
702: desc "Run the test suite"
703: task :test do
704: if File.exist? 'test/setup.rb'
705: Echoe.silence do
706: puts "Setting up test environment"
707: system("ruby test/setup.rb")
708: end
709: end
710: begin
711: test = Rake::Task[:test_inner]
712: if test.respond_to? :already_invoked=
713: # Method provided by MultiRails
714: test.already_invoked = false
715: end
716: test.invoke
717: ensure
718: if File.exist? 'test/teardown.rb'
719: Echoe.silence do
720: puts "Tearing down test environment"
721: system("ruby test/teardown.rb")
722: end
723: end
724: end
725: end
726:
727: task :default => :test
728: end
729:
730: if defined? Spec and spec_pattern.any?
731: desc "Run the spec suite"
732: Spec::Rake::SpecTask.new('spec') do |t|
733: t.spec_files = spec_pattern
734: end
735:
736: task :default => :spec
737: end
738:
739: if defined? Rcov
740: Rcov::RcovTask.new(:coverage) do |t|
741: t.test_files = test_pattern
742: t.rcov_opts << rcov_options if rcov_options
743: t.verbose = true
744: end
745: task :rcov => :coverage
746: end
747: end
# File lib/echoe.rb, line 303
303: def honor_gitignore!
304: self.ignore_pattern += Dir["**/.gitignore"].inject([]) do |pattern,gitignore|
305: pattern.concat File.readlines(gitignore).
306: map { |line| line.strip }.
307: reject { |line| "" == line }.
308: map { |glob|
309: d = File.dirname(gitignore)
310: d == "." ? glob : File.join(d, glob)
311: }
312: end.flatten.uniq
313: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.