# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with this # work for additional information regarding copyright ownership. The ASF # licenses this file to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under # the License. require 'jruby' require 'rbconfig' require 'tmpdir' require 'buildr/drb' module Buildr # This addon is provided for fast interaction with a DRb BuildrServer (buildr/drb). # # This module delegates task invocation to the BuildrServer, it only implements # nailgun required logic (server/client). # # Usage: # # buildr -r buildr/nailgun nailgun:start # # Once the server has been started you can invoke tasks using the nailgun client # installed on $JRUBY_HOME/tool/nailgun. It's recommended to add this path to # your PATH environment variable, so that the ng command is available at any dir. # # ng build # invoke the build task # module Nailgun extend self VERSION = '0.7.1' NAME = "nailgun-#{VERSION}" URL = "http://downloads.sourceforge.net/nailgun/#{NAME}.zip" ARTIFACT_SPEC = "com.martiansoftware:nailgun:jar:#{VERSION}" PORT = DRbApplication::PORT + 2 ADDON_BIN = File.dirname(__FILE__) # Returns the path to JRUBY_HOME. def jruby_home ENV['JRUBY_HOME'] || Config::CONFIG['prefix'] end # Returns the path to NAILGUN_HOME. def nailgun_home ENV['NAILGUN_HOME'] || File.expand_path('tool/nailgun', jruby_home) end def tmp_path(*paths) File.join(Dir.tmpdir, 'nailgun', *paths) end module Util extend self def add_to_sysloader(path) sysloader = java.lang.ClassLoader.getSystemClassLoader add_url_method = java.lang.Class.forName('java.net.URLClassLoader'). getDeclaredMethod('addURL', [java.net.URL.java_class].to_java(java.lang.Class)) add_url_method.setAccessible(true) add_url_method.invoke(sysloader, [java.io.File.new(path).toURI.toURL].to_java(java.net.URL)) end # invoke a java constructor def ctor(on_class, *args) parameters = [] classes = [] args.each do |obj| case obj when nil classes.push(nil) parameters.push(nil) when Hash vclass = obj.keys.first value = obj[vclass] classes.push(vclass.java_class) parameters.push(value) else parameters.push obj classes.push obj.class.java_class end end on_class = [on_class.java_class].to_java(java.lang.Class)[0] ctor = on_class.getDeclaredConstructor(classes.to_java(java.lang.Class)) ctor.setAccessible(true) ctor.newInstance(parameters.to_java(java.lang.Object)) end end # Util module Client def main(nail) nail.out.println "Connected to #{nail.getNGServer}" runtime = JRuby.runtime stdout = Util.ctor(org.jruby.RubyIO, runtime, java.io.OutputStream => nail.out) stderr = Util.ctor(org.jruby.RubyIO, runtime, java.io.OutputStream => nail.err) stdin = Util.ctor(org.jruby.RubyIO, runtime, java.io.InputStream => nail.in) dir = nail.getWorkingDirectory argv = [nail.command] + nail.args DRbApplication.remote_run :dir => dir, :argv => argv, :in => stdin, :out => stdout, :err => stderr rescue => e nail.err.println e unless SystemExit === e nail.exit 1 end end # Client module Server def initialize(host, port) @host = host || "*" @port = port super(host, port) end def start self.allow_nails_by_class_name = false NGClient::Main.nail = NGClient.new self.default_nail_class = NGClient::Main @thread = java.lang.Thread.new(self) @thread.setName(to_s) @thread.start sleep 1 while getPort == 0 info "#{self} Started." end def stop @thread.kill end def to_s version = "Buildr #{Buildr::VERSION} #{RUBY_PLATFORM[/java/] && '(JRuby '+JRUBY_VERSION+')'}" self.class.name+'('+[version, @host, @port].join(', ')+')' end end # Server namespace(:nailgun) do dist_zip = Buildr.download(tmp_path(NAME + '.zip') => URL) dist_dir = Buildr.unzip(tmp_path(NAME) => dist_zip) nailgun_jar = file(tmp_path(NAME, NAME, NAME + '.jar')) nailgun_jar.enhance [dist_dir] unless File.exist?(nailgun_jar.to_s) attr_reader :artifact @artifact = Buildr.artifact(ARTIFACT_SPEC).from(nailgun_jar) compiled_bin = file(tmp_path(NAME, NAME, 'ng' + Config::CONFIG['EXEEXT']) => dist_dir.target) do |task| unless task.to_s.pathmap('%x') == '.exe' Dir.chdir(task.to_s.pathmap('%d')) do info "Compiling #{task.to_s}" system('make', task.to_s.pathmap('%f')) or fail "Nailgun binary compilation failed." end end end attr_reader :installed_bin @installed_bin = file(File.expand_path(compiled_bin.to_s.pathmap('%f'), nailgun_home) => compiled_bin) do |task| mkpath task.to_s.pathmap('%d'), :verbose => false cp compiled_bin.to_s, task.to_s, :verbose => false end task('drb-notice') do info '' info 'Running in JRuby, a nailgun server will be started so that' info 'you can use your nailgun client to invoke buildr tasks: ' info '' info ' '+Nailgun.installed_bin.to_s info '' end task('drb' => ['drb-notice', 'start']) desc 'Start the nailgun server' task('start' => [installed_bin, 'setup']) do |task| server = NGServer.new(nil, PORT) server.start end task('setup' => artifact) do module Util include Buildr::Util end Util.add_to_sysloader artifact.to_s Util.add_to_sysloader ADDON_BIN class NGClient include org.apache.buildr.BuildrNail include Client end class NGServer < com.martiansoftware.nailgun.NGServer include Server end end end # ng_tasks end # module Nailgun end