Repositorio del curso CCOM4030 el semestre B91 del proyecto Artesanías con el Instituto de Cultura

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. /*
  2. The MIT License (MIT)
  3. Copyright (c) 2014 Shazron Abdullah
  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. The above copyright notice and this permission notice shall be included in
  11. all copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  18. THE SOFTWARE.
  19. */
  20. // jscs:disable maximumLineLength
  21. const path = require('path')
  22. const fs = require('fs')
  23. const util = require('util')
  24. let simctl
  25. let bplist
  26. let plist
  27. function findFirstAvailableDevice (list) {
  28. /*
  29. // Example result:
  30. {
  31. name : 'iPhone 6',
  32. id : 'A1193D97-F5EE-468D-9DBA-786F403766E6',
  33. runtime : 'iOS 8.3'
  34. }
  35. */
  36. // the object to return
  37. let ret_obj = {
  38. name: null,
  39. id: null,
  40. runtime: null
  41. }
  42. let available_runtimes = {}
  43. list.runtimes.forEach(function (runtime) {
  44. available_runtimes[ runtime.name ] = (runtime.availability === '(available)')
  45. })
  46. Object.keys(list.devices).some(function (deviceGroup) {
  47. return list.devices[deviceGroup].some(function (device) {
  48. // deviceGroup has not been normalized, it can either be the namespaced name, or the
  49. // human readable name. We normalize it
  50. let normalizedRuntimeName = fixRuntimeName(deviceGroup)
  51. if (available_runtimes[normalizedRuntimeName]) {
  52. ret_obj = {
  53. name: device.name,
  54. id: device.udid,
  55. runtime: normalizedRuntimeName
  56. }
  57. return true
  58. }
  59. return false
  60. })
  61. })
  62. return ret_obj
  63. }
  64. function findRuntimesGroupByDeviceProperty (list, deviceProperty, availableOnly, options = {}) {
  65. /*
  66. // Example result:
  67. {
  68. "iPhone 6" : [ "iOS 8.2", "iOS 8.3"],
  69. "iPhone 6 Plus" : [ "iOS 8.2", "iOS 8.3"]
  70. }
  71. */
  72. let runtimes = {}
  73. let available_runtimes = {}
  74. list.runtimes.forEach(function (runtime) {
  75. // key value changed to "isAvailble" from "availability"
  76. available_runtimes[ runtime.name ] = (runtime.availability ? (runtime.availability === '(available)') : runtime.isAvailable)
  77. })
  78. Object.keys(list.devices).forEach(function (deviceGroup) {
  79. list.devices[deviceGroup].forEach(function (device) {
  80. // deviceGroup has not been normalized, it can either be the namespaced name, or the
  81. // human readable name. We normalize it
  82. let normalizedRuntimeName = fixRuntimeName(deviceGroup)
  83. let devicePropertyValue = device[deviceProperty]
  84. if (options.lowerCase) {
  85. devicePropertyValue = devicePropertyValue.toLowerCase()
  86. }
  87. if (!runtimes[devicePropertyValue]) {
  88. runtimes[devicePropertyValue] = []
  89. }
  90. if (availableOnly) {
  91. if (available_runtimes[normalizedRuntimeName]) {
  92. runtimes[devicePropertyValue].push(normalizedRuntimeName)
  93. }
  94. } else {
  95. runtimes[devicePropertyValue].push(normalizedRuntimeName)
  96. }
  97. })
  98. })
  99. return runtimes
  100. }
  101. function findAvailableRuntime (list, device_name) {
  102. device_name = device_name.toLowerCase()
  103. let all_druntimes = findRuntimesGroupByDeviceProperty(list, 'name', true, { lowerCase: true })
  104. let druntime = all_druntimes[ filterDeviceName(device_name) ] || all_druntimes[ device_name ]
  105. let runtime_found = druntime && druntime.length > 0
  106. if (!runtime_found) {
  107. console.error(util.format('No available runtimes could be found for "%s".', device_name))
  108. process.exit(1)
  109. }
  110. // return most modern runtime
  111. return druntime.sort().pop()
  112. }
  113. function getDeviceFromDeviceTypeId (devicetypeid) {
  114. /*
  115. // Example result:
  116. {
  117. name : 'iPhone 6',
  118. id : 'A1193D97-F5EE-468D-9DBA-786F403766E6',
  119. runtime : 'iOS 8.3'
  120. }
  121. */
  122. // the object to return
  123. let ret_obj = {
  124. name: null,
  125. id: null,
  126. runtime: null
  127. }
  128. let options = { 'silent': true }
  129. let list = simctl.list(options).json
  130. list = fixSimCtlList(list)
  131. let arr = []
  132. if (devicetypeid) {
  133. arr = devicetypeid.split(',')
  134. }
  135. // get the devicetype from --devicetypeid
  136. // --devicetypeid is a string in the form "devicetype, runtime_version" (optional: runtime_version)
  137. let devicetype = null
  138. if (arr.length < 1) {
  139. let dv = findFirstAvailableDevice(list)
  140. console.error(util.format('--devicetypeid was not specified, using first available device: %s.', dv.name))
  141. return dv
  142. } else {
  143. devicetype = arr[0].trim()
  144. if (arr.length > 1) {
  145. ret_obj.runtime = arr[1].trim()
  146. }
  147. }
  148. // check whether devicetype has the "com.apple.CoreSimulator.SimDeviceType." prefix, if not, add it
  149. let prefix = 'com.apple.CoreSimulator.SimDeviceType.'
  150. if (devicetype.indexOf(prefix) !== 0) {
  151. devicetype = prefix + devicetype
  152. }
  153. // now find the devicename from the devicetype
  154. let devicename_found = list.devicetypes.some(function (deviceGroup) {
  155. if (deviceGroup.identifier === devicetype) {
  156. ret_obj.name = deviceGroup.name
  157. return true
  158. }
  159. return false
  160. })
  161. // device name not found, exit
  162. if (!devicename_found) {
  163. console.error(util.format('Device type "%s" could not be found.', devicetype))
  164. process.exit(1)
  165. }
  166. // if runtime_version was not specified, we use a default. Use first available that has the device
  167. if (!ret_obj.runtime) {
  168. ret_obj.runtime = findAvailableRuntime(list, ret_obj.name)
  169. }
  170. // prepend iOS to runtime version, if necessary
  171. if (ret_obj.runtime.indexOf('OS') === -1) {
  172. ret_obj.runtime = util.format('iOS %s', ret_obj.runtime)
  173. }
  174. // now find the deviceid (by runtime and devicename)
  175. let deviceid_found = Object.keys(list.devices).some(function (deviceGroup) {
  176. // deviceGroup has not been normalized, it can either be the namespaced name, or the
  177. // human readable name. We normalize it
  178. let normalizedRuntimeName = fixRuntimeName(deviceGroup)
  179. // found the runtime, now find the actual device matching devicename
  180. if (normalizedRuntimeName === ret_obj.runtime) {
  181. return list.devices[deviceGroup].some(function (device) {
  182. if (filterDeviceName(device.name).toLowerCase() === filterDeviceName(ret_obj.name).toLowerCase()) {
  183. ret_obj.id = device.udid
  184. return true
  185. }
  186. return false
  187. })
  188. }
  189. return false
  190. })
  191. if (!deviceid_found) {
  192. console.error(
  193. util.format('Device id for device name "%s" and runtime "%s" could not be found, or is not available.', ret_obj.name, ret_obj.runtime)
  194. )
  195. process.exit(1)
  196. }
  197. return ret_obj
  198. }
  199. // Parses array of KEY=Value strings into map of strings
  200. // If fixsymctl == true, updates variables for correct usage with simctl
  201. function parseEnvironmentVariables (envVariables, fixsymctl) {
  202. envVariables = envVariables || []
  203. fixsymctl = typeof fixsymctl !== 'undefined' ? fixsymctl : true
  204. let envMap = {}
  205. envVariables.forEach(function (variable) {
  206. let envPair = variable.split('=', 2)
  207. if (envPair.length === 2) {
  208. let key = envPair[0]
  209. let value = envPair[1]
  210. if (fixsymctl) {
  211. key = 'SIMCTL_CHILD_' + key
  212. }
  213. envMap[ key ] = value
  214. }
  215. })
  216. return envMap
  217. }
  218. // Injects specified environt variables to the process and then runs action
  219. // returns environment variables back to original state after action completes
  220. function withInjectedEnvironmentVariablesToProcess (process, envVariables, action) {
  221. let oldVariables = Object.assign({}, process.env)
  222. // Inject additional environment variables to process
  223. for (let key in envVariables) {
  224. let value = envVariables[key]
  225. process.env[key] = value
  226. }
  227. action()
  228. // restore old envs
  229. process.env = oldVariables
  230. }
  231. // replace hyphens in iPad Pro name which differ in 'Device Types' and 'Devices'
  232. function filterDeviceName (deviceName) {
  233. // replace hyphens in iPad Pro name which differ in 'Device Types' and 'Devices'
  234. if (/^iPad Pro/i.test(deviceName)) {
  235. return deviceName.replace(/-/g, ' ').trim()
  236. }
  237. // replace ʀ in iPhone Xʀ
  238. if (deviceName.indexOf('ʀ') > -1) {
  239. return deviceName.replace('ʀ', 'R')
  240. }
  241. return deviceName
  242. }
  243. function fixNameKey (array, mapping) {
  244. if (!array || !mapping) {
  245. return array
  246. }
  247. return array.map(function (elem) {
  248. let name = mapping[elem.name]
  249. if (name) {
  250. elem.name = name
  251. }
  252. return elem
  253. })
  254. }
  255. function fixSimCtlList (list) {
  256. // Xcode 9 `xcrun simctl list devicetypes` have obfuscated names for 2017 iPhones and Apple Watches.
  257. let deviceTypeNameMap = {
  258. 'iPhone2017-A': 'iPhone 8',
  259. 'iPhone2017-B': 'iPhone 8 Plus',
  260. 'iPhone2017-C': 'iPhone X',
  261. 'Watch2017 - 38mm': 'Apple Watch Series 3 - 38mm',
  262. 'Watch2017 - 42mm': 'Apple Watch Series 3 - 42mm'
  263. }
  264. list.devicetypes = fixNameKey(list.devicetypes, deviceTypeNameMap)
  265. // `iPad Pro` in iOS 9.3 has mapped to `iPad Pro (9.7 inch)`
  266. // `Apple TV 1080p` has mapped to `Apple TV`
  267. let deviceNameMap = {
  268. 'Apple TV 1080p': 'Apple TV',
  269. 'iPad Pro': 'iPad Pro (9.7-inch)'
  270. }
  271. Object.keys(list.devices).forEach(function (key) {
  272. list.devices[key] = fixNameKey(list.devices[key], deviceNameMap)
  273. })
  274. return list
  275. }
  276. function fixRuntimeName (runtimeName) {
  277. // looking for format 'com.apple.CoreSimulator.SimRuntime.iOS-12-0'
  278. const pattern = /^com\.apple\.CoreSimulator\.SimRuntime\.(([a-zA-Z0-9]+)-(\S+))$/i
  279. const match = pattern.exec(runtimeName)
  280. if (match) {
  281. const [ , , os, version ] = match
  282. // all or nothing -- os, version will always have a value for match
  283. return `${os} ${version.replace('-', '.')}`
  284. }
  285. return runtimeName
  286. }
  287. let lib = {
  288. init: function () {
  289. if (!simctl) {
  290. simctl = require('simctl')
  291. }
  292. let output = simctl.check_prerequisites()
  293. if (output.code !== 0) {
  294. console.error(output.output)
  295. }
  296. if (!bplist) {
  297. bplist = require('bplist-parser')
  298. }
  299. return output.code
  300. },
  301. // jscs:disable disallowUnusedParams
  302. showsdks: function (args) {
  303. let options = { silent: true, runtimes: true }
  304. let list = simctl.list(options).json
  305. let output = 'Simulator SDK Roots:\n'
  306. list.runtimes.forEach(function (runtime) {
  307. if (runtime.availability === '(available)') {
  308. output += util.format('"%s" (%s)\n', runtime.name, runtime.buildversion)
  309. output += util.format('\t(unknown)\n')
  310. }
  311. })
  312. return output
  313. },
  314. // jscs:enable disallowUnusedParams
  315. // jscs:disable disallowUnusedParams
  316. getdevicetypes: function (args) {
  317. let options = { silent: true }
  318. let list = simctl.list(options).json
  319. list = fixSimCtlList(list)
  320. let druntimes = findRuntimesGroupByDeviceProperty(list, 'name', true, { lowerCase: true })
  321. let name_id_map = {}
  322. list.devicetypes.forEach(function (device) {
  323. name_id_map[ filterDeviceName(device.name).toLowerCase() ] = device.identifier
  324. })
  325. list = []
  326. let remove = function (devicename, runtime) {
  327. // remove "iOS" prefix in runtime, remove prefix "com.apple.CoreSimulator.SimDeviceType." in id
  328. list.push(util.format('%s, %s', name_id_map[ devicename ].replace(/^com.apple.CoreSimulator.SimDeviceType./, ''), runtime.replace(/^iOS /, '')))
  329. }
  330. let cur = function (devicename) {
  331. return function (runtime) {
  332. remove(devicename, runtime)
  333. }
  334. }
  335. for (let deviceName in druntimes) {
  336. let runtimes = druntimes[ deviceName ]
  337. let dname = filterDeviceName(deviceName).toLowerCase()
  338. if (!(dname in name_id_map)) {
  339. continue
  340. }
  341. runtimes.forEach(cur(dname))
  342. }
  343. return list
  344. },
  345. // jscs:enable disallowUnusedParams
  346. // jscs:disable disallowUnusedParams
  347. showdevicetypes: function (args) {
  348. let output = ''
  349. this.getdevicetypes().forEach(function (device) {
  350. output += util.format('%s\n', device)
  351. })
  352. return output
  353. },
  354. // jscs:enable disallowUnusedParams
  355. launch: function (app_path, devicetypeid, log, exit, setenv, argv) {
  356. let wait_for_debugger = false
  357. let info_plist_path
  358. let app_identifier
  359. info_plist_path = path.join(app_path, 'Info.plist')
  360. if (!fs.existsSync(info_plist_path)) {
  361. console.error(info_plist_path + ' file not found.')
  362. process.exit(1)
  363. }
  364. bplist.parseFile(info_plist_path, function (err, obj) {
  365. if (err) {
  366. // try to see if a regular plist parser will work
  367. if (!plist) {
  368. plist = require('plist')
  369. }
  370. obj = plist.parse(fs.readFileSync(info_plist_path, 'utf8'))
  371. if (obj) {
  372. app_identifier = obj.CFBundleIdentifier
  373. } else {
  374. throw err
  375. }
  376. } else {
  377. app_identifier = obj[0].CFBundleIdentifier
  378. }
  379. argv = argv || []
  380. setenv = setenv || []
  381. let environmentVariables = parseEnvironmentVariables(setenv)
  382. withInjectedEnvironmentVariablesToProcess(process, environmentVariables, function () {
  383. // get the deviceid from --devicetypeid
  384. // --devicetypeid is a string in the form "devicetype, runtime_version" (optional: runtime_version)
  385. let device = getDeviceFromDeviceTypeId(devicetypeid)
  386. // log device information
  387. console.log(util.format('device.name: %s', device.name))
  388. console.log(util.format('device.runtime: %s', device.runtime))
  389. console.log(util.format('device.id: %s', device.id))
  390. // so now we have the deviceid, we can proceed
  391. simctl.extensions.start(device.id)
  392. simctl.install(device.id, app_path)
  393. simctl.launch(wait_for_debugger, device.id, app_identifier, argv)
  394. simctl.extensions.log(device.id, log)
  395. if (log) {
  396. console.log(util.format('logPath: %s', path.resolve(log)))
  397. }
  398. if (exit) {
  399. process.exit(0)
  400. }
  401. })
  402. })
  403. },
  404. install: function (app_path, devicetypeid, log, exit) {
  405. let info_plist_path
  406. info_plist_path = path.join(app_path, 'Info.plist')
  407. if (!fs.existsSync(info_plist_path)) {
  408. console.error(info_plist_path + ' file not found.')
  409. process.exit(1)
  410. }
  411. bplist.parseFile(info_plist_path, function (err, obj) {
  412. if (err) {
  413. throw err
  414. }
  415. // get the deviceid from --devicetypeid
  416. // --devicetypeid is a string in the form "devicetype, runtime_version" (optional: runtime_version)
  417. let device = getDeviceFromDeviceTypeId(devicetypeid)
  418. // so now we have the deviceid, we can proceed
  419. simctl.extensions.start(device.id)
  420. simctl.install(device.id, app_path)
  421. simctl.extensions.log(device.id, log)
  422. if (log) {
  423. console.log(util.format('logPath: %s', path.resolve(log)))
  424. }
  425. if (exit) {
  426. process.exit(0)
  427. }
  428. })
  429. },
  430. start: function (devicetypeid) {
  431. let device = getDeviceFromDeviceTypeId(devicetypeid)
  432. simctl.extensions.start(device.id)
  433. },
  434. _parseEnvironmentVariables: parseEnvironmentVariables
  435. }
  436. module.exports = lib