diff --git a/README.md b/README.md index 54bfac8..639c466 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,10 @@ You will need to make sure you'll have the following elements installed: - **Windows only:** [Visual Studio Build Tools](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022) with ".NET desktop build tools" workload - **Linux/macOS only:** [mono-complete](https://www.mono-project.com/docs/getting-started/install/) with msbuild, and [nuget CLI](https://www.nuget.org/downloads) +> **Note:** The `.slnx` solution format is fully supported on Windows. On Linux/macOS, +> `.slnx` requires a recent Mono installation with an updated MSBuild. If your Mono +> version does not support `.slnx`, use the classic `.sln` format instead. + ### Linux-specific Requirements On Linux, you may need to add your user to the `dialout` group to access serial ports: @@ -208,6 +212,8 @@ This extension works on: Step over in debug mode is like continue so far. We're activey working on improving this. You can setup as many break points as you want, so, if you need an equivalent of setp over, you can do this! +The new `.slnx` solution format is supported on Windows (Visual Studio Build Tools with MSBuild 17.12+). On Linux and macOS, `.slnx` support requires a recent Mono installation that includes an updated MSBuild. If your Mono version does not support `.slnx`, use the classic `.sln` format instead. + ## Developing for the VS Code extension Documentation about development for the extension can be found [here](installation.md). diff --git a/installation.md b/installation.md index 52ae88a..9b7cd4d 100644 --- a/installation.md +++ b/installation.md @@ -88,6 +88,9 @@ The following packages/tools/frameworks are required on all platforms: > The [preview](https://www.mono-project.com/download/preview/) version is recommended > as the [stable](https://www.mono-project.com/download/stable/) version is outdated. +> **Note:** The `.slnx` solution format requires a recent Mono/MSBuild version. +> If your Mono installation does not support `.slnx`, use the classic `.sln` format. + > **Note:** If you're running into a `langversion:9` error, try installing the latest mono-nightly. ### Verify PowerShell Installation diff --git a/package.json b/package.json index 11090a2..1f60604 100644 --- a/package.json +++ b/package.json @@ -242,12 +242,12 @@ "menus": { "explorer/context": [ { - "when": "resourceExtname == .sln || resourceExtname == .nfproj", + "when": "resourceExtname == .sln || resourceExtname == .slnx || resourceExtname == .nfproj", "command": "vscode-nanoframework.nfbuild", "group": "navigation@1" }, { - "when": "resourceExtname == .sln", + "when": "resourceExtname == .sln || resourceExtname == .slnx", "command": "vscode-nanoframework.nfdeploy", "group": "navigation@1" }, @@ -257,22 +257,22 @@ "group": "navigation@1" }, { - "when": "resourceExtname == .sln", + "when": "resourceExtname == .sln || resourceExtname == .slnx", "command": "vscode-nanoframework.nfadd", "group": "navigation@1" }, { - "when": "resourceExtname == .sln || resourceExtname == .nfproj", + "when": "resourceExtname == .sln || resourceExtname == .slnx || resourceExtname == .nfproj", "command": "vscode-nanoframework.nfaddnuget", "group": "navigation@2" }, { - "when": "resourceExtname == .sln || resourceExtname == .nfproj", + "when": "resourceExtname == .sln || resourceExtname == .slnx || resourceExtname == .nfproj", "command": "vscode-nanoframework.nfremovenuget", "group": "navigation@2" }, { - "when": "resourceExtname == .sln || resourceExtname == .nfproj", + "when": "resourceExtname == .sln || resourceExtname == .slnx || resourceExtname == .nfproj", "command": "vscode-nanoframework.nfupdatenuget", "group": "navigation@2" } diff --git a/src/debugger/nanoDebugSession.ts b/src/debugger/nanoDebugSession.ts index 560abf8..036ec92 100644 --- a/src/debugger/nanoDebugSession.ts +++ b/src/debugger/nanoDebugSession.ts @@ -26,7 +26,7 @@ import * as path from 'path'; * Launch request arguments for nanoFramework debugging */ interface ILaunchRequestArguments extends DebugProtocol.LaunchRequestArguments { - /** Path to the .nfproj or .sln file */ + /** Path to the .nfproj, .sln, or .slnx file */ program: string; /** Target device (COM port or IP address) */ device?: string; diff --git a/src/debugger/types/debugTypes.ts b/src/debugger/types/debugTypes.ts index 419b71a..19175ef 100644 --- a/src/debugger/types/debugTypes.ts +++ b/src/debugger/types/debugTypes.ts @@ -17,7 +17,7 @@ export interface NanoLaunchConfig { request: 'launch' | 'attach'; /** Configuration name */ name: string; - /** Path to the .nfproj or .sln file */ + /** Path to the .nfproj, .sln, or .slnx file */ program?: string; /** Target device (COM port or IP address) */ device?: string; diff --git a/src/dotnet.ts b/src/dotnet.ts index 704674f..8422591 100644 --- a/src/dotnet.ts +++ b/src/dotnet.ts @@ -15,6 +15,7 @@ import * as https from 'https'; import { Executor } from "./executor"; import * as cp from 'child_process'; import * as vscode from 'vscode'; +import { isSolutionFile } from './utils'; const mdpBuildProperties = ' -p:NFMDP_PE_Verbose=false -p:NFMDP_PE_VerboseMinimize=false -p:UseSharedCompilation=false'; @@ -474,7 +475,7 @@ async function findOrDownloadWindowsNuget(extensionPath: string): Promise f.endsWith('.sln')); + const slnFiles = fs.readdirSync(parentDir).filter(f => isSolutionFile(f)); if (slnFiles.length > 0) { return { target: path.join(parentDir, slnFiles[0]), extraArgs: '' }; } @@ -608,7 +609,7 @@ export class Dotnet { configuration = await vscode.window.showQuickPick(['Debug', 'Release'], { placeHolder: 'Select build configuration', canPickMany: false }) || 'Debug'; } if (!fileUri) { - vscode.window.showErrorMessage('No solution file selected. Please select a .sln file.'); + vscode.window.showErrorMessage('No solution file selected. Please select a .sln or .slnx file.'); return; } diff --git a/src/extension.ts b/src/extension.ts index 78cb689..aef43a4 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -13,7 +13,7 @@ import { NuGetManager, showNuGetPackagePicker, showInstalledPackagePicker, showU import { multiStepInput } from './multiStepInput'; import { - getDocumentWorkspaceFolder, solvePath, chooseSerialPort, chooseSolutionWorkspace, + getDocumentWorkspaceFolder, isSolutionFile, solvePath, chooseSerialPort, chooseSolutionWorkspace, chooseName, chooseProjectType } from './utils'; import { SerialPortCtrl } from './serialportctrl'; @@ -258,7 +258,7 @@ export async function activate(context: vscode.ExtensionContext) { if (filePath.endsWith('.nfproj')) { // Direct project file projectPath = filePath; - } else if (filePath.endsWith('.sln')) { + } else if (isSolutionFile(filePath)) { // Solution file - let user pick a project projectPath = await showProjectPicker(filePath); } @@ -321,7 +321,7 @@ export async function activate(context: vscode.ExtensionContext) { if (filePath.endsWith('.nfproj')) { projectPath = filePath; - } else if (filePath.endsWith('.sln')) { + } else if (isSolutionFile(filePath)) { projectPath = await showProjectPicker(filePath); } } else { @@ -391,7 +391,7 @@ export async function activate(context: vscode.ExtensionContext) { if (filePath.endsWith('.nfproj')) { projectPath = filePath; - } else if (filePath.endsWith('.sln')) { + } else if (isSolutionFile(filePath)) { projectPath = await showProjectPicker(filePath); } } else { diff --git a/src/utils.ts b/src/utils.ts index c169983..fd959d6 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -13,6 +13,13 @@ import { SerialPortCtrl } from "./serialportctrl"; const axios = require('axios'); +/** + * Checks whether the given file path points to a solution file (.sln or .slnx). + */ +export function isSolutionFile(filePath: string): boolean { + return filePath.endsWith('.sln') || filePath.endsWith('.slnx'); +} + /** * Gets absolute path of current open workspace in VSCode * @returns (first) absolute path of the workspace folder @@ -22,16 +29,19 @@ export function getDocumentWorkspaceFolder(): string | undefined { } /** - * Finds all *.sln files in your VSCode Workspace + * Finds all solution files (*.sln and *.slnx) in your VSCode Workspace * Shows a QuickPick window that lets the user select one of these solutions * Returns the absolute path to the selected solution * @param workspaceFolder absolute path to workspace - * @returns absolute path to selected *.sln + * @returns absolute path to selected solution file */ export async function chooseSolution(workspaceFolder: string) { // Use VS Code's built-in findFiles API instead of globby - const files = await vscode.workspace.findFiles('**/*.sln', '**/node_modules/**'); - const paths = files.map(file => file.fsPath); + const [slnFiles, slnxFiles] = await Promise.all([ + vscode.workspace.findFiles('**/*.sln', '**/node_modules/**'), + vscode.workspace.findFiles('**/*.slnx', '**/node_modules/**') + ]); + const paths = [...slnFiles, ...slnxFiles].map(file => file.fsPath); const result = await vscode.window.showQuickPick(paths, { placeHolder: 'Select the solution you would like to build/deploy', @@ -121,11 +131,11 @@ export async function chooseTarget(_toolPath: string) { } /** - * If a path to a specific .sln is given, this is used. - * Otherwise, the user is prompted with a selection of all *.sln in workspace to choose from - * @param fileUri *.sln (can be empty) + * If a path to a specific solution file (.sln/.slnx) is given, this is used. + * Otherwise, the user is prompted with a selection of all solution files in workspace to choose from + * @param fileUri solution file (can be empty) * @param workspaceFolder absolute path to workspace - * @returns absolute path to selected *.sln file + * @returns absolute path to selected solution file */ export async function solvePath(fileUri: vscode.Uri, workspaceFolder: string) { let path = fileUri ? fileUri.fsPath: '';