1- import { homedir } from "os" ;
2- import {
3- downloadAndInstallArchive ,
4- downloadAndReadFile ,
5- getScriptsRoot ,
6- } from "./download.mjs" ;
7- import { getRustReleases } from "./githubREST.mjs" ;
8- import { join as joinPosix } from "path/posix" ;
9- import {
10- existsSync ,
11- mkdirSync ,
12- readdirSync ,
13- renameSync ,
14- rmSync ,
15- symlinkSync ,
16- } from "fs" ;
1+ import { homedir , tmpdir } from "os" ;
2+ import { downloadAndInstallGithubAsset } from "./download.mjs" ;
3+ import { getRustToolsReleases , GithubRepository } from "./githubREST.mjs" ;
4+ import { mkdirSync , renameSync } from "fs" ;
175import Logger , { LoggerSource } from "../logger.mjs" ;
186import { unknownErrorToString } from "./errorHelper.mjs" ;
19- import { env , ProgressLocation , Uri , window } from "vscode" ;
20- import type { Progress as GotProgress } from "got" ;
21- import { parse as parseToml } from "toml" ;
7+ import { env , ProgressLocation , Uri , window , workspace } from "vscode" ;
228import { promisify } from "util" ;
23- import { exec , execSync } from "child_process" ;
9+ import { exec } from "child_process" ;
2410import { dirname , join } from "path" ;
25- import { copyFile , mkdir , readdir , rm , stat } from "fs/promises" ;
26- import findPython from "./pythonHelper.mjs" ;
2711
28- const STABLE_INDEX_DOWNLOAD_URL =
29- "https://static.rust-lang.org/dist/channel-rust-stable.toml" ;
12+ /* const STABLE_INDEX_DOWNLOAD_URL =
13+ "https://static.rust-lang.org/dist/channel-rust-stable.toml";*/
3014
3115const execAsync = promisify ( exec ) ;
3216
17+ /*
3318interface IndexToml {
3419 pkg?: {
3520 // eslint-disable-next-line @typescript -eslint/naming-convention
@@ -89,14 +74,15 @@ function computeDownloadLink(release: string): string {
8974 "https://static.rust-lang.org/dist" +
9075 `/rust-${release}-${arch}-${platform}.tar.xz`
9176 );
92- }
77+ }*/
9378
9479export async function cargoInstall (
9580 packageName : string ,
9681 locked = false
9782) : Promise < boolean > {
9883 const command = process . platform === "win32" ? "cargo.exe" : "cargo" ;
9984 try {
85+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
10086 const { stdout, stderr } = await execAsync (
10187 `${ command } install ${ locked ? "--locked " : "" } ${ packageName } ` ,
10288 {
@@ -105,11 +91,14 @@ export async function cargoInstall(
10591 ) ;
10692
10793 if ( stderr ) {
94+ // TODO: find better solution
10895 if (
10996 stderr . toLowerCase ( ) . includes ( "already exists" ) ||
110- stderr . toLowerCase ( ) . includes ( "to your path" )
97+ stderr . toLowerCase ( ) . includes ( "to your path" ) ||
98+ stderr . toLowerCase ( ) . includes ( "is already installed" ) ||
99+ stderr . toLowerCase ( ) . includes ( "yanked in registry" )
111100 ) {
112- Logger . warn (
101+ Logger . debug (
113102 LoggerSource . rustUtil ,
114103 `Cargo package '${ packageName } ' is already installed ` +
115104 "or cargo bin not in PATH:" ,
@@ -132,7 +121,9 @@ export async function cargoInstall(
132121 const msg = unknownErrorToString ( error ) ;
133122 if (
134123 msg . toLowerCase ( ) . includes ( "already exists" ) ||
135- msg . toLowerCase ( ) . includes ( "to your path" )
124+ msg . toLowerCase ( ) . includes ( "to your path" ) ||
125+ msg . toLowerCase ( ) . includes ( "is already installed" ) ||
126+ msg . toLowerCase ( ) . includes ( "yanked in registry" )
136127 ) {
137128 Logger . warn (
138129 LoggerSource . rustUtil ,
@@ -314,7 +305,13 @@ export async function checkRustInstallation(): Promise<boolean> {
314305 }
315306}
316307
317- export async function installEmbeddedRust ( ) : Promise < boolean > {
308+ /**
309+ * Installs all requirements for embedded Rust development.
310+ * (if required)
311+ *
312+ * @returns {boolean } True if all requirements are met or have been installed, false otherwise.
313+ */
314+ export async function downloadAndInstallRust ( ) : Promise < boolean > {
318315 /*try {
319316 const rustup = process.platform === "win32" ? "rustup.exe" : "rustup";
320317 const cargo = process.platform === "win32" ? "cargo.exe" : "cargo";
@@ -378,8 +375,8 @@ export async function installEmbeddedRust(): Promise<boolean> {
378375 const result = await cargoInstall ( flipLink , false ) ;
379376 if ( ! result ) {
380377 void window . showErrorMessage (
381- `Failed to install cargo package '${ flipLink } '.` ,
382- "Please check the logs."
378+ `Failed to install cargo package '${ flipLink } '.` +
379+ "Please check the logs."
383380 ) ;
384381
385382 return false ;
@@ -390,8 +387,8 @@ export async function installEmbeddedRust(): Promise<boolean> {
390387 const result2 = await cargoInstall ( probeRsTools , true ) ;
391388 if ( ! result2 ) {
392389 void window . showErrorMessage (
393- `Failed to install cargo package '${ probeRsTools } '.` ,
394- "Please check the logs."
390+ `Failed to install cargo package '${ probeRsTools } '.` +
391+ "Please check the logs."
395392 ) ;
396393
397394 return false ;
@@ -402,8 +399,106 @@ export async function installEmbeddedRust(): Promise<boolean> {
402399 const result3 = await cargoInstall ( elf2uf2Rs , true ) ;
403400 if ( ! result3 ) {
404401 void window . showErrorMessage (
405- `Failed to install cargo package '${ elf2uf2Rs } '.` ,
406- "Please check the logs."
402+ `Failed to install cargo package '${ elf2uf2Rs } '.` +
403+ "Please check the logs."
404+ ) ;
405+
406+ return false ;
407+ }
408+
409+ // install cargo-generate binary
410+ const result4 = await installCargoGenerate ( ) ;
411+ if ( ! result4 ) {
412+ void window . showErrorMessage (
413+ "Failed to install cargo-generate. Please check the logs."
414+ ) ;
415+
416+ return false ;
417+ }
418+
419+ return true ;
420+ }
421+
422+ function platformToGithubMatrix ( platform : string ) : string {
423+ switch ( platform ) {
424+ case "darwin" :
425+ return "macos-latest" ;
426+ case "linux" :
427+ return "ubuntu-latest" ;
428+ case "win32" :
429+ return "windows-latest" ;
430+ default :
431+ throw new Error ( `Unsupported platform: ${ platform } ` ) ;
432+ }
433+ }
434+
435+ function archToGithubMatrix ( arch : string ) : string {
436+ switch ( arch ) {
437+ case "x64" :
438+ return "x86_64" ;
439+ case "arm64" :
440+ return "aarch64" ;
441+ default :
442+ throw new Error ( `Unsupported architecture: ${ arch } ` ) ;
443+ }
444+ }
445+
446+ async function installCargoGenerate ( ) : Promise < boolean > {
447+ const release = await getRustToolsReleases ( ) ;
448+ if ( ! release ) {
449+ Logger . error ( LoggerSource . rustUtil , "Failed to get Rust tools releases" ) ;
450+
451+ return false ;
452+ }
453+
454+ const assetName = `cargo-generate-${ platformToGithubMatrix (
455+ process . platform
456+ ) } -${ archToGithubMatrix ( process . arch ) } .zip`;
457+
458+ const tmpLoc = join ( tmpdir ( ) , "pico-vscode-rs" ) ;
459+
460+ const result = await downloadAndInstallGithubAsset (
461+ release [ 0 ] ,
462+ release [ 0 ] ,
463+ GithubRepository . rsTools ,
464+ tmpLoc ,
465+ "cargo-generate.zip" ,
466+ assetName ,
467+ "cargo-generate"
468+ ) ;
469+
470+ if ( ! result ) {
471+ Logger . error ( LoggerSource . rustUtil , "Failed to install cargo-generate" ) ;
472+
473+ return false ;
474+ }
475+
476+ const cargoBin = join ( homedir ( ) , ".cargo" , "bin" ) ;
477+
478+ try {
479+ mkdirSync ( cargoBin , { recursive : true } ) ;
480+ renameSync (
481+ join (
482+ tmpLoc ,
483+ "cargo-generate" + ( process . platform === "win32" ? ".exe" : "" )
484+ ) ,
485+ join (
486+ cargoBin ,
487+ "cargo-generate" + ( process . platform === "win32" ? ".exe" : "" )
488+ )
489+ ) ;
490+
491+ if ( process . platform !== "win32" ) {
492+ await execAsync ( `chmod +x ${ join ( cargoBin , "cargo-generate" ) } ` , {
493+ windowsHide : true ,
494+ } ) ;
495+ }
496+ } catch ( error ) {
497+ Logger . error (
498+ LoggerSource . rustUtil ,
499+ `Failed to move cargo-generate to ~/.cargo/bin: ${ unknownErrorToString (
500+ error
501+ ) } `
407502 ) ;
408503
409504 return false ;
@@ -412,4 +507,62 @@ export async function installEmbeddedRust(): Promise<boolean> {
412507 return true ;
413508}
414509
415- export
510+ export async function generateRustProject (
511+ projectFolder : string ,
512+ name : string ,
513+ flashMethod : string
514+ ) : Promise < boolean > {
515+ try {
516+ const valuesFile = join ( tmpdir ( ) , "pico-vscode" , "values.toml" ) ;
517+ await workspace . fs . createDirectory ( Uri . file ( dirname ( valuesFile ) ) ) ;
518+ await workspace . fs . writeFile (
519+ Uri . file ( valuesFile ) ,
520+ // TODO: make selectable in UI
521+ Buffer . from ( `[values]\nflash_method="${ flashMethod } "\n` , "utf-8" )
522+ ) ;
523+
524+ // TODO: fix outside function (maybe)
525+ let projectRoot = projectFolder ;
526+ if ( projectFolder . endsWith ( name ) ) {
527+ projectRoot = projectFolder . slice ( 0 , projectFolder . length - name . length ) ;
528+ }
529+
530+ // cache template and use --path
531+ const command =
532+ "cargo generate --git " +
533+ "https://github.com/rp-rs/rp2040-project-template " +
534+ ` --name ${ name } --values-file "${ valuesFile } " ` +
535+ `--destination "${ projectRoot } "` ;
536+
537+ const customEnv = { ...process . env } ;
538+ customEnv [ "PATH" ] += `${ process . platform === "win32" ? ";" : ":" } ${ join (
539+ homedir ( ) ,
540+ ".cargo" ,
541+ "bin"
542+ ) } `;
543+ // TODO: add timeout
544+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
545+ const { stdout, stderr } = await execAsync ( command , {
546+ windowsHide : true ,
547+ env : customEnv ,
548+ } ) ;
549+
550+ if ( stderr ) {
551+ Logger . error (
552+ LoggerSource . rustUtil ,
553+ `Failed to generate Rust project: ${ stderr } `
554+ ) ;
555+
556+ return false ;
557+ }
558+ } catch ( error ) {
559+ Logger . error (
560+ LoggerSource . rustUtil ,
561+ `Failed to generate Rust project: ${ unknownErrorToString ( error ) } `
562+ ) ;
563+
564+ return false ;
565+ }
566+
567+ return true ;
568+ }
0 commit comments