diff --git a/ruby/fixup/fixup.rb b/ruby/fixup/fixup.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f49cde5c8eac7cc68f4e6c9e89ab49d3ae42685d
--- /dev/null
+++ b/ruby/fixup/fixup.rb
@@ -0,0 +1,123 @@
+require "fileutils"
+
+class BundleFixer
+  def initialize(bundle)
+    @bundle_dir = bundle.sub(%r{/$},"")
+    raise "No such bundle #{bundle}" unless File.directory? @bundle_dir
+    basename = File.basename(@bundle_dir, ".app")
+    @embed_dir = "#{@bundle_dir}/Contents/Frameworks"
+    @embed_rel_dir = "@executable_path/../Frameworks"
+
+    @job = Dir["#{@bundle_dir}/Contents/MacOS/*"].select{|path| File.executable? path} + Dir["#{@bundle_dir}/Contents/PlugIns/**/*.dylib"]
+
+    # ignore prerequisites matching these patterns
+    @ignore_patterns = [
+      # system libraries and frameworks
+      %r{^/usr/lib},
+      %r{^/System},
+      # already fixed links
+      %r{@executable_path}
+    ]
+  end
+
+  def fixup
+    @copy_hash = {}
+    @rename_hashes = Hash.new {|h,k| h[k]={}}
+
+    puts "Scanning for prerequisites..."
+    @job.each do |path|
+      puts " - #{path}"
+      scan path
+    end
+    puts "Copying prerequisites into the bundle..."
+    copy
+    puts "Fixing link names..."
+    fix_names
+  end
+
+  # scan given object file for prerequisites (using otool -L) and collect them into data structures:
+  # @copy_hash maps absolute path to embedded path (relative to the bundle's @embed_dir)
+  # @rename_hashes maps each embedded path to a hash that maps old names (absolute or using @loader_path) to new names (using @executable_path)
+  def scan object, embedded_object=object
+    context = File.dirname(object)
+    IO.popen "otool -L #{object}" do | lines |
+      lines.each do | l |
+        l.match( %r{\t([^ ]+) \(compatibility} ) do | m |
+          old_name = m[1]
+          unless ignore? old_name
+            if File.basename(old_name) == File.basename(object) then
+              @rename_hashes[embedded_object][old_name] = object.sub(%r{#{@bundle_dir}/Contents/},"@executable_path/../")
+            else
+              absolute_path = make_absolute_path old_name, context
+              copy_src = absolute_path
+              embedded_path = File.basename absolute_path
+              absolute_path.match( %r{(.+)\.framework(/.*/\1)} ) do | m |
+                copy_src = absolute_path[0, m.offset(2)[0]]
+              embedded_path = m[0]
+              end
+              @rename_hashes[embedded_object][old_name] = "#{@embed_rel_dir}/#{embedded_path}"
+              unless @copy_hash.key? copy_src
+                @copy_hash[copy_src] = File.basename copy_src
+                scan absolute_path, embedded_path
+              end
+            end
+          end
+        end
+      end
+    end
+  end
+
+  # copy prerequisites from @copy_hash
+  def copy
+    FileUtils.mkdir_p @embed_dir
+    @copy_hash.each do |absolute,embedded|
+      dst = "#{@embed_dir}/#{embedded}"
+      unless File.exists? dst
+        puts "copy #{File.basename absolute}"
+        unless system "cp -a #{absolute} #{dst}"
+          puts "Error copying #{absolute} to #{dst}"
+        end
+      end
+    end
+  end
+
+  # rewrite object id's and names under which they refer to each other
+  def fix_names
+    @rename_hashes.each do |basename,h|
+      puts "fixing #{basename}"
+      changes = h.map{|old,new| "\\\n -change #{old} #{new}"}
+      if @job.include?(basename)
+        path =  basename
+        new_name = basename.sub(%r{#{@bundle_dir}/Contents/},"@executable_path/../")
+      else
+        path = "#{@embed_dir}/#{basename}"
+        new_name = "#{@embed_rel_dir}/#{basename}"
+      end
+      system "chmod u+w #{path}"
+      cmd = "install_name_tool \\\n -id #{new_name} #{changes.join(' ')} \\\n #{path}"
+      unless system cmd
+        puts "Error fixing names in #{path}"
+        puts "Command line was:\n#{cmd}"
+      end
+    end
+  end
+
+  def ignore? id
+    @ignore_patterns.any? { |re| id.match(re) }
+  end
+
+  def make_absolute_path path, context
+    # resolve @loader_path references
+    path = path.sub("@loader_path", context)
+    # follow symlinks
+    File.realpath path
+  end
+
+end
+
+
+ARGV.each do |bundle|
+  f = BundleFixer.new bundle
+  f.fixup
+end
+