initialize.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const child_process_1 = require("child_process");
  4. const fs = require("fs");
  5. const glob = require("glob");
  6. const ini = require("ini");
  7. const path = require("path");
  8. const q = require("q");
  9. const config_1 = require("../config");
  10. const utils_1 = require("../utils");
  11. const noop = () => { };
  12. // Make a function which configures a child process to automatically respond
  13. // to a certain question
  14. function respondFactory(question, answer, verbose) {
  15. return (child) => {
  16. child.stdin.setDefaultEncoding('utf-8');
  17. child.stdout.on('data', (data) => {
  18. if (data != null) {
  19. if (verbose) {
  20. process.stdout.write(data);
  21. }
  22. if (data.toString().indexOf(question) != -1) {
  23. child.stdin.write(answer + '\n');
  24. }
  25. }
  26. });
  27. };
  28. }
  29. // Run a command on the android SDK
  30. function runAndroidSDKCommand(sdkPath, cmd, args, stdio, config_fun) {
  31. let child = utils_1.spawn(path.resolve(sdkPath, 'tools', 'android'), [cmd].concat(args), stdio);
  32. if (config_fun) {
  33. config_fun(child);
  34. }
  35. ;
  36. let deferred = q.defer();
  37. child.on('exit', (code) => {
  38. if (deferred != null) {
  39. if (code) {
  40. deferred.reject(code);
  41. }
  42. else {
  43. deferred.resolve();
  44. }
  45. deferred = null;
  46. }
  47. });
  48. child.on('error', (err) => {
  49. if (deferred != null) {
  50. deferred.reject(err);
  51. deferred = null;
  52. }
  53. });
  54. return deferred.promise;
  55. }
  56. // Download updates via the android SDK
  57. function downloadAndroidUpdates(sdkPath, targets, search_all, auto_accept, verbose) {
  58. return runAndroidSDKCommand(sdkPath, 'update', ['sdk', '-u'].concat(search_all ? ['-a'] : []).concat(['-t', targets.join(',')]), auto_accept ? 'pipe' : 'inherit', auto_accept ? respondFactory('Do you accept the license', 'y', verbose) : noop);
  59. }
  60. // Setup hardware acceleration for x86-64 emulation
  61. function setupHardwareAcceleration(sdkPath) {
  62. // TODO(sjelin): linux setup
  63. let toolDir = path.resolve(sdkPath, 'extras', 'intel', 'Hardware_Accelerated_Execution_Manager');
  64. if (config_1.Config.osType() == 'Darwin') {
  65. console.log('Enabling hardware acceleration (requires root access)');
  66. // We don't need the wrapped spawnSync because we know we're on OSX
  67. child_process_1.spawnSync('sudo', ['silent_install.sh'], { stdio: 'inherit', cwd: toolDir });
  68. }
  69. else if (config_1.Config.osType() == 'Windows_NT') {
  70. console.log('Enabling hardware acceleration (requires admin access)');
  71. // We don't need the wrapped spawnSync because we know we're on windows
  72. child_process_1.spawnSync('silent_install.bat', [], { stdio: 'inherit', cwd: toolDir });
  73. }
  74. }
  75. // Get a list of all the SDK download targets for a given set of APIs,
  76. // architectures, and platforms
  77. function getAndroidSDKTargets(apiLevels, architectures, platforms, oldAVDs) {
  78. function getSysImgTarget(architecture, platform, level) {
  79. if (platform.toUpperCase() == 'DEFAULT') {
  80. platform = 'android';
  81. }
  82. return 'sys-img-' + architecture + '-' + platform + '-' + level;
  83. }
  84. let targets = apiLevels
  85. .map((level) => {
  86. return 'android-' + level;
  87. })
  88. .concat(architectures.reduce((targets, architecture) => {
  89. return targets.concat.apply(targets, platforms.map((platform) => {
  90. return apiLevels.map(getSysImgTarget.bind(null, architecture, platform));
  91. }));
  92. }, []));
  93. oldAVDs.forEach((name) => {
  94. let avd = new AVDDescriptor(name);
  95. if (targets.indexOf(avd.api) == -1) {
  96. targets.push(avd.api);
  97. }
  98. let sysImgTarget = getSysImgTarget(avd.architecture, avd.platform, avd.api.slice('android-'.length));
  99. if (targets.indexOf(sysImgTarget) == -1) {
  100. targets.push(sysImgTarget);
  101. }
  102. });
  103. return targets;
  104. }
  105. // All the information about an android virtual device
  106. class AVDDescriptor {
  107. constructor(api, platform, architecture) {
  108. if (platform != undefined) {
  109. this.api = api;
  110. this.platform = platform;
  111. this.architecture = architecture;
  112. this.name = [api, platform, architecture].join('-');
  113. }
  114. else {
  115. this.name = api;
  116. let nameParts = this.name.split('-');
  117. this.api = nameParts[0] + '-' + nameParts[1];
  118. if (/v[0-9]+[a-z]+/.test(nameParts[nameParts.length - 1]) &&
  119. (nameParts[nameParts.length - 2].slice(0, 3) == 'arm')) {
  120. this.architecture = nameParts[nameParts.length - 2] + '-' + nameParts[nameParts.length - 1];
  121. }
  122. else {
  123. this.architecture = nameParts[nameParts.length - 1];
  124. }
  125. this.platform = this.name.slice(this.api.length + 1, -this.architecture.length - 1);
  126. }
  127. this.abi =
  128. (this.platform.toUpperCase() == 'DEFAULT' ? '' : this.platform + '/') + this.architecture;
  129. }
  130. avdName(version) {
  131. return this.name + '-v' + version + '-wd-manager';
  132. }
  133. }
  134. // Gets the descriptors for all AVDs which are possible to make given the
  135. // SDKs which were downloaded
  136. function getAVDDescriptors(sdkPath) {
  137. let deferred = q.defer();
  138. // `glob` package always prefers patterns to use `/`
  139. glob('system-images/*/*/*', { cwd: sdkPath }, (err, files) => {
  140. if (err) {
  141. deferred.reject(err);
  142. }
  143. else {
  144. deferred.resolve(files.map((file) => {
  145. // `file` could use `/` or `\`, so we use `path.normalize`
  146. let info = path.normalize(file).split(path.sep).slice(-3);
  147. return new AVDDescriptor(info[0], info[1], info[2]);
  148. }));
  149. }
  150. });
  151. return deferred.promise;
  152. }
  153. function sequentialForEach(array, func) {
  154. let ret = q(null);
  155. array.forEach((x) => {
  156. ret = ret.then(() => {
  157. return func(x);
  158. });
  159. });
  160. return ret;
  161. }
  162. // Configures the hardware.ini file for a system image of a new AVD
  163. function configureAVDHardware(sdkPath, desc) {
  164. let file = path.resolve(sdkPath, 'system-images', desc.api, desc.platform, desc.architecture, 'hardware.ini');
  165. return q.nfcall(fs.stat, file)
  166. .then((stats) => {
  167. return q.nfcall(fs.readFile, file);
  168. }, (err) => {
  169. return q('');
  170. })
  171. .then((contents) => {
  172. let config = ini.parse(contents.toString());
  173. config['hw.keyboard'] = 'yes';
  174. config['hw.battery'] = 'yes';
  175. config['hw.ramSize'] = 1024;
  176. return q.nfcall(fs.writeFile, file, ini.stringify(config));
  177. });
  178. }
  179. // Make an android virtual device
  180. function makeAVD(sdkPath, desc, version, verbose) {
  181. return runAndroidSDKCommand(sdkPath, 'delete', ['avd', '--name', desc.avdName(version)])
  182. .then(noop, noop)
  183. .then(() => {
  184. return runAndroidSDKCommand(sdkPath, 'create', ['avd', '--name', desc.avdName(version), '--target', desc.api, '--abi', desc.abi], 'pipe', respondFactory('Do you wish to create a custom hardware profile', 'no', verbose));
  185. });
  186. }
  187. // Initialize the android SDK
  188. function android(sdkPath, apiLevels, architectures, platforms, acceptLicenses, version, oldAVDs, logger, verbose) {
  189. let avdDescriptors;
  190. let tools = ['platform-tool', 'tool'];
  191. if ((config_1.Config.osType() == 'Darwin') || (config_1.Config.osType() == 'Windows_NT')) {
  192. tools.push('extra-intel-Hardware_Accelerated_Execution_Manager');
  193. }
  194. logger.info('android-sdk: Downloading additional SDK updates');
  195. downloadAndroidUpdates(sdkPath, tools, false, acceptLicenses, verbose)
  196. .then(() => {
  197. return setupHardwareAcceleration(sdkPath);
  198. })
  199. .then(() => {
  200. logger.info('android-sdk: Downloading more additional SDK updates ' +
  201. '(this may take a while)');
  202. return downloadAndroidUpdates(sdkPath, ['build-tools-24.0.0'].concat(getAndroidSDKTargets(apiLevels, architectures, platforms, oldAVDs)), true, acceptLicenses, verbose);
  203. })
  204. .then(() => {
  205. return getAVDDescriptors(sdkPath);
  206. })
  207. .then((descriptors) => {
  208. avdDescriptors = descriptors;
  209. logger.info('android-sdk: Configuring virtual device hardware');
  210. return sequentialForEach(avdDescriptors, (descriptor) => {
  211. return configureAVDHardware(sdkPath, descriptor);
  212. });
  213. })
  214. .then(() => {
  215. return sequentialForEach(avdDescriptors, (descriptor) => {
  216. logger.info('android-sdk: Setting up virtual device "' + descriptor.name + '"');
  217. return makeAVD(sdkPath, descriptor, version, verbose);
  218. });
  219. })
  220. .then(() => {
  221. return q.nfcall(fs.writeFile, path.resolve(sdkPath, 'available_avds.json'), JSON.stringify(avdDescriptors.map((descriptor) => {
  222. return descriptor.name;
  223. })));
  224. })
  225. .then(() => {
  226. logger.info('android-sdk: Initialization complete');
  227. })
  228. .done();
  229. }
  230. exports.android = android;
  231. ;
  232. function iOS(logger) {
  233. if (config_1.Config.osType() != 'Darwin') {
  234. throw new Error('Must be on a Mac to simulate iOS devices.');
  235. }
  236. try {
  237. fs.statSync('/Applications/Xcode.app');
  238. }
  239. catch (e) {
  240. logger.warn('You must install the xcode commandline tools!');
  241. }
  242. }
  243. exports.iOS = iOS;
  244. //# sourceMappingURL=initialize.js.map