initial commit
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execFileSync } = require('child_process');
|
||||
|
||||
const STATIC_INSTALL_DIRS = [
|
||||
process.env.SEGGER_JLINK_PATH,
|
||||
process.env.JLINK_PATH,
|
||||
'C:\\Program Files\\SEGGER\\JLink',
|
||||
'C:\\Program Files\\SEGGER\\JLink_V942',
|
||||
'C:\\Program Files (x86)\\SEGGER\\JLink',
|
||||
'C:\\Program Files (x86)\\SEGGER\\JLink_V942',
|
||||
path.join(process.env.ProgramFiles || '', 'SEGGER', 'JLink'),
|
||||
path.join(process.env.ProgramFiles || '', 'SEGGER', 'JLink_V942'),
|
||||
path.join(process.env['ProgramFiles(x86)'] || '', 'SEGGER', 'JLink'),
|
||||
path.join(process.env['ProgramFiles(x86)'] || '', 'SEGGER', 'JLink_V942'),
|
||||
'C:\\nrfutil\\bin',
|
||||
path.join(process.env.LOCALAPPDATA || '', 'Programs', 'SEGGER', 'JLink'),
|
||||
].filter(Boolean);
|
||||
|
||||
const SEGGER_ROOTS = [
|
||||
path.join(process.env.ProgramFiles || 'C:\\Program Files', 'SEGGER'),
|
||||
path.join(process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)', 'SEGGER'),
|
||||
path.join(process.env.LOCALAPPDATA || '', 'Programs', 'SEGGER'),
|
||||
];
|
||||
|
||||
/** SEGGER installer default: Program Files\SEGGER\JLink_Vxxx */
|
||||
const discoverSeggerJLinkDirs = () => {
|
||||
const discovered = [];
|
||||
|
||||
for (const root of SEGGER_ROOTS) {
|
||||
if (!root || !fs.existsSync(root)) continue;
|
||||
|
||||
let entries = [];
|
||||
try {
|
||||
entries = fs.readdirSync(root, { withFileTypes: true });
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const entry of entries) {
|
||||
if (!entry.isDirectory()) continue;
|
||||
if (!/^JLink/i.test(entry.name)) continue;
|
||||
discovered.push(path.join(root, entry.name));
|
||||
}
|
||||
}
|
||||
|
||||
return discovered;
|
||||
};
|
||||
|
||||
const parseJLinkFolderVersion = directory => {
|
||||
const name = path.basename(directory);
|
||||
const match = name.match(/^JLink(?:_V)?(.+)$/i);
|
||||
if (!match) {
|
||||
return { sortKey: 0, label: name };
|
||||
}
|
||||
|
||||
const token = match[1];
|
||||
const numeric = Number.parseInt(token, 10) || 0;
|
||||
const suffix = token.slice(String(numeric).length);
|
||||
|
||||
return {
|
||||
sortKey: numeric * 1000 + (suffix ? suffix.charCodeAt(0) : 0),
|
||||
label: name,
|
||||
};
|
||||
};
|
||||
|
||||
/** Prefer newest SEGGER pack (e.g. JLink_V942 over JLink_V898). */
|
||||
const sortInstallDirsNewestFirst = directories =>
|
||||
[...directories].sort((left, right) => {
|
||||
const leftVersion = parseJLinkFolderVersion(left);
|
||||
const rightVersion = parseJLinkFolderVersion(right);
|
||||
if (rightVersion.sortKey !== leftVersion.sortKey) {
|
||||
return rightVersion.sortKey - leftVersion.sortKey;
|
||||
}
|
||||
|
||||
try {
|
||||
return fs.statSync(right).mtimeMs - fs.statSync(left).mtimeMs;
|
||||
} catch {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
const getInstallDirectories = () => {
|
||||
const envDirs = [process.env.SEGGER_JLINK_PATH, process.env.JLINK_PATH].filter(Boolean);
|
||||
const discovered = sortInstallDirsNewestFirst(discoverSeggerJLinkDirs());
|
||||
const staticDirs = STATIC_INSTALL_DIRS.filter(
|
||||
directory => directory && !envDirs.includes(directory)
|
||||
);
|
||||
|
||||
return [...new Set([...envDirs, ...discovered, ...staticDirs])];
|
||||
};
|
||||
|
||||
const findExecutable = (name, directories = getInstallDirectories()) => {
|
||||
for (const directory of directories) {
|
||||
const candidate = path.join(directory, name);
|
||||
if (fs.existsSync(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const findOnPath = name => {
|
||||
const pathEntries = (process.env.PATH || '').split(path.delimiter).filter(Boolean);
|
||||
|
||||
for (const entry of pathEntries) {
|
||||
const candidate = path.join(entry, name);
|
||||
if (fs.existsSync(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const getNrfjprogExe = () => findExecutable('nrfjprog.exe') || findOnPath('nrfjprog.exe');
|
||||
|
||||
const getJLinkExe = () => {
|
||||
const direct = findExecutable('JLink.exe') || findOnPath('JLink.exe');
|
||||
if (direct) return direct;
|
||||
|
||||
const nrfjprogExe = getNrfjprogExe();
|
||||
if (nrfjprogExe) {
|
||||
const sibling = path.join(path.dirname(nrfjprogExe), 'JLink.exe');
|
||||
if (fs.existsSync(sibling)) return sibling;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const getJLinkGdbServerExe = () => {
|
||||
const candidates = ['JLinkGDBServerCL.exe', 'JLinkGDBServer.exe'];
|
||||
for (const name of candidates) {
|
||||
const direct = findExecutable(name) || findOnPath(name);
|
||||
if (direct) return direct;
|
||||
}
|
||||
|
||||
const nrfjprogExe = getNrfjprogExe();
|
||||
if (nrfjprogExe) {
|
||||
const directory = path.dirname(nrfjprogExe);
|
||||
for (const name of candidates) {
|
||||
const sibling = path.join(directory, name);
|
||||
if (fs.existsSync(sibling)) return sibling;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const hasSeggerRttStack = () => Boolean(getJLinkGdbServerExe());
|
||||
|
||||
let cachedDiagnostics;
|
||||
|
||||
const clearJLinkDiagnosticsCache = () => {
|
||||
cachedDiagnostics = null;
|
||||
};
|
||||
|
||||
const getJLinkDiagnostics = () => {
|
||||
if (cachedDiagnostics) return cachedDiagnostics;
|
||||
|
||||
let nrfjprogIds = [];
|
||||
const nrfjprogExe = getNrfjprogExe();
|
||||
const jlinkExe = getJLinkExe();
|
||||
const gdbServerExe = getJLinkGdbServerExe();
|
||||
|
||||
if (nrfjprogExe) {
|
||||
try {
|
||||
const output = execFileSync(nrfjprogExe, ['--ids'], {
|
||||
encoding: 'utf8',
|
||||
timeout: 10000,
|
||||
windowsHide: true,
|
||||
});
|
||||
nrfjprogIds = output
|
||||
.split(/\r?\n/)
|
||||
.map(line => line.trim())
|
||||
.filter(line => /^\d+$/.test(line));
|
||||
} catch {
|
||||
nrfjprogIds = [];
|
||||
}
|
||||
}
|
||||
|
||||
cachedDiagnostics = {
|
||||
nrfjprogExe,
|
||||
jlinkExe,
|
||||
gdbServerExe,
|
||||
gdbServerDir: gdbServerExe ? path.dirname(gdbServerExe) : '',
|
||||
nrfjprogIds,
|
||||
hasRttStack: Boolean(gdbServerExe),
|
||||
installDirectories: getInstallDirectories(),
|
||||
};
|
||||
|
||||
return cachedDiagnostics;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
STATIC_INSTALL_DIRS,
|
||||
getInstallDirectories,
|
||||
discoverSeggerJLinkDirs,
|
||||
findExecutable,
|
||||
findOnPath,
|
||||
getNrfjprogExe,
|
||||
getJLinkExe,
|
||||
getJLinkGdbServerExe,
|
||||
hasSeggerRttStack,
|
||||
getJLinkDiagnostics,
|
||||
clearJLinkDiagnosticsCache,
|
||||
};
|
||||
Reference in New Issue
Block a user