initial commit

This commit is contained in:
2026-05-18 17:36:05 +09:00
commit fadeba40cc
24 changed files with 12173 additions and 0 deletions
+208
View File
@@ -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,
};