#!/usr/bin/env ruby # Usage: # rb2exe script.rb [OPTIONS] # require_relative "../lib/rb2exe/version.rb" require "tmpdir" require 'optparse' def blank?(arg) "#{arg}".strip == "" end # Arguments options = { quiet: false, add: nil, output: nil, rails: false, target: 'l64' } opt_parser = OptionParser.new do |opts| opts.banner = "Usage: rb2exe RUBY_SCRIPT [options]" opts.version = Rb2exe::VERSION opts.on("-q", "--quiet", "Do not run verbosely") { |v| options[:quiet] = v } opts.on("-a", "--add=FOLDER", "Add an entire folder (eg. \".\")") { |v| options[:add] = v } opts.on("-o", "--output=OUTPUT", "Output executable filename") { |v| options[:output] = v } opts.on("-r", "--rails", "Rails support") { |v| options[:rails] = v } opts.on("-t", "--target=[osx|l32|l64|win]", "Target platform (binary)") { |v| options[:target] = v } opts.on("-h","--help", "Help") { puts opt_parser } end opt_parser.parse! # Gem path gem_dir = File.expand_path(File.dirname(__FILE__) + "/..") # Main ruby app (filename) main_app_path = ARGV[0] abort("#{opt_parser}") if ! options[:rails] && blank?(main_app_path) abort("#{ARGV[0]} doesn't exist.") if ! options[:rails] && ! File.exists?(ARGV[0]) # App directory pwd = Dir.pwd if options[:rails] app_dir = File.expand_path('.') else if blank?(options[:add]) app_dir = nil else app_dir = File.expand_path(options[:add]) abort "Directory #{app_dir} doesn't exist." if ! Dir.exists?(app_dir) end end # Executable filename # If not specified, uses the main app filename without its extension. Eg.: # script.rb -> script if blank?(options[:output]) && options[:rails] exe_fn = "output" else exe_fn = blank?(options[:output]) ? File.basename(ARGV[0], File.extname(ARGV[0])) : options[:output] abort("Can't use '#{exe_fn}' as a filename (there's a folder with this name).") if Dir.exists?("#{pwd}/#{exe_fn}") end # Ruby version should be 2.2.2, 64 bits: version = `echo $RUBY_VERSION`.strip if version != "ruby-2.2.2" abort "Your ruby version should be EXACTLY 2.2.2, not higher, not lower (your is #{version})." end # Temporary directory result = "Error" Dir.mktmpdir do |tmp_dir| FileUtils.mkdir_p("#{tmp_dir}/payload/lib") # Copy ruby traveler 2.2.2 to "payload/lib/ruby" ruby_src = case options[:target] when "l64" then "#{gem_dir}/bin/traveling-ruby-2.2.2/l64" when "l32" then "#{gem_dir}/bin/traveling-ruby-2.2.2/l32" when "osx" then "#{gem_dir}/bin/traveling-ruby-2.2.2/osx" when "win" then "#{gem_dir}/bin/traveling-ruby-2.2.2/win" else abort("Unknow platform (#{options[:target]})") end FileUtils.cp_r(ruby_src, "#{tmp_dir}/payload/lib/ruby") # Move the "app" folder to "payload/lib" if app_dir.nil? FileUtils.mkdir_p("#{tmp_dir}/payload/lib/app/") FileUtils.cp_r(main_app_path, "#{tmp_dir}/payload/lib/app/") else FileUtils.cp_r(app_dir, "#{tmp_dir}/payload/lib/app") end FileUtils.cp_r("#{gem_dir}/bin/installer", "#{tmp_dir}/payload/") # Create a wrapper script (name it as "installer") # Gemfile support if File.exists?("#{tmp_dir}/payload/lib/app/Gemfile") puts "Gemfile detected. Loading gems." unless options[:quiet] FileUtils.mkdir_p("#{tmp_dir}/payload/lib/tmp") # Create a "tmp" folder on ".package/payload/lib" FileUtils.cp("#{tmp_dir}/payload/lib/app/Gemfile", "#{tmp_dir}/payload/lib/tmp/") # Copy the project Gemfile to tmp FileUtils.cp("#{tmp_dir}/payload/lib/app/Gemfile.lock", "#{tmp_dir}/payload/lib/tmp/") if File.exists?("#{tmp_dir}/payload/lib/app/Gemfile.lock") # Download gems into the "lib/vendor" folder Bundler.with_clean_env do Dir.chdir("#{tmp_dir}/payload/lib/app") do `bundle install --path #{tmp_dir}/payload/lib/vendor --without development` end end FileUtils.rm_rf("#{tmp_dir}/payload/lib/tmp/") # Delete tmp folder %x[cd #{tmp_dir}/payload/lib/ && rm -f vendor/*/*/cache/*] # Delete gem's cache in the vendor folder FileUtils.cp("#{tmp_dir}/payload/lib/app/Gemfile", "#{tmp_dir}/payload/lib/vendor/") # Copy the Gemfile to the vendor folder FileUtils.cp("#{tmp_dir}/payload/lib/app/Gemfile.lock", "#{tmp_dir}/payload/lib/vendor/") if File.exists?("#{tmp_dir}/payload/lib/app/Gemfile.lock") # Create a bundler config FileUtils.mkdir_p("#{tmp_dir}/payload/lib/vendor/.bundle") FileUtils.cp("#{gem_dir}/lib/bundler-config", "#{tmp_dir}/payload/lib/vendor/.bundle/config") end FileUtils.cp_r("#{gem_dir}/bin/build", "#{tmp_dir}/") # Package builder FileUtils.cp_r("#{gem_dir}/bin/decompress", "#{tmp_dir}/") result = `#{tmp_dir}/build #{tmp_dir} #{main_app_path} #{exe_fn} #{options[:quiet]} #{options[:rails]}` FileUtils.mv("#{tmp_dir}/output", "#{pwd}/#{exe_fn}") # Output end print result