u3d 0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.licenses.json +19 -0
- data/.rspec +1 -0
- data/.rubocop.yml +43 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +98 -0
- data/LICENSE +21 -0
- data/LICENSE.fastlane +22 -0
- data/LOG_RULES.md +170 -0
- data/README.md +72 -0
- data/Rakefile +28 -0
- data/TODO.md +15 -0
- data/build.sh +5 -0
- data/config/log_rules.json +230 -0
- data/examples/Example1/.gitignore +19 -0
- data/examples/Example1/Assets/Editor.meta +9 -0
- data/examples/Example1/Assets/Editor/EditorRun.cs +23 -0
- data/examples/Example1/Assets/Editor/EditorRun.cs.meta +12 -0
- data/examples/Example1/Assets/Editor/FileSystemUtil.cs +26 -0
- data/examples/Example1/Assets/Editor/FileSystemUtil.cs.meta +12 -0
- data/examples/Example1/Assets/Scene1.unity +264 -0
- data/examples/Example1/Assets/Scene1.unity.meta +8 -0
- data/examples/Example1/Gemfile +8 -0
- data/examples/Example1/Gemfile.lock +165 -0
- data/examples/Example1/ProjectSettings/AudioManager.asset +16 -0
- data/examples/Example1/ProjectSettings/ClusterInputManager.asset +6 -0
- data/examples/Example1/ProjectSettings/DynamicsManager.asset +18 -0
- data/examples/Example1/ProjectSettings/EditorBuildSettings.asset +7 -0
- data/examples/Example1/ProjectSettings/EditorSettings.asset +14 -0
- data/examples/Example1/ProjectSettings/GraphicsSettings.asset +61 -0
- data/examples/Example1/ProjectSettings/InputManager.asset +295 -0
- data/examples/Example1/ProjectSettings/NavMeshAreas.asset +89 -0
- data/examples/Example1/ProjectSettings/NetworkManager.asset +8 -0
- data/examples/Example1/ProjectSettings/Physics2DSettings.asset +35 -0
- data/examples/Example1/ProjectSettings/ProjectSettings.asset +591 -0
- data/examples/Example1/ProjectSettings/ProjectVersion.txt +1 -0
- data/examples/Example1/ProjectSettings/QualitySettings.asset +180 -0
- data/examples/Example1/ProjectSettings/TagManager.asset +43 -0
- data/examples/Example1/ProjectSettings/TimeManager.asset +9 -0
- data/examples/Example1/ProjectSettings/UnityConnectSettings.asset +32 -0
- data/examples/Example1/README.md +5 -0
- data/examples/Example1/Rakefile +5 -0
- data/examples/Example1/fastlane/Fastfile +4 -0
- data/examples/Example1/fastlane/Pluginfile +1 -0
- data/examples/Example1/run.sh +1 -0
- data/examples/Example2/.gitignore +20 -0
- data/examples/Example2/Assets/Editor.meta +9 -0
- data/examples/Example2/Assets/Editor/EditorRun.cs +33 -0
- data/examples/Example2/Assets/Editor/EditorRun.cs.meta +12 -0
- data/examples/Example2/Assets/Editor/PostprocessBuildPlayer.cs +92 -0
- data/examples/Example2/Assets/Editor/PostprocessBuildPlayer.cs.meta +8 -0
- data/examples/Example2/Assets/Editor/PostprocessBuildPlayer_log.sh +31 -0
- data/examples/Example2/Assets/Editor/PostprocessBuildPlayer_log.sh.meta +8 -0
- data/examples/Example2/Assets/Editor/SimpleBuildSetup.cs +20 -0
- data/examples/Example2/Assets/Editor/SimpleBuildSetup.cs.meta +12 -0
- data/examples/Example2/Assets/Scene.unity +278 -0
- data/examples/Example2/Assets/Scene.unity.meta +8 -0
- data/examples/Example2/Gemfile +8 -0
- data/examples/Example2/Gemfile.lock +165 -0
- data/examples/Example2/ProjectSettings/AudioManager.asset +17 -0
- data/examples/Example2/ProjectSettings/ClusterInputManager.asset +6 -0
- data/examples/Example2/ProjectSettings/DynamicsManager.asset +19 -0
- data/examples/Example2/ProjectSettings/EditorBuildSettings.asset +10 -0
- data/examples/Example2/ProjectSettings/EditorSettings.asset +14 -0
- data/examples/Example2/ProjectSettings/GraphicsSettings.asset +63 -0
- data/examples/Example2/ProjectSettings/InputManager.asset +295 -0
- data/examples/Example2/ProjectSettings/NavMeshAreas.asset +89 -0
- data/examples/Example2/ProjectSettings/NetworkManager.asset +8 -0
- data/examples/Example2/ProjectSettings/Physics2DSettings.asset +36 -0
- data/examples/Example2/ProjectSettings/ProjectSettings.asset +591 -0
- data/examples/Example2/ProjectSettings/ProjectVersion.txt +1 -0
- data/examples/Example2/ProjectSettings/QualitySettings.asset +193 -0
- data/examples/Example2/ProjectSettings/TagManager.asset +43 -0
- data/examples/Example2/ProjectSettings/TimeManager.asset +9 -0
- data/examples/Example2/ProjectSettings/UnityConnectSettings.asset +34 -0
- data/examples/Example2/README.md +10 -0
- data/examples/Example2/fastlane/Fastfile +4 -0
- data/examples/Example2/fastlane/Pluginfile +1 -0
- data/exe/u3d +7 -0
- data/fastlane-plugin-u3d/.gitignore +10 -0
- data/fastlane-plugin-u3d/.licenses.json +9 -0
- data/fastlane-plugin-u3d/.rspec +3 -0
- data/fastlane-plugin-u3d/.rubocop.yml +253 -0
- data/fastlane-plugin-u3d/.travis.yml +4 -0
- data/fastlane-plugin-u3d/Gemfile +6 -0
- data/fastlane-plugin-u3d/LICENSE +21 -0
- data/fastlane-plugin-u3d/README.md +52 -0
- data/fastlane-plugin-u3d/Rakefile +9 -0
- data/fastlane-plugin-u3d/circle.yml +9 -0
- data/fastlane-plugin-u3d/fastlane-plugin-u3d.gemspec +31 -0
- data/fastlane-plugin-u3d/fastlane/Fastfile +3 -0
- data/fastlane-plugin-u3d/fastlane/Pluginfile +1 -0
- data/fastlane-plugin-u3d/lib/fastlane/plugin/u3d.rb +38 -0
- data/fastlane-plugin-u3d/lib/fastlane/plugin/u3d/actions/u3d_action.rb +80 -0
- data/fastlane-plugin-u3d/lib/fastlane/plugin/u3d/helper/u3d_helper.rb +34 -0
- data/fastlane-plugin-u3d/lib/fastlane/plugin/u3d/version.rb +27 -0
- data/fastlane-plugin-u3d/spec/spec_helper.rb +32 -0
- data/lib/u3d.rb +33 -0
- data/lib/u3d/cache.rb +120 -0
- data/lib/u3d/commands.rb +307 -0
- data/lib/u3d/commands_generator.rb +163 -0
- data/lib/u3d/downloader.rb +363 -0
- data/lib/u3d/iniparser.rb +83 -0
- data/lib/u3d/installer.rb +445 -0
- data/lib/u3d/log_analyzer.rb +221 -0
- data/lib/u3d/unity_version_number.rb +71 -0
- data/lib/u3d/unity_versions.rb +207 -0
- data/lib/u3d/utils.rb +121 -0
- data/lib/u3d/version.rb +31 -0
- data/lib/u3d_core.rb +30 -0
- data/lib/u3d_core/command_executor.rb +134 -0
- data/lib/u3d_core/command_runner.rb +93 -0
- data/lib/u3d_core/credentials.rb +116 -0
- data/lib/u3d_core/globals.rb +84 -0
- data/lib/u3d_core/helper.rb +149 -0
- data/lib/u3d_core/ui/disable_colors.rb +40 -0
- data/lib/u3d_core/ui/implementations/shell.rb +157 -0
- data/lib/u3d_core/ui/interface.rb +182 -0
- data/lib/u3d_core/ui/ui.rb +49 -0
- data/local_gem_install.sh +6 -0
- data/scripts/be +14 -0
- data/u3d.gemspec +41 -0
- metadata +388 -0
@@ -0,0 +1,363 @@
|
|
1
|
+
## --- BEGIN LICENSE BLOCK ---
|
2
|
+
# Copyright (c) 2016-present WeWantToKnow AS
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in all
|
12
|
+
# copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
20
|
+
# SOFTWARE.
|
21
|
+
## --- END LICENSE BLOCK ---
|
22
|
+
|
23
|
+
require 'net/http'
|
24
|
+
require 'u3d/iniparser'
|
25
|
+
require 'u3d/utils'
|
26
|
+
|
27
|
+
module U3d
|
28
|
+
# Take care of downloading files and packages
|
29
|
+
module Downloader
|
30
|
+
# Name of the directory for the package downloading
|
31
|
+
DOWNLOAD_DIRECTORY = 'Unity_Packages'.freeze
|
32
|
+
# Path to the directory for the package downloading
|
33
|
+
DOWNLOAD_PATH = "#{ENV['HOME']}/Downloads".freeze
|
34
|
+
# Regex to get the name of a package out of its file name
|
35
|
+
UNITY_MODULE_FILE_REGEX = %r{\/([\w\-_\.\+]+\.(?:pkg|exe|zip|sh|deb))}
|
36
|
+
|
37
|
+
class << self
|
38
|
+
def hash_validation(expected: nil, actual: nil)
|
39
|
+
if expected
|
40
|
+
if expected != actual
|
41
|
+
UI.verbose "Expected hash is #{expected}, file hash is #{actual}"
|
42
|
+
UI.important 'File looks corrupted (wrong hash)'
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
else
|
46
|
+
UI.verbose 'No hash validation available. File is assumed correct but may not be.'
|
47
|
+
end
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
def size_validation(expected: nil, actual: nil)
|
52
|
+
if expected
|
53
|
+
if expected != actual
|
54
|
+
UI.verbose "Expected size is #{expected}, file size is #{actual}"
|
55
|
+
UI.important 'File looks corrupted (wrong size)'
|
56
|
+
return false
|
57
|
+
end
|
58
|
+
else
|
59
|
+
UI.verbose 'No size validation available. File is assumed correct but may not be.'
|
60
|
+
end
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
def download_package(path, url, size: nil)
|
65
|
+
File.open(path, 'wb') do |f|
|
66
|
+
uri = URI(url)
|
67
|
+
current = 0
|
68
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
69
|
+
request = Net::HTTP::Get.new uri
|
70
|
+
http.request request do |response|
|
71
|
+
begin
|
72
|
+
size ||= Integer(response['Content-Length'])
|
73
|
+
rescue ArgumentError
|
74
|
+
UI.verbose 'Unable to get length of file in download'
|
75
|
+
end
|
76
|
+
started_at = Time.now.to_i - 1
|
77
|
+
response.read_body do |segment|
|
78
|
+
f.write(segment)
|
79
|
+
current += segment.length
|
80
|
+
next unless UI.interactive?
|
81
|
+
if size
|
82
|
+
Utils.print_progress(current, size, started_at)
|
83
|
+
else
|
84
|
+
Utils.print_progress_nosize(current, started_at)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
print "\n" if UI.interactive?
|
90
|
+
end
|
91
|
+
rescue Interrupt => e
|
92
|
+
# Ensure that the file is deleted if download is aborted
|
93
|
+
File.delete path
|
94
|
+
raise e
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class MacDownloader
|
99
|
+
class << self
|
100
|
+
# Downloads all packages available for given version
|
101
|
+
def download_all(version, cached_versions)
|
102
|
+
if cached_versions[version].nil?
|
103
|
+
UI.error "No version #{version} was found in cache. It might need updating."
|
104
|
+
return nil
|
105
|
+
end
|
106
|
+
files = []
|
107
|
+
ini_file = INIparser.load_ini(version, cached_versions)
|
108
|
+
ini_file.keys.each do |k|
|
109
|
+
result = download_specific(k, version, cached_versions)
|
110
|
+
files << [k, result[0], result[1]] unless result.nil?
|
111
|
+
end
|
112
|
+
files
|
113
|
+
end
|
114
|
+
|
115
|
+
# Downloads a specific package for given version
|
116
|
+
def download_specific(package, version, cached_versions)
|
117
|
+
if cached_versions[version].nil?
|
118
|
+
UI.error "No version #{version} was found in cache. It might need updating."
|
119
|
+
return nil
|
120
|
+
end
|
121
|
+
|
122
|
+
ini_file = INIparser.load_ini(version, cached_versions)
|
123
|
+
if ini_file[package].empty?
|
124
|
+
UI.error "No package \"#{package}\" was found for version #{version}."
|
125
|
+
return nil
|
126
|
+
end
|
127
|
+
|
128
|
+
url = cached_versions[version]
|
129
|
+
dir = File.join(DOWNLOAD_PATH, DOWNLOAD_DIRECTORY, version)
|
130
|
+
Utils.ensure_dir(dir)
|
131
|
+
return [get_package(package, ini_file, dir, url), ini_file[package]]
|
132
|
+
end
|
133
|
+
|
134
|
+
private #---------------------------------------------------------------
|
135
|
+
|
136
|
+
def get_package(name, ini_file, main_dir, base_url)
|
137
|
+
file_name = UNITY_MODULE_FILE_REGEX.match(ini_file[name]['url'])[1]
|
138
|
+
file_path = File.expand_path(file_name, main_dir)
|
139
|
+
|
140
|
+
# Check if file already exists and validate it
|
141
|
+
if File.file?(file_path)
|
142
|
+
if Downloader.size_validation(expected: ini_file[name]['size'], actual: File.size(file_path)) &&
|
143
|
+
Downloader.hash_validation(expected: ini_file[name]['md5'], actual: Utils.hashfile(file_path))
|
144
|
+
UI.important "#{name.capitalize} already downloaded at #{file_path}"
|
145
|
+
return file_path
|
146
|
+
else
|
147
|
+
UI.verbose "Deleting existing file at #{file_path}"
|
148
|
+
File.delete(file_path)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Download file
|
153
|
+
url = base_url + ini_file[name]['url']
|
154
|
+
UI.header "Downloading #{name}"
|
155
|
+
UI.verbose 'Downloading from ' + url.to_s.cyan.underline
|
156
|
+
Downloader.download_package(file_path, url, size: ini_file[name]['size'])
|
157
|
+
|
158
|
+
# Validation download
|
159
|
+
if Downloader.size_validation(expected: ini_file[name]['size'], actual: File.size(file_path)) &&
|
160
|
+
Downloader.hash_validation(expected: ini_file[name]['md5'], actual: Utils.hashfile(file_path))
|
161
|
+
UI.success "Successfully downloaded #{name}."
|
162
|
+
else
|
163
|
+
File.delete(file_path)
|
164
|
+
raise 'Download failed: file is corrupted, deleting it.'
|
165
|
+
end
|
166
|
+
|
167
|
+
file_path
|
168
|
+
end
|
169
|
+
|
170
|
+
def all_local_files(version)
|
171
|
+
files = []
|
172
|
+
ini_file = INIparser.load_ini(version, {}, offline: true)
|
173
|
+
ini_file.keys.each do |k|
|
174
|
+
result = local_file(k, version)
|
175
|
+
files << [k, result[0], result[1]] unless result.nil?
|
176
|
+
end
|
177
|
+
files
|
178
|
+
end
|
179
|
+
|
180
|
+
def local_file(package, version)
|
181
|
+
ini_file = INIparser.load_ini(version, {}, offline: true)
|
182
|
+
if ini_file[package].empty?
|
183
|
+
UI.error "No package \"#{package}\" was found for version #{version}."
|
184
|
+
return nil
|
185
|
+
end
|
186
|
+
|
187
|
+
dir = File.join(DOWNLOAD_PATH, DOWNLOAD_DIRECTORY, version)
|
188
|
+
raise "Main directory #{dir} does not exist. Nothing has been downloaded for version #{version}" unless Dir.exist?(dir)
|
189
|
+
|
190
|
+
file_name = UNITY_MODULE_FILE_REGEX.match(ini_file[package]['url'])[1]
|
191
|
+
file_path = File.expand_path(file_name, dir)
|
192
|
+
|
193
|
+
unless File.file?(file_path)
|
194
|
+
UI.error "Package #{package} has not been downloaded"
|
195
|
+
return nil
|
196
|
+
end
|
197
|
+
|
198
|
+
unless Downloader.size_validation(expected: ini_file[package]['size'], actual: File.size(file_path)) &&
|
199
|
+
Downloader.hash_validation(expected: ini_file[package]['md5'], actual: Utils.hashfile(file_path))
|
200
|
+
UI.error "File at #{file_path} is corrupted, deleting it"
|
201
|
+
File.delete(file_path)
|
202
|
+
return nil
|
203
|
+
end
|
204
|
+
|
205
|
+
return [file_path, ini_file[package]]
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
class LinuxDownloader
|
211
|
+
class << self
|
212
|
+
def download(version, cached_versions)
|
213
|
+
if cached_versions[version].nil?
|
214
|
+
UI.error "No version #{version} was found in cache. It might need updating."
|
215
|
+
return nil
|
216
|
+
end
|
217
|
+
url = cached_versions[version]
|
218
|
+
dir = File.join(DOWNLOAD_PATH, DOWNLOAD_DIRECTORY, version)
|
219
|
+
Utils.ensure_dir(dir)
|
220
|
+
file_name = UNITY_MODULE_FILE_REGEX.match(url)[1]
|
221
|
+
file_path = File.expand_path(file_name, dir)
|
222
|
+
|
223
|
+
# Check if file already exists
|
224
|
+
# Note: without size or hash validation, the file is assumed to be correct
|
225
|
+
if File.file?(file_path)
|
226
|
+
UI.important "File already downloaded at #{file_path}"
|
227
|
+
return file_path
|
228
|
+
end
|
229
|
+
|
230
|
+
# Download file
|
231
|
+
UI.header "Downloading Unity #{version}"
|
232
|
+
UI.verbose 'Downloading from ' + url.to_s.cyan.underline
|
233
|
+
Downloader.download_package(file_path, url)
|
234
|
+
U3dCore::CommandExecutor.execute(command: "chmod a+x #{file_path}")
|
235
|
+
file_path
|
236
|
+
end
|
237
|
+
|
238
|
+
def local_file(version)
|
239
|
+
dir = File.join(DOWNLOAD_PATH, DOWNLOAD_DIRECTORY, version)
|
240
|
+
raise "Main directory #{dir} does not exist. Nothing has been downloaded for version #{version}" unless Dir.exist?(dir)
|
241
|
+
find_cmd = "find #{dir}/ -maxdepth 2 -name '*.sh'"
|
242
|
+
files = U3dCore::CommandExecutor.execute(command: find_cmd).split("\n")
|
243
|
+
return files[0] unless files.empty?
|
244
|
+
raise 'No file has been downloaded'
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
class WindowsDownloader
|
250
|
+
class << self
|
251
|
+
def download_all(version, cached_versions)
|
252
|
+
if cached_versions[version].nil?
|
253
|
+
UI.error "No version #{version} was found in cache. It might need updating."
|
254
|
+
return nil
|
255
|
+
end
|
256
|
+
files = []
|
257
|
+
ini_file = INIparser.load_ini(version, cached_versions)
|
258
|
+
ini_file.keys.each do |k|
|
259
|
+
result = download_specific(k, version, cached_versions)
|
260
|
+
files << [k, result[0], result[1]] unless result.nil?
|
261
|
+
end
|
262
|
+
files
|
263
|
+
end
|
264
|
+
|
265
|
+
# Downloads a specific package for given version
|
266
|
+
def download_specific(package, version, cached_versions)
|
267
|
+
if cached_versions[version].nil?
|
268
|
+
UI.error "No version #{version} was found in cache. It might need updating."
|
269
|
+
return nil
|
270
|
+
end
|
271
|
+
|
272
|
+
ini_file = INIparser.load_ini(version, cached_versions)
|
273
|
+
if ini_file[package].empty?
|
274
|
+
UI.error "No package \"#{package}\" was found for version #{version}."
|
275
|
+
return nil
|
276
|
+
end
|
277
|
+
|
278
|
+
url = cached_versions[version]
|
279
|
+
dir = File.join(DOWNLOAD_PATH, DOWNLOAD_DIRECTORY, version)
|
280
|
+
Utils.ensure_dir(dir)
|
281
|
+
return [get_package(package, ini_file, dir, url), ini_file[package]]
|
282
|
+
end
|
283
|
+
|
284
|
+
def all_local_files(version)
|
285
|
+
files = []
|
286
|
+
ini_file = INIparser.load_ini(version, {}, offline: true)
|
287
|
+
ini_file.keys.each do |k|
|
288
|
+
result = local_file(k, version)
|
289
|
+
files << [k, result[0], result[1]] unless result.nil?
|
290
|
+
end
|
291
|
+
files
|
292
|
+
end
|
293
|
+
|
294
|
+
def local_file(package, version)
|
295
|
+
ini_file = INIparser.load_ini(version, {}, offline: true)
|
296
|
+
if ini_file[package].empty?
|
297
|
+
UI.error "No package \"#{package}\" was found for version #{version}."
|
298
|
+
return nil
|
299
|
+
end
|
300
|
+
|
301
|
+
dir = File.join(DOWNLOAD_PATH, DOWNLOAD_DIRECTORY, version)
|
302
|
+
raise "Main directory #{dir} does not exist. Nothing has been downloaded for version #{version}" unless Dir.exist?(dir)
|
303
|
+
|
304
|
+
file_name = UNITY_MODULE_FILE_REGEX.match(ini_file[package]['url'])[1]
|
305
|
+
file_path = File.expand_path(file_name, dir)
|
306
|
+
|
307
|
+
unless File.file?(file_path)
|
308
|
+
UI.error "Package #{package} has not been downloaded"
|
309
|
+
return nil
|
310
|
+
end
|
311
|
+
|
312
|
+
rounded_size = (File.size(file_path).to_f / 1024).floor
|
313
|
+
unless Downloader.size_validation(expected: ini_file[package]['size'], actual: rounded_size) &&
|
314
|
+
Downloader.hash_validation(expected: ini_file[package]['md5'], actual: Utils.hashfile(file_path))
|
315
|
+
UI.error "File at #{file_path} is corrupted, deleting it"
|
316
|
+
File.delete(file_path)
|
317
|
+
return nil
|
318
|
+
end
|
319
|
+
|
320
|
+
return [file_path, ini_file[package]]
|
321
|
+
end
|
322
|
+
|
323
|
+
private #---------------------------------------------------------------
|
324
|
+
|
325
|
+
def get_package(name, ini_file, main_dir, base_url)
|
326
|
+
file_name = UNITY_MODULE_FILE_REGEX.match(ini_file[name]['url'])[1]
|
327
|
+
file_path = File.expand_path(file_name, main_dir)
|
328
|
+
|
329
|
+
# Check if file already exists and validate it
|
330
|
+
if File.file?(file_path)
|
331
|
+
rounded_size = (File.size(file_path).to_f / 1024).floor
|
332
|
+
if Downloader.size_validation(expected: ini_file[name]['size'], actual: rounded_size) &&
|
333
|
+
Downloader.hash_validation(expected: ini_file[name]['md5'], actual: Utils.hashfile(file_path))
|
334
|
+
UI.important "File already downloaded at #{file_path}"
|
335
|
+
return file_path
|
336
|
+
else
|
337
|
+
UI.verbose 'Deleting existing file'
|
338
|
+
File.delete(file_path)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
# Download file
|
343
|
+
url = base_url + ini_file[name]['url']
|
344
|
+
UI.header "Downloading #{name}"
|
345
|
+
UI.verbose 'Downloading from ' + url.to_s.cyan.underline
|
346
|
+
Downloader.download_package(file_path, url, size: ini_file[name]['size'] * 1024)
|
347
|
+
|
348
|
+
# Validation download
|
349
|
+
rounded_size = (File.size(file_path).to_f / 1024).floor
|
350
|
+
if Downloader.size_validation(expected: ini_file[name]['size'], actual: rounded_size) &&
|
351
|
+
Downloader.hash_validation(expected: ini_file[name]['md5'], actual: Utils.hashfile(file_path))
|
352
|
+
UI.success "Successfully downloaded #{name}."
|
353
|
+
else
|
354
|
+
File.delete(file_path)
|
355
|
+
raise 'Download failed: file is corrupted, deleting it.'
|
356
|
+
end
|
357
|
+
|
358
|
+
file_path
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
## --- BEGIN LICENSE BLOCK ---
|
2
|
+
# Copyright (c) 2016-present WeWantToKnow AS
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in all
|
12
|
+
# copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
20
|
+
# SOFTWARE.
|
21
|
+
## --- END LICENSE BLOCK ---
|
22
|
+
|
23
|
+
require 'inifile'
|
24
|
+
require 'u3d/utils'
|
25
|
+
require 'u3d_core/helper'
|
26
|
+
|
27
|
+
module U3d
|
28
|
+
# Load and parse INI files
|
29
|
+
module INIparser
|
30
|
+
#####################################################
|
31
|
+
# @!group INI parameters to load and save ini files
|
32
|
+
#####################################################
|
33
|
+
INI_NAME = 'unity-%{version}-%{os}.ini'.freeze
|
34
|
+
INI_LINUX_PATH = File.join(ENV['HOME'], '.u3d', 'ini_files').freeze
|
35
|
+
INI_MAC_PATH = File.join(ENV['HOME'], 'Library', 'Application Support', 'u3d', 'ini_files').freeze
|
36
|
+
INI_WIN_PATH = File.join(ENV['HOME'], 'AppData', 'Local', 'u3d', 'ini_files').freeze
|
37
|
+
|
38
|
+
class << self
|
39
|
+
def load_ini(version, cached_versions, os: U3dCore::Helper.operating_system, offline: false)
|
40
|
+
unless os == :win || os == :mac
|
41
|
+
raise ArgumentError, "OS #{os.id2name} does not use ini files"
|
42
|
+
end
|
43
|
+
os = if os == :mac
|
44
|
+
'osx'
|
45
|
+
else
|
46
|
+
os.id2name
|
47
|
+
end
|
48
|
+
ini_name = INI_NAME % { version: version, os: os }
|
49
|
+
Utils.ensure_dir(default_ini_path)
|
50
|
+
ini_path = File.expand_path(ini_name, default_ini_path)
|
51
|
+
unless File.file?(ini_path)
|
52
|
+
raise "INI file does not exist at #{ini_path}" if offline
|
53
|
+
uri = URI(cached_versions[version] + ini_name)
|
54
|
+
File.open(ini_path, 'wb') do |f|
|
55
|
+
data = Net::HTTP.get(uri)
|
56
|
+
data.tr!("\"", '')
|
57
|
+
data.gsub!(/Note:.+\n/, '')
|
58
|
+
f.write(data)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
begin
|
62
|
+
result = IniFile.load(ini_path).to_h
|
63
|
+
rescue => e
|
64
|
+
raise "Could not parse INI data (#{e})"
|
65
|
+
end
|
66
|
+
result
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def default_ini_path
|
72
|
+
case U3dCore::Helper.operating_system
|
73
|
+
when :linux
|
74
|
+
INI_LINUX_PATH
|
75
|
+
when :mac
|
76
|
+
INI_MAC_PATH
|
77
|
+
when :win
|
78
|
+
INI_WIN_PATH
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,445 @@
|
|
1
|
+
## --- BEGIN LICENSE BLOCK ---
|
2
|
+
# Copyright (c) 2016-present WeWantToKnow AS
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in all
|
12
|
+
# copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
20
|
+
# SOFTWARE.
|
21
|
+
## --- END LICENSE BLOCK ---
|
22
|
+
|
23
|
+
require 'u3d/utils'
|
24
|
+
require 'fileutils'
|
25
|
+
require 'file-tail'
|
26
|
+
|
27
|
+
# Mac specific only right now
|
28
|
+
module U3d
|
29
|
+
DEFAULT_LINUX_INSTALL = '/opt/'.freeze
|
30
|
+
DEFAULT_MAC_INSTALL = '/'.freeze
|
31
|
+
DEFAULT_WINDOWS_INSTALL = 'C:/Program Files/'.freeze
|
32
|
+
UNITY_DIR = "Unity_%s".freeze
|
33
|
+
UNITY_DIR_CHECK = /Unity_\d+\.\d+\.\d+[a-z]\d+/
|
34
|
+
|
35
|
+
class Installation
|
36
|
+
def self.create(path: nil)
|
37
|
+
if Helper.mac?
|
38
|
+
MacInstallation.new path
|
39
|
+
elsif Helper.linux?
|
40
|
+
LinuxInstallation.new path
|
41
|
+
else
|
42
|
+
WindowsInstallation.new path
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class MacInstallation < Installation
|
48
|
+
attr_reader :path
|
49
|
+
|
50
|
+
require 'plist'
|
51
|
+
|
52
|
+
def initialize(path: nil)
|
53
|
+
@path = path
|
54
|
+
end
|
55
|
+
|
56
|
+
def version
|
57
|
+
plist['CFBundleVersion']
|
58
|
+
end
|
59
|
+
|
60
|
+
def default_log_file
|
61
|
+
"#{ENV['HOME']}/Library/Logs/Unity/Editor.log"
|
62
|
+
end
|
63
|
+
|
64
|
+
def exe_path
|
65
|
+
"#{path}/Contents/MacOS/Unity"
|
66
|
+
end
|
67
|
+
|
68
|
+
def packages
|
69
|
+
if Utils.parse_unity_version(version)[0].to_i <= 4
|
70
|
+
# Unity < 5 doesn't have packages
|
71
|
+
return []
|
72
|
+
end
|
73
|
+
fpath = File.expand_path('../PlaybackEngines', path)
|
74
|
+
raise "Unity installation does not seem correct. Couldn't locate PlaybackEngines." unless Dir.exist? fpath
|
75
|
+
Dir.entries(fpath).select { |e| File.directory?(File.join(fpath, e)) && !(e == '.' || e == '..') }
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def plist
|
81
|
+
@plist ||= Plist.parse_xml("#{@path}/Contents/Info.plist")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class LinuxInstallation < Installation
|
86
|
+
attr_reader :path
|
87
|
+
|
88
|
+
def initialize(path: nil)
|
89
|
+
@path = path
|
90
|
+
end
|
91
|
+
|
92
|
+
def version
|
93
|
+
# I don't find an easy way to extract the version on Linux
|
94
|
+
require 'rexml/document'
|
95
|
+
fpath = "#{path}/Data/PlaybackEngines/LinuxStandaloneSupport/ivy.xml"
|
96
|
+
raise "Couldn't find file #{fpath}" unless File.exist? fpath
|
97
|
+
doc = REXML::Document.new(File.read(fpath))
|
98
|
+
version = REXML::XPath.first(doc, 'ivy-module/info/@e:unityVersion').value
|
99
|
+
if m = version.match(/^(.*)x(.*)Linux$/)
|
100
|
+
version = "#{m[1]}#{m[2]}"
|
101
|
+
end
|
102
|
+
version
|
103
|
+
end
|
104
|
+
|
105
|
+
def default_log_file
|
106
|
+
"#{ENV['HOME']}/.config/unity3d/Editor.log"
|
107
|
+
end
|
108
|
+
|
109
|
+
def exe_path
|
110
|
+
"#{path}/Unity"
|
111
|
+
end
|
112
|
+
|
113
|
+
def packages
|
114
|
+
false
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class WindowsInstallation < Installation
|
119
|
+
attr_reader :path
|
120
|
+
|
121
|
+
def initialize(path: nil)
|
122
|
+
@path = path
|
123
|
+
end
|
124
|
+
|
125
|
+
def version
|
126
|
+
require 'rexml/document'
|
127
|
+
fpath = "#{path}/Editor/Data/PlaybackEngines/windowsstandalonesupport/ivy.xml"
|
128
|
+
raise "Couldn't find file #{fpath}" unless File.exist? fpath
|
129
|
+
doc = REXML::Document.new(File.read(fpath))
|
130
|
+
version = REXML::XPath.first(doc, 'ivy-module/info/@e:unityVersion').value
|
131
|
+
|
132
|
+
version
|
133
|
+
end
|
134
|
+
|
135
|
+
def default_log_file
|
136
|
+
if @logfile.nil?
|
137
|
+
begin
|
138
|
+
loc_appdata = Utils.windows_local_appdata
|
139
|
+
log_dir = File.expand_path('Unity/Editor/', loc_appdata)
|
140
|
+
UI.important "Log directory (#{log_dir}) does not exist" unless Dir.exist? log_dir
|
141
|
+
@logfile = File.expand_path('Editor.log', log_dir)
|
142
|
+
rescue RuntimeError => ex
|
143
|
+
UI.error "Unable to retrieve the editor logfile: #{ex}"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
@logfile
|
147
|
+
end
|
148
|
+
|
149
|
+
def exe_path
|
150
|
+
File.join(@path, 'Editor', 'Unity.exe')
|
151
|
+
end
|
152
|
+
|
153
|
+
def packages
|
154
|
+
# Unity prior to Unity5 did not have package
|
155
|
+
return [] if Utils.parse_unity_version(version)[0].to_i <= 4
|
156
|
+
fpath = "#{path}/Editor/Data/PlaybackEngines/"
|
157
|
+
raise "Unity installation does not seem correct. Couldn't locate PlaybackEngines." unless Dir.exist? fpath
|
158
|
+
Dir.entries(fpath).select { |e| File.directory?(File.join(fpath, e)) && !(e == '.' || e == '..') }
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class Runner
|
163
|
+
def run(installation, args, raw_logs: false)
|
164
|
+
require 'fileutils'
|
165
|
+
|
166
|
+
log_file = find_logFile_in_args(args)
|
167
|
+
|
168
|
+
if log_file # we wouldn't want to do that for the default log file.
|
169
|
+
File.delete(log_file) if File.exist?(log_file)
|
170
|
+
else
|
171
|
+
log_file = installation.default_log_file
|
172
|
+
end
|
173
|
+
|
174
|
+
FileUtils.touch(log_file)
|
175
|
+
|
176
|
+
tail_thread = Thread.new do
|
177
|
+
begin
|
178
|
+
if raw_logs
|
179
|
+
pipe(log_file) { |l| UI.message l.rstrip }
|
180
|
+
else
|
181
|
+
analyzer = LogAnalyzer.new
|
182
|
+
pipe(log_file) { |l| analyzer.parse_line l }
|
183
|
+
end
|
184
|
+
rescue => e
|
185
|
+
UI.error "Failure while trying to pipe #{log_file}: #{e.message}"
|
186
|
+
e.backtrace.each { |l| UI.error " #{l}" }
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
begin
|
191
|
+
args.unshift(installation.exe_path)
|
192
|
+
if Helper.windows?
|
193
|
+
args.map! { |a| a =~ / / ? "\"#{a}\"" : a }
|
194
|
+
else
|
195
|
+
args.map!(&:shellescape)
|
196
|
+
end
|
197
|
+
U3dCore::CommandExecutor.execute(command: args)
|
198
|
+
ensure
|
199
|
+
sleep 0.5
|
200
|
+
Thread.kill(tail_thread)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def find_logFile_in_args(args)
|
205
|
+
find_arg_in_args('-logFile', args)
|
206
|
+
end
|
207
|
+
|
208
|
+
def find_projectpath_in_args(args)
|
209
|
+
find_arg_in_args('-projectpath', args)
|
210
|
+
end
|
211
|
+
|
212
|
+
def find_arg_in_args(arg_to_find, args)
|
213
|
+
raise 'Only arguments of type array supported right now' unless args.is_a?(Array)
|
214
|
+
args.each_with_index do |arg, index|
|
215
|
+
return args[index + 1] if arg == arg_to_find && index < args.count - 1
|
216
|
+
end
|
217
|
+
nil
|
218
|
+
end
|
219
|
+
|
220
|
+
private
|
221
|
+
|
222
|
+
def pipe(file)
|
223
|
+
File.open(file, 'r') do |f|
|
224
|
+
f.extend File::Tail
|
225
|
+
f.interval = 0.1
|
226
|
+
f.max_interval = 0.4
|
227
|
+
f.backward 100
|
228
|
+
f.tail { |l| yield l }
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
class Installer
|
234
|
+
def self.create
|
235
|
+
installer = if Helper.mac?
|
236
|
+
MacInstaller.new
|
237
|
+
elsif Helper.linux?
|
238
|
+
LinuxInstaller.new
|
239
|
+
else
|
240
|
+
WindowsInstaller.new
|
241
|
+
end
|
242
|
+
if UI.interactive?
|
243
|
+
unclean = []
|
244
|
+
installer.installed.each { |unity| unclean << unity unless unity.path =~ UNITY_DIR_CHECK }
|
245
|
+
if !unclean.empty? && UI.confirm("#{unclean.count} Unity installation should be moved. Proceed?")
|
246
|
+
unclean.each { |unity| installer.sanitize_install(unity) }
|
247
|
+
end
|
248
|
+
end
|
249
|
+
installer
|
250
|
+
end
|
251
|
+
|
252
|
+
def self.install_module(file_path, version, installation_path: nil, info: {})
|
253
|
+
extension = File.extname(file_path)
|
254
|
+
if extension == '.pkg'
|
255
|
+
path = installation_path || DEFAULT_MAC_INSTALL
|
256
|
+
MacInstaller.install_pkg(
|
257
|
+
file_path,
|
258
|
+
version: version,
|
259
|
+
target_path: path
|
260
|
+
)
|
261
|
+
elsif extension == '.exe'
|
262
|
+
path = installation_path || File.join(DEFAULT_WINDOWS_INSTALL, UNITY_DIR % version)
|
263
|
+
WindowsInstaller.install_exe(
|
264
|
+
file_path,
|
265
|
+
installation_path: path,
|
266
|
+
info: info
|
267
|
+
)
|
268
|
+
elsif extension == '.sh'
|
269
|
+
path = installation_path || File.join(DEFAULT_LINUX_INSTALL, UNITY_DIR % version)
|
270
|
+
LinuxInstaller.install_sh(
|
271
|
+
file_path,
|
272
|
+
installation_path: path
|
273
|
+
)
|
274
|
+
else
|
275
|
+
raise "File type #{extension} not yet supported"
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
class MacInstaller
|
281
|
+
def sanitize_install(unity)
|
282
|
+
source_path = File.expand_path('..', unity.path)
|
283
|
+
parent = File.expand_path('..', source_path)
|
284
|
+
new_path = File.join(parent, UNITY_DIR % unity.version)
|
285
|
+
UI.important "Moving #{source_path} to #{new_path}..."
|
286
|
+
source_path = "\"#{source_path}\"" if source_path =~ / /
|
287
|
+
new_path = "\"#{new_path}\"" if new_path =~ / /
|
288
|
+
U3dCore::CommandExecutor.execute(command: "mv #{source_path} #{new_path}", admin: true)
|
289
|
+
rescue => e
|
290
|
+
UI.error "Unable to move #{source_path} to #{new_path}: #{e}"
|
291
|
+
else
|
292
|
+
UI.success "Successfully moved #{source_path} to #{new_path}"
|
293
|
+
end
|
294
|
+
|
295
|
+
def installed
|
296
|
+
unless (`mdutil -s /` =~ /disabled/).nil?
|
297
|
+
$stderr.puts 'Please enable Spotlight indexing for /Applications.'
|
298
|
+
exit(1)
|
299
|
+
end
|
300
|
+
|
301
|
+
bundle_identifiers = ['com.unity3d.UnityEditor4.x', 'com.unity3d.UnityEditor5.x']
|
302
|
+
|
303
|
+
mdfind_args = bundle_identifiers.map { |bi| "kMDItemCFBundleIdentifier == '#{bi}'" }.join(' || ')
|
304
|
+
|
305
|
+
cmd = "mdfind \"#{mdfind_args}\" 2>/dev/null"
|
306
|
+
UI.verbose cmd
|
307
|
+
versions = `#{cmd}`.split("\n").map { |path| MacInstallation.new(path: path) }
|
308
|
+
|
309
|
+
# sorting should take into account stable/patch etc
|
310
|
+
versions.sort! { |x, y| x.version <=> y.version }
|
311
|
+
end
|
312
|
+
|
313
|
+
def self.install_pkg(file_path, version: nil, target_path: nil)
|
314
|
+
target_path ||= DEFAULT_MAC_INSTALL
|
315
|
+
command = "installer -pkg #{file_path.shellescape} -target #{target_path.shellescape}"
|
316
|
+
unity = Installer.create.installed.find { |u| u.version == version }
|
317
|
+
if unity.nil?
|
318
|
+
UI.verbose "No Unity install for version #{version} was found"
|
319
|
+
U3dCore::CommandExecutor.execute(command: command, admin: true)
|
320
|
+
else
|
321
|
+
begin
|
322
|
+
path = File.expand_path('..', unity.path)
|
323
|
+
temp_path = File.join(target_path, 'Applications', 'Unity')
|
324
|
+
move_to_temp = (temp_path != path)
|
325
|
+
if move_to_temp
|
326
|
+
UI.verbose "Temporary switching location of #{path} to #{temp_path} for installation purpose"
|
327
|
+
FileUtils.mv path, temp_path
|
328
|
+
end
|
329
|
+
U3dCore::CommandExecutor.execute(command: command, admin: true)
|
330
|
+
ensure
|
331
|
+
FileUtils.mv temp_path, path if move_to_temp
|
332
|
+
end
|
333
|
+
end
|
334
|
+
rescue => e
|
335
|
+
UI.error "Failed to install pkg at #{file_path}: #{e}"
|
336
|
+
else
|
337
|
+
UI.success "Successfully installed package from #{file_path}"
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
class LinuxInstaller
|
342
|
+
def sanitize_install(unity)
|
343
|
+
source_path = File.expand_path(unity.path)
|
344
|
+
parent = File.expand_path('..', source_path)
|
345
|
+
new_path = File.join(parent, UNITY_DIR % unity.version)
|
346
|
+
UI.important "Moving #{source_path} to #{new_path}..."
|
347
|
+
source_path = "\"#{source_path}\"" if source_path =~ / /
|
348
|
+
new_path = "\"#{new_path}\"" if new_path =~ / /
|
349
|
+
U3dCore::CommandExecutor.execute(command: "mv #{source_path} #{new_path}", admin: true)
|
350
|
+
rescue => e
|
351
|
+
UI.error "Unable to move #{source_path} to #{new_path}: #{e}"
|
352
|
+
else
|
353
|
+
UI.success "Successfully moved #{source_path} to #{new_path}"
|
354
|
+
end
|
355
|
+
|
356
|
+
def installed
|
357
|
+
find = File.join(DEFAULT_LINUX_INSTALL, 'Unity*')
|
358
|
+
versions = Dir[find].map { |path| LinuxInstallation.new(path: path) }
|
359
|
+
|
360
|
+
# sorting should take into account stable/patch etc
|
361
|
+
versions.sort! { |x, y| x.version <=> y.version }
|
362
|
+
end
|
363
|
+
|
364
|
+
def self.install_sh(file, installation_path: nil)
|
365
|
+
cmd = file.shellescape
|
366
|
+
if installation_path
|
367
|
+
Utils.ensure_dir(installation_path)
|
368
|
+
U3dCore::CommandExecutor.execute(command: "cd #{installation_path}; #{cmd}", admin: true)
|
369
|
+
else
|
370
|
+
U3dCore::CommandExecutor.execute(command: cmd, admin: true)
|
371
|
+
end
|
372
|
+
rescue => e
|
373
|
+
UI.error "Failed to install bash file at #{file_path}: #{e}"
|
374
|
+
else
|
375
|
+
UI.success 'Installation successful'
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
class WindowsInstaller
|
380
|
+
def sanitize_install(unity)
|
381
|
+
source_path = File.expand_path(unity.path)
|
382
|
+
parent = File.expand_path('..', source_path)
|
383
|
+
new_path = File.join(parent, UNITY_DIR % unity.version)
|
384
|
+
UI.important "Moving #{source_path} to #{new_path}..."
|
385
|
+
source_path.tr!('/', '\\')
|
386
|
+
new_path.tr!('/', '\\')
|
387
|
+
source_path = "\"" + source_path + "\"" if source_path =~ / /
|
388
|
+
new_path = "\"" + new_path + "\"" if new_path =~ / /
|
389
|
+
U3dCore::CommandExecutor.execute(command: "move #{source_path} #{new_path}", admin: true)
|
390
|
+
rescue => e
|
391
|
+
UI.error "Unable to move #{source_path} to #{new_path}: #{e}"
|
392
|
+
else
|
393
|
+
UI.success "Successfully moved #{source_path} to #{new_path}"
|
394
|
+
end
|
395
|
+
|
396
|
+
def installed
|
397
|
+
find = File.join(DEFAULT_WINDOWS_INSTALL, 'Unity*', 'Editor', 'Uninstall.exe')
|
398
|
+
versions = Dir[find].map { |path| WindowsInstallation.new(path: File.expand_path('../..', path)) }
|
399
|
+
|
400
|
+
# sorting should take into account stable/patch etc
|
401
|
+
versions.sort! { |x, y| x.version <=> y.version }
|
402
|
+
end
|
403
|
+
|
404
|
+
def self.install_exe(file_path, installation_path: nil, info: {})
|
405
|
+
installation_path ||= DEFAULT_WINDOWS_INSTALL
|
406
|
+
final_path = installation_path.tr('/', '\\')
|
407
|
+
Utils.ensure_dir(final_path)
|
408
|
+
begin
|
409
|
+
command = nil
|
410
|
+
if info['cmd']
|
411
|
+
command = info['cmd']
|
412
|
+
command.sub!(/{FILENAME}/, file_path)
|
413
|
+
command.sub!(/{INSTDIR}/, final_path)
|
414
|
+
command.sub!(/{DOCDIR}/, final_path)
|
415
|
+
command.sub!(/{MODULEDIR}/, final_path)
|
416
|
+
command.sub!(/\/D=/, '/S /D=') unless /\/S/ =~ command
|
417
|
+
end
|
418
|
+
command ||= file_path.to_s
|
419
|
+
U3dCore::CommandExecutor.execute(command: command, admin: true)
|
420
|
+
rescue => e
|
421
|
+
UI.error "Failed to install exe at #{file_path}: #{e}"
|
422
|
+
else
|
423
|
+
UI.success "Successfully installed #{info['title']}"
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
class UnityProject
|
429
|
+
attr_reader :path
|
430
|
+
|
431
|
+
def initialize(path)
|
432
|
+
@path = path
|
433
|
+
end
|
434
|
+
|
435
|
+
def exist?
|
436
|
+
Dir.exist?("#{@path}/Assets") && Dir.exist?("#{@path}/ProjectSettings")
|
437
|
+
end
|
438
|
+
|
439
|
+
def editor_version
|
440
|
+
require 'yaml'
|
441
|
+
yaml = YAML.load(File.read("#{@path}/ProjectSettings/ProjectVersion.txt"))
|
442
|
+
yaml['m_EditorVersion']
|
443
|
+
end
|
444
|
+
end
|
445
|
+
end
|