Skip to content

Commit 1756e25

Browse files
Improvement to offline packaging (dotnet#4781)
This PR makes various improvements to our offline package support in preparation for switching to that as our primary means of distribution. More significant changes: 1. Added support for the missing supported platforms 2. Switched to using the same schema as VS Code for the names of platforms 3. Stop dropping the debugger install complete file. The install complete file is used to do some first run OS validation logic, so it should not have been dropped by the offline installer. 4. Lots of error handling improvements: * Add a try/catch around `doOfflinePackage` so we could know which package failed * Detect and fail if a package download fails * Detect and fail if the vsce process exits with a non-zero exit code * Detect and fail if the vsce process fails to create the file we expect it to 5. Fix offline package creation on Linux-x64. This would fail due to multiple files with the same name but different casing. 6. Fix incorrect check in debugger activation code A few minor changes too: 1. Used more async APIs 2. Move the win32 check up to the start instead of failing each package 3. All the debugger references were to the '.NET Core' debugger and I dropped the 'Core' from this.
1 parent f56356b commit 1756e25

File tree

2 files changed

+67
-33
lines changed

2 files changed

+67
-33
lines changed

src/coreclr-debug/activate.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ export async function activate(thisExtension: vscode.Extension<CSharpExtensionEx
2222

2323
if (!CoreClrDebugUtil.existsSync(_debugUtil.debugAdapterDir())) {
2424
let isValidArchitecture: boolean = await checkIsValidArchitecture(platformInformation, eventStream);
25-
if (!isValidArchitecture) {
25+
// If this is a valid architecture, we should have had a debugger, so warn if we didn't, otherwise
26+
// a warning was already issued, so do nothing.
27+
if (isValidArchitecture) {
2628
eventStream.post(new DebuggerPrerequisiteFailure("[ERROR]: C# Extension failed to install the debugger package."));
2729
showInstallErrorMessage(eventStream);
2830
}
@@ -41,7 +43,7 @@ async function checkIsValidArchitecture(platformInformation: PlatformInformation
4143
if (platformInformation) {
4244
if (platformInformation.isMacOS()) {
4345
if (platformInformation.architecture === "arm64") {
44-
eventStream.post(new DebuggerPrerequisiteWarning(`[WARNING]: arm64 macOS is not officially supported by the .NET Core debugger. You may experience unexpected issues when running in this configuration.`));
46+
eventStream.post(new DebuggerPrerequisiteWarning(`[WARNING]: arm64 macOS is not officially supported by the .NET debugger. You may experience unexpected issues when running in this configuration.`));
4547
return true;
4648
}
4749

@@ -56,7 +58,7 @@ async function checkIsValidArchitecture(platformInformation: PlatformInformation
5658
}
5759
else if (platformInformation.isWindows()) {
5860
if (platformInformation.architecture === "x86") {
59-
eventStream.post(new DebuggerPrerequisiteWarning(`[WARNING]: x86 Windows is not currently supported by the .NET Core debugger. Debugging will not be available.`));
61+
eventStream.post(new DebuggerPrerequisiteWarning(`[WARNING]: x86 Windows is not supported by the .NET debugger. Debugging will not be available.`));
6062
return false;
6163
}
6264

@@ -85,7 +87,6 @@ async function completeDebuggerInstall(platformInformation: PlatformInformation,
8587

8688
// Write install.complete
8789
CoreClrDebugUtil.writeEmptyFile(_debugUtil.installCompleteFilePath());
88-
vscode.window.setStatusBarMessage('Successfully installed .NET Core Debugger.', 5000);
8990

9091
return true;
9192
}, (err) => {
@@ -100,7 +101,7 @@ async function completeDebuggerInstall(platformInformation: PlatformInformation,
100101

101102
function showInstallErrorMessage(eventStream: EventStream) {
102103
eventStream.post(new DebuggerNotInstalledFailure());
103-
vscode.window.showErrorMessage("An error occurred during installation of the .NET Core Debugger. The C# extension may need to be reinstalled.");
104+
vscode.window.showErrorMessage("An error occurred during installation of the .NET Debugger. The C# extension may need to be reinstalled.");
104105
}
105106

106107
function showDotnetToolsWarning(message: string): void {

tasks/offlinePackagingTasks.ts

Lines changed: 61 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
* Licensed under the MIT License. See License.txt in the project root for license information.
55
*--------------------------------------------------------------------------------------------*/
66

7-
import * as debugUtil from '../src/coreclr-debug/util';
87
import * as del from 'del';
98
import * as fs from 'fs';
109
import * as gulp from 'gulp';
1110
import * as path from 'path';
11+
import * as util from '../src/common';
1212
import spawnNode from '../tasks/spawnNode';
1313
import { codeExtensionPath, offlineVscodeignorePath, vscodeignorePath, vscePath, packedVsixOutputRoot } from '../tasks/projectPaths';
1414
import { CsharpLoggerObserver } from '../src/observers/CsharpLoggerObserver';
@@ -24,6 +24,13 @@ import { getAbsolutePathPackagesToInstall } from '../src/packageManager/getAbsol
2424
import { isValidDownload } from '../src/packageManager/isValidDownload';
2525

2626
gulp.task('vsix:offline:package', async () => {
27+
28+
if (process.platform === 'win32') {
29+
throw new Error('Do not build offline packages on windows. Runtime executables will not be marked executable in *nix packages.');
30+
}
31+
32+
await cleanAsync(true);
33+
2734
del.sync(vscodeignorePath);
2835

2936
fs.copyFileSync(offlineVscodeignorePath, vscodeignorePath);
@@ -39,10 +46,10 @@ gulp.task('vsix:offline:package', async () => {
3946
async function doPackageOffline() {
4047
if (commandLineOptions.retainVsix) {
4148
//if user doesnot want to clean up the existing vsix packages
42-
cleanSync(false);
49+
await cleanAsync(false);
4350
}
4451
else {
45-
cleanSync(true);
52+
await cleanAsync(true);
4653
}
4754

4855
const packageJSON = getPackageJSON();
@@ -51,36 +58,41 @@ async function doPackageOffline() {
5158
const packageName = name + '.' + version;
5259

5360
const packages = [
54-
new PlatformInformation('win32', 'x86_64'),
55-
new PlatformInformation('darwin', 'x86_64'),
56-
new PlatformInformation('linux', 'x86_64')
61+
{ platformInfo: new PlatformInformation('win32', 'x86_64'), id: "win32-x64" },
62+
{ platformInfo: new PlatformInformation('win32', 'x86'), id: "win32-ia32" },
63+
{ platformInfo: new PlatformInformation('win32', 'arm64'), id: "win32-arm64" },
64+
{ platformInfo: new PlatformInformation('linux', 'x86_64'), id: "linux-x64" },
65+
{ platformInfo: new PlatformInformation('darwin', 'x86_64'), id: "darwin-x64" },
66+
{ platformInfo: new PlatformInformation('darwin', 'arm64'), id: "darwin-arm64" },
5767
];
5868

59-
for (let platformInfo of packages) {
60-
await doOfflinePackage(platformInfo, packageName, packageJSON, packedVsixOutputRoot);
69+
for (let p of packages) {
70+
try
71+
{
72+
await doOfflinePackage(p.platformInfo, p.id, packageName, packageJSON, packedVsixOutputRoot);
73+
}
74+
catch (err)
75+
{
76+
// NOTE: Extra `\n---` at the end is because gulp will print this message following by the
77+
// stack trace of this line. So that seperates the two stack traces.
78+
throw Error(`Failed to create package ${p.id}. ${err.stack ?? err ?? '<unknown error>'}\n---`);
79+
}
6180
}
6281
}
6382

64-
function cleanSync(deleteVsix: boolean) {
65-
del.sync('install.*');
66-
del.sync('.omnisharp*');
67-
del.sync('.debugger');
68-
del.sync('.razor');
83+
async function cleanAsync(deleteVsix: boolean) {
84+
await del([ 'install.*', '.omnisharp*', '.debugger', '.razor']);
6985

7086
if (deleteVsix) {
71-
del.sync('*.vsix');
87+
await del('*.vsix');
7288
}
7389
}
7490

75-
async function doOfflinePackage(platformInfo: PlatformInformation, packageName: string, packageJSON: any, outputFolder: string) {
76-
if (process.platform === 'win32') {
77-
throw new Error('Do not build offline packages on windows. Runtime executables will not be marked executable in *nix packages.');
78-
}
79-
80-
cleanSync(false);
81-
const packageFileName = `${packageName}-${platformInfo.platform}-${platformInfo.architecture}.vsix`;
91+
async function doOfflinePackage(platformInfo: PlatformInformation, vscodePlatformId: string, packageName: string, packageJSON: any, outputFolder: string) {
92+
await cleanAsync(false);
93+
const packageFileName = `${packageName}-${vscodePlatformId}.vsix`;
8294
await install(platformInfo, packageJSON);
83-
await doPackageSync(packageFileName, outputFolder);
95+
await createPackageAsync(packageFileName, outputFolder, vscodePlatformId);
8496
}
8597

8698
// Install Tasks
@@ -89,31 +101,52 @@ async function install(platformInfo: PlatformInformation, packageJSON: any) {
89101
const logger = new Logger(message => process.stdout.write(message));
90102
let stdoutObserver = new CsharpLoggerObserver(logger);
91103
eventStream.subscribe(stdoutObserver.post);
92-
const debuggerUtil = new debugUtil.CoreClrDebugUtil(path.resolve('.'));
93104
let runTimeDependencies = getRuntimeDependenciesPackages(packageJSON);
94105
let packagesToInstall = await getAbsolutePathPackagesToInstall(runTimeDependencies, platformInfo, codeExtensionPath);
95106
let provider = () => new NetworkSettings(undefined, undefined);
96-
await downloadAndInstallPackages(packagesToInstall, provider, eventStream, isValidDownload);
97-
await debugUtil.CoreClrDebugUtil.writeEmptyFile(debuggerUtil.installCompleteFilePath());
107+
if (!(await downloadAndInstallPackages(packagesToInstall, provider, eventStream, isValidDownload)))
108+
{
109+
throw Error("Failed to download package.");
110+
}
111+
112+
// The VSIX Format doesn't allow files that differ only by case. The Linux OmniSharp package had a lowercase version of these files ('.targets') targets from mono,
113+
// and an upper case ('.Targets') from Microsoft.Build.Runtime. Remove the lowercase versions.
114+
await del([ '.omnisharp/*/omnisharp/.msbuild/Current/Bin/Workflow.targets', '.omnisharp/*/omnisharp/.msbuild/Current/Bin/Workflow.VisualBasic.targets' ]);
98115
}
99116

100117
/// Packaging (VSIX) Tasks
101-
async function doPackageSync(packageName: string, outputFolder: string) {
118+
async function createPackageAsync(packageName: string, outputFolder: string, vscodePlatformId: string) {
102119

103120
let vsceArgs = [];
121+
let packagePath = undefined;
122+
123+
if (!(await util.fileExists(vscePath))) {
124+
throw new Error(`vsce does not exist at expected location: '${vscePath}'`);
125+
}
126+
104127
vsceArgs.push(vscePath);
105128
vsceArgs.push('package'); // package command
106129

107130
if (packageName !== undefined) {
108131
vsceArgs.push('-o');
109132
if (outputFolder) {
110133
//if we have specified an output folder then put the files in that output folder
111-
vsceArgs.push(path.join(outputFolder, packageName));
134+
packagePath = path.join(outputFolder, packageName);
135+
vsceArgs.push(packagePath);
112136
}
113137
else {
114138
vsceArgs.push(packageName);
115139
}
116140
}
117141

118-
return spawnNode(vsceArgs);
142+
const spawnResult = await spawnNode(vsceArgs);
143+
if (spawnResult.code != 0) {
144+
throw new Error(`'${vsceArgs.join(' ')}' failed with code ${spawnResult.code}.`);
145+
}
146+
147+
if (packagePath) {
148+
if (!(await util.fileExists(packagePath))) {
149+
throw new Error(`vsce failed to create: '${packagePath}'`);
150+
}
151+
}
119152
}

0 commit comments

Comments
 (0)