Skip to content

Commit da6ce4c

Browse files
committed
Support for zephyr-sdk 1.0.0 + added uninstaller to quick access
Signed-off-by: paulober <[email protected]>
1 parent e54534b commit da6ce4c

File tree

5 files changed

+174
-4
lines changed

5 files changed

+174
-4
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
"command": "raspberry-pi-pico.switchSDK",
8282
"title": "Switch Pico SDK",
8383
"category": "Raspberry Pi Pico",
84-
"enablement": "raspberry-pi-pico.isPicoProject && !raspberry-pi-pico.isRustProject && !raspberry-pi-pico.isZephyrProject"
84+
"enablement": "raspberry-pi-pico.isPicoProject && !raspberry-pi-pico.isRustProject"
8585
},
8686
{
8787
"command": "raspberry-pi-pico.switchBoard",
@@ -277,7 +277,7 @@
277277
},
278278
{
279279
"command": "raspberry-pi-pico.openUninstaller",
280-
"title": "Open Uninstaller",
280+
"title": "Manage Installed Components",
281281
"category": "Raspberry Pi Pico"
282282
},
283283
{

src/utils/semverUtil.mts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,87 @@ export function compareLtMajor(first: string, second: string): boolean {
141141
// Compare only the major versions
142142
return firstMajor < secondMajor;
143143
}
144+
145+
export type Pre = { tag: "alpha" | "beta" | "rc" | null; num: number };
146+
147+
export type ParsedVer = {
148+
major: number;
149+
minor: number;
150+
patch: number;
151+
pre: Pre; // null tag means "final"
152+
};
153+
154+
function parseVer(input: string): ParsedVer | null {
155+
const s = input.trim().toLowerCase().replace(/^v/, "");
156+
const [core, preRaw = ""] = s.split("-", 2);
157+
158+
// allow 2 or 3 core parts; pad missing with 0
159+
const parts = core.split(".").map(n => Number(n));
160+
if (parts.length < 2 || parts.length > 3) {
161+
return null;
162+
}
163+
const [major, minor, patch = 0] = parts;
164+
if (![major, minor, patch].every(n => Number.isInteger(n) && n >= 0)) {
165+
return null;
166+
}
167+
168+
let pre: Pre = { tag: null, num: 0 };
169+
if (preRaw) {
170+
// accept alpha/beta/rc with optional number (default 0)
171+
const m = /^(alpha|beta|rc)(\d+)?$/.exec(preRaw);
172+
if (!m) {
173+
return null;
174+
}
175+
pre = { tag: m[1] as Pre["tag"], num: m[2] ? Number(m[2]) : 0 };
176+
}
177+
178+
return { major, minor, patch, pre };
179+
}
180+
181+
const rank: Record<NonNullable<Pre["tag"]> | "final", number> = {
182+
alpha: 0,
183+
beta: 1,
184+
rc: 2,
185+
final: 3,
186+
};
187+
188+
/** Compare a vs b: -1 if a<b, 0 if equal, 1 if a>b */
189+
export function compareSemverPre(a: string, b: string): number {
190+
const A = parseVer(a),
191+
B = parseVer(b);
192+
if (!A || !B) {
193+
throw new Error(`Invalid version: "${a}" or "${b}"`);
194+
}
195+
196+
if (A.major !== B.major) {
197+
return A.major < B.major ? -1 : 1;
198+
}
199+
if (A.minor !== B.minor) {
200+
return A.minor < B.minor ? -1 : 1;
201+
}
202+
if (A.patch !== B.patch) {
203+
return A.patch < B.patch ? -1 : 1;
204+
}
205+
206+
// prerelease ranking: alpha < beta < rc < final
207+
const rA = rank[A.pre.tag ?? "final"];
208+
const rB = rank[B.pre.tag ?? "final"];
209+
if (rA !== rB) {
210+
return rA < rB ? -1 : 1;
211+
}
212+
213+
// same prerelease tag (or both final)
214+
if (A.pre.tag === null) {
215+
return 0;
216+
} // both final
217+
if (A.pre.num !== B.pre.num) {
218+
return A.pre.num < B.pre.num ? -1 : 1;
219+
}
220+
221+
return 0;
222+
}
223+
224+
export const geSemverPre = (a: string, b: string): boolean =>
225+
compareSemverPre(a, b) >= 0;
226+
export const ltSemverPre = (a: string, b: string): boolean =>
227+
compareSemverPre(a, b) < 0;

src/utils/setupZephyr.mts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import type { ITask } from "../models/task.mjs";
4141
import { getWestConfigValue, updateZephyrBase } from "./westConfig.mjs";
4242
import { addZephyrVariant } from "./westManifest.mjs";
4343
import LastUsedDepsStore from "./lastUsedDeps.mjs";
44+
import { geSemverPre } from "./semverUtil.mjs";
4445

4546
interface ZephyrSetupValue {
4647
cmakeMode: number;
@@ -1631,6 +1632,78 @@ export async function updateZephyrCompilerPath(
16311632
}
16321633
}
16331634

1635+
// support for v1.0.0 zephyr sdk toolchain location change
1636+
const launchUri = Uri.joinPath(workspaceUri, ".vscode", "launch.json");
1637+
if (geSemverPre(sdkVersion, "v1.0.0-beta1")) {
1638+
zephyrConfig.compilerPath.replace(
1639+
`${sdkVersion}/arm-zephyr-eabi`,
1640+
`${sdkVersion}/gnu/arm-zephyr-eabi`
1641+
);
1642+
1643+
try {
1644+
await workspace.fs.stat(launchUri);
1645+
1646+
const launchJson = JSON.parse(
1647+
td.decode(await workspace.fs.readFile(launchUri))
1648+
) as {
1649+
configurations: Array<{ name: string; armToolchainPath: string }>;
1650+
};
1651+
1652+
const picoDebugConfig = launchJson.configurations.find(
1653+
c => c.name === "Pico Debug (Zephyr)"
1654+
);
1655+
if (picoDebugConfig) {
1656+
picoDebugConfig.armToolchainPath =
1657+
picoDebugConfig.armToolchainPath.replace(
1658+
"}/arm-zephyr-eabi/",
1659+
"}/gnu/arm-zephyr-eabi/"
1660+
);
1661+
1662+
const te = new TextEncoder();
1663+
await workspace.fs.writeFile(
1664+
launchUri,
1665+
te.encode(JSON.stringify(launchJson, null, 2))
1666+
);
1667+
}
1668+
} catch {
1669+
// do nothing
1670+
}
1671+
} else {
1672+
zephyrConfig.compilerPath.replace(
1673+
`${sdkVersion}/gnu/arm-zephyr-eabi`,
1674+
`${sdkVersion}/arm-zephyr-eabi`
1675+
);
1676+
1677+
try {
1678+
await workspace.fs.stat(launchUri);
1679+
1680+
const launchJson = JSON.parse(
1681+
td.decode(await workspace.fs.readFile(launchUri))
1682+
) as {
1683+
configurations: Array<{ name: string; armToolchainPath: string }>;
1684+
};
1685+
1686+
const picoDebugConfig = launchJson.configurations.find(
1687+
c => c.name === "Pico Debug (Zephyr)"
1688+
);
1689+
if (picoDebugConfig) {
1690+
picoDebugConfig.armToolchainPath =
1691+
picoDebugConfig.armToolchainPath.replace(
1692+
"}/gnu/arm-zephyr-eabi/",
1693+
"}/arm-zephyr-eabi/"
1694+
);
1695+
1696+
const te = new TextEncoder();
1697+
await workspace.fs.writeFile(
1698+
launchUri,
1699+
te.encode(JSON.stringify(launchJson, null, 2))
1700+
);
1701+
}
1702+
} catch {
1703+
// do nothing
1704+
}
1705+
}
1706+
16341707
const te = new TextEncoder();
16351708
await workspace.fs.writeFile(
16361709
cppPropertiesUri,

src/webview/activityBar.mts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
NEW_EXAMPLE_PROJECT,
2525
NEW_PROJECT,
2626
OPEN_SDK_DOCUMENTATION,
27+
OPEN_UNINSTALLER,
2728
RUN_PROJECT,
2829
SWITCH_BOARD,
2930
SWITCH_BUILD_TYPE,
@@ -61,6 +62,7 @@ const CLEAN_CMAKE_PROJECT_LABEL = "Clean CMake";
6162
const SWITCH_BUILD_TYPE_LABEL = "Switch Build Type";
6263
const DEBUG_PROJECT_LABEL = "Debug Project";
6364
const DEBUG_LAYOUT_PROJECT_LABEL = "Debug Layout";
65+
const MANAGE_COMPONENTS_LABEL = "Manage Components";
6466

6567
export class PicoProjectActivityBar
6668
implements TreeDataProvider<QuickAccessCommand>
@@ -126,6 +128,9 @@ export class PicoProjectActivityBar
126128
// alt. "file-code"
127129
element.iconPath = new ThemeIcon("file-symlink-directory");
128130
break;
131+
case MANAGE_COMPONENTS_LABEL:
132+
element.iconPath = new ThemeIcon("package");
133+
break;
129134

130135
case DEBUG_PROJECT_LABEL:
131136
element.iconPath = new ThemeIcon("debug-alt");
@@ -253,6 +258,14 @@ export class PicoProjectActivityBar
253258
arguments: [true],
254259
}
255260
),
261+
new QuickAccessCommand(
262+
MANAGE_COMPONENTS_LABEL,
263+
TreeItemCollapsibleState.None,
264+
{
265+
command: `${extensionName}.${OPEN_UNINSTALLER}`,
266+
title: MANAGE_COMPONENTS_LABEL,
267+
}
268+
),
256269
];
257270
} else if (element.label === PROJECT_COMMANDS_PARENT_LABEL) {
258271
return [

src/webview/uninstallerPanel.mts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export class UninstallerPanel {
4545

4646
const panel = window.createWebviewPanel(
4747
UninstallerPanel.viewType,
48-
"Pico SDK Uninstaller",
48+
"Manage Installed Components",
4949
column || ViewColumn.One,
5050
getWebviewOptions(extensionUri)
5151
);
@@ -176,7 +176,7 @@ export class UninstallerPanel {
176176
}
177177

178178
private async _update(): Promise<void> {
179-
this._panel.title = "Pico SDK Uninstaller";
179+
this._panel.title = "Manage Installed Components";
180180

181181
this._panel.iconPath = Uri.joinPath(
182182
this._extensionUri,

0 commit comments

Comments
 (0)