lib/u3d/installer.rb in u3d-0.9.3 vs lib/u3d/installer.rb in u3d-0.9.4

- old
+ new

@@ -19,203 +19,86 @@ # 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/installation' require 'fileutils' require 'file-tail' -# Mac specific only right now module U3d DEFAULT_LINUX_INSTALL = '/opt/'.freeze DEFAULT_MAC_INSTALL = '/'.freeze DEFAULT_WINDOWS_INSTALL = 'C:/Program Files/'.freeze UNITY_DIR = "Unity_%s".freeze UNITY_DIR_LINUX = "unity-editor-%s".freeze - UNITY_DIR_CHECK = /Unity_\d+\.\d+\.\d+[a-z]\d+/ - UNITY_DIR_CHECK_LINUX = /unity-editor-\d+\.\d+\.\d+[a-z]\d+\z/ - class Installation - def self.create(path: nil) - if Helper.mac? - MacInstallation.new path - elsif Helper.linux? - LinuxInstallation.new path - else - WindowsInstallation.new path - end - end - end - - class MacInstallation < Installation - attr_reader :path - - require 'plist' - - def initialize(path: nil) - @path = path - end - - def version - plist['CFBundleVersion'] - end - - def default_log_file - "#{ENV['HOME']}/Library/Logs/Unity/Editor.log" - end - - def exe_path - "#{path}/Contents/MacOS/Unity" - end - - def packages - if Utils.parse_unity_version(version)[0].to_i <= 4 - # Unity < 5 doesn't have packages - return [] - end - fpath = File.expand_path('../PlaybackEngines', path) - raise "Unity installation does not seem correct. Couldn't locate PlaybackEngines." unless Dir.exist? fpath - Dir.entries(fpath).select { |e| File.directory?(File.join(fpath, e)) && !(e == '.' || e == '..') } - end - - def clean_install? - path =~ UNITY_DIR_CHECK - end - - private - - def plist - @plist ||= Plist.parse_xml("#{@path}/Contents/Info.plist") - end - end - - class LinuxInstallation < Installation - attr_reader :path - - def initialize(path: nil) - @path = path - end - - def version - # I don't find an easy way to extract the version on Linux - require 'rexml/document' - fpath = "#{path}/Editor/Data/PlaybackEngines/LinuxStandaloneSupport/ivy.xml" - raise "Couldn't find file #{fpath}" unless File.exist? fpath - doc = REXML::Document.new(File.read(fpath)) - version = REXML::XPath.first(doc, 'ivy-module/info/@e:unityVersion').value - if m = version.match(/^(.*)x(.*)Linux$/) - version = "#{m[1]}#{m[2]}" - end - version - end - - def default_log_file - "#{ENV['HOME']}/.config/unity3d/Editor.log" - end - - def exe_path - "#{path}/Editor/Unity" - end - - def packages - false - end - - def clean_install? - path =~ UNITY_DIR_CHECK_LINUX - end - end - - class WindowsInstallation < Installation - attr_reader :path - - def initialize(path: nil) - @path = path - end - - def version - require 'rexml/document' - fpath = "#{path}/Editor/Data/PlaybackEngines/windowsstandalonesupport/ivy.xml" - raise "Couldn't find file #{fpath}" unless File.exist? fpath - doc = REXML::Document.new(File.read(fpath)) - version = REXML::XPath.first(doc, 'ivy-module/info/@e:unityVersion').value - - version - end - - def default_log_file - if @logfile.nil? - begin - loc_appdata = Utils.windows_local_appdata - log_dir = File.expand_path('Unity/Editor/', loc_appdata) - UI.important "Log directory (#{log_dir}) does not exist" unless Dir.exist? log_dir - @logfile = File.expand_path('Editor.log', log_dir) - rescue RuntimeError => ex - UI.error "Unable to retrieve the editor logfile: #{ex}" - end - end - @logfile - end - - def exe_path - File.join(@path, 'Editor', 'Unity.exe') - end - - def packages - # Unity prior to Unity5 did not have package - return [] if Utils.parse_unity_version(version)[0].to_i <= 4 - fpath = "#{path}/Editor/Data/PlaybackEngines/" - raise "Unity installation does not seem correct. Couldn't locate PlaybackEngines." unless Dir.exist? fpath - Dir.entries(fpath).select { |e| File.directory?(File.join(fpath, e)) && !(e == '.' || e == '..') } - end - - def clean_install? - path =~ UNITY_DIR_CHECK - end - end - class Installer def self.create installer = if Helper.mac? MacInstaller.new elsif Helper.linux? LinuxInstaller.new else WindowsInstaller.new end - if UI.interactive? - unclean = [] - installer.installed.each { |unity| unclean << unity unless unity.clean_install? } - if !unclean.empty? && UI.confirm("#{unclean.count} Unity installation should be moved. Proceed?") - unclean.each { |unity| installer.sanitize_install(unity) } - end - end + sanitize_installs(installer) installer 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 Unity3d 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) } + end + def self.install_modules(files, version, installation_path: nil) installer = Installer.create files.each do |name, file, info| UI.verbose "Installing #{name}#{info['mandatory'] ? ' (mandatory package)' : ''}, with file #{file}" installer.install(file, version, installation_path: installation_path, info: info) end end end + 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 => e + UI.error "Unable to move '#{source_path}' to '#{new_path}': #{e}" + end + end + class MacInstaller - def sanitize_install(unity) + def sanitize_install(unity, dry_run: false) source_path = File.expand_path('..', unity.path) parent = File.expand_path('..', source_path) new_path = File.join(parent, UNITY_DIR % unity.version) - UI.important "Moving #{source_path} to #{new_path}..." - source_path = "\"#{source_path}\"" if source_path =~ / / - new_path = "\"#{new_path}\"" if new_path =~ / / - U3dCore::CommandExecutor.execute(command: "mv #{source_path} #{new_path}", admin: true) - rescue => e - UI.error "Unable to move #{source_path} to #{new_path}: #{e}" - else - UI.success "Successfully moved #{source_path} to #{new_path}" + source_path = source_path.shellescape + new_path = new_path.shellescape + + command = "mv #{source_path} #{new_path}" + + CommonInstaller.sanitize_install(source_path, new_path, command, dry_run: dry_run) end def installed unless (`mdutil -s /` =~ /disabled/).nil? $stderr.puts 'Please enable Spotlight indexing for /Applications.' @@ -232,11 +115,13 @@ # sorting should take into account stable/patch etc versions.sort! { |x, y| x.version <=> y.version } end + # rubocop:disable UnusedMethodArgument def install(file_path, version, installation_path: nil, info: {}) + # rubocop:enable UnusedMethodArgument extension = File.extname(file_path) raise "Installation of #{extension} files is not supported on Mac" if extension != '.pkg' path = installation_path || DEFAULT_MAC_INSTALL install_pkg( file_path, @@ -247,17 +132,19 @@ def install_pkg(file_path, version: nil, target_path: nil) target_path ||= DEFAULT_MAC_INSTALL command = "installer -pkg #{file_path.shellescape} -target #{target_path.shellescape}" unity = installed.find { |u| u.version == version } + temp_path = File.join(target_path, 'Applications', 'Unity') if unity.nil? UI.verbose "No Unity install for version #{version} was found" U3dCore::CommandExecutor.execute(command: command, admin: true) + destination_path = File.join(target_path, 'Applications', UNITY_DIR % version) + FileUtils.mv temp_path, destination_path else begin path = File.expand_path('..', unity.path) - temp_path = File.join(target_path, 'Applications', 'Unity') move_to_temp = (temp_path != path) if move_to_temp UI.verbose "Temporary switching location of #{path} to #{temp_path} for installation purpose" FileUtils.mv path, temp_path end @@ -272,33 +159,33 @@ UI.success "Successfully installed package from #{file_path}" end end class LinuxInstaller - def sanitize_install(unity) + def sanitize_install(unity, dry_run: false) source_path = File.expand_path(unity.path) parent = File.expand_path('..', source_path) new_path = File.join(parent, UNITY_DIR_LINUX % unity.version) - UI.important "Moving #{source_path} to #{new_path}..." - source_path = "\"#{source_path}\"" if source_path =~ / / - new_path = "\"#{new_path}\"" if new_path =~ / / - U3dCore::CommandExecutor.execute(command: "mv #{source_path} #{new_path}", admin: true) - rescue => e - UI.error "Unable to move #{source_path} to #{new_path}: #{e}" - else - UI.success "Successfully moved #{source_path} to #{new_path}" + source_path = source_path.shellescape + new_path = new_path.shellescape + + command = "mv #{source_path} #{new_path}" + + CommonInstaller.sanitize_install(source_path, new_path, command, dry_run: dry_run) end def installed find = File.join(DEFAULT_LINUX_INSTALL, 'unity-editor-*') versions = Dir[find].map { |path| LinuxInstallation.new(path: path) } # sorting should take into account stable/patch etc versions.sort! { |x, y| x.version <=> y.version } end + # rubocop:disable UnusedMethodArgument def install(file_path, version, installation_path: nil, info: {}) + # rubocop:enable UnusedMethodArgument extension = File.extname(file_path) raise "Installation of #{extension} files is not supported on Linux" if extension != '.sh' path = installation_path || DEFAULT_LINUX_INSTALL install_sh( file_path, @@ -325,24 +212,23 @@ UI.success 'Installation successful' end end class WindowsInstaller - def sanitize_install(unity) + def sanitize_install(unity, dry_run: false) source_path = File.expand_path(unity.path) parent = File.expand_path('..', source_path) new_path = File.join(parent, UNITY_DIR % unity.version) - UI.important "Moving #{source_path} to #{new_path}..." + source_path.tr!('/', '\\') new_path.tr!('/', '\\') source_path = "\"" + source_path + "\"" if source_path =~ / / new_path = "\"" + new_path + "\"" if new_path =~ / / - U3dCore::CommandExecutor.execute(command: "move #{source_path} #{new_path}", admin: true) - rescue => e - UI.error "Unable to move #{source_path} to #{new_path}: #{e}" - else - UI.success "Successfully moved #{source_path} to #{new_path}" + + command = "move #{source_path} #{new_path}" + + CommonInstaller.sanitize_install(source_path, new_path, command, dry_run: dry_run) end def installed find = File.join(DEFAULT_WINDOWS_INSTALL, 'Unity*', 'Editor', 'Uninstall.exe') versions = Dir[find].map { |path| WindowsInstallation.new(path: File.expand_path('../..', path)) } @@ -372,37 +258,17 @@ command = info['cmd'] command.sub!(/{FILENAME}/, file_path) command.sub!(/{INSTDIR}/, final_path) command.sub!(/{DOCDIR}/, final_path) command.sub!(/{MODULEDIR}/, final_path) - command.sub!(/\/D=/, '/S /D=') unless /\/S/ =~ command + command.sub!(%r{\/D=}, '/S /D=') unless %r{\/S} =~ command end command ||= file_path.to_s U3dCore::CommandExecutor.execute(command: command, admin: true) rescue => e UI.error "Failed to install exe at #{file_path}: #{e}" else UI.success "Successfully installed #{info['title']}" end - end - end - - class UnityProject - attr_reader :path - - def initialize(path) - @path = path - end - - def exist? - Dir.exist?("#{@path}/Assets") && Dir.exist?("#{@path}/ProjectSettings") - end - - def editor_version - require 'yaml' - yaml = YAML.load(File.read("#{@path}/ProjectSettings/ProjectVersion.txt")) - version = yaml['m_EditorVersion'] - version.gsub!(/(\d+\.\d+\.\d+)(?:x)?(\w\d+)(?:Linux)?/, '\1\2') if Helper.linux? - version end end end