diff --git a/packages/adl-vs/Directory.Build.targets b/packages/adl-vs/Directory.Build.targets
deleted file mode 100644
index 60bd66e1c..000000000
--- a/packages/adl-vs/Directory.Build.targets
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/packages/adl-vs/Microsoft.Adl.VisualStudio.csproj b/packages/adl-vs/Microsoft.Adl.VS.props
similarity index 64%
rename from packages/adl-vs/Microsoft.Adl.VisualStudio.csproj
rename to packages/adl-vs/Microsoft.Adl.VS.props
index 63700a312..433ef4453 100644
--- a/packages/adl-vs/Microsoft.Adl.VisualStudio.csproj
+++ b/packages/adl-vs/Microsoft.Adl.VS.props
@@ -1,10 +1,9 @@
-
+
net472
Embedded
true
false
- false
Latest
Enable
true
@@ -12,24 +11,22 @@
strict
42.42.42
+
+ false
- false
- $(AssemblyName).vsix
+ $(MSBuildThisFileDirectory)$(AssemblyName).vsix
-
-
-
+
+
+
+
-
-
-
-
-
+
diff --git a/packages/adl-vs/Microsoft.Adl.VS.sln b/packages/adl-vs/Microsoft.Adl.VS.sln
new file mode 100644
index 000000000..a0d08c157
--- /dev/null
+++ b/packages/adl-vs/Microsoft.Adl.VS.sln
@@ -0,0 +1,40 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31202.260
+MinimumVisualStudioVersion = 16.0.31202.260
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Adl.VS2019", "VS2019\Microsoft.Adl.VS2019.csproj", "{3B3CF7E4-5B54-47EA-8BE3-60D8F806C69E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Adl.VS2022", "VS2022\Microsoft.Adl.VS2022.csproj", "{C7566715-9191-4D95-B673-4E569800CE32}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{128D9265-5E6B-4C7E-889E-15EE684BEFD6}"
+ ProjectSection(SolutionItems) = preProject
+ scripts\build.js = scripts\build.js
+ Directory.Build.rsp = Directory.Build.rsp
+ Microsoft.Adl.VS.props = Microsoft.Adl.VS.props
+ Microsoft.Adl.VS.targets = Microsoft.Adl.VS.targets
+ nuget.config = nuget.config
+ package.json = package.json
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3B3CF7E4-5B54-47EA-8BE3-60D8F806C69E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3B3CF7E4-5B54-47EA-8BE3-60D8F806C69E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3B3CF7E4-5B54-47EA-8BE3-60D8F806C69E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3B3CF7E4-5B54-47EA-8BE3-60D8F806C69E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C7566715-9191-4D95-B673-4E569800CE32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C7566715-9191-4D95-B673-4E569800CE32}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C7566715-9191-4D95-B673-4E569800CE32}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C7566715-9191-4D95-B673-4E569800CE32}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {1DFC3838-4459-4321-B2A1-D473D4654597}
+ EndGlobalSection
+EndGlobal
diff --git a/packages/adl-vs/Microsoft.Adl.VS.targets b/packages/adl-vs/Microsoft.Adl.VS.targets
new file mode 100644
index 000000000..2728451ff
--- /dev/null
+++ b/packages/adl-vs/Microsoft.Adl.VS.targets
@@ -0,0 +1,58 @@
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/adl-vs/Microsoft.Adl.VisualStudio.sln b/packages/adl-vs/Microsoft.Adl.VisualStudio.sln
deleted file mode 100644
index a3fdbc56f..000000000
--- a/packages/adl-vs/Microsoft.Adl.VisualStudio.sln
+++ /dev/null
@@ -1,25 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.31202.260
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Adl.VisualStudio", "Microsoft.Adl.VisualStudio.csproj", "{E56D4BDB-4A17-4BBB-81C1-258FBA49979F}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {E56D4BDB-4A17-4BBB-81C1-258FBA49979F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {E56D4BDB-4A17-4BBB-81C1-258FBA49979F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {E56D4BDB-4A17-4BBB-81C1-258FBA49979F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {E56D4BDB-4A17-4BBB-81C1-258FBA49979F}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {1DFC3838-4459-4321-B2A1-D473D4654597}
- EndGlobalSection
-EndGlobal
diff --git a/packages/adl-vs/VS2019/Microsoft.Adl.VS2019.csproj b/packages/adl-vs/VS2019/Microsoft.Adl.VS2019.csproj
new file mode 100644
index 000000000..32a8b1bb5
--- /dev/null
+++ b/packages/adl-vs/VS2019/Microsoft.Adl.VS2019.csproj
@@ -0,0 +1,15 @@
+
+
+
+
+ $(DefineConstants);VS2019
+ 16.0
+ 17.0
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/adl-vs/Properties/launchSettings.json b/packages/adl-vs/VS2019/Properties/launchSettings.json
similarity index 69%
rename from packages/adl-vs/Properties/launchSettings.json
rename to packages/adl-vs/VS2019/Properties/launchSettings.json
index 81f446102..e797e8a9d 100644
--- a/packages/adl-vs/Properties/launchSettings.json
+++ b/packages/adl-vs/VS2019/Properties/launchSettings.json
@@ -1,9 +1,9 @@
{
"profiles": {
- "Microsoft.Adl.VisualStudio": {
+ "Visual Studio Extension": {
"commandName": "Executable",
"executablePath": "$(DevEnvDir)devenv.exe",
- "commandLineArgs": "/rootSuffix Exp ../../../adl-samples/petstore",
+ "commandLineArgs": "/rootSuffix Exp ../../../../adl-samples/petstore",
"environmentVariables": {
"ADL_SERVER_NODE_OPTIONS": "--nolazy --inspect=4242",
"ADL_DEVELOPMENT_MODE": "true"
diff --git a/packages/adl-vs/source.extension.vsixmanifest b/packages/adl-vs/VS2019/source.extension.vsixmanifest
similarity index 81%
rename from packages/adl-vs/source.extension.vsixmanifest
rename to packages/adl-vs/VS2019/source.extension.vsixmanifest
index 04ecb6db7..b6439cb25 100644
--- a/packages/adl-vs/source.extension.vsixmanifest
+++ b/packages/adl-vs/VS2019/source.extension.vsixmanifest
@@ -1,14 +1,14 @@
-
+
ADL Language Support
- ADL Language Support for Visual Studio
+ ADL Language Support for Visual Studio 2019
https://github.com/azure/adl
LICENSE
-
+
@@ -19,6 +19,6 @@
-
+
diff --git a/packages/adl-vs/VS2022/Microsoft.Adl.VS2022.csproj b/packages/adl-vs/VS2022/Microsoft.Adl.VS2022.csproj
new file mode 100644
index 000000000..37fa28ddd
--- /dev/null
+++ b/packages/adl-vs/VS2022/Microsoft.Adl.VS2022.csproj
@@ -0,0 +1,15 @@
+
+
+
+
+ $(DefineConstants);VS2022
+ 17.0
+ 18.0
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/adl-vs/VS2022/Properties/launchSettings.json b/packages/adl-vs/VS2022/Properties/launchSettings.json
new file mode 100644
index 000000000..3aac303e4
--- /dev/null
+++ b/packages/adl-vs/VS2022/Properties/launchSettings.json
@@ -0,0 +1,9 @@
+{
+ "profiles": {
+ "N/A": {
+ "commandName": "Executable",
+ "executablePath": "cmd.exe",
+ "commandLineArgs": "/c echo Use Microsoft.Adl.VS2019 project to launch, even when using VS 2022."
+ }
+ }
+}
diff --git a/packages/adl-vs/VS2022/source.extension.vsixmanifest b/packages/adl-vs/VS2022/source.extension.vsixmanifest
new file mode 100644
index 000000000..7ea9cfd5d
--- /dev/null
+++ b/packages/adl-vs/VS2022/source.extension.vsixmanifest
@@ -0,0 +1,26 @@
+
+
+
+
+ ADL Language Support
+ ADL Language Support for Visual Studio 2022
+ https://github.com/azure/adl
+ LICENSE
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/adl-vs/package.json b/packages/adl-vs/package.json
index 59e684774..277473b7e 100644
--- a/packages/adl-vs/package.json
+++ b/packages/adl-vs/package.json
@@ -21,7 +21,8 @@
"node": ">=14.0.0"
},
"files": [
- "Microsoft.Adl.VisualStudio.vsix"
+ "Microsoft.Adl.VS2019.vsix",
+ "Microsoft.Adl.VS2022.vsix"
],
"scripts": {
"build": "node scripts/build.js"
diff --git a/packages/adl-vs/scripts/build.js b/packages/adl-vs/scripts/build.js
index 9750a3866..b5704e513 100644
--- a/packages/adl-vs/scripts/build.js
+++ b/packages/adl-vs/scripts/build.js
@@ -65,6 +65,6 @@ if (process.argv[2] === "--restore") {
msbuildArgs.push("/restore");
}
-msbuildArgs.push(join(dir, "Microsoft.Adl.VisualStudio.csproj"));
+msbuildArgs.push(join(dir, "Microsoft.Adl.VS.sln"));
proc = run(msbuild, msbuildArgs, { throwOnNonZeroExit: false });
process.exit(proc.status ?? 1);
diff --git a/packages/adl-vs/src/VSExtension.cs b/packages/adl-vs/src/VSExtension.cs
index c63ff27c5..7dd5f4fcf 100644
--- a/packages/adl-vs/src/VSExtension.cs
+++ b/packages/adl-vs/src/VSExtension.cs
@@ -51,7 +51,7 @@ namespace Microsoft.Adl.VisualStudio {
_outputWindow = window;
}
- public async Task ActivateAsync(CancellationToken token) {
+ public async Task ActivateAsync(CancellationToken token) {
await Task.Yield();
var options = Environment.GetEnvironmentVariable("ADL_SERVER_NODE_OPTIONS");
diff --git a/packages/adl/compiler/cli.ts b/packages/adl/compiler/cli.ts
index bf1d314cf..2171dc7b2 100644
--- a/packages/adl/compiler/cli.ts
+++ b/packages/adl/compiler/cli.ts
@@ -1,8 +1,8 @@
-import { spawnSync, SpawnSyncOptions } from "child_process";
+import { spawnSync, SpawnSyncOptionsWithStringEncoding } from "child_process";
import { mkdtemp, readdir, rmdir } from "fs/promises";
import mkdirp from "mkdirp";
import os from "os";
-import path, { join, resolve } from "path";
+import { basename, join, resolve } from "path";
import url from "url";
import yargs from "yargs";
import { CompilerOptions } from "../compiler/options.js";
@@ -140,7 +140,7 @@ async function getCompilerOptions(): Promise {
}
async function generateClient(options: CompilerOptions) {
- const clientPath = path.resolve(args["output-path"], "client");
+ const clientPath = resolve(args["output-path"], "client");
const autoRestBin = process.platform === "win32" ? "autorest.cmd" : "autorest";
const autoRestPath = new url.URL(`../../node_modules/.bin/${autoRestBin}`, import.meta.url);
@@ -168,9 +168,9 @@ async function generateClient(options: CompilerOptions) {
}
}
-async function installVsix(pkg: string, install: (vsixPath: string) => void) {
+async function installVsix(pkg: string, install: (vsixPaths: string[]) => void) {
// download npm package to temporary directory
- const temp = await mkdtemp(path.join(os.tmpdir(), "adl"));
+ const temp = await mkdtemp(join(os.tmpdir(), "adl"));
const npmArgs = ["install"];
// hide npm output unless --debug was passed to adl
@@ -183,24 +183,32 @@ async function installVsix(pkg: string, install: (vsixPath: string) => void) {
// to pass --prefix even though we're using cwd as otherwise, npm might
// find a package.json file in a parent directory and install to that
// directory.
- npmArgs.push("--prefix", ".", pkg);
+ npmArgs.push("--prefix", ".");
+
+ // To debug with a locally built package rather than pulling from npm,
+ // specify the full path to the packed .tgz using ADL_DEBUG_VSIX_TGZ
+ // environment variable.
+ npmArgs.push(process.env.ADL_DEBUG_VSIX_TGZ ?? pkg);
+
run("npm", npmArgs, { cwd: temp });
// locate .vsix
- const dir = path.join(temp, "node_modules", pkg);
+ const dir = join(temp, "node_modules", pkg);
const files = await readdir(dir);
- let vsix: string | undefined;
+ let vsixPaths: string[] = [];
for (const file of files) {
if (file.endsWith(".vsix")) {
- vsix = path.join(dir, file);
- break;
+ vsixPaths.push(join(dir, file));
}
}
- compilerAssert(vsix, `Installed ${pkg} from npm, but didn't find its .vsix file.`);
+ compilerAssert(
+ vsixPaths.length > 0,
+ `Installed ${pkg} from npm, but didn't find any .vsix files in it.`
+ );
// install extension
- install(vsix);
+ install(vsixPaths);
// delete temporary directory
await rmdir(temp, { recursive: true });
@@ -214,8 +222,8 @@ async function runCode(codeArgs: string[]) {
}
async function installVSCodeExtension() {
- await installVsix("adl-vscode", (vsix) => {
- runCode(["--install-extension", vsix]);
+ await installVsix("adl-vscode", (vsixPaths) => {
+ runCode(["--install-extension", vsixPaths[0]]);
});
}
@@ -224,28 +232,94 @@ async function uninstallVSCodeExtension() {
}
function getVsixInstallerPath(): string {
+ return getVSInstallerPath(
+ "resources/app/ServiceHub/Services/Microsoft.VisualStudio.Setup.Service/VSIXInstaller.exe"
+ );
+}
+
+function getVSWherePath(): string {
+ return getVSInstallerPath("vswhere.exe");
+}
+
+function getVSInstallerPath(relativePath: string) {
if (process.platform !== "win32") {
- console.error("error: Visual Studio extension is not supported on non-Windows");
+ console.error("error: Visual Studio extension is not supported on non-Windows.");
process.exit(1);
}
return join(
process.env["ProgramFiles(x86)"] ?? "",
- "Microsoft Visual Studio/Installer/resources/app/ServiceHub/Services/Microsoft.VisualStudio.Setup.Service",
- "VSIXInstaller.exe"
+ "Microsoft Visual Studio/Installer",
+ relativePath
);
}
+function isVSInstalled(versionRange: string) {
+ const vswhere = getVSWherePath();
+ const proc = run(vswhere, ["-property", "instanceid", "-prerelease", "-version", versionRange], {
+ stdio: [null, "pipe", "inherit"],
+ allowNotFound: true,
+ });
+ return proc.status === 0 && proc.stdout;
+}
+
+const VSIX_ALREADY_INSTALLED = 1001;
+const VSIX_NOT_INSTALLED = 1002;
+const VSIX_USER_CANCELED = 2005;
+
async function installVSExtension() {
const vsixInstaller = getVsixInstallerPath();
- await installVsix("@azure-tools/adl-vs", (vsix) => {
- run(vsixInstaller, [vsix]);
+ const versionMap = new Map([
+ [
+ "Microsoft.Adl.VS2019.vsix",
+ {
+ friendlyVersion: "2019",
+ versionRange: "[16.0, 17.0)",
+ installed: false,
+ },
+ ],
+ [
+ "Microsoft.Adl.VS2022.vsix",
+ {
+ friendlyVersion: "2022",
+ versionRange: "[17.0, 18.0)",
+ installed: false,
+ },
+ ],
+ ]);
+
+ let vsFound = false;
+ for (const entry of versionMap.values()) {
+ if (isVSInstalled(entry.versionRange)) {
+ vsFound = entry.installed = true;
+ }
+ }
+
+ if (!vsFound) {
+ console.error("error: No compatible version of Visual Studio found.");
+ process.exit(1);
+ }
+
+ await installVsix("@azure-tools/adl-vs", (vsixPaths) => {
+ for (const vsix of vsixPaths) {
+ const vsixFilename = basename(vsix);
+ const entry = versionMap.get(vsixFilename);
+ compilerAssert(entry, "Unexpected vsix filename:" + vsix);
+ if (entry.installed) {
+ console.log(`Installing extension for Visual Studio ${entry?.friendlyVersion}...`);
+ run(vsixInstaller, [vsix], {
+ allowedExitCodes: [VSIX_ALREADY_INSTALLED, VSIX_USER_CANCELED],
+ });
+ }
+ }
});
}
async function uninstallVSExtension() {
const vsixInstaller = getVsixInstallerPath();
- run(vsixInstaller, ["/uninstall:88b9492f-c019-492c-8aeb-f325a7e4cf23"]);
+ run(vsixInstaller, ["/uninstall:88b9492f-c019-492c-8aeb-f325a7e4cf23"], {
+ allowedExitCodes: [VSIX_NOT_INSTALLED, VSIX_USER_CANCELED],
+ });
}
/**
@@ -274,8 +348,10 @@ async function printInfo() {
// ENOENT checking and handles spaces poorly in some cases.
const isCmdOnWindows = ["code", "code-insiders", "npm"];
-interface RunOptions extends SpawnSyncOptions {
+interface RunOptions extends Partial {
extraEnv?: NodeJS.ProcessEnv;
+ allowNotFound?: boolean;
+ allowedExitCodes?: number[];
}
function run(command: string, commandArgs: string[], options?: RunOptions) {
@@ -293,18 +369,24 @@ function run(command: string, commandArgs: string[], options?: RunOptions) {
};
}
- const baseCommandName = path.basename(command);
+ const baseCommandName = basename(command);
if (process.platform === "win32" && isCmdOnWindows.includes(command)) {
command += ".cmd";
}
- const proc = spawnSync(command, commandArgs, {
+ const finalOptions: SpawnSyncOptionsWithStringEncoding = {
+ encoding: "utf-8",
stdio: "inherit",
...(options ?? {}),
- });
+ };
+
+ const proc = spawnSync(command, commandArgs, finalOptions);
+ if (args.debug) {
+ console.log(proc);
+ }
if (proc.error) {
- if ((proc.error as any).code === "ENOENT") {
+ if ((proc.error as any).code === "ENOENT" && !options?.allowNotFound) {
console.error(`error: Command '${baseCommandName}' not found.`);
if (args.debug) {
console.log(proc.error.stack);
@@ -315,7 +397,7 @@ function run(command: string, commandArgs: string[], options?: RunOptions) {
}
}
- if (proc.status !== 0) {
+ if (proc.status !== 0 && !options?.allowedExitCodes?.includes(proc.status ?? 0)) {
console.error(
`error: Command '${baseCommandName} ${commandArgs.join(" ")}' failed with exit code ${
proc.status