lib/u3d/installer.rb in u3d-1.0.20 vs lib/u3d/installer.rb in u3d-1.0.21
- old
+ new
@@ -19,10 +19,11 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
## --- END LICENSE BLOCK ---
require 'u3d/utils'
+require 'u3d_core/admin_tools'
require 'u3d_core/core_ext/string'
require 'u3d/installation'
require 'fileutils'
require 'file-tail'
require 'pathname'
@@ -30,36 +31,27 @@
module U3d
DEFAULT_LINUX_INSTALL = '/opt/'.freeze
DEFAULT_MAC_INSTALL = '/'.freeze
DEFAULT_WINDOWS_INSTALL = 'C:/Program Files/'.freeze
UNITY_DIR = "Unity_%<version>s".freeze
+ UNITY_DIR_LONG = "Unity_%<version>s_%<build_number>s".freeze
UNITY_DIR_LINUX = "unity-editor-%<version>s".freeze
+ UNITY_DIR_LINUX_LONG = "unity-editor-%<version>s_%<build_number>s".freeze
class Installer
def self.create
- installer = if Helper.mac?
- MacInstaller.new
- elsif Helper.linux?
- LinuxInstaller.new
- else
- WindowsInstaller.new
- end
- sanitize_installs(installer)
- installer
+ if Helper.mac?
+ MacInstaller.new
+ elsif Helper.linux?
+ LinuxInstaller.new
+ else
+ WindowsInstaller.new
+ end
end
def self.sanitize_installs(installer)
- return unless UI.interactive? || Helper.test?
- unclean = []
- installer.installed.each { |unity| unclean << unity unless unity.clean_install? }
- return if unclean.empty?
- UI.important("u3d can optionally standardize the existing Unity installation names and locations.")
- UI.important("Check the documentation for more information:")
- UI.important("** https://github.com/DragonBox/u3d/blob/master/README.md#default-installation-paths **")
- unclean.each { |unity| installer.sanitize_install(unity, dry_run: true) }
- return unless UI.confirm("#{unclean.count} Unity installation(s) will be moved. Proceed??")
- unclean.each { |unity| installer.sanitize_install(unity) }
+ installer.sanitize_installs
end
def self.install_modules(files, version, installation_path: nil)
installer = Installer.create
files.each do |name, file, info|
@@ -73,38 +65,55 @@
installer = Installer.create
installer.uninstall(unity: unity)
end
end
+ class BaseInstaller
+ def sanitize_installs
+ return unless UI.interactive? || Helper.test?
+ unclean = []
+ installed.each { |unity| unclean << unity unless unity.clean_install? }
+ return if unclean.empty?
+ UI.important("u3d can optionally standardize the existing Unity installation names and locations.")
+ UI.important("Check the documentation for more information:")
+ UI.important("** https://github.com/DragonBox/u3d/blob/master/README.md#default-installation-paths **")
+ unclean.each { |unity| sanitize_install(unity, dry_run: true) }
+ return unless UI.confirm("#{unclean.count} Unity installation(s) will be moved. Proceed??")
+ unclean.each { |unity| sanitize_install(unity) }
+ end
+
+ def installed_sorted_by_versions
+ list = installed
+ return [] if list.empty?
+ # version -> installations
+ arraym = list.map { |a| [a.version, a] }
+ map = Hash[*arraym.flatten]
+ # sorted versions
+ vcomparators = map.keys.map { |k| UnityVersionComparator.new(k) }
+ sorted_keys = vcomparators.sort.map { |v| v.version.to_s }
+ sorted_keys.map { |k| map[k] }
+ end
+ end
+
+ # deprecated
class CommonInstaller
def self.sanitize_install(source_path, new_path, command, dry_run: false)
- if source_path == new_path
- UI.important "sanitize_install does nothing if the path won't change (#{source_path})"
- return
- end
-
- if dry_run
- UI.message "'#{source_path}' would move to '#{new_path}'"
- else
- UI.important "Moving '#{source_path}' to '#{new_path}'..."
- U3dCore::CommandExecutor.execute(command: command, admin: true)
- UI.success "Successfully moved '#{source_path}' to '#{new_path}'"
- end
- rescue StandardError => e
- UI.error "Unable to move '#{source_path}' to '#{new_path}': #{e}"
+ UI.deprecated("Use U3dCore::AdminTools.move_files")
+ U3dCore::AdminTools.move_file(source_path, new_path, command, dry_run: dry_run)
end
end
- class MacInstaller
- def sanitize_install(unity, dry_run: false)
+ class MacInstaller < BaseInstaller
+ def sanitize_install(unity, long: false, dry_run: false)
source_path = unity.root_path
parent = File.expand_path('..', source_path)
- new_path = File.join(parent, format(UNITY_DIR, version: unity.version))
+ dir_name = format(long ? UNITY_DIR_LONG : UNITY_DIR,
+ version: unity.version, build_number: unity.build_number)
+ new_path = File.join(parent, dir_name)
- command = "mv #{source_path.shellescape} #{new_path.shellescape}"
-
- CommonInstaller.sanitize_install(source_path, new_path, command, dry_run: dry_run)
+ moved = U3dCore::AdminTools.move_os_file(:mac, source_path, new_path, dry_run: dry_run)
+ unity.root_path = new_path if moved && !dry_run
end
def installed
paths = (list_installed_paths + spotlight_installed_paths).uniq
paths.map { |path| MacInstallation.new(root_path: path) }
@@ -190,39 +199,45 @@
UI.verbose "Found spotlight_installed_paths: #{paths}"
paths
end
end
- class LinuxInstaller
- def sanitize_install(unity, dry_run: false)
+ # rubocop:disable ClassLength
+ class LinuxInstaller < BaseInstaller
+ def sanitize_install(unity, long: false, dry_run: false)
source_path = File.expand_path(unity.root_path)
parent = File.expand_path('..', source_path)
- new_path = File.join(parent, format(UNITY_DIR_LINUX, version: unity.version))
+ dir_name = format(long ? UNITY_DIR_LINUX_LONG : UNITY_DIR_LINUX,
+ version: unity.version, build_number: unity.build_number)
+ new_path = File.join(parent, dir_name)
- command = "mv #{source_path.shellescape} #{new_path.shellescape}"
-
- CommonInstaller.sanitize_install(source_path, new_path, command, dry_run: dry_run)
+ moved = U3dCore::AdminTools.move_os_file(:linux, source_path, new_path, dry_run: dry_run)
+ unity.root_path = new_path if moved && !dry_run
end
def installed
paths = (list_installed_paths + debian_installed_paths).uniq
paths.map { |path| LinuxInstallation.new(root_path: path) }
end
- # rubocop:disable UnusedMethodArgument
+ # rubocop:disable UnusedMethodArgument, PerceivedComplexity
def install(file_path, version, installation_path: nil, info: {})
- # rubocop:enable UnusedMethodArgument
+ # rubocop:enable UnusedMethodArgument, PerceivedComplexity
extension = File.extname(file_path)
- raise "Installation of #{extension} files is not supported on Linux" unless ['.sh', '.xz'].include? extension
+ raise "Installation of #{extension} files is not supported on Linux" unless ['.sh', '.xz', '.pkg'].include? extension
if extension == '.sh'
path = installation_path || DEFAULT_LINUX_INSTALL
install_sh(file_path, installation_path: path)
elsif extension == '.xz'
new_path = File.join(DEFAULT_LINUX_INSTALL, format(UNITY_DIR_LINUX, version: version))
path = installation_path || new_path
install_xz(file_path, installation_path: path)
+ elsif extension == '.pkg'
+ new_path = File.join(DEFAULT_LINUX_INSTALL, format(UNITY_DIR_LINUX, version: version))
+ path = installation_path || new_path
+ install_pkg(file_path, installation_path: path)
end
# Forces sanitation for installation of 'weird' versions eg 5.6.1xf1Linux
unity = installed.select { |u| u.version == version }.first
if unity
@@ -260,10 +275,37 @@
UI.error "Failed to install xz file #{file} at #{installation_path}: #{e}"
else
UI.success 'Installation successful'
end
+ def install_pkg(file, installation_path: nil)
+ raise 'Missing installation_path' unless installation_path
+ raise 'Only able to install pkg on top of existing Unity installs' unless File.exist? installation_path
+ raise 'Missing 7z' if `which 7z`.empty?
+
+ Dir.mktmpdir do |tmp_dir|
+ UI.verbose "Working in tmp dir #{tmp_dir}"
+
+ command = "7z -aos -o#{tmp_dir.shellescape} e #{file.shellescape}"
+ U3dCore::CommandExecutor.execute(command: command)
+
+ target_location = pkg_install_path(installation_path, "#{tmp_dir}/PackageInfo")
+
+ # raise "Path for #{target_location} already exists" if path File.exist? target_location
+
+ command = "cd #{target_location.shellescape}; gzip -dc #{tmp_dir}/Payload | cpio -i '*' -"
+ command = "mkdir -p #{target_location.shellescape}; #{command}" # unless File.directory? installation_path
+
+ U3dCore::CommandExecutor.execute(command: command, admin: true)
+ end
+ rescue StandardError => e
+ UI.verbose(e.backtrace.join("\n"))
+ UI.error "Failed to install pkg file #{file} at #{installation_path}: #{e}"
+ else
+ UI.success 'Installation successful'
+ end
+
def uninstall(unity: nil)
UI.verbose("Uninstalling Unity at '#{unity.root_path}'...")
command = "rm -r #{unity.root_path}"
U3dCore::CommandExecutor.execute(command: command, admin: true)
rescue StandardError => e
@@ -272,10 +314,31 @@
UI.success "Successfully uninstalled '#{unity.root_path}'"
end
private
+ def pkg_install_path(unity_root_path, pinfo_path)
+ raise "PackageInfo not found under #{pinfo_path}" unless File.exist? pinfo_path
+ pinfo = File.read(pinfo_path)
+ require 'rexml/document'
+ d = REXML::Document.new(pinfo)
+ identifier = d.root.attributes['identifier']
+
+ case identifier
+ when 'com.unity3d.Documentation'
+ "#{unity_root_path}/Editor/Data/"
+ when 'com.unity3d.StandardAssets'
+ "#{unity_root_path}/Editor/Standard Assets/"
+ when 'com.unity3d.ExampleProject'
+ unity_root_path
+ else
+ install_location = d.root.attributes['install-location']
+ raise "Not sure how to install this module with identifier #{identifier} install-location: #{install_location}" unless install_location.start_with? '/Applications/Unity/'
+ install_location.gsub(%(\/Applications\/Unity), "#{unity_root_path}/Editor/Data")
+ end
+ end
+
def list_installed_paths
find = File.join(DEFAULT_LINUX_INSTALL, 'unity-editor-*', 'Editor')
paths = Dir[find]
paths = paths.map { |u| Pathname.new(u).parent.to_s }
UI.verbose "Found list_installed_paths: #{paths}"
@@ -288,23 +351,22 @@
paths = paths.map { |u| Pathname.new(u).parent.to_s }
UI.verbose "Found debian_installed_paths: #{paths}"
paths
end
end
+ # rubocop:enable ClassLength
- class WindowsInstaller
- def sanitize_install(unity, dry_run: false)
+ class WindowsInstaller < BaseInstaller
+ def sanitize_install(unity, long: false, dry_run: false)
source_path = File.expand_path(unity.root_path)
parent = File.expand_path('..', source_path)
- new_path = File.join(parent, format(UNITY_DIR, version: unity.version))
+ dir_name = format(long ? UNITY_DIR_LONG : UNITY_DIR,
+ version: unity.version, build_number: unity.build_number)
+ new_path = File.join(parent, dir_name)
- source_path.tr!('/', '\\')
- new_path.tr!('/', '\\')
-
- command = "move #{source_path.argescape} #{new_path.argescape}"
-
- CommonInstaller.sanitize_install(source_path, new_path, command, dry_run: dry_run)
+ moved = U3dCore::AdminTools.move_os_file(:win, source_path, new_path, dry_run: dry_run)
+ unity.root_path = new_path if moved && !dry_run
end
def installed
find = File.join(DEFAULT_WINDOWS_INSTALL, 'Unity*', 'Editor', 'Uninstall.exe')
Dir[find].map { |path| WindowsInstallation.new(root_path: File.expand_path('../..', path)) }
@@ -321,26 +383,26 @@
)
end
def install_exe(file_path, installation_path: nil, info: {})
installation_path ||= DEFAULT_WINDOWS_INSTALL
- final_path = Utils.windows_path(installation_path)
+ final_path = U3dCore::Helper.windows_path(installation_path)
Utils.ensure_dir(final_path)
begin
command = nil
if info['cmd']
command = info['cmd']
if /msiexec/ =~ command
- command.sub!(/{FILENAME}/, '"' + Utils.windows_path(file_path) + '"')
+ command.sub!(/{FILENAME}/, '"' + U3dCore::Helper.windows_path(file_path) + '"')
else
- command.sub!(/{FILENAME}/, file_path)
+ command.sub!(/{FILENAME}/, file_path.argescape)
end
command.sub!(/{INSTDIR}/, final_path)
command.sub!(/{DOCDIR}/, final_path)
command.sub!(/{MODULEDIR}/, final_path)
command.sub!(%r{\/D=}, '/S /D=') unless %r{\/S} =~ command
end
- command ||= file_path.to_s
+ command ||= file_path.argescape
U3dCore::CommandExecutor.execute(command: command, admin: true)
rescue StandardError => e
UI.error "Failed to install package at #{file_path}: #{e}"
else
UI.success "Successfully installed #{info['title']}"