reset-simulators.rb 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. #!/usr/bin/ruby
  2. require 'json'
  3. require 'open3'
  4. def platform_for_runtime(runtime)
  5. runtime['identifier'].gsub(/com.apple.CoreSimulator.SimRuntime.([^-]+)-.*/, '\1')
  6. end
  7. def platform_for_device_type(device_type)
  8. case device_type['identifier']
  9. when /Watch/
  10. 'watchOS'
  11. when /TV/
  12. 'tvOS'
  13. else
  14. 'iOS'
  15. end
  16. end
  17. def simctl(args)
  18. # When running on a machine with Xcode 11 installed, Xcode 10 sometimes
  19. # incorrectly thinks that it has not completed its first-run installation.
  20. # This results in it printing errors related to that to stdout in front of
  21. # the actual JSON output that we want.
  22. Open3.popen3('xcrun simctl ' + args) do |stdin, stdout, strerr, wait_thr|
  23. while line = stdout.gets
  24. if not line.start_with? 'Install'
  25. return line + stdout.read, wait_thr.value.exitstatus
  26. end
  27. end
  28. end
  29. end
  30. def wait_for_core_simulator_service
  31. # Run until we get a result since switching simulator versions often causes CoreSimulatorService to throw an exception.
  32. while simctl('list devices')[0].empty?
  33. end
  34. end
  35. def running_devices(devices)
  36. devices.select { |device| device['state'] != 'Shutdown' }
  37. end
  38. def shutdown_simulator_devices(devices)
  39. # Shut down any simulators that need it.
  40. running_devices(devices).each do |device|
  41. puts "Shutting down simulator #{device['udid']}"
  42. system("xcrun simctl shutdown #{device['udid']}") or puts " Failed to shut down simulator #{device['udid']}"
  43. end
  44. end
  45. attempts = 0
  46. begin
  47. # Kill all the current simulator processes as they may be from a different Xcode version
  48. print 'Killing running Simulator processes...'
  49. while system('pgrep -q Simulator')
  50. system('pkill Simulator 2>/dev/null')
  51. # CoreSimulatorService doesn't exit when sent SIGTERM
  52. system('pkill -9 Simulator 2>/dev/null')
  53. end
  54. wait_for_core_simulator_service
  55. puts ' done!'
  56. print 'Shut down existing simulator devices...'
  57. # Shut down any running simulator devices. This may take multiple attempts if some
  58. # simulators are currently in the process of booting or being created.
  59. all_available_devices = []
  60. (0..5).each do |shutdown_attempt|
  61. begin
  62. devices_json = simctl('list devices -j')[0]
  63. all_devices = JSON.parse(devices_json)['devices'].flat_map { |_, devices| devices }
  64. rescue JSON::ParserError
  65. sleep shutdown_attempt if shutdown_attempt > 0
  66. next
  67. end
  68. # Exclude devices marked as unavailable as they're from a different version of Xcode.
  69. all_available_devices = all_devices.reject { |device| device['availability'] =~ /unavailable/ }
  70. break if running_devices(all_available_devices).empty?
  71. shutdown_simulator_devices all_available_devices
  72. sleep shutdown_attempt if shutdown_attempt > 0
  73. end
  74. puts ' done!'
  75. # Delete all simulators.
  76. print 'Deleting all simulators...'
  77. all_available_devices.each do |device|
  78. system("xcrun simctl delete #{device['udid']}") or raise "Failed to delete simulator #{device['udid']}"
  79. end
  80. puts ' done!'
  81. # Recreate all simulators.
  82. runtimes = JSON.parse(simctl('list runtimes -j')[0])['runtimes']
  83. device_types = JSON.parse(simctl('list devicetypes -j')[0])['devicetypes']
  84. runtimes_by_platform = Hash.new { |hash, key| hash[key] = [] }
  85. runtimes.each do |runtime|
  86. next unless runtime['availability'] == '(available)' || runtime['isAvailable'] == true
  87. runtimes_by_platform[platform_for_runtime(runtime)] << runtime
  88. end
  89. print 'Creating fresh simulators...'
  90. device_types.each do |device_type|
  91. platform = platform_for_device_type(device_type)
  92. runtimes_by_platform[platform].each do |runtime|
  93. output, ec = simctl("create '#{device_type['name']}' '#{device_type['identifier']}' '#{runtime['identifier']}' 2>&1")
  94. next if ec == 0
  95. # Error code 161-163 indicate that the given device is not supported by the runtime, such as the iPad 2 and
  96. # iPhone 4s not being supported by the iOS 10 simulator runtime.
  97. next if output =~ /(domain=com.apple.CoreSimulator.SimError, code=16[123])/
  98. puts "Failed to create device of type #{device_type['identifier']} with runtime #{runtime['identifier']}:"
  99. output.each_line do |line|
  100. puts " #{line}"
  101. end
  102. end
  103. end
  104. puts ' done!'
  105. print 'Booting iPhone 8 simulator...'
  106. system("xcrun simctl boot 'iPhone 8'") or raise "Failed to boot iPhone 8 simulator"
  107. puts ' done!'
  108. rescue => e
  109. if (attempts += 1) < 5
  110. puts ''
  111. puts e.message
  112. e.backtrace.each { |line| puts line }
  113. puts ''
  114. puts 'Retrying...'
  115. retry
  116. end
  117. system('ps auxwww')
  118. system('xcrun simctl list')
  119. raise
  120. end