diff --git a/tools/mac-libs.rb b/tools/mac-libs.rb
new file mode 100755
index 00000000..1a379f96
--- /dev/null
+++ b/tools/mac-libs.rb
@@ -0,0 +1,227 @@
+#!/usr/bin/env ruby
+
+require "open3"
+require "fileutils"
+
+$app_name = "melonDS"
+$build_dmg = false
+$build_dir = ""
+$bundle = ""
+$fallback_rpaths = ["/usr/local/lib", "/opt/local/lib"]
+
+def frameworks_dir
+  File.join($bundle, "Contents", "Frameworks")
+end
+
+def executable
+  File.join($bundle, "Contents", "MacOS", $app_name)
+end
+
+def get_rpaths(lib)
+  out = `otool -l #{lib}`.split("\n")
+  rpaths = []
+
+  out.each_with_index do |line, i|
+    if line.match(/^ *cmd LC_RPATH$/)
+      rpaths << out[i + 2].strip.split(" ")[1]
+    end
+  end
+
+  return rpaths
+end
+
+def get_load_libs(lib)
+  `otool -L #{lib}`
+    .split("\n")
+    .drop(1)
+    .map { |it| it.strip.gsub(/ \(.*/, "") }
+end
+
+def expand_load_path(lib, path)
+  if path.match(/@(rpath|loader_path|executable_path)/) 
+    path_type = $1
+    file_name = path.gsub(/^@#{path_type}\//, "")
+
+    case path_type
+      when "rpath"
+        get_rpaths(lib).each do |rpath|
+          file = File.join(rpath, file_name)
+          return file, :rpath if File.exist? file
+          if rpath.match? /^@executable_path(.*)/
+            relative = rpath.sub(/^@executable_path/, "")
+            return "#{$bundle}/Contents/MacOS#{relative}", :executable_path
+          end
+        end
+        file = $fallback_rpaths
+          .map { |it| File.join(it, file_name) }
+          .find { |it| File.exist? it }
+        return file, :rpath if file
+      when "executable_path"
+        file = File.join(File.dirname(executable), file_name)
+        return file, :executable_path if File.exist? file
+      when "loader_path"
+        file = File.join(File.dirname(lib), file_name)
+        return file, :loader_path if File.exist? file
+      else
+        throw "Unknown @path type"
+    end
+  else
+    return File.absolute_path(path), :absolute
+  end
+
+  puts lib
+  puts path
+  exit
+  return nil
+end
+
+def system_path?(path)
+  path.match(/^\/usr\/lib|^\/System/) != nil
+end
+
+def system_lib?(lib)
+  system_path? File.dirname(lib)
+end
+
+def install_name_tool(exec, action, path1, path2 = nil)
+  args = ["-#{action.to_s}", path1]
+  args << path2 if path2 != nil
+
+  FileUtils.chmod("u+w", exec)
+  out, status = Open3.capture2e("install_name_tool", *args, exec)
+  if status != 0
+    puts out
+    exit status
+  end
+end
+
+def fixup_libs(prog, orig_path)
+  throw "fixup_libs: #{prog} doesn't exist" unless File.exist? prog
+
+  libs = get_load_libs(prog).map { |it| expand_load_path(orig_path, it) }.select { |it| not system_lib? it[0] }
+
+  libs.each do |lib|
+    libpath, libtype = lib
+    if File.basename(libpath) == File.basename(prog)
+      if libtype == :absolute
+        install_name_tool prog, :change, libpath, File.join("@rpath", File.basename(libpath))
+      end
+      next
+    end
+    
+    framework = libpath.match(/(.*).framework/)
+    framework = framework.to_s if framework
+
+    if framework
+      fwlib = libpath.sub(framework + "/", "")
+      fwname = File.basename(framework)
+
+      unless libtype == :rpath
+        install_name_tool prog, :change, libpath, File.join("@rpath", fwname, fwlib)
+      end
+      
+      next if File.exist? File.join(frameworks_dir, fwname)
+      expath, _ = expand_load_path(orig_path, framework)
+      FileUtils.cp_r(expath, frameworks_dir, preserve: true)
+      fixup_libs File.join(frameworks_dir, fwname, fwlib), libpath
+    else
+      libname = File.basename(libpath)
+      dest = File.join(frameworks_dir, libname)
+
+      if libtype == :absolute
+        install_name_tool prog, :change, libpath, File.join("@rpath", libname)
+      end
+
+      next if File.exist? dest
+      expath, _ = expand_load_path(orig_path, libpath)
+      FileUtils.copy expath, frameworks_dir
+      fixup_libs dest, libpath
+    end
+  end
+end
+
+if ARGV[0] == "--dmg"
+  $build_dmg = true
+  ARGV.shift
+end
+
+if ARGV.length != 1
+  puts "Usage: #{Process.argv0} [--dmg] <build-dir>"
+  return
+end
+
+$build_dir = ARGV[0]
+unless File.exist? $build_dir
+  puts "#{$build_dir} doesn't exist"
+end
+
+
+$bundle = File.join($build_dir, "#{$app_name}.app")
+
+unless File.exist? $bundle and File.exist? File.join($build_dir, "CMakeCache.txt")
+  puts "#{$build_dir} doesn't look like a valid build directory"
+  exit 1
+end
+
+File.read(File.join($build_dir, "CMakeCache.txt"))
+  .split("\n")
+  .find { |it| it.match /Qt(.)_DIR:PATH=(.*)/ }
+
+qt_major = $1
+qt_dir = $2
+qt_dir = File.absolute_path("#{qt_dir}/../../..")
+
+$fallback_rpaths << File.join(qt_dir, "lib")
+
+plugin_paths = [
+  File.join(qt_dir, "libexec", "qt#{qt_major}", "plugins"),
+  File.join(qt_dir, "plugins"),
+  File.join(qt_dir, "share", "qt", "plugins")
+]
+
+qt_plugins = plugin_paths.find { |file| File.exist? file }
+
+if qt_plugins == nil
+  puts "Couldn't find Qt plugins, tried looking for:"
+  plugin_paths.each { |path| puts " - #{path}" }
+  exit 1
+end
+
+FileUtils.mkdir_p(frameworks_dir)
+fixup_libs(executable, executable)
+
+bundle_plugins = File.join($bundle, "Contents", "PlugIns")
+
+want_plugins = ["styles/libqmacstyle.dylib", "platforms/libqcocoa.dylib"]
+want_plugins.each do |plug|
+  destdir = File.join(bundle_plugins, File.dirname(plug))
+  FileUtils.mkdir_p(destdir)
+  FileUtils.copy(File.join(qt_plugins, plug), destdir)
+  fixup_libs File.join(bundle_plugins, plug), File.join(qt_plugins, plug)
+end
+
+want_rpath = "@executable_path/../Frameworks"
+exec_rpaths = get_rpaths(executable)
+exec_rpaths.select { |path| path != want_rpath }.each do |path|
+  install_name_tool executable, :delete_rpath, path
+end
+
+unless exec_rpaths.include? want_rpath
+  install_name_tool executable, :add_rpath, want_rpath
+end
+
+exec_rpaths = get_rpaths(executable)
+
+Dir.glob("#{frameworks_dir}/**/Headers").each do |dir|
+  FileUtils.rm_rf dir
+end
+
+if $build_dmg
+    dmg_dir = File.join($build_dir, "dmg")
+    FileUtils.mkdir_p(dmg_dir)
+    FileUtils.cp_r($bundle, dmg_dir, preserve: true)
+    FileUtils.ln_s("/Applications", File.join(dmg_dir, "Applications"))
+
+    `hdiutil create -fs HFS+ -volname melonDS -srcfolder "#{dmg_dir}" -ov -format UDBZ "#{$build_dir}/melonDS.dmg"`
+    FileUtils.rm_rf(dmg_dir)
+end