Skip to content

Instantly share code, notes, and snippets.

@pepijnve
Last active December 16, 2015 05:48
Show Gist options
  • Save pepijnve/5386437 to your computer and use it in GitHub Desktop.
Save pepijnve/5386437 to your computer and use it in GitHub Desktop.
Buildr extension that adds support for obfuscation using Proguard
require 'buildr'
module Buildr
class ProguardTask < Rake::FileTask
# Proguard version number.
VERSION = '4.7'
OPTIONS = [:configuration,
:target,
:forceprocessing,
:printseeds,
:shrink,
:printusage,
:optimize,
:optimizationpasses,
:allowaccessmodification,
:mergeinterfacesaggressively,
:obfuscate,
:printmapping,
:applymapping,
:obfuscationdictionary,
:classobfuscationdictionary,
:packageobfuscationdictionary,
:overloadaggressively,
:useuniqueclassmembernames,
:usemixedcaseclassnames,
:flattenpackagehierarchy,
:repackageclasses,
:renamesourcefileattribute,
:keepparameternames,
:preverify,
:microedition,
:verbose,
:note,
:warn,
:ignorewarnings,
:printconfiguration,
:dump]
FILTER_OPTIONS = [:filter, :jarfilter, :warfilter, :earfilter, :zipfilter]
MODIFIER_SPEC = [:allowshrinking, :allowoptimization, :allowobfuscation]
CLASS_SPEC = [:access, :type, :name, :extends, :implements, :field, :method, :constructor]
MODIFIER_CLASS_SPEC = [MODIFIER_SPEC, CLASS_SPEC].flatten
MEMBER_SPEC = [:access, :type, :name, :parameters]
KEEP_OPTIONS = ["", :classmembers, :classeswithmembers, :names, :classmembernames, :classeswithmembernames, :packagename, :packagenames, :attribute, :attributes]
KEEP_SPECS = {
"" => MODIFIER_CLASS_SPEC,
:classmembers => MODIFIER_CLASS_SPEC,
:classeswithmembers => MODIFIER_CLASS_SPEC,
:names => CLASS_SPEC,
:classmembernames => CLASS_SPEC,
:classeswithmembernames => CLASS_SPEC,
:packagename => [:name],
:packagenames => [:filter],
:attribute => [:name],
:attributes => [:filter]
}
class << self
def version
Buildr.settings.build['proguard'] || VERSION
end
def dependencies
@dependencies ||= ["net.sourceforge.proguard:proguard:jar:#{version}"]
end
end
def initialize(*args) #:nodoc:
super
@sources = []
@dependencies = []
@keep = []
@whykeeping = []
@assumenosfct = []
@params = {:optimize => false}
@main_jar = nil
java_home = nil
Buildr.ant('proguard') do |ant|
java_home = ant.project.getProperty("java.home")
end
Dir["#{java_home}/**/rt.jar"].each { |f| use f }
enhance { obfuscate }
end
def manifest
@main_jar.manifest
end
attr_reader :main_jar
attr_accessor :sources
def include(*args)
@main_jar = args[0] unless @main_jar
filter_options = Hash === args.last ? args.pop.dup : {}
rake_check_options filter_options, *ProguardTask::FILTER_OPTIONS
artifacts = Buildr.artifacts(args.flatten).uniq
@sources << {:artifacts => artifacts, :filter_options => filter_options}
self
end
alias :in_jar :include
attr_accessor :dependencies
def use(*args)
filter_options = Hash === args.last ? args.pop.dup : {}
rake_check_options filter_options, *ProguardTask::FILTER_OPTIONS
artifacts = Buildr.artifacts(args.flatten).uniq
@dependencies << {:artifacts => artifacts, :filter_options => filter_options}
self
end
alias :library_jar :use
def with(options)
manifest_value = options.delete(:manifest)
if manifest_value
@main_jar.with :manifest => manifest_value
end
rake_check_options options, *ProguardTask::OPTIONS
@params.merge! options
self
end
def keep(*args)
@keep << keep_spec(*args)
end
def why_are_you_keeping(*args)
@whykeeping << keep_spec(*args)
end
private
def keep_spec(*args)
keep_spec = Hash === args.last ? args.pop.dup : {}
keep = args.shift || ""
raise ArgumentError, "Invalid why_are_you_keeping option: #{keep}" unless ProguardTask::KEEP_OPTIONS.member? keep
rake_check_options keep_spec, *ProguardTask::KEEP_SPECS[keep]
keep_spec[:keep] = keep
[:field, :method, :constructor].each do |key|
case keep_spec[key]
when Hash
rake_check_options keep_spec[key], *ProguardTask::MEMBER_SPEC
keep_spec[key] = [keep_spec[key]]
when Array
keep_spec[key].each { |spec| rake_check_options spec, *ProguardTask::MEMBER_SPEC }
else
keep_spec[key] = []
end
end
keep_spec
end
def invoke_prerequisites(args, chain) #:nodoc:
sources = @sources.map { |s| s[:artifacts] }.flatten.uniq
dependencies = @dependencies.map { |s| s[:artifacts] }.flatten.uniq
@prerequisites |= sources + dependencies
super
end
def obfuscate() #:nodoc:
Buildr.ant('proguard') do |ant|
java_home = ant.project.getProperty("java.home")
classpath = Buildr.artifacts(ProguardTask.dependencies).each(&:invoke).map(&:to_s).join(File::PATH_SEPARATOR)
ant.taskdef :resource=>'proguard/ant/task.properties', :classpath=> classpath
ant.proguard @params do
@sources.each do |s|
s[:artifacts].each do |a|
ant.injar({:path => a.to_s}.merge s[:filter_options])
end
end
ant.outjar :path => name
@dependencies.each do |s|
s[:artifacts].each do |a|
ant.libraryjar({:path => a.to_s}.merge s[:filter_options])
end
end
{'keep' => @keep, 'whyareyoukeeping' => @whykeeping}.each do |tag, spec|
params = spec.dup
keep = params.delete(:keep)
field = params.delete(:field)
method = params.delete(:method)
constructor = params.delete(:constructor)
ant.send("#{tag}#{keep}", params) do
field.each { |f| ant.field f }
method.each { |m| ant.method_missing(:method, m) }
constructor.each { |c| ant.constructor c }
end
end
end
end
end
end
module ProguardExtension # :nodoc:
include Buildr::Extension
def obfuscate(file, &block)
ProguardTask.define_task(path_to(file)).enhance &block
end
def package_as_obfuscatedjar(file_name) #:nodoc:
dir = File.dirname(file_name)
basename = File.basename(file_name, ".jar")
jar = package_as_jar("#{dir}/#{basename}-noobf.jar")
obfuscate(file_name).include jar
end
def package_as_obfuscatedjar_spec(spec) #:nodoc:
spec[:type] = :jar
spec
end
end
end
class Buildr::Project
include Buildr::ProguardExtension
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment