mirror of
https://github.com/zhigang1992/RubyMotion.git
synced 2026-03-29 09:09:07 +08:00
add support for the R class (thanks to Mark for the patch)
This commit is contained in:
@@ -2,52 +2,49 @@
|
||||
# encoding: utf-8
|
||||
|
||||
require 'fileutils'
|
||||
require 'pathname'
|
||||
require 'optparse'
|
||||
|
||||
if ARGV.size != 2
|
||||
$stderr.puts "Usage: #{__FILE__} <jar-file> <output-file>"
|
||||
exit 1
|
||||
end
|
||||
def gen_bridge_metadata(file)
|
||||
|
||||
jar_path = File.expand_path(ARGV[0])
|
||||
bs_path = File.expand_path(ARGV[1])
|
||||
file = File.expand_path(file)
|
||||
|
||||
tmp_path = File.join(ENV['TMPDIR'], '__android_bridgesupport__', File.basename(jar_path))
|
||||
FileUtils.rm_rf tmp_path
|
||||
FileUtils.mkdir_p tmp_path
|
||||
case ext = File.extname(file)
|
||||
when '.jar'
|
||||
class_path = File.join(ENV['TMPDIR'], '__android_bridgesupport__', File.basename(file))
|
||||
FileUtils.rm_rf class_path
|
||||
FileUtils.mkdir_p class_path
|
||||
puts "Extracting #{file} to #{class_path}"
|
||||
exit 1 unless system "/usr/bin/unzip -q \"#{file}\" -d \"#{class_path}\""
|
||||
|
||||
puts "Extracting #{jar_path} to #{tmp_path}"
|
||||
|
||||
Dir.chdir(tmp_path) do
|
||||
# Unzip the .jar archive.
|
||||
exit 1 unless system "/usr/bin/unzip -q \"#{jar_path}\""
|
||||
classes = Dir.glob(File.join(class_path ,"**/*.class")).map { |c| c.gsub(class_path, '') }
|
||||
when '.class'
|
||||
class_path = File.dirname(file)
|
||||
classes = [File.basename(file)]
|
||||
else
|
||||
die "Invalid file extension '#{ext}'. Supported extensions are 'jar' and 'class'"
|
||||
end
|
||||
|
||||
# Decompile classes.
|
||||
classes = Dir.glob("**/*.class").map { |x| x.gsub('/', '.').gsub('$', '.').sub(/\.class$/, '') }
|
||||
javap_tmp_path = File.join(File.dirname(tmp_path), 'classes.txt')
|
||||
system "/usr/bin/javap -s #{classes.join(' ')} > \"#{javap_tmp_path}\""
|
||||
classes = classes.map { |x| x.gsub('/', '.').gsub('$', '.').sub(/\.class$/, '') }
|
||||
txt = `/usr/bin/javap -classpath "#{class_path}" -s #{classes.join(' ')}`
|
||||
|
||||
# Create the BridgeSupport text.
|
||||
bs_data = ''
|
||||
bs_data << <<EOS
|
||||
<?xml version='1.0'?>
|
||||
<signatures version='1.0'>
|
||||
EOS
|
||||
txt = File.read(javap_tmp_path)
|
||||
res = txt.scan(/(class)\s+([^\s]+)\s+extends\s+[^{]+\s*\{([^}]+)\}/)
|
||||
res += txt.scan(/(class)\s([^\s{]+)\s*\{([^}]+)\}/) # Also grab classes without superclasses (ex. java.lang.Object)
|
||||
res += txt.scan(/(interface)\s([^\s{]+)\s*\{([^}]+)\}/) # Also grab interfaces
|
||||
res.each do |type, elem, body_txt|
|
||||
elem_path = elem.gsub(/\./, '/')
|
||||
bs_data << <<EOS
|
||||
elem_path = encode_xml(elem.gsub(/\./, '/'))
|
||||
@bs_data << <<EOS
|
||||
<#{type} name=\"#{elem_path}\">
|
||||
EOS
|
||||
body_txt.strip.split(/\n/).each_cons(2) do |elem_line, signature_line|
|
||||
signature_line = signature_line.strip
|
||||
signature_line = encode_xml(signature_line.strip)
|
||||
md = signature_line.match(/^Signature:\s+(.+)$/)
|
||||
next unless md
|
||||
signature = md[1]
|
||||
|
||||
elem_line = elem_line.strip
|
||||
elem_line = encode_xml(elem_line.strip)
|
||||
if md = elem_line.match(/\s([^(\s]+)\(/)
|
||||
# A method.
|
||||
method = md[1]
|
||||
@@ -56,27 +53,60 @@ EOS
|
||||
method = '<init>'
|
||||
end
|
||||
class_method = elem_line.include?('static')
|
||||
bs_data << <<EOS
|
||||
@bs_data << <<EOS
|
||||
<method name=\"#{method}\" type=\"#{signature}\"#{class_method ? ' class_method="true"' : ''}/>
|
||||
EOS
|
||||
elsif md = elem_line.match(/\s([^;\s]+);$/)
|
||||
=begin
|
||||
# A constant.
|
||||
constant = md[1]
|
||||
bs_data << <<EOS
|
||||
constant = md[1]
|
||||
@bs_data << <<EOS
|
||||
<const name=\"#{constant}\" type=\"#{signature}\"/>
|
||||
EOS
|
||||
=end
|
||||
end
|
||||
end
|
||||
bs_data << <<EOS
|
||||
@bs_data << <<EOS
|
||||
</#{type}>
|
||||
EOS
|
||||
end
|
||||
bs_data << <<EOS
|
||||
end
|
||||
|
||||
def encode_xml(string)
|
||||
string.gsub(/[&<>"]/, ">" => ">", "<" => "<", "&" => "&", '"' => """)
|
||||
end
|
||||
|
||||
def die(*msg)
|
||||
$stderr.puts msg
|
||||
exit 1
|
||||
end
|
||||
|
||||
if __FILE__ == $0
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "Usage: #{File.basename(__FILE__)} [options] [<jar-file>|<class-file>...]"
|
||||
opts.separator ''
|
||||
opts.separator 'Options:'
|
||||
|
||||
opts.on('-o', '--output FILE', 'Write output to the given file.') do |opt|
|
||||
die 'Output file can\'t be specified more than once' if @out_file
|
||||
@out_file = opt
|
||||
end
|
||||
|
||||
if ARGV.empty?
|
||||
die opts.banner
|
||||
else
|
||||
opts.parse!(ARGV)
|
||||
@bs_data = ''
|
||||
@bs_data << <<EOS
|
||||
<?xml version='1.0'?>
|
||||
<signatures version='1.0'>
|
||||
EOS
|
||||
ARGV.each { |file| gen_bridge_metadata(file) }
|
||||
|
||||
@bs_data << <<EOS
|
||||
</signatures>
|
||||
EOS
|
||||
|
||||
# Write the BridgeSupport data down.
|
||||
File.open(bs_path, 'w') { |io| io.write(bs_data) }
|
||||
File.open(@out_file, 'w') { |io| io.write(@bs_data) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -37,11 +37,104 @@ task :build do
|
||||
app_build_dir = App.config.versionized_build_dir
|
||||
mkdir_p app_build_dir
|
||||
|
||||
# Generate the Android manifest file.
|
||||
android_manifest_txt = ''
|
||||
android_manifest_txt << <<EOS
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="#{App.config.package}" android:versionCode="#{App.config.version_code}" android:versionName="#{App.config.version_name}">
|
||||
<uses-sdk android:minSdkVersion="#{App.config.api_version}"/>
|
||||
EOS
|
||||
# Application permissions
|
||||
permissions = Array(App.config.permissions)
|
||||
if App.config.development?
|
||||
# In development mode, we need the INTERNET permission in order to create
|
||||
# the REPL socket.
|
||||
permissions |= ['android.permission.INTERNET']
|
||||
end
|
||||
permissions.each do |permission|
|
||||
permission = "android.permission.#{permission.to_s.upcase}" if permission.is_a?(Symbol)
|
||||
android_manifest_txt << <<EOS
|
||||
<uses-permission android:name="#{permission}"></uses-permission>
|
||||
EOS
|
||||
end
|
||||
# Custom manifest entries.
|
||||
App.config.manifest_xml_lines(nil).each { |line| android_manifest_txt << "\t" + line + "\n" }
|
||||
android_manifest_txt << <<EOS
|
||||
<application android:label="#{App.config.name}" android:debuggable="#{App.config.development? ? 'true' : 'false'}" #{App.config.icon ? ('android:icon="@drawable/' + App.config.icon + '"') : ''}>
|
||||
EOS
|
||||
App.config.manifest_xml_lines('application').each { |line| android_manifest_txt << "\t\t" + line + "\n" }
|
||||
# Main activity.
|
||||
android_manifest_txt << <<EOS
|
||||
<activity android:name="#{App.config.main_activity}" android:label="#{App.config.name}">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
EOS
|
||||
# Sub-activities.
|
||||
(App.config.sub_activities.uniq - [App.config.main_activity]).each do |activity|
|
||||
android_manifest_txt << <<EOS
|
||||
<activity android:name="#{activity}" android:label="#{activity}" android:parentActivityName="#{App.config.main_activity}">
|
||||
<meta-data android:name="android.support.PARENT_ACTIVITY" android:value="#{App.config.main_activity}"/>
|
||||
</activity>
|
||||
EOS
|
||||
end
|
||||
android_manifest_txt << <<EOS
|
||||
</application>
|
||||
</manifest>
|
||||
EOS
|
||||
android_manifest = File.join(app_build_dir, 'AndroidManifest.xml')
|
||||
if !File.exist?(android_manifest) or File.read(android_manifest) != android_manifest_txt
|
||||
App.info 'Create', android_manifest
|
||||
File.open(android_manifest, 'w') { |io| io.write(android_manifest_txt) }
|
||||
end
|
||||
|
||||
# Create R.java files.
|
||||
java_dir = File.join(app_build_dir, 'java')
|
||||
java_app_package_dir = File.join(java_dir, *App.config.package.split(/\./))
|
||||
mkdir_p java_app_package_dir
|
||||
r_bs = File.join(app_build_dir, 'R.bridgesupport')
|
||||
|
||||
android_jar = "#{App.config.sdk_path}/platforms/android-#{App.config.api_version}/android.jar"
|
||||
resources_dirs = []
|
||||
App.config.resources_dirs.flatten.each do |dir|
|
||||
next unless File.exist?(dir)
|
||||
next unless File.directory?(dir)
|
||||
resources_dirs << dir
|
||||
end
|
||||
all_resources = (resources_dirs + App.config.vendored_projects.map { |x| x[:resources] }.compact)
|
||||
aapt_resources_flags = all_resources.map { |x| '-S "' + x + '"' }.join(' ')
|
||||
|
||||
r_java_mtime = Dir.glob(java_dir + '/**/R.java').map { |x| File.mtime(x) }.max
|
||||
|
||||
bs_files = []
|
||||
classes_changed = false
|
||||
if !r_java_mtime or all_resources.any? { |x| Dir.glob(x + '/**/*').any? { |y| File.mtime(y) > r_java_mtime } }
|
||||
extra_packages = App.config.vendored_projects.map { |x| x[:package] }.compact.map { |x| "--extra-packages #{x}" }.join(' ')
|
||||
sh "\"#{App.config.build_tools_dir}/aapt\" package -f -M \"#{android_manifest}\" #{aapt_resources_flags} -I \"#{android_jar}\" -m -J \"#{java_dir}\" #{extra_packages} --auto-add-overlay"
|
||||
|
||||
|
||||
r_java = Dir.glob(java_dir + '/**/R.java')
|
||||
classes_dir = File.join(app_build_dir, 'classes')
|
||||
mkdir_p classes_dir
|
||||
|
||||
r_java.each do |java_path|
|
||||
sh "/usr/bin/javac -d \"#{classes_dir}\" -classpath #{classes_dir} -sourcepath \"#{java_dir}\" -target 1.5 -bootclasspath \"#{android_jar}\" -encoding UTF-8 -g -source 1.5 \"#{java_path}\""
|
||||
end
|
||||
|
||||
r_classes = Dir.glob(classes_dir + '/**/R\$*[a-z]*.class').map { |c| "'#{c}'" }
|
||||
sh "#{App.config.bin_exec('android/gen_bridge_metadata')} #{r_classes.join(' ')} -o \"#{r_bs}\" "
|
||||
|
||||
classes_changed = true
|
||||
end
|
||||
bs_files << r_bs if File.exist?(r_bs)
|
||||
|
||||
# Compile Ruby files.
|
||||
ruby = App.config.bin_exec('ruby')
|
||||
init_func_n = 0
|
||||
ruby_objs = []
|
||||
bs_files = Dir.glob(File.join(App.config.versioned_datadir, 'BridgeSupport/*.bridgesupport'))
|
||||
bs_files += Dir.glob(File.join(App.config.versioned_datadir, 'BridgeSupport/*.bridgesupport'))
|
||||
bs_files += App.config.vendored_bs_files
|
||||
ruby_bs_flags = bs_files.map { |x| "--uses-bs \"#{x}\"" }.join(' ')
|
||||
objs_build_dir = File.join(app_build_dir, 'obj', 'local', App.config.armeabi_directory_name)
|
||||
@@ -147,59 +240,6 @@ EOS
|
||||
end
|
||||
end
|
||||
|
||||
# Generate the Android manifest file.
|
||||
android_manifest_txt = ''
|
||||
android_manifest_txt << <<EOS
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="#{App.config.package}" android:versionCode="#{App.config.version_code}" android:versionName="#{App.config.version_name}">
|
||||
<uses-sdk android:minSdkVersion="#{App.config.api_version}"/>
|
||||
EOS
|
||||
# Application permissions
|
||||
permissions = Array(App.config.permissions)
|
||||
if App.config.development?
|
||||
# In development mode, we need the INTERNET permission in order to create
|
||||
# the REPL socket.
|
||||
permissions |= ['android.permission.INTERNET']
|
||||
end
|
||||
permissions.each do |permission|
|
||||
permission = "android.permission.#{permission.to_s.upcase}" if permission.is_a?(Symbol)
|
||||
android_manifest_txt << <<EOS
|
||||
<uses-permission android:name="#{permission}"></uses-permission>
|
||||
EOS
|
||||
end
|
||||
# Custom manifest entries.
|
||||
App.config.manifest_xml_lines(nil).each { |line| android_manifest_txt << "\t" + line + "\n" }
|
||||
android_manifest_txt << <<EOS
|
||||
<application android:label="#{App.config.name}" android:debuggable="#{App.config.development? ? 'true' : 'false'}" #{App.config.icon ? ('android:icon="@drawable/' + App.config.icon + '"') : ''}>
|
||||
EOS
|
||||
App.config.manifest_xml_lines('application').each { |line| android_manifest_txt << "\t\t" + line + "\n" }
|
||||
# Main activity.
|
||||
android_manifest_txt << <<EOS
|
||||
<activity android:name="#{App.config.main_activity}" android:label="#{App.config.name}">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
EOS
|
||||
# Sub-activities.
|
||||
(App.config.sub_activities.uniq - [App.config.main_activity]).each do |activity|
|
||||
android_manifest_txt << <<EOS
|
||||
<activity android:name="#{activity}" android:label="#{activity}" android:parentActivityName="#{App.config.main_activity}">
|
||||
<meta-data android:name="android.support.PARENT_ACTIVITY" android:value="#{App.config.main_activity}"/>
|
||||
</activity>
|
||||
EOS
|
||||
end
|
||||
android_manifest_txt << <<EOS
|
||||
</application>
|
||||
</manifest>
|
||||
EOS
|
||||
android_manifest = File.join(app_build_dir, 'AndroidManifest.xml')
|
||||
if !File.exist?(android_manifest) or File.read(android_manifest) != android_manifest_txt
|
||||
App.info 'Create', android_manifest
|
||||
File.open(android_manifest, 'w') { |io| io.write(android_manifest_txt) }
|
||||
end
|
||||
|
||||
# Create java files based on the classes map files.
|
||||
java_classes = {}
|
||||
Dir.glob(objs_build_dir + '/**/*.map') do |map|
|
||||
@@ -250,9 +290,6 @@ EOS
|
||||
end
|
||||
end
|
||||
end
|
||||
java_dir = File.join(app_build_dir, 'java')
|
||||
java_app_package_dir = File.join(java_dir, *App.config.package.split(/\./))
|
||||
mkdir_p java_app_package_dir
|
||||
java_classes.each do |name, klass|
|
||||
klass_super = klass[:super]
|
||||
klass_super = 'java.lang.Object' if klass_super == '$blank$'
|
||||
@@ -279,29 +316,12 @@ EOS
|
||||
end
|
||||
end
|
||||
|
||||
# Create R.java files.
|
||||
android_jar = "#{App.config.sdk_path}/platforms/android-#{App.config.api_version}/android.jar"
|
||||
resources_dirs = []
|
||||
App.config.resources_dirs.flatten.each do |dir|
|
||||
next unless File.exist?(dir)
|
||||
next unless File.directory?(dir)
|
||||
resources_dirs << dir
|
||||
end
|
||||
all_resources = (resources_dirs + App.config.vendored_projects.map { |x| x[:resources] }.compact)
|
||||
aapt_resources_flags = all_resources.map { |x| '-S "' + x + '"' }.join(' ')
|
||||
r_java_mtime = Dir.glob(java_dir + '/**/R.java').map { |x| File.mtime(x) }.max
|
||||
if !r_java_mtime or all_resources.any? { |x| Dir.glob(x + '/**/*').any? { |y| File.mtime(y) > r_java_mtime } }
|
||||
extra_packages = App.config.vendored_projects.map { |x| x[:package] }.compact.map { |x| "--extra-packages #{x}" }.join(' ')
|
||||
sh "\"#{App.config.build_tools_dir}/aapt\" package -f -M \"#{android_manifest}\" #{aapt_resources_flags} -I \"#{android_jar}\" -m -J \"#{java_dir}\" #{extra_packages} --auto-add-overlay"
|
||||
end
|
||||
|
||||
# Compile java files.
|
||||
vendored_jars = App.config.vendored_projects.map { |x| x[:jar] }
|
||||
vendored_jars += [File.join(App.config.versioned_datadir, 'rubymotion.jar')]
|
||||
classes_dir = File.join(app_build_dir, 'classes')
|
||||
mkdir_p classes_dir
|
||||
class_path = [classes_dir, "#{App.config.sdk_path}/tools/support/annotations.jar", *vendored_jars].map { |x| "\"#{x}\"" }.join(':')
|
||||
classes_changed = false
|
||||
Dir.glob(File.join(app_build_dir, 'java', '**', '*.java')).each do |java_path|
|
||||
paths = java_path.split('/')
|
||||
paths[paths.index('java')] = 'classes'
|
||||
|
||||
@@ -240,7 +240,7 @@ module Motion; module Project;
|
||||
bs_file = File.join(File.dirname(jar_file), File.basename(jar_file) + '.bridgesupport')
|
||||
if !File.exist?(bs_file) or File.mtime(jar_file) > File.mtime(bs_file)
|
||||
App.info 'Create', bs_file
|
||||
sh "#{bin_exec('android/gen_bridge_metadata')} \"#{jar_file}\" \"#{bs_file}\""
|
||||
sh "#{bin_exec('android/gen_bridge_metadata')} -o \"#{bs_file}\" \"#{jar_file}\""
|
||||
end
|
||||
bs_file
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user