Skip to content

Instantly share code, notes, and snippets.

@donv
Created March 26, 2012 22:40
Show Gist options
  • Save donv/2210325 to your computer and use it in GitHub Desktop.
Save donv/2210325 to your computer and use it in GitHub Desktop.
if `ant -version` !~ /version (\d+)\.(\d+)\.(\d+)/ || $1.to_i < 1 || ($1.to_i == 1 && $2.to_i < 8)
puts "ANT version 1.8.0 or later required. Version found: #{$1}.#{$2}.#{$3}"
exit 1
end
require 'time'
def manifest() @manifest ||= REXML::Document.new(File.read(MANIFEST_FILE)) end
def package() manifest.root.attribute('package') end
def build_project_name() @build_project_name ||= REXML::Document.new(File.read('build.xml')).elements['project'].attribute(:name).value end
def scripts_path() @sdcard_path ||= "/mnt/sdcard/Android/data/#{package}/files/scripts" end
def app_files_path() @app_files_path ||= "/data/data/#{package}/files" end
require 'rake/clean'
require 'rexml/document'
PROJECT_DIR = Dir.getwd
UPDATE_MARKER_FILE = File.expand_path(File.join('bin', 'LAST_UPDATE'), File.dirname(__FILE__))
BUNDLE_JAR = File.expand_path 'libs/bundle.jar'
BUNDLE_PATH = File.expand_path 'bin/bundle'
MANIFEST_FILE = File.expand_path 'AndroidManifest.xml'
RUBOTO_CONFIG_FILE = File.expand_path 'ruboto.yml'
GEM_FILE = File.expand_path('Gemfile.apk')
GEM_LOCK_FILE = File.expand_path('Gemfile.apk.lock')
RELEASE_APK_FILE = File.expand_path "bin/#{build_project_name}-release.apk"
APK_FILE = File.expand_path "bin/#{build_project_name}-debug.apk"
TEST_APK_FILE = File.expand_path "test/bin/#{build_project_name}Test-debug.apk"
JRUBY_JARS = Dir[File.expand_path 'libs/jruby-*.jar']
RESOURCE_FILES = Dir[File.expand_path 'res/**/*']
JAVA_SOURCE_FILES = Dir[File.expand_path 'src/**/*.java']
RUBY_SOURCE_FILES = Dir[File.expand_path 'src/**/*.rb']
APK_DEPENDENCIES = [MANIFEST_FILE, RUBOTO_CONFIG_FILE, BUNDLE_JAR] + JRUBY_JARS + JAVA_SOURCE_FILES + RESOURCE_FILES + RUBY_SOURCE_FILES
KEYSTORE_FILE = (key_store = File.readlines('ant.properties').grep(/^key.store=/).first) ? key_store.chomp.sub(/^key.store=/, '') : "#{build_project_name}.keystore"
KEYSTORE_ALIAS = (key_alias = File.readlines('ant.properties').grep(/^key.alias=/).first) ? key_alias.chomp.sub(/^key.alias=/, '') : build_project_name
CLEAN.include('bin')
task :default => :debug
file JRUBY_JARS => RUBOTO_CONFIG_FILE do
next unless File.exists? RUBOTO_CONFIG_FILE
jruby_jars_mtime = JRUBY_JARS.map { |f| File.mtime(f) }.min
ruboto_yml_mtime = File.mtime(RUBOTO_CONFIG_FILE)
next if jruby_jars_mtime > ruboto_yml_mtime
puts '*' * 80
if JRUBY_JARS.empty?
puts ' The JRuby jars are missing.'
else
puts " The JRuby jars need reconfiguring after changes to #{RUBOTO_CONFIG_FILE}"
puts " #{RUBOTO_CONFIG_FILE}: #{ruboto_yml_mtime}"
puts " #{JRUBY_JARS.join(', ')}: #{jruby_jars_mtime}"
end
puts ' Run "ruboto update jruby" to regenerate the JRuby jars'
puts '*' * 80
end
desc 'build debug package'
task :debug => APK_FILE
namespace :debug do
desc 'build debug package if compiled files have changed'
task :quick => [MANIFEST_FILE, RUBOTO_CONFIG_FILE, BUNDLE_JAR] + JRUBY_JARS + JAVA_SOURCE_FILES + RESOURCE_FILES do |t|
build_apk(t, false)
end
end
desc "build package and install it on the emulator or device"
task :install => APK_FILE do
install_apk
end
namespace :install do
desc 'uninstall, build, and install the application'
task :clean => [:uninstall, APK_FILE, :install]
desc 'Install the application, but only if compiled files are changed.'
task :quick => 'debug:quick' do
install_apk
end
end
desc 'Build APK for release'
task :release => RELEASE_APK_FILE
file RELEASE_APK_FILE => [KEYSTORE_FILE] + APK_DEPENDENCIES do |t|
build_apk(t, true)
end
desc 'Create a keystore for signing the release APK'
file KEYSTORE_FILE do
unless File.read('ant.properties') =~ /^key.store=/
File.open('ant.properties', 'a'){|f| f << "\nkey.store=#{KEYSTORE_FILE}\n"}
end
unless File.read('ant.properties') =~ /^key.alias=/
File.open('ant.properties', 'a'){|f| f << "\nkey.alias=#{KEYSTORE_ALIAS}\n"}
end
sh "keytool -genkey -v -keystore #{KEYSTORE_FILE} -alias #{KEYSTORE_ALIAS} -keyalg RSA -keysize 2048 -validity 10000"
end
desc 'Tag this working copy with the current version'
task :tag => :release do
unless `git branch` =~ /^\* master$/
puts "You must be on the master branch to release!"
exit!
end
# sh "git commit --allow-empty -a -m 'Release #{version}'"
output = `git status --porcelain`
raise "Workspace not clean!\n#{output}" unless output.empty?
sh "git tag #{version}"
sh "git push origin master --tags"
end
task :sign => :release do
sh "jarsigner -keystore #{ENV['RUBOTO_KEYSTORE']} -signedjar bin/#{build_project_name}.apk bin/#{build_project_name}-unsigned.apk #{ENV['RUBOTO_KEY_ALIAS']}"
end
task :align => :sign do
sh "zipalign 4 bin/#{build_project_name}.apk #{build_project_name}.apk"
end
task :publish => :align do
puts "#{build_project_name}.apk is ready for the market!"
end
desc 'Start the emulator with larger disk'
task :emulator do
sh 'emulator -partition-size 1024 -avd Android_3.0'
end
task :start do
start_app
end
task :stop do
raise "Unable to stop app. Only available on emulator." unless stop_app
end
desc 'Restart the application'
task :restart => [:stop, :start]
task :uninstall do
uninstall_apk
end
file MANIFEST_FILE
file RUBOTO_CONFIG_FILE
file APK_FILE => APK_DEPENDENCIES do |t|
build_apk(t, false)
end
desc 'Copy scripts to emulator or device'
task :update_scripts => ['install:quick'] do
update_scripts
end
namespace :update_scripts do
desc 'Copy scripts to emulator and restart the app'
task :restart => APK_DEPENDENCIES do |t|
if build_apk(t, false) || !stop_app
install_apk
else
update_scripts
end
start_app
end
end
task :test => :uninstall do
Dir.chdir('test') do
puts 'Running tests'
sh "adb uninstall #{package}.tests"
sh "ant instrument install test"
end
end
namespace :test do
task :quick => :update_scripts do
Dir.chdir('test') do
puts 'Running quick tests'
sh 'ant instrument'
sh 'ant installi'
sh "ant run-tests-quick"
end
end
end
file GEM_FILE
file GEM_LOCK_FILE
desc 'Generate bundle jar from Gemfile'
task :bundle => BUNDLE_JAR
file BUNDLE_JAR => [GEM_FILE, GEM_LOCK_FILE] do
next unless File.exists? GEM_FILE
puts "Generating #{BUNDLE_JAR}"
FileUtils.mkdir_p BUNDLE_PATH
sh "gem install jruby-openssl-0.7.6.2.gem --install-dir #{BUNDLE_PATH}/ruby/1.8"
sh "bundle install --gemfile #{GEM_FILE} --path=#{BUNDLE_PATH}"
gem_path = Dir["#{BUNDLE_PATH}/*ruby/{1.8,shared}/gems"][0]
if package != 'org.ruboto.core' && JRUBY_JARS.none? { |f| File.exists? f }
Dir.chdir gem_path do
Dir['activerecord-jdbc-adapter-*'].each do |g|
puts "Removing #{g} gem since it is included in the RubotoCore platform apk."
FileUtils.rm_rf g
end
end
else
Dir.chdir gem_path do
Dir['jruby-openssl-*/lib'].each do |g|
rel_dir = "#{g}/lib/ruby"
unless File.exists? rel_dir
puts "Relocating #{g} files to match standard load path."
#FileUtils.rm_rf "#{g}/1.9"
dirs = Dir["#{g}/*"]
FileUtils.mkdir_p rel_dir
dirs.each do |d|
FileUtils.cp_r d, rel_dir
end
end
end
end
end
# Remove duplicate files
Dir.chdir gem_path do
scanned_files = []
source_files = RUBY_SOURCE_FILES.map { |f| f.gsub("#{PROJECT_DIR}/src/", '') }
Dir["*/lib/**/*"].each do |f|
next if File.directory? f
raise "Malformed file name" unless f =~ %r{^(.*?)/lib/(.*)$}
gem_name, lib_file = $1, $2
if existing_file = scanned_files.find { |sf| sf =~ %r{(.*?)/lib/#{lib_file}} }
puts "Overwriting duplicate file #{lib_file} in gem #{$1} with file in #{gem_name}"
FileUtils.rm existing_file
scanned_files.delete existing_file
elsif source_files.include? lib_file
puts "Removing duplicate file #{lib_file} in gem #{gem_name}"
puts "Already present in project source src/#{lib_file}"
FileUtils.rm f
next
end
scanned_files << f
end
end
# Expand JARs
Dir.chdir gem_path do
Dir['*'].each do |gem_lib|
Dir.chdir "#{gem_lib}/lib" do
Dir['**/*.jar'].each do |jar|
if jar == 'arjdbc/jdbc/adapter_java.jar'
jar_load_code = <<-END_CODE
require 'jruby'
Java::arjdbc.jdbc.AdapterJavaService.new.basicLoad(JRuby.runtime)
END_CODE
elsif jar =~ /shared\/jopenssl.jar$/
jar_load_code = <<-END_CODE
require 'jruby'
puts 'Starting JRuby OpenSSL Service'
public
Java::JopensslService.new.basicLoad(JRuby.runtime)
END_CODE
else
jar_load_code = ''
end
unless jar =~ /sqlite-jdbc/
puts "Expanding #{gem_lib} #{jar} into #{BUNDLE_JAR}"
`jar xf #{jar}`
end
puts "Writing dummy JAR file #{jar + '.rb'}"
File.open(jar + '.rb', 'w') { |f| f << jar_load_code }
if jar.end_with?('.jar')
puts "Writing dummy JAR file #{jar.sub(/.jar$/, '.rb')}"
File.open(jar.sub(/.jar$/, '.rb'), 'w') { |f| f << jar_load_code }
end
FileUtils.rm_f(jar)
end
end
end
end
FileUtils.rm_f BUNDLE_JAR
Dir["#{gem_path}/*"].each_with_index do |gem_dir, i|
`jar #{i == 0 ? 'c' : 'u'}f #{BUNDLE_JAR} -C #{gem_dir}/lib .`
end
# FileUtils.rm_rf BUNDLE_PATH
end
# Methods
def mark_update(time = Time.now)
FileUtils.mkdir_p File.dirname(UPDATE_MARKER_FILE)
File.open(UPDATE_MARKER_FILE, 'w') { |f| f << time.iso8601 }
end
def clear_update
mark_update File.ctime APK_FILE
if device_path_exists?(scripts_path)
sh "adb shell rm -r #{scripts_path}"
puts "Deleted scripts directory #{scripts_path}"
end
end
def strings(name)
@strings ||= REXML::Document.new(File.read('res/values/strings.xml'))
value = @strings.elements["//string[@name='#{name.to_s}']"] or raise "string '#{name}' not found in strings.xml"
value.text
end
def version()
strings :version_name
end
def app_name()
strings :app_name
end
def main_activity()
manifest.root.elements['application'].elements["activity[@android:label='@string/app_name']"].attribute('android:name')
end
def device_path_exists?(path)
path_output =`adb shell ls #{path}`
result = path_output.chomp !~ /No such file or directory|opendir failed, Permission denied/
result
end
def package_installed? test = false
package_name = "#{package}#{'.tests' if test}"
['', '-0', '-1', '-2'].each do |i|
p = "/data/app/#{package_name}#{i}.apk"
o = `adb shell ls -l #{p}`.chomp
if o =~ /^-rw-r--r-- system\s+system\s+(\d+) \d{4}-\d{2}-\d{2} \d{2}:\d{2} #{File.basename(p)}$/
apk_file = test ? TEST_APK_FILE : APK_FILE
if !File.exists?(apk_file) || $1.to_i == File.size(apk_file)
return true
else
return false
end
end
end
return nil
end
private
def replace_faulty_code(faulty_file, faulty_code)
explicit_requires = Dir["#{faulty_file.chomp('.rb')}/*.rb"].sort.map { |f| File.basename(f) }.map do |filename|
"require 'active_model/validations/#{filename}'"
end.join("\n")
old_code = File.read(faulty_file)
new_code = old_code.gsub faulty_code, explicit_requires
if new_code != old_code
puts "Replaced directory listing code in file #{faulty_file} with explicit requires."
File.open(faulty_file, 'w') { |f| f << new_code }
else
puts "Could not find expected faulty code\n\n#{faulty_code}\n\nin file #{faulty_file}\n\n#{old_code}\n\n"
end
end
def build_apk(t, release)
apk_file = release ? RELEASE_APK_FILE : APK_FILE
if File.exist?(apk_file)
changed_prereqs = t.prerequisites.select do |p|
File.file?(p) && !Dir[p].empty? && Dir[p].map { |f| File.mtime(f) }.max > File.mtime(APK_FILE)
end
return false if changed_prereqs.empty?
changed_prereqs.each { |f| puts "#{f} changed." }
puts "Forcing rebuild of #{apk_file}."
end
if release
sh 'ant release'
else
sh 'ant debug'
end
return true
end
def install_apk
case package_installed?
when true
puts "Package already installed."
return
when false
puts "Package installed, but of wrong size."
end
sh 'ant installd'
clear_update
end
def uninstall_apk
return if package_installed?.nil?
puts "Uninstalling package #{package}"
system "adb uninstall #{package}"
if $? != 0 && package_installed?
puts "Uninstall failed exit code #{$?}"
exit $?
end
end
def update_scripts
`adb shell mkdir -p #{scripts_path}` if !device_path_exists?(scripts_path)
puts "Pushing files to apk public file area."
last_update = File.exists?(UPDATE_MARKER_FILE) ? Time.parse(File.read(UPDATE_MARKER_FILE)) : Time.parse('1970-01-01T00:00:00')
# TODO(uwe): Use `adb sync src` instead?
Dir.chdir('src') do
Dir["**/*.rb"].each do |script_file|
next if File.directory? script_file
next if File.mtime(script_file) < last_update
next if script_file =~ /~$/
print "#{script_file}: "; $stdout.flush
`adb push #{script_file} #{scripts_path}/#{script_file}`
end
end
mark_update
end
def start_app
`adb shell am start -a android.intent.action.MAIN -n #{package}/.#{main_activity}`
end
def stop_app
output = `adb shell ps | grep #{package} | awk '{print $2}' | xargs adb shell kill`
return output !~ /Operation not permitted/
end
#######################################################
#
# ruboto/base.rb
#
# Code shared by other ruboto components.
#
#######################################################
# Only used needed for ruboto-core apps
require 'ruboto/version'
$RUBOTO_VERSION = 10
def confirm_ruboto_version(required_version, exact=true)
raise "requires $RUBOTO_VERSION=#{required_version} or greater, current version #{$RUBOTO_VERSION}" if $RUBOTO_VERSION < required_version and not exact
raise "requires $RUBOTO_VERSION=#{required_version}, current version #{$RUBOTO_VERSION}" if $RUBOTO_VERSION != required_version and exact
end
require 'java'
$package_name = ($activity || $service || $broadcast_receiver).package_name
$package = eval("Java::#{$package_name}")
# Create convenience method for top-level android package so we do not need to prefix with 'Java::'.
module Kernel
def android
JavaUtilities.get_package_module_dot_format('android')
end
end
java_import "android.R"
module Ruboto
java_import "#{$package_name}.R"
begin
Id = JavaUtilities.get_proxy_class("#{$package_name}.R$id")
rescue NameError
Java::android.util.Log.d "RUBOTO", "no R$id"
end
end
AndroidIds = JavaUtilities.get_proxy_class("android.R$id")
#
# Callbacks
#
module Ruboto
module CallbackClass
def new_with_callbacks &block
new.initialize_ruboto_callbacks &block
end
end
module Callbacks
def initialize_ruboto_callbacks &block
instance_eval &block
setup_ruboto_callbacks
self
end
def ruboto_callback_methods
(singleton_methods - ["on_create", "on_receive"]).select{|i| i =~ /^on_/}
end
def setup_ruboto_callbacks
ruboto_callback_methods.each do |i|
begin
setCallbackProc(self.class.const_get(i.sub(/^on_/, "CB_").upcase), method(i))
rescue
end
end
end
end
end
#
# Import a class and set it up for handlers
#
def ruboto_import(*package_classes)
java_import(package_classes).each do |package_class|
package_class.class_eval do
extend Ruboto::CallbackClass
include Ruboto::Callbacks
end
end
end
require 'ruboto/activity'
require 'ruboto/widget'
require 'ruboto/util/toast'
require 'ruboto/util/stack'
ruboto_import_widgets :Button, :LinearLayout, :TextView
$activity.start_ruboto_activity "$sample_activity" do
setTitle 'This is the Title'
def on_create(bundle)
self.content_view =
linear_layout(:orientation => :vertical) do
@text_view = text_view :text => 'What hath Matz wrought?', :id => 42
button :text => 'http', :width => :wrap_content, :id => 43,
:on_click_listener => @handle_click
button :text => 'https', :width => :wrap_content, :id => 44,
:on_click_listener => @handle_click
end
end
@handle_click = proc do |view|
Thread.start do
begin
run_on_ui_thread { toast 'Loading HTTPS library' }
with_large_stack { require 'net/https' }
run_on_ui_thread { toast 'Getting HTTPS response' }
response = nil
#host = 'evening-ocean-4975.herokuapp.com'
#host = 'www.google.com'
host = 'www.google.no'
uri = URI("#{view.text}://#{host}/")
use_ssl = view.text.to_s == 'https'
run_on_ui_thread { toast("host: #{uri.host}, port: #{uri.port}, ssl: #{use_ssl}") }
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = use_ssl
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
http.start do |http|
req = Net::HTTP::Get.new("/")
response = http.request(req)
resp = response.body
puts resp.inspect
response =resp
end
run_on_ui_thread { @text_view.text = response }
run_on_ui_thread { toast response }
run_on_ui_thread { @text_view.text = response }
rescue
puts "Got exception: #$!"
puts $!.backtrace.join("\n")
puts
e = $!
run_on_ui_thread do
toast "Exception: #{$!}"
@text_view.text = $!.to_s
end
end
end
end
end
@herestomwiththeweather
Copy link

a jruby-openssl 0.7.6.2 is referenced in the rakefile. does it matter whether it's 0.7.6.2 or 0.7.6.1? if so, is it possible to get 0.7.6.2?

@donv
Copy link
Author

donv commented Mar 29, 2012

jruby-openssl 0.7.6.2 is 0.7.6.1 with some logging added. 0.7.6.1 is fully usable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment