Skip to content

Commit a5515e6

Browse files
Enhance download-tools script to fetch gdb (#381)
* Enhance download-tools script to fetch gdb * Fixup: Enforce creation of download path * Fixup: Remove duplicate doc files * Replace outdated decompress with fast-extract module * add back extract-zip for Windows * some yarn.lock clean up from prev merge --------- Signed-off-by: Jens Reinecke <jens.reinecke@arm.com> Co-authored-by: Jens Reinecke <jens.reinecke@arm.com>
1 parent 4cf358d commit a5515e6

5 files changed

Lines changed: 630 additions & 87 deletions

File tree

.vscode/launch.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@
2525
"name": "Run download-tools.ts",
2626
"skipFiles": ["<node_internals>/**"],
2727
"program": "${workspaceFolder}/scripts/download-tools.ts",
28-
"runtimeArgs": ["-r", "ts-node/register"],
29-
"args": ["pyocd"]
28+
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/ts-node",
29+
"runtimeArgs": [],
30+
"args": ["-f", "gdb"]
3031
},
3132
{
3233
"type": "node",

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@
118118
"eslint": "^9.30.1",
119119
"eslint-plugin-security": "^3.0.1",
120120
"extract-zip": "^2.0.1",
121+
"fast-extract": "^1.7.2",
122+
"file-type": "^21.0.0",
121123
"globby": "^14.1.0",
122124
"jest": "^30.0.4",
123125
"jest-html-reporter": "^4.3.0",
@@ -139,6 +141,7 @@
139141
"yargs": "^18.0.0"
140142
},
141143
"cmsis": {
142-
"pyocd": "pyocd/pyOCD@0.37.0"
144+
"pyocd": "pyocd/pyOCD@0.37.0",
145+
"gdb": "14.3.1"
143146
}
144147
}

scripts/download-tools.ts

Lines changed: 96 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } fr
2121
import path from 'path';
2222
import { downloadFile } from './file-download';
2323
import yargs from 'yargs';
24-
import extractZip from 'extract-zip';
2524
import { execSync } from 'child_process';
2625
import { hideBin } from 'yargs/helpers';
26+
import console from 'console';
27+
import fastExtract from 'fast-extract';
28+
import extractZip from 'extract-zip';
2729

2830
// OS/architecture pairs from vsce --publish
2931
type VsceTarget = 'win32-x64' | 'win32-arm64' | 'linux-x64' | 'linux-arm64' | 'darwin-x64' | 'darwin-arm64';
@@ -41,6 +43,7 @@ type ToolOptions = {
4143

4244
const TOOLS = {
4345
'pyocd': downloadPyOCD,
46+
'gdb': downloadGDB,
4447
};
4548

4649
const PACKAGE_JSON = path.resolve(__dirname, '../package.json');
@@ -104,8 +107,7 @@ async function retrieveSha256(url?: string, token?: string) {
104107
}
105108
return undefined;
106109
}
107-
108-
async function download(url: string, options?: ToolOptions & { cache_key?: string }) {
110+
async function downloadAsset(url: string, options?: ToolOptions & { cache_key?: string }) {
109111
const cachePath = (options?.cache && options?.cache_key) ? path.join(options.cache, options.cache_key) : undefined;
110112
if (cachePath && existsSync(cachePath)) {
111113
console.debug(`Found asset in cache ${cachePath} ...`);
@@ -121,7 +123,7 @@ async function download(url: string, options?: ToolOptions & { cache_key?: strin
121123

122124
const extractPath = cachePath ?? downloadFilePath.replace('.zip', '');
123125
console.debug(`Extracting to ${extractPath} ...`);
124-
await extractZip(downloadFilePath, { dir: extractPath }).catch(error => {
126+
await fastExtract(downloadFilePath, extractPath).catch(error => {
125127
throw new Error(`Failed to extract ${url}`, { cause: error });
126128
});
127129

@@ -180,7 +182,7 @@ async function downloadPyOCD(target: VsceTarget, dest: string, options?: ToolOpt
180182
cache: options?.cache,
181183
cache_key: sha256sum ? `cmsis-pyocd-${version}-${sha256sum}` : undefined
182184
};
183-
const { mode, path: extractPath } = await download(asset.url, dloptions);
185+
const { mode, path: extractPath } = await downloadAsset(asset.url, dloptions);
184186

185187
if (existsSync(destPath)) {
186188
console.debug(`Removing existing ${destPath} ...`);
@@ -202,6 +204,93 @@ async function downloadPyOCD(target: VsceTarget, dest: string, options?: ToolOpt
202204
}
203205
}
204206

207+
async function downloadGDB(target: VsceTarget, dest: string, options?: ToolOptions) {
208+
209+
const jsonVersion = getVersionFromPackageJson(PACKAGE_JSON, 'gdb');
210+
211+
if (!jsonVersion) {
212+
throw new Error('GDB version not found in package.json');
213+
}
214+
215+
const destPath = path.join(dest, 'gdb');
216+
const versionFilePath = path.join(destPath, 'version.txt');
217+
const targetFilePath = path.join(destPath, 'target.txt');
218+
219+
if (!options?.force && existsSync(versionFilePath) && existsSync(targetFilePath)) {
220+
const hasVersion = readFileSync(versionFilePath, { encoding: 'utf8' });
221+
const hasTarget = readFileSync(targetFilePath, { encoding: 'utf8' });
222+
223+
if (jsonVersion === hasVersion && target === hasTarget) {
224+
console.log(`GDB version ${jsonVersion} (${target}) already available.`);
225+
return;
226+
}
227+
}
228+
229+
console.log(`Downloading GDB version ${jsonVersion} (${target}) ...`);
230+
231+
const { build, ext } = {
232+
'win32-x64': { build: 'mingw-w64-x86_64', ext: 'zip' },
233+
'win32-arm64': { build: 'mingw-w64-x86_64', ext: 'zip' },
234+
'linux-x64': { build: 'x86_64', ext: 'tar.xz'},
235+
'linux-arm64': { build: 'aarch64', ext: 'tar.xz'},
236+
'darwin-x64': { build: 'darwin-arm64', ext: 'tar.xz'},
237+
'darwin-arm64': { build: 'darwin-arm64', ext: 'tar.xz'},
238+
}[target];
239+
240+
const asset_name = `arm-gnu-toolchain-${build}-arm-none-eabi-gdb.${ext}`;
241+
const url = `https://artifacts.tools.arm.com/arm-none-eabi-gdb/${jsonVersion}/${asset_name}`;
242+
243+
const tempfile = await import('tempfile');
244+
245+
const { mode, downloadPath } = (options?.cache)
246+
? { mode: 'cache', downloadPath: options.cache }
247+
: { mode: 'temp', downloadPath: tempfile.default() };
248+
const downloadFilePath = path.join(downloadPath, asset_name);
249+
250+
if (options?.force === false && mode === 'cache' && existsSync(downloadFilePath)) {
251+
console.debug(`Found GDB asset in cache ${downloadFilePath} ...`);
252+
} else {
253+
console.debug(`Downloading ${url} ...`);
254+
mkdirSync(downloadPath, { recursive: true });
255+
await downloadFile(url, downloadFilePath)
256+
}
257+
258+
if (existsSync(destPath)) {
259+
console.debug(`Removing existing ${destPath} ...`);
260+
rmSync(destPath, { recursive: true, force: true });
261+
}
262+
263+
console.debug(`Extracting ${downloadFilePath} to ${destPath} ...`);
264+
mkdirSync(dest, { recursive: true });
265+
266+
const {fileTypeFromFile} = await import('file-type');
267+
268+
const fileType = await fileTypeFromFile(downloadFilePath);
269+
270+
if (nodeOs.platform() === 'win32') {
271+
await extractZip(downloadFilePath, { dir: destPath }).catch(error => {
272+
throw new Error(`Failed to extract ${url}`, { cause: error });
273+
});
274+
} else {
275+
await fastExtract(downloadFilePath, destPath, { strip: 1, type: fileType?.ext }).catch(error => {
276+
throw new Error(`Failed to extract ${downloadFilePath}`, { cause: error });
277+
});
278+
}
279+
280+
281+
// Remove doc directory as it contains duplicate files (names differ only in case)
282+
// which are not supported by ZIP (VSIX) archives
283+
rmSync(path.join(destPath, 'share', 'doc'), { recursive: true, force: true });
284+
285+
if (mode === 'temp') {
286+
console.debug(`Removing temporary ${downloadPath} ...`);
287+
rmSync(downloadPath, { recursive: true, force: true });
288+
}
289+
290+
writeFileSync(versionFilePath, jsonVersion, { encoding: 'utf8' });
291+
writeFileSync(targetFilePath, target, { encoding: 'utf8' });
292+
}
293+
205294
async function main() {
206295
// Get Yarn cache directory
207296
const yarnCacheDir = execSync('yarn cache dir').toString().trim();
@@ -256,11 +345,11 @@ async function main() {
256345
}
257346

258347
for (const tool of new Set(tools)) {
259-
TOOLS[tool](target, dest, { cache: cacheFolder(cache), force });
348+
await TOOLS[tool](target, dest, { cache: cacheFolder(cache), force });
260349
}
261350
}
262351

263352
main().catch(error => {
264353
console.error(error);
265-
process.exit(1);
354+
process.exitCode = 1;
266355
});

scripts/file-download.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ export const downloadFile: DownloadFile = (url, outputPath, token?) => new Promi
2929
};
3030

3131
const req = https.request(url, requestOptions, res => {
32-
if ((res.statusCode === 301 || res.statusCode === 302) && res.headers.location) {
33-
return downloadFile(res.headers.location, outputPath, token).then(resolve, reject);
34-
}
35-
36-
if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300)) {
32+
if (res.statusCode !== undefined && (res.statusCode < 200 || res.statusCode >= 300)) {
33+
res.destroy();
34+
if ((res.statusCode === 301 || res.statusCode === 302) && res.headers.location) {
35+
return downloadFile(res.headers.location, outputPath, token).then(resolve, reject);
36+
}
3737
return reject(new Error(`Status Code: ${res.statusCode}`));
3838
}
3939

0 commit comments

Comments
 (0)