mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-17 01:25:36 -05:00
Feature/project build (#10332)
* initial build command execution * adding tests * Clean up test names * update SqltoolsService release in ADS for Build * Updating as per PR comments * updating yarn lock * Adding one more test for command run * Test fixes
This commit is contained in:
93
extensions/sql-database-projects/src/tools/buildHelper.ts
Normal file
93
extensions/sql-database-projects/src/tools/buildHelper.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { promises as fs, existsSync } from 'fs';
|
||||
import * as utils from '../common/utils';
|
||||
import { mssqlNotFound } from '../common/constants';
|
||||
|
||||
const buildDirectory = 'BuildDirectory';
|
||||
const buildFiles: string[] = [
|
||||
'Microsoft.Data.SqlClient.dll',
|
||||
'Microsoft.Data.Tools.Schema.Sql.dll',
|
||||
'Microsoft.Data.Tools.Schema.Tasks.Sql.dll',
|
||||
'Microsoft.Data.Tools.Utilities.dll',
|
||||
'Microsoft.SqlServer.Dac.dll',
|
||||
'Microsoft.SqlServer.Dac.Extensions.dll',
|
||||
'Microsoft.SqlServer.TransactSql.ScriptDom.dll',
|
||||
'Microsoft.SqlServer.Types.dll',
|
||||
'System.ComponentModel.Composition.dll',
|
||||
'Microsoft.Data.Tools.Schema.SqlTasks.targets'
|
||||
];
|
||||
|
||||
export class BuildHelper {
|
||||
|
||||
private extensionDir: string;
|
||||
private extensionBuildDir: string;
|
||||
private initialized: boolean = false;
|
||||
|
||||
constructor() {
|
||||
this.extensionDir = vscode.extensions.getExtension('Microsoft.sql-database-projects')?.extensionPath ?? '';
|
||||
this.extensionBuildDir = path.join(this.extensionDir, buildDirectory);
|
||||
}
|
||||
|
||||
// create build dlls directory
|
||||
// this should not be required. temporary solution for issue #10273
|
||||
public async createBuildDirFolder(): Promise<void> {
|
||||
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!existsSync(this.extensionBuildDir)) {
|
||||
await fs.mkdir(this.extensionBuildDir);
|
||||
}
|
||||
|
||||
const buildfilesPath = await this.getBuildDirPathFromMssqlTools();
|
||||
|
||||
buildFiles.forEach(async (fileName) => {
|
||||
if (existsSync(path.join(buildfilesPath, fileName))) {
|
||||
await fs.copyFile(path.join(buildfilesPath, fileName), path.join(this.extensionBuildDir, fileName));
|
||||
}
|
||||
});
|
||||
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
// get mssql sqltoolsservice path
|
||||
private async getBuildDirPathFromMssqlTools(): Promise<string> {
|
||||
const mssqlConfigDir = path.join(this.extensionDir, '..', 'mssql');
|
||||
|
||||
if (existsSync(path.join(mssqlConfigDir, 'config.json'))) {
|
||||
const rawConfig = await fs.readFile(path.join(mssqlConfigDir, 'config.json'));
|
||||
const config = JSON.parse(rawConfig.toString());
|
||||
const installDir = config.installDirectory?.replace('{#version#}', config.version).replace('{#platform#}', this.getPlatform());
|
||||
if (installDir) {
|
||||
return path.join(mssqlConfigDir, installDir);
|
||||
}
|
||||
}
|
||||
throw new Error(mssqlNotFound(mssqlConfigDir));
|
||||
}
|
||||
|
||||
private getPlatform(): string {
|
||||
return os.platform() === 'win32' ? 'Windows' :
|
||||
os.platform() === 'darwin' ? 'OSX' :
|
||||
os.platform() === 'linux' ? 'Linux' :
|
||||
'';
|
||||
}
|
||||
|
||||
public get extensionBuildDirPath(): string {
|
||||
return this.extensionBuildDir;
|
||||
}
|
||||
|
||||
public constructBuildArguments(projectPath: string, buildDirPath: string): string {
|
||||
projectPath = utils.getSafePath(projectPath);
|
||||
buildDirPath = utils.getSafePath(buildDirPath);
|
||||
return ` build ${projectPath} /p:NetCoreBuild=true /p:NETCoreTargetsPath=${buildDirPath}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,10 @@ import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import { isNullOrUndefined } from 'util';
|
||||
import * as cp from 'promisify-child-process';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { isNullOrUndefined } from 'util';
|
||||
import * as utils from '../common/utils';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export const DBProjectConfigurationKey: string = 'sqlDatabaseProjects';
|
||||
@@ -18,12 +20,26 @@ export const NetCoreInstallationConfirmation: string = localize('sqlDatabaseProj
|
||||
export const UpdateNetCoreLocation: string = localize('sqlDatabaseProjects.UpdateNetCoreLocation', "Update .Net Core location");
|
||||
export const InstallNetCore: string = localize('sqlDatabaseProjects.InstallNetCore', "Install .Net Core SDK");
|
||||
|
||||
const projectsOutputChannel = localize('sqlDatabaseProjects.outputChannel', "Database Projects");
|
||||
const dotnet = os.platform() === 'win32' ? 'dotnet.exe' : 'dotnet';
|
||||
|
||||
export interface DotNetCommandOptions {
|
||||
workingDirectory?: string;
|
||||
additionalEnvironmentVariables?: NodeJS.ProcessEnv;
|
||||
commandTitle?: string;
|
||||
argument?: string;
|
||||
}
|
||||
|
||||
export class NetCoreTool {
|
||||
|
||||
public findOrInstallNetCore(): void {
|
||||
private _outputChannel: vscode.OutputChannel = vscode.window.createOutputChannel(projectsOutputChannel);
|
||||
|
||||
public findOrInstallNetCore(): boolean {
|
||||
if (!this.isNetCoreInstallationPresent) {
|
||||
this.showInstallDialog();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private showInstallDialog(): void {
|
||||
@@ -40,7 +56,7 @@ export class NetCoreTool {
|
||||
});
|
||||
}
|
||||
|
||||
public get isNetCoreInstallationPresent(): Boolean {
|
||||
private get isNetCoreInstallationPresent(): Boolean {
|
||||
return (!isNullOrUndefined(this.netcoreInstallLocation) && fs.existsSync(this.netcoreInstallLocation));
|
||||
}
|
||||
|
||||
@@ -75,4 +91,71 @@ export class NetCoreTool {
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public async runDotnetCommand(options: DotNetCommandOptions): Promise<string> {
|
||||
if (options && options.commandTitle !== undefined && options.commandTitle !== null) {
|
||||
this._outputChannel.appendLine(`\t[ ${options.commandTitle} ]`);
|
||||
}
|
||||
|
||||
if (!this.findOrInstallNetCore()) {
|
||||
throw new Error(NetCoreInstallationConfirmation);
|
||||
}
|
||||
|
||||
const dotnetPath = utils.getSafePath(path.join(this.netcoreInstallLocation, dotnet));
|
||||
const command = dotnetPath + ' ' + options.argument;
|
||||
|
||||
try {
|
||||
return await this.runStreamedCommand(command, this._outputChannel, options);
|
||||
} catch (error) {
|
||||
this._outputChannel.append(localize('sqlDatabaseProject.RunCommand.ErroredOut', "\t>>> {0} … errored out: {1}", command, utils.getErrorMessage(error))); //errors are localized in our code where emitted, other errors are pass through from external components that are not easily localized
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// spawns the dotnet command with aruments and redirects the error and output to ADS output channel
|
||||
public async runStreamedCommand(command: string, outputChannel: vscode.OutputChannel, options?: DotNetCommandOptions): Promise<string> {
|
||||
const stdoutData: string[] = [];
|
||||
outputChannel.appendLine(` > ${command}`);
|
||||
|
||||
const spawnOptions = {
|
||||
cwd: options && options.workingDirectory,
|
||||
env: Object.assign({}, process.env, options && options.additionalEnvironmentVariables),
|
||||
encoding: 'utf8',
|
||||
maxBuffer: 10 * 1024 * 1024, // 10 Mb of output can be captured.
|
||||
shell: true,
|
||||
detached: false,
|
||||
windowsHide: true
|
||||
};
|
||||
|
||||
const child = cp.spawn(command, [], spawnOptions);
|
||||
outputChannel.show();
|
||||
|
||||
// Add listeners to print stdout and stderr and exit code
|
||||
child.on('exit', (code: number | null, signal: string | null) => {
|
||||
if (code !== null) {
|
||||
outputChannel.appendLine(localize('sqlDatabaseProjects.RunStreamedCommand.ExitedWithCode', " >>> {0} … exited with code: {1}", command, code));
|
||||
} else {
|
||||
outputChannel.appendLine(localize('sqlDatabaseProjects.RunStreamedCommand.ExitedWithSignal', " >>> {0} … exited with signal: {1}", command, signal));
|
||||
}
|
||||
});
|
||||
|
||||
child.stdout!.on('data', (data: string | Buffer) => {
|
||||
stdoutData.push(data.toString());
|
||||
this.outputDataChunk(data, outputChannel, localize('sqlDatabaseProjects.RunCommand.stdout', " stdout: "));
|
||||
});
|
||||
|
||||
child.stderr!.on('data', (data: string | Buffer) => {
|
||||
this.outputDataChunk(data, outputChannel, localize('sqlDatabaseProjects.RunCommand.stderr', " stderr: "));
|
||||
});
|
||||
await child;
|
||||
|
||||
return stdoutData.join('');
|
||||
}
|
||||
|
||||
private outputDataChunk(data: string | Buffer, outputChannel: vscode.OutputChannel, header: string): void {
|
||||
data.toString().split(/\r?\n/)
|
||||
.forEach(line => {
|
||||
outputChannel.appendLine(header + line);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user