|
2 | 2 | "use strict";
|
3 | 3 |
|
4 | 4 | import * as path from "path";
|
| 5 | +import * as shelljs from "shelljs"; |
| 6 | +import * as os from "os"; |
5 | 7 |
|
6 | 8 | export class StaticConfigBase implements Config.IStaticConfig {
|
7 | 9 | public PROJECT_FILE_NAME: string = null;
|
@@ -52,24 +54,56 @@ export class StaticConfigBase implements Config.IStaticConfig {
|
52 | 54 |
|
53 | 55 | private getAdbFilePathCore(): IFuture<string> {
|
54 | 56 | return ((): string => {
|
55 |
| - let defaultAdbFilePath = path.join(__dirname, `resources/platform-tools/android/${process.platform}/adb`); |
56 | 57 | let $childProcess: IChildProcess = this.$injector.resolve("$childProcess");
|
57 | 58 |
|
58 | 59 | try {
|
59 | 60 | let proc = $childProcess.spawnFromEvent("adb", ["version"], "exit", undefined, { throwError: false }).wait();
|
60 | 61 |
|
61 | 62 | if(proc.stderr) {
|
62 |
| - return defaultAdbFilePath; |
| 63 | + return this.spawnPrivateAdb().wait(); |
63 | 64 | }
|
64 | 65 | } catch(e) {
|
65 | 66 | if(e.code === "ENOENT") {
|
66 |
| - return defaultAdbFilePath; |
| 67 | + return this.spawnPrivateAdb().wait(); |
67 | 68 | }
|
68 | 69 | }
|
69 | 70 |
|
70 | 71 | return "adb";
|
71 | 72 | }).future<string>()();
|
72 | 73 | }
|
73 | 74 |
|
| 75 | + /* |
| 76 | + Problem: |
| 77 | + 1. Adb forks itself as a server which keeps running until adb kill-server is invoked or crashes |
| 78 | + 2. On Windows running processes lock their image files due to memory mapping. Locked files prevent their parent directories from deletion and cannot be overwritten. |
| 79 | + 3. Update and uninstall scenarios are broken |
| 80 | + Solution: |
| 81 | + - Copy adb and associated files into a temporary directory. Let this copy of adb run persistently |
| 82 | + - On Posix OSes, immediately delete the file to not take file space |
| 83 | + - Tie common lib version to updates of adb, so that when we integrate a newer adb we can use it |
| 84 | + - Adb is named differently on OSes and may have additional files. The code is hairy to accommodate these differences |
| 85 | + */ |
| 86 | + private spawnPrivateAdb(): IFuture<string> { |
| 87 | + return (():string => { |
| 88 | + let $fs: IFileSystem = this.$injector.resolve("$fs"); |
| 89 | + let $childProcess: IChildProcess = this.$injector.resolve("$childProcess"); |
| 90 | + |
| 91 | + // prepare the directory to host our copy of adb |
| 92 | + let defaultAdbDirPath = path.join(__dirname, `resources/platform-tools/android/${process.platform}`); |
| 93 | + let commonLibVersion = require(path.join(__dirname, "package.json")).version; |
| 94 | + let tmpDir = path.join(os.tmpdir(), `telerik-common-lib-${commonLibVersion}`); |
| 95 | + $fs.createDirectory(tmpDir).wait(); |
| 96 | + |
| 97 | + // copy the adb and associated files |
| 98 | + shelljs.cp(path.join(defaultAdbDirPath, "*"), tmpDir); // deliberately ignore copy errors |
| 99 | + |
| 100 | + // let adb start its global server |
| 101 | + let targetAdb = path.join(tmpDir, "adb"); |
| 102 | + $childProcess.spawnFromEvent(targetAdb, ["start-server"], "exit", undefined, { throwError: false }).wait(); |
| 103 | + |
| 104 | + return targetAdb; |
| 105 | + }).future<string>()(); |
| 106 | + } |
| 107 | + |
74 | 108 | public PATH_TO_BOOTSTRAP: string;
|
75 | 109 | }
|
0 commit comments