run_loop 1.5.5 → 1.5.6.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/run_loop/cli/simctl.rb +37 -82
- data/lib/run_loop/core.rb +20 -17
- data/lib/run_loop/core_simulator.rb +648 -0
- data/lib/run_loop/device.rb +8 -8
- data/lib/run_loop/directory.rb +104 -17
- data/lib/run_loop/dot_dir.rb +83 -0
- data/lib/run_loop/environment.rb +27 -8
- data/lib/run_loop/instruments.rb +89 -1
- data/lib/run_loop/logging.rb +11 -0
- data/lib/run_loop/process_terminator.rb +5 -17
- data/lib/run_loop/sim_control.rb +114 -77
- data/lib/run_loop/version.rb +15 -3
- data/lib/run_loop.rb +9 -10
- metadata +6 -7
- data/lib/run_loop/life_cycle/core_simulator.rb +0 -515
- data/lib/run_loop/life_cycle/simulator.rb +0 -73
- data/lib/run_loop/simctl/bridge.rb +0 -499
data/lib/run_loop/directory.rb
CHANGED
@@ -42,27 +42,114 @@ module RunLoop
|
|
42
42
|
debug = RunLoop::Environment.debug?
|
43
43
|
|
44
44
|
sha = OpenSSL::Digest::SHA256.new
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
45
|
+
entries.each do |file|
|
46
|
+
unless self.skip_file?(file, 'SHA1', debug)
|
47
|
+
begin
|
48
|
+
sha << File.read(file)
|
49
|
+
rescue => e
|
50
|
+
if debug
|
51
|
+
RunLoop.log_warn(%Q{
|
52
|
+
RunLoop::Directory.directory_digest raised an error:
|
53
|
+
|
54
|
+
#{e}
|
55
|
+
|
56
|
+
while trying to find the SHA of this file:
|
57
|
+
|
58
|
+
#{file}
|
59
|
+
|
60
|
+
Please report this here:
|
61
|
+
|
62
|
+
https://github.com/calabash/run_loop/issues
|
63
|
+
|
64
|
+
})
|
65
|
+
end
|
62
66
|
end
|
63
67
|
end
|
64
68
|
end
|
65
69
|
sha.hexdigest
|
66
70
|
end
|
71
|
+
|
72
|
+
def self.size(path, format)
|
73
|
+
|
74
|
+
allowed_formats = [:bytes, :kb, :mb, :gb]
|
75
|
+
unless allowed_formats.include?(format)
|
76
|
+
raise ArgumentError, "Expected '#{format}' to be one of #{allowed_formats.join(', ')}"
|
77
|
+
end
|
78
|
+
|
79
|
+
unless File.exist?(path)
|
80
|
+
raise ArgumentError, "Expected '#{path}' to exist"
|
81
|
+
end
|
82
|
+
|
83
|
+
unless File.directory?(path)
|
84
|
+
raise ArgumentError, "Expected '#{path}' to be a directory"
|
85
|
+
end
|
86
|
+
|
87
|
+
entries = self.recursive_glob_for_entries(path)
|
88
|
+
|
89
|
+
if entries.empty?
|
90
|
+
raise ArgumentError, "Expected a non-empty dir at '#{path}' found '#{entries}'"
|
91
|
+
end
|
92
|
+
|
93
|
+
size = self.iterate_for_size(entries)
|
94
|
+
|
95
|
+
case format
|
96
|
+
when :bytes
|
97
|
+
size
|
98
|
+
when :kb
|
99
|
+
size/1000.0
|
100
|
+
when :mb
|
101
|
+
size/1000.0/1000.0
|
102
|
+
when :gb
|
103
|
+
size/1000.0/1000.0/1000.0
|
104
|
+
else
|
105
|
+
# Not expected to reach this.
|
106
|
+
size
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def self.skip_file?(file, task, debug)
|
113
|
+
skip = false
|
114
|
+
if File.directory?(file)
|
115
|
+
# Skip directories
|
116
|
+
skip = true
|
117
|
+
elsif !Pathname.new(file).exist?
|
118
|
+
# Skip broken symlinks
|
119
|
+
skip = true
|
120
|
+
elsif !File.exist?(file)
|
121
|
+
# Skip files that don't exist
|
122
|
+
skip = true
|
123
|
+
else
|
124
|
+
case File.ftype(file)
|
125
|
+
when 'fifo'
|
126
|
+
RunLoop.log_warn("#{task} IS SKIPPING FIFO #{file}") if debug
|
127
|
+
skip = true
|
128
|
+
when 'socket'
|
129
|
+
RunLoop.log_warn("#{task} IS SKIPPING SOCKET #{file}") if debug
|
130
|
+
skip = true
|
131
|
+
when 'characterSpecial'
|
132
|
+
RunLoop.log_warn("#{task} IS SKIPPING CHAR SPECIAL #{file}") if debug
|
133
|
+
skip = true
|
134
|
+
when 'blockSpecial'
|
135
|
+
skip = true
|
136
|
+
RunLoop.log_warn("#{task} SKIPPING BLOCK SPECIAL #{file}") if debug
|
137
|
+
else
|
138
|
+
|
139
|
+
end
|
140
|
+
end
|
141
|
+
skip
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.iterate_for_size(entries)
|
145
|
+
debug = RunLoop::Environment.debug?
|
146
|
+
size = 0
|
147
|
+
entries.each do |file|
|
148
|
+
unless self.skip_file?(file, "SIZE", debug)
|
149
|
+
size = size + File.size(file)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
size
|
153
|
+
end
|
67
154
|
end
|
68
155
|
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# A module for managing the ~/.run-loop directory.
|
2
|
+
module RunLoop::DotDir
|
3
|
+
|
4
|
+
def self.directory
|
5
|
+
home = RunLoop::Environment.user_home_directory
|
6
|
+
dir = File.join(home, ".run-loop")
|
7
|
+
if !File.exist?(dir)
|
8
|
+
FileUtils.mkdir_p(dir)
|
9
|
+
end
|
10
|
+
dir
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.make_results_dir
|
14
|
+
if RunLoop::Environment.xtc?
|
15
|
+
next_results_dir = Dir.mktmpdir("run_loop")
|
16
|
+
else
|
17
|
+
results_dir = File.join(self.directory, 'results')
|
18
|
+
next_results_dir = self.next_timestamped_dirname(results_dir)
|
19
|
+
FileUtils.mkdir_p(next_results_dir)
|
20
|
+
end
|
21
|
+
|
22
|
+
next_results_dir
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.rotate_result_directories
|
26
|
+
return :xtc if RunLoop::Environment.xtc?
|
27
|
+
|
28
|
+
start = Time.now
|
29
|
+
|
30
|
+
glob = "#{self.directory}/results/*"
|
31
|
+
|
32
|
+
RunLoop.log_debug("Searching for run-loop results with glob: #{glob}")
|
33
|
+
|
34
|
+
directories = Dir.glob(glob).select do |path|
|
35
|
+
File.directory?(path)
|
36
|
+
end
|
37
|
+
|
38
|
+
oldest_first = directories.sort_by { |f| File.mtime(f) }
|
39
|
+
|
40
|
+
RunLoop.log_debug("Found #{oldest_first.count} previous run-loop results")
|
41
|
+
oldest_first.pop(5)
|
42
|
+
|
43
|
+
RunLoop.log_debug("Will delete #{oldest_first.count} previous run-loop results")
|
44
|
+
|
45
|
+
oldest_first.each do |path|
|
46
|
+
FileUtils.rm_rf(path)
|
47
|
+
end
|
48
|
+
|
49
|
+
elapsed = Time.now - start
|
50
|
+
|
51
|
+
RunLoop.log_debug("Deleted #{oldest_first.count} previous results in #{elapsed} seconds")
|
52
|
+
rescue StandardError => e
|
53
|
+
RunLoop.log_error("While rotating previous results, encounterd: #{e}")
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def self.timestamped_dirname(plus_seconds = 0)
|
59
|
+
(Time.now + plus_seconds).strftime("%Y-%m-%d_%H-%M-%S")
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.next_timestamped_dirname(base_dir)
|
63
|
+
dir = File.join(base_dir, self.timestamped_dirname)
|
64
|
+
return dir if !File.exist?(dir)
|
65
|
+
|
66
|
+
# Rather than wait, just increment the second. Per-second accuracy
|
67
|
+
# is not important; uniqueness is.
|
68
|
+
counter = 0
|
69
|
+
loop do
|
70
|
+
break if !File.exist?(dir)
|
71
|
+
break if counter == 4
|
72
|
+
counter = counter + 1
|
73
|
+
dir = File.join(base_dir, self.timestamped_dirname(counter))
|
74
|
+
end
|
75
|
+
|
76
|
+
# If all else fails, just return a unique UUID
|
77
|
+
if File.exist?(dir)
|
78
|
+
dir = File.join(base_dir, SecureRandom.uuid)
|
79
|
+
end
|
80
|
+
dir
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
data/lib/run_loop/environment.rb
CHANGED
@@ -1,6 +1,18 @@
|
|
1
1
|
module RunLoop
|
2
2
|
class Environment
|
3
3
|
|
4
|
+
# Returns the user home directory
|
5
|
+
def self.user_home_directory
|
6
|
+
if self.xtc?
|
7
|
+
home = File.join("./", "tmp", "home")
|
8
|
+
FileUtils.mkdir_p(home)
|
9
|
+
home
|
10
|
+
else
|
11
|
+
require 'etc'
|
12
|
+
Etc.getpwuid.dir
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
4
16
|
# Returns true if debugging is enabled.
|
5
17
|
def self.debug?
|
6
18
|
ENV['DEBUG'] == '1'
|
@@ -51,13 +63,13 @@ module RunLoop
|
|
51
63
|
if !value || value == ''
|
52
64
|
nil
|
53
65
|
else
|
54
|
-
value
|
66
|
+
File.expand_path(value)
|
55
67
|
end
|
56
68
|
end
|
57
69
|
|
58
70
|
# Returns the value of DEVELOPER_DIR
|
59
71
|
#
|
60
|
-
# @note Never call this directly. Always create an
|
72
|
+
# @note Never call this directly. Always create an Xcode instance
|
61
73
|
# and allow it to derive the path to the Xcode toolchain.
|
62
74
|
def self.developer_dir
|
63
75
|
value = ENV['DEVELOPER_DIR']
|
@@ -96,13 +108,20 @@ module RunLoop
|
|
96
108
|
end
|
97
109
|
end
|
98
110
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
111
|
+
# !@visibility private
|
112
|
+
def self.with_debugging(debug, &block)
|
113
|
+
if debug
|
114
|
+
original_value = ENV['DEBUG']
|
115
|
+
|
116
|
+
begin
|
117
|
+
ENV['DEBUG'] = '1'
|
118
|
+
block.call
|
119
|
+
ensure
|
120
|
+
ENV['DEBUG'] = original_value
|
121
|
+
end
|
122
|
+
|
123
|
+
else
|
103
124
|
block.call
|
104
|
-
ensure
|
105
|
-
ENV['DEBUG'] = original_value
|
106
125
|
end
|
107
126
|
end
|
108
127
|
end
|
data/lib/run_loop/instruments.rb
CHANGED
@@ -7,6 +7,73 @@ module RunLoop
|
|
7
7
|
|
8
8
|
include RunLoop::Regex
|
9
9
|
|
10
|
+
# @!visibility private
|
11
|
+
#
|
12
|
+
# Rotates xrtmp__ directories in /Library/Caches/com.apple.dt.instruments
|
13
|
+
# keeping the last 5. On CI systems these can be hundreds of gigabytes.
|
14
|
+
def self.rotate_cache_directories
|
15
|
+
|
16
|
+
# Never run on the XTC
|
17
|
+
return :xtc if RunLoop::Environment.xtc?
|
18
|
+
|
19
|
+
cache = self.library_cache_dir
|
20
|
+
|
21
|
+
# If the directory does not exist, do nothing.
|
22
|
+
return :no_cache if !cache || !File.exist?(cache)
|
23
|
+
|
24
|
+
start = Time.now
|
25
|
+
|
26
|
+
glob = "#{cache}/xrtmp__*"
|
27
|
+
|
28
|
+
RunLoop.log_debug("Searching for instruments caches with glob: #{glob}")
|
29
|
+
|
30
|
+
directories = Dir.glob(glob).select do |path|
|
31
|
+
File.directory?(path)
|
32
|
+
end
|
33
|
+
|
34
|
+
log_progress = false
|
35
|
+
if directories.count > 25
|
36
|
+
RunLoop.log_info2("Found #{directories.count} instruments caches: ~#{20 * directories.count} Mb")
|
37
|
+
RunLoop.log_info2("Deleting them could take a long time.")
|
38
|
+
RunLoop.log_info2("This delay will only happen once!")
|
39
|
+
RunLoop.log_info2("Please be patient and allow the directories to be deleted")
|
40
|
+
log_progress = true
|
41
|
+
else
|
42
|
+
RunLoop.log_debug("Found #{directories.count} instruments caches")
|
43
|
+
end
|
44
|
+
|
45
|
+
if log_progress
|
46
|
+
RunLoop.log_info2("Sorting instruments caches by modification time...")
|
47
|
+
end
|
48
|
+
|
49
|
+
oldest_first = directories.sort_by { |f| File.mtime(f) }
|
50
|
+
|
51
|
+
oldest_first.pop(5)
|
52
|
+
|
53
|
+
if log_progress
|
54
|
+
RunLoop.log_info2("Will delete #{oldest_first.count} instruments caches...")
|
55
|
+
else
|
56
|
+
RunLoop.log_debug("Will delete #{oldest_first.count} instruments caches")
|
57
|
+
end
|
58
|
+
|
59
|
+
oldest_first.each do |path|
|
60
|
+
FileUtils.rm_rf(path)
|
61
|
+
if log_progress
|
62
|
+
printf "."
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
elapsed = Time.now - start
|
67
|
+
|
68
|
+
if log_progress
|
69
|
+
puts ""
|
70
|
+
RunLoop.log_info2("Deleted #{oldest_first.count} instruments caches in #{elapsed} seconds")
|
71
|
+
else
|
72
|
+
RunLoop.log_debug("Deleted #{oldest_first.count} instruments caches in #{elapsed} seconds")
|
73
|
+
end
|
74
|
+
true
|
75
|
+
end
|
76
|
+
|
10
77
|
attr_reader :xcode
|
11
78
|
|
12
79
|
def pbuddy
|
@@ -192,7 +259,8 @@ Please update your sources to pass an instance of RunLoop::Xcode))
|
|
192
259
|
fetch_devices[:out].chomp.split("\n").map do |line|
|
193
260
|
stripped = line.strip
|
194
261
|
if line_is_simulator?(stripped) &&
|
195
|
-
!line_is_simulator_paired_with_watch?(stripped)
|
262
|
+
!line_is_simulator_paired_with_watch?(stripped) &&
|
263
|
+
!line_is_apple_tv?(stripped)
|
196
264
|
|
197
265
|
version = stripped[VERSION_REGEX, 0]
|
198
266
|
|
@@ -402,6 +470,11 @@ Please update your sources to pass an instance of RunLoop::Xcode))
|
|
402
470
|
line[CORE_SIMULATOR_UDID_REGEX, 0] && line[/Apple Watch/, 0]
|
403
471
|
end
|
404
472
|
|
473
|
+
# @!visibility private
|
474
|
+
def line_is_apple_tv?(line)
|
475
|
+
line[/Apple TV/, 0]
|
476
|
+
end
|
477
|
+
|
405
478
|
# @!visibility private
|
406
479
|
def path_to_instruments_app_plist
|
407
480
|
@path_to_instruments_app_plist ||=
|
@@ -412,5 +485,20 @@ Please update your sources to pass an instance of RunLoop::Xcode))
|
|
412
485
|
'Contents',
|
413
486
|
'Info.plist'))
|
414
487
|
end
|
488
|
+
|
489
|
+
# @!visibility private
|
490
|
+
#
|
491
|
+
# Instruments caches files in this directory and it can become quite large
|
492
|
+
# over time; particularly on CI system.
|
493
|
+
def self.library_cache_dir
|
494
|
+
path = "/Library/Caches/com.apple.dt.instruments"
|
495
|
+
|
496
|
+
if File.exist?(path)
|
497
|
+
path
|
498
|
+
else
|
499
|
+
nil
|
500
|
+
end
|
501
|
+
end
|
415
502
|
end
|
416
503
|
end
|
504
|
+
|
data/lib/run_loop/logging.rb
CHANGED
@@ -59,6 +59,12 @@ module RunLoop
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
+
# .log_info is already taken by the XTC logger. (>_O)
|
63
|
+
# green
|
64
|
+
def self.log_info2(msg)
|
65
|
+
puts self.green(" INFO: #{msg}") if msg
|
66
|
+
end
|
67
|
+
|
62
68
|
# red
|
63
69
|
def self.log_error(msg)
|
64
70
|
puts self.red("ERROR: #{msg}") if msg
|
@@ -101,4 +107,9 @@ module RunLoop
|
|
101
107
|
def self.cyan(string)
|
102
108
|
colorize(string, 36)
|
103
109
|
end
|
110
|
+
|
111
|
+
# @!visibility private
|
112
|
+
def self.green(string)
|
113
|
+
colorize(string, 32)
|
114
|
+
end
|
104
115
|
end
|
@@ -55,32 +55,22 @@ module RunLoop
|
|
55
55
|
def kill_process
|
56
56
|
return true unless process_alive?
|
57
57
|
|
58
|
-
debug_logging = RunLoop::Environment.debug?
|
59
58
|
begin
|
60
|
-
|
61
|
-
puts "Sending '#{kill_signal}' to #{display_name} process '#{pid}'"
|
62
|
-
end
|
59
|
+
RunLoop.log_debug("Sending '#{kill_signal}' to #{display_name} process '#{pid}'")
|
63
60
|
Process.kill(kill_signal, pid.to_i)
|
64
61
|
# Don't wait.
|
65
62
|
# We might not own this process and a WNOHANG would be a nop.
|
66
63
|
# Process.wait(pid, Process::WNOHANG)
|
67
64
|
rescue Errno::ESRCH
|
68
|
-
|
69
|
-
puts "Process with pid '#{pid}' does not exist; nothing to do."
|
70
|
-
end
|
65
|
+
RunLoop.log_debug("Process with pid '#{pid}' does not exist; nothing to do.")
|
71
66
|
# Return early; there is no need to wait if the process does not exist.
|
72
67
|
return true
|
73
68
|
rescue Errno::EPERM
|
74
|
-
|
75
|
-
puts "Cannot kill process '#{pid}' with '#{kill_signal}'; not a child of this process"
|
76
|
-
end
|
69
|
+
RunLoop.log_debug("Cannot kill process '#{pid}' with '#{kill_signal}'; not a child of this process")
|
77
70
|
rescue SignalException => e
|
78
71
|
raise e.message
|
79
72
|
end
|
80
|
-
|
81
|
-
if debug_logging
|
82
|
-
puts "Waiting for #{display_name} '#{pid}' to terminate"
|
83
|
-
end
|
73
|
+
RunLoop.log_debug("Waiting for #{display_name} with pid '#{pid}' to terminate")
|
84
74
|
wait_for_process_to_terminate
|
85
75
|
end
|
86
76
|
|
@@ -127,9 +117,7 @@ module RunLoop
|
|
127
117
|
sleep delay
|
128
118
|
end
|
129
119
|
|
130
|
-
|
131
|
-
puts "Waited for #{Time.now - now} seconds for #{display_name} with '#{pid}' to terminate"
|
132
|
-
end
|
120
|
+
RunLoop.log_debug("Waited for #{Time.now - now} seconds for #{display_name} with pid '#{pid}' to terminate")
|
133
121
|
|
134
122
|
if @options[:raise_on_no_terminate] and !has_terminated
|
135
123
|
raise "Waited #{options[:timeout]} seconds for #{display_name} (#{ps_details}) to terminate"
|
data/lib/run_loop/sim_control.rb
CHANGED
@@ -52,6 +52,10 @@ module RunLoop
|
|
52
52
|
xcode.developer_dir
|
53
53
|
end
|
54
54
|
|
55
|
+
def xcrun
|
56
|
+
@xcrun ||= RunLoop::Xcrun.new
|
57
|
+
end
|
58
|
+
|
55
59
|
# Return an instance of PlistBuddy.
|
56
60
|
# @return [RunLoop::PlistBuddy] The plist buddy instance that is used internally.
|
57
61
|
def pbuddy
|
@@ -1137,55 +1141,61 @@ module RunLoop
|
|
1137
1141
|
# base sdk version.
|
1138
1142
|
# @see #simctl_list
|
1139
1143
|
def simctl_list_devices
|
1140
|
-
args = 'simctl list devices'
|
1141
|
-
|
1142
|
-
out = stdout.read.strip
|
1143
|
-
err = stderr.read.strip
|
1144
|
-
if ENV['DEBUG_UNIX_CALLS'] == '1'
|
1145
|
-
cmd = "xcrun #{args.join(' ')}"
|
1146
|
-
puts "#{cmd} => stdout: '#{out}' | stderr: '#{err}'"
|
1147
|
-
end
|
1144
|
+
args = ['simctl', 'list', 'devices']
|
1145
|
+
hash = xcrun.exec(args)
|
1148
1146
|
|
1149
|
-
|
1150
|
-
|
1151
|
-
out.split("\n").each do |line|
|
1147
|
+
current_sdk = nil
|
1148
|
+
simulators = {}
|
1152
1149
|
|
1153
|
-
|
1154
|
-
if possible_sdk
|
1155
|
-
current_sdk = possible_sdk
|
1156
|
-
res[current_sdk] = []
|
1157
|
-
next
|
1158
|
-
end
|
1150
|
+
out = hash[:out]
|
1159
1151
|
|
1160
|
-
|
1161
|
-
if unavailable_sdk
|
1162
|
-
current_sdk = nil
|
1163
|
-
next
|
1164
|
-
end
|
1152
|
+
out.split("\n").each do |line|
|
1165
1153
|
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1154
|
+
possible_sdk = line[VERSION_REGEX,0]
|
1155
|
+
if possible_sdk
|
1156
|
+
current_sdk = possible_sdk
|
1157
|
+
simulators[current_sdk] = []
|
1158
|
+
next
|
1159
|
+
end
|
1171
1160
|
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1161
|
+
unavailable_sdk = line[/Unavailable/, 0]
|
1162
|
+
if unavailable_sdk
|
1163
|
+
current_sdk = nil
|
1164
|
+
next
|
1165
|
+
end
|
1177
1166
|
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1167
|
+
watch_os = line[/watchOS/, 0]
|
1168
|
+
if watch_os
|
1169
|
+
current_sdk = nil
|
1170
|
+
next
|
1171
|
+
end
|
1172
|
+
|
1173
|
+
watch = line[/Apple Watch/, 0]
|
1174
|
+
if watch
|
1175
|
+
current_sdk = nil
|
1176
|
+
next
|
1177
|
+
end
|
1178
|
+
|
1179
|
+
tv = line[/Apple TV/, 0]
|
1180
|
+
if tv
|
1181
|
+
current_sdk = nil
|
1182
|
+
next
|
1183
|
+
end
|
1184
|
+
|
1185
|
+
if current_sdk
|
1186
|
+
unless line[/unavailable/,0]
|
1187
|
+
name = line.split('(').first.strip
|
1188
|
+
udid = line[CORE_SIMULATOR_UDID_REGEX,0]
|
1189
|
+
state = line[/(Booted|Shutdown)/,0]
|
1190
|
+
simulators[current_sdk] << {
|
1191
|
+
:name => name,
|
1192
|
+
:udid => udid,
|
1193
|
+
:state => state
|
1194
|
+
}
|
1185
1195
|
end
|
1186
1196
|
end
|
1187
|
-
res
|
1188
1197
|
end
|
1198
|
+
simulators
|
1189
1199
|
end
|
1190
1200
|
|
1191
1201
|
# @!visibility private
|
@@ -1193,48 +1203,75 @@ module RunLoop
|
|
1193
1203
|
#
|
1194
1204
|
# @example
|
1195
1205
|
# RunLoop::SimControl.new.simctl_list :runtimes
|
1196
|
-
#
|
1197
|
-
#
|
1198
|
-
#
|
1199
|
-
#
|
1200
|
-
#
|
1201
|
-
# "7.1" => {
|
1202
|
-
# :sdk => "7.1",
|
1203
|
-
# :runtime => "com.apple.CoreSimulator.SimRuntime.iOS-7-1"
|
1206
|
+
# :iOS => {
|
1207
|
+
# <Version 8.1> => {
|
1208
|
+
# :name => "iOS",
|
1209
|
+
# :runtime => "com.apple.CoreSimulator.SimRuntime.iOS-8-1",
|
1210
|
+
# :complete => "iOS 8.1 (8.1 - 12B411) (com.apple.CoreSimulator.SimRuntime.iOS-8-1)"
|
1204
1211
|
# },
|
1205
|
-
#
|
1206
|
-
#
|
1207
|
-
#
|
1208
|
-
#
|
1209
|
-
#
|
1212
|
+
# ...
|
1213
|
+
# },
|
1214
|
+
#
|
1215
|
+
# :tvOS => {
|
1216
|
+
# <Version 9.0> => {
|
1217
|
+
# :name => "tvOS",
|
1218
|
+
# :runtime => "com.apple.CoreSimulator.SimRuntime.tvOS-9-0",
|
1219
|
+
# :complete => "tvOS 9.0 (9.0 - 13T5365h) (com.apple.CoreSimulator.SimRuntime.tvOS-9-0)"
|
1220
|
+
# },
|
1221
|
+
# ...
|
1222
|
+
# },
|
1223
|
+
#
|
1224
|
+
# :watchOS => {
|
1225
|
+
# <Version 2.0> => {
|
1226
|
+
# :name => "watchOS",
|
1227
|
+
# :runtime => "com.apple.CoreSimulator.SimRuntime.watchOS-2-0",
|
1228
|
+
# :complete => "watchOS 2.0 (2.0 - 13S343) (com.apple.CoreSimulator.SimRuntime.watchOS-2-0)"
|
1229
|
+
# },
|
1230
|
+
# ...
|
1231
|
+
# }
|
1232
|
+
#
|
1210
1233
|
# @see #simctl_list
|
1211
1234
|
def simctl_list_runtimes
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
+
args = ['simctl', 'list', 'runtimes']
|
1236
|
+
hash = xcrun.exec(args)
|
1237
|
+
|
1238
|
+
# Ex.
|
1239
|
+
# == Runtimes ==
|
1240
|
+
# iOS 7.0 (7.0.3 - 11B507) (com.apple.CoreSimulator.SimRuntime.iOS-7-0)
|
1241
|
+
# iOS 7.1 (7.1 - 11D167) (com.apple.CoreSimulator.SimRuntime.iOS-7-1)
|
1242
|
+
# iOS 8.0 (8.0 - 12A4331d) (com.apple.CoreSimulator.SimRuntime.iOS-8-0)
|
1243
|
+
|
1244
|
+
out = hash[:out]
|
1245
|
+
|
1246
|
+
runtimes = {}
|
1247
|
+
|
1248
|
+
out.split("\n").each do |line|
|
1249
|
+
next if line[/unavailable/, 0]
|
1250
|
+
next if !line[/com.apple.CoreSimulator.SimRuntime/,0]
|
1251
|
+
|
1252
|
+
tokens = line.split(' ')
|
1253
|
+
|
1254
|
+
name = tokens.first
|
1255
|
+
|
1256
|
+
key = name.to_sym
|
1257
|
+
|
1258
|
+
unless runtimes[key]
|
1259
|
+
runtimes[key] = {}
|
1235
1260
|
end
|
1236
|
-
|
1261
|
+
|
1262
|
+
version_str = tokens[1]
|
1263
|
+
version = RunLoop::Version.new(version_str)
|
1264
|
+
|
1265
|
+
runtime = line[/com.apple.CoreSimulator.SimRuntime.*/, 0].chomp(')')
|
1266
|
+
|
1267
|
+
runtimes[key][version] =
|
1268
|
+
{
|
1269
|
+
:name => name,
|
1270
|
+
:runtime => runtime,
|
1271
|
+
:complete => line
|
1272
|
+
}
|
1237
1273
|
end
|
1274
|
+
runtimes
|
1238
1275
|
end
|
1239
1276
|
end
|
1240
1277
|
end
|