]> Git Repo - pico-vscode.git/blob - src/utils/vscodeConfigUtil.mts
Merge branch 'main' into main
[pico-vscode.git] / src / utils / vscodeConfigUtil.mts
1 import { readFile, writeFile } from "fs/promises";
2 import Logger from "../logger.mjs";
3 import { join } from "path";
4 import { SettingsKey } from "../settings.mjs";
5 import { type WorkspaceConfiguration, workspace } from "vscode";
6 import { dirname } from "path/posix";
7 import { compareGe } from "./semverUtil.mjs";
8
9 interface Configuration {
10   includePath: string[];
11   forcedInclude: string[];
12   compilerPath: string;
13 }
14
15 interface CppProperties {
16   configurations: Configuration[];
17 }
18
19 async function updateCppPropertiesFile(
20   file: string,
21   newSDKVersion: string,
22   newToolchainVersion: string
23 ): Promise<void> {
24   try {
25     // Read the JSON file
26     const jsonData = await readFile(file, "utf8");
27     const cppProperties: CppProperties = JSON.parse(jsonData) as CppProperties;
28
29     // Update the compilerPath value
30     cppProperties.configurations.forEach(config => {
31       // Remove the old pico-sdk includePath values set by this extension
32       config.includePath = config.includePath.filter(
33         item => !item.startsWith("${userHome}/.pico-sdk")
34       );
35       // Add the new pico-sdk includePath
36       config.includePath.push(`\${userHome}/.pico-sdk/sdk/${newSDKVersion}/**`);
37
38       // Remove the old pico-sdk forcedInclude values set by this extension
39       config.forcedInclude = config.forcedInclude.filter(
40         item => !item.startsWith("${userHome}/.pico-sdk")
41       );
42       const baseHeadersFolderName = compareGe(newSDKVersion, "2.0.0")
43         ? "pico_base_headers"
44         : "pico_base";
45       // Add the new pico-sdk forcedInclude
46       config.forcedInclude.push(
47         `\${userHome}/.pico-sdk/sdk/${newSDKVersion}` +
48           `/src/common/${baseHeadersFolderName}/include/pico.h`
49       );
50
51       // Update the compilerPath
52       config.compilerPath =
53         "${userHome}/.pico-sdk/toolchain" +
54         `/${newToolchainVersion}/bin/${
55           newToolchainVersion.includes("RISCV")
56             ? newToolchainVersion.includes("COREV")
57               ? "riscv32-corev-elf-gcc"
58               : "riscv32-unknown-elf-gcc"
59             : "arm-none-eabi-gcc"
60         }`;
61     });
62
63     // Write the updated JSON back to the file
64     const updatedJsonData = JSON.stringify(cppProperties, null, 4);
65     await writeFile(file, updatedJsonData, "utf8");
66
67     console.log("cpp_properties.json file updated successfully.");
68   } catch {
69     Logger.log("Error updating cpp_properties.json file.");
70   }
71 }
72
73 async function updateTasksFile(
74   tasksFile: string,
75   newNinjaVersion: string
76 ): Promise<void> {
77   // read tasksFile (it contains the path)
78   let content = await readFile(tasksFile, "utf8");
79
80   // if a string with following format .pico-sdk/ninja/<version>/ exists
81   // then replace <version> with the new version
82   if (content.includes(".pico-sdk/ninja/")) {
83     const oldNinjaVersion = content.match(/(?<=\.pico-sdk\/ninja\/)(.*)(?=\/)/);
84     if (oldNinjaVersion !== null) {
85       content = content.replaceAll(
86         `.pico-sdk/ninja/${oldNinjaVersion[0]}`,
87         `.pico-sdk/ninja/${newNinjaVersion}`
88       );
89     }
90   }
91
92   // save tasksFile
93   await writeFile(tasksFile, content, "utf8");
94 }
95
96 function buildCMakePath(cmakeVersion: string): string {
97   return `\${userHome}/.pico-sdk/cmake/${cmakeVersion}/bin/cmake`;
98 }
99
100 function buildCMakeHomePath(cmakeVersion: string): string {
101   return `\${HOME}/.pico-sdk/cmake/${cmakeVersion}/bin/cmake`;
102 }
103
104 function buildNinjaHomePath(ninjaVersion: string): string {
105   return `\${HOME}/.pico-sdk/ninja/${ninjaVersion}/ninja`;
106 }
107
108 async function updateSettingsFile(
109   newSDKVersion: string,
110   newToolchainVersion: string,
111   newNinjaVersion?: string,
112   newCMakeVersion?: string
113 ): Promise<void> {
114   const workspaceFolder = workspace.workspaceFolders?.[0];
115   if (!workspaceFolder) {
116     return;
117   }
118
119   const config: WorkspaceConfiguration = workspace.getConfiguration(
120     undefined,
121     workspaceFolder
122   );
123
124   for (const key of [
125     "terminal.integrated.env.windows",
126     "terminal.integrated.env.osx",
127     "terminal.integrated.env.linux",
128   ]) {
129     if (!config.has(key)) {
130       await config.update(key, {}, null);
131     }
132
133     const currentValue: { [key: string]: string } = config.get(key) ?? {};
134
135     currentValue["PICO_SDK_PATH"] = `\${${
136       key.includes("windows") ? "env:USERPROFILE" : "env:HOME"
137     }}/.pico-sdk/sdk/${newSDKVersion}`;
138     currentValue["PICO_TOOLCHAIN_PATH"] = `\${${
139       key.includes("windows") ? "env:USERPROFILE" : "env:HOME"
140     }}/.pico-sdk/toolchain/${newToolchainVersion}`;
141
142     // PATH
143     // replace env: with env_ before splitting at :
144     const oldPath = currentValue[
145       key.includes("windows") ? "Path" : "PATH"
146     ].replaceAll("${env:", "${env_");
147     Logger.log(`Oldpath ${oldPath}`);
148     const pathList = oldPath.split(key.includes("windows") ? ";" : ":");
149     let toolchainIdx = -1;
150     let cmakeIdx = -1;
151     let ninjaIdx = -1;
152
153     for (let i = 0; i < pathList.length; i++) {
154       pathList[i] = pathList[i].replaceAll("${env_", "${env:");
155       Logger.log(pathList[i]);
156       const item = pathList[i];
157       if (item.includes(".pico-sdk/toolchain")) {
158         toolchainIdx = i;
159       }
160       if (item.includes(".pico-sdk/cmake")) {
161         cmakeIdx = i;
162       }
163       if (item.includes(".pico-sdk/ninja")) {
164         ninjaIdx = i;
165       }
166     }
167
168     Logger.log(`PathList ${pathList.join(" - ")}`);
169
170     pathList[toolchainIdx] = currentValue["PICO_TOOLCHAIN_PATH"] + "/bin";
171
172     if (newCMakeVersion && newCMakeVersion !== "cmake") {
173       let newCmakePath = "";
174       if (!newCMakeVersion.includes("/")) {
175         newCmakePath = `\${${
176           key.includes("windows") ? "env:USERPROFILE" : "env:HOME"
177         }}/.pico-sdk/cmake/${newCMakeVersion}/bin`;
178       } else {
179         newCmakePath = dirname(newCMakeVersion);
180       }
181       if (cmakeIdx > 0) {
182         pathList[cmakeIdx] = newCmakePath;
183       } else {
184         pathList.splice(pathList.length - 1, 0, newCmakePath);
185       }
186     }
187
188     if (newNinjaVersion && newNinjaVersion !== "ninja") {
189       let newNinjaPath = "";
190       if (!newNinjaVersion.includes("/")) {
191         newNinjaPath = `\${${
192           key.includes("windows") ? "env:USERPROFILE" : "env:HOME"
193         }}/.pico-sdk/ninja/${newNinjaVersion}`;
194       } else {
195         newNinjaPath = dirname(newNinjaVersion);
196       }
197       if (ninjaIdx > 0) {
198         pathList[ninjaIdx] = newNinjaPath;
199       } else {
200         pathList.splice(pathList.length - 1, 0, newNinjaPath);
201       }
202     }
203
204     const newPath = pathList.join(key.includes("windows") ? ";" : ":");
205     currentValue[key.includes("windows") ? "Path" : "PATH"] = newPath;
206
207     await config.update(key, currentValue, null);
208   }
209
210   if (newNinjaVersion) {
211     await config.update(
212       "raspberry-pi-pico." + SettingsKey.ninjaPath,
213       buildNinjaHomePath(newNinjaVersion),
214       null
215     );
216   }
217   if (newCMakeVersion) {
218     await config.update(
219       "cmake.cmakePath",
220       buildCMakePath(newCMakeVersion),
221       null
222     );
223     await config.update(
224       "raspberry-pi-pico." + SettingsKey.cmakePath,
225       buildCMakeHomePath(newCMakeVersion),
226       null
227     );
228   }
229 }
230
231 async function updateLaunchFile(
232   launchFile: string,
233   newSDKVersion: string
234 ): Promise<void> {
235   let content = await readFile(launchFile, "utf8");
236
237   if (content.includes(".pico-sdk/sdk/")) {
238     const oldPicoSDKVersion = content.match(
239       /(?<=\.pico-sdk\/sdk\/)([^/]*)(?=\/)/
240     );
241     if (oldPicoSDKVersion !== null) {
242       content = content.replaceAll(
243         `.pico-sdk/sdk/${oldPicoSDKVersion[0]}`,
244         `.pico-sdk/sdk/${newSDKVersion}`
245       );
246     }
247   }
248
249   // save launchFile
250   await writeFile(launchFile, content, "utf8");
251 }
252
253 /**
254  * Updates the configs in .vscode/*.json files with the new SDK and toolchain versions.
255  *
256  * @param folder Path to the workspace folder containing the .vscode folder.
257  * @param newSDKVersion The new SDK version to set.
258  * @param newToolchainVersion The new toolchain version to set.
259  */
260 export async function updateVSCodeStaticConfigs(
261   folder: string,
262   newSDKVersion: string,
263   newToolchainVersion: string,
264   newNinjaVersion?: string,
265   newCMakeVersion?: string
266 ): Promise<void> {
267   const cppPropertiesFile = join(folder, ".vscode", "c_cpp_properties.json");
268   await updateCppPropertiesFile(
269     cppPropertiesFile,
270     newSDKVersion,
271     newToolchainVersion
272   );
273
274   await updateSettingsFile(
275     newSDKVersion,
276     newToolchainVersion,
277     newNinjaVersion,
278     newCMakeVersion
279   );
280
281   const launchFile = join(folder, ".vscode", "launch.json");
282   await updateLaunchFile(launchFile, newSDKVersion);
283
284   if (newNinjaVersion) {
285     const tasksFile = join(folder, ".vscode", "tasks.json");
286     await updateTasksFile(tasksFile, newNinjaVersion);
287   }
288 }
This page took 0.037665 seconds and 4 git commands to generate.