Skip to content

Commit fc16079

Browse files
Get asset generation working
1 parent 9bd3a22 commit fc16079

File tree

4 files changed

+197
-27
lines changed

4 files changed

+197
-27
lines changed

src/assets.ts

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export class AssetGenerator {
5959

6060
private hasProject: boolean;
6161
private projectPath: string;
62-
private projectJsonPath: string;
62+
private projectFilePath: string;
6363
private targetFramework: string;
6464
private executableName: string;
6565
private configurationName: string;
@@ -82,9 +82,29 @@ export class AssetGenerator {
8282
// this when we allow selecting configurations.
8383
const configurationName = 'Debug';
8484

85-
const executableProjects = this.findExecutableProjects(workspaceInfo.DotNet.Projects, configurationName);
85+
// First, we'll check for .NET Core .csproj projects.
86+
if (workspaceInfo.MsBuild && workspaceInfo.MsBuild.Projects) {
87+
const executableMSBuildProjects = findExecutableMSBuildProjects(workspaceInfo.MsBuild.Projects);
8688

87-
// TODO: We arbitrarily pick the first executable projec that we find. This will need
89+
const targetMSBuildProject = executableMSBuildProjects.length > 0
90+
? executableMSBuildProjects[0]
91+
: undefined;
92+
93+
if (targetMSBuildProject) {
94+
this.hasProject = true;
95+
this.projectPath = path.dirname(targetMSBuildProject.Path);
96+
this.projectFilePath = targetMSBuildProject.Path;
97+
this.targetFramework = findNetCoreAppTargetFramework(targetMSBuildProject).ShortName;
98+
this.executableName = targetMSBuildProject.AssemblyName + ".dll";
99+
this.configurationName = configurationName;
100+
return;
101+
}
102+
}
103+
104+
// Next, we'll try looking for project.json projects.
105+
const executableProjects = findExecutableProjectJsonProjects(workspaceInfo.DotNet.Projects, configurationName);
106+
107+
// TODO: We arbitrarily pick the first executable project that we find. This will need
88108
// revisiting when we project a "start up project" selector.
89109
const targetProject = executableProjects.length > 0
90110
? executableProjects[0]
@@ -95,7 +115,7 @@ export class AssetGenerator {
95115
if (config) {
96116
this.hasProject = true;
97117
this.projectPath = targetProject.Path;
98-
this.projectJsonPath = path.join(targetProject.Path, 'project.json');
118+
this.projectFilePath = path.join(targetProject.Path, 'project.json');
99119
this.targetFramework = targetProject.Frameworks[0].ShortName;
100120
this.executableName = path.basename(config.CompilationOutputAssemblyFile);
101121
this.configurationName = configurationName;
@@ -105,28 +125,14 @@ export class AssetGenerator {
105125
return undefined;
106126
}
107127

108-
private findExecutableProjects(projects: protocol.DotNetProject[], configName: string) {
109-
let result: protocol.DotNetProject[] = [];
110-
111-
projects.forEach(project => {
112-
project.Configurations.forEach(configuration => {
113-
if (configuration.Name === configName && configuration.EmitEntryPoint === true) {
114-
if (project.Frameworks.length > 0) {
115-
result.push(project);
116-
}
117-
}
118-
});
119-
});
120-
121-
return result;
122-
}
123-
124128
public hasWebServerDependency(): boolean {
125-
if (!this.projectJsonPath) {
129+
// TODO: Update to handle .NET Core projects.
130+
131+
if (!this.projectFilePath || path.extname(this.projectFilePath) !== 'json') {
126132
return false;
127133
}
128134

129-
let projectJson = fs.readFileSync(this.projectJsonPath, 'utf8');
135+
let projectJson = fs.readFileSync(this.projectFilePath, 'utf8');
130136
projectJson = projectJson.replace(/^\uFEFF/, '');
131137

132138
let projectJsonObject: any;
@@ -253,7 +259,7 @@ export class AssetGenerator {
253259
private createBuildTaskDescription(): tasks.TaskDescription {
254260
let buildPath = '';
255261
if (this.hasProject) {
256-
buildPath = path.join('${workspaceRoot}', path.relative(this.rootPath, this.projectJsonPath));
262+
buildPath = path.join('${workspaceRoot}', path.relative(this.rootPath, this.projectFilePath));
257263
}
258264

259265
return {
@@ -275,6 +281,49 @@ export class AssetGenerator {
275281
}
276282
}
277283

284+
function findNetCoreAppTargetFramework(project: protocol.MSBuildProject) {
285+
return project.TargetFrameworks.find(tf => tf.ShortName.startsWith('netcoreapp'));
286+
}
287+
288+
function findExecutableMSBuildProjects(projects: protocol.MSBuildProject[]) {
289+
let result: protocol.MSBuildProject[] = [];
290+
291+
projects.forEach(project => {
292+
if (project.IsExe && findNetCoreAppTargetFramework(project) !== undefined) {
293+
result.push(project);
294+
}
295+
});
296+
297+
return result;
298+
}
299+
300+
function findExecutableProjectJsonProjects(projects: protocol.DotNetProject[], configurationName: string) {
301+
let result: protocol.DotNetProject[] = [];
302+
303+
projects.forEach(project => {
304+
project.Configurations.forEach(configuration => {
305+
if (configuration.Name === configurationName && configuration.EmitEntryPoint === true) {
306+
if (project.Frameworks.length > 0) {
307+
result.push(project);
308+
}
309+
}
310+
});
311+
});
312+
313+
return result;
314+
}
315+
316+
function containsDotNetCoreProjects(workspaceInfo: protocol.WorkspaceInformationResponse) {
317+
if (workspaceInfo.DotNet && findExecutableProjectJsonProjects(workspaceInfo.DotNet.Projects, 'Debug').length > 0) {
318+
return true;
319+
}
320+
321+
if (workspaceInfo.MsBuild && findExecutableMSBuildProjects(workspaceInfo.MsBuild.Projects).length > 0) {
322+
return true;
323+
}
324+
}
325+
326+
278327
interface Operations {
279328
addTasksJson?: boolean;
280329
updateTasksJson?: boolean;
@@ -416,7 +465,7 @@ export function addAssetsIfNecessary(server: OmniSharpServer): Promise<AddAssetR
416465

417466
serverUtils.requestWorkspaceInformation(server).then(info => {
418467
// If there are no .NET Core projects, we won't bother offering to add assets.
419-
if (info.DotNet && info.DotNet.Projects.length > 0) {
468+
if (containsDotNetCoreProjects(info)) {
420469
const generator = new AssetGenerator(info);
421470
return getOperations(generator).then(operations => {
422471
if (!hasOperations(operations)) {
@@ -512,7 +561,7 @@ function shouldGenerateAssets(generator: AssetGenerator) {
512561

513562
export function generateAssets(server: OmniSharpServer) {
514563
serverUtils.requestWorkspaceInformation(server).then(info => {
515-
if (info.DotNet && info.DotNet.Projects.length > 0) {
564+
if (containsDotNetCoreProjects(info)) {
516565
const generator = new AssetGenerator(info);
517566
getOperations(generator).then(operations => {
518567
if (hasOperations(operations)) {

src/features/status.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,10 @@ export function reportDocumentStatus(server: OmniSharpServer): vscode.Disposable
111111
projectStatus = undefined;
112112
defaultStatus.text = undefined;
113113

114-
vscode.Disposable.from(...localDisposables).dispose();
114+
if (localDisposables) {
115+
vscode.Disposable.from(...localDisposables).dispose();
116+
}
117+
115118
localDisposables = undefined;
116119
}));
117120

src/omnisharp/protocol.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,16 @@ export interface MSBuildProject {
294294
TargetPath: string;
295295
TargetFramework: string;
296296
SourceFiles: string[];
297+
TargetFrameworks: TargetFramework[];
298+
OutputPath: string;
299+
IsExe: boolean;
300+
IsUnityProject: boolean;
301+
}
302+
303+
export interface TargetFramework {
304+
Name: string;
305+
FriendlyName: string;
306+
ShortName: string;
297307
}
298308

299309
export interface DotNetWorkspaceInformation {

test/assets.test.ts

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import * as path from 'path';
88
import * as protocol from '../src/omnisharp/protocol';
99
import { AssetGenerator } from '../src/assets';
1010

11-
suite("Asset generation", () => {
11+
suite("Asset generation: project.json", () => {
1212
suiteSetup(() => should());
1313

1414
test("Create tasks.json for project opened in workspace", () => {
@@ -115,3 +115,111 @@ function createDotNetWorkspaceInformation(projectPath: string, compilationOutput
115115
}
116116
};
117117
}
118+
119+
suite("Asset generation: csproj", () => {
120+
suiteSetup(() => should());
121+
122+
test("Create tasks.json for project opened in workspace", () => {
123+
let rootPath = path.resolve('testRoot');
124+
let info = createMSBuildWorkspaceInformation(rootPath, 'testApp.dll', 'netcoreapp1.0');
125+
let generator = new AssetGenerator(info, rootPath);
126+
let tasksJson = generator.createTasksConfiguration();
127+
let buildPath = tasksJson.tasks[0].args[0];
128+
129+
// ${workspaceRoot}/project.json
130+
let segments = buildPath.split(path.sep);
131+
segments.should.deep.equal(['${workspaceRoot}', 'project.json']);
132+
});
133+
134+
test("Create tasks.json for nested project opened in workspace", () => {
135+
let rootPath = path.resolve('testRoot');
136+
let info = createMSBuildWorkspaceInformation(path.join(rootPath, 'nested'), 'testApp.dll', 'netcoreapp1.0');
137+
let generator = new AssetGenerator(info, rootPath);
138+
let tasksJson = generator.createTasksConfiguration();
139+
let buildPath = tasksJson.tasks[0].args[0];
140+
141+
// ${workspaceRoot}/nested/project.json
142+
let segments = buildPath.split(path.sep);
143+
segments.should.deep.equal(['${workspaceRoot}', 'nested', 'project.json']);
144+
});
145+
146+
test("Create launch.json for project opened in workspace", () => {
147+
let rootPath = path.resolve('testRoot');
148+
let info = createMSBuildWorkspaceInformation(rootPath, 'testApp.dll', 'netcoreapp1.0');
149+
let generator = new AssetGenerator(info, rootPath);
150+
let launchJson = generator.createLaunchJson(/*isWebProject*/ false);
151+
let programPath = launchJson.configurations[0].program;
152+
153+
// ${workspaceRoot}/bin/Debug/netcoreapp1.0/testApp.dll
154+
let segments = programPath.split(path.sep);
155+
segments.should.deep.equal(['${workspaceRoot}', 'bin', 'Debug', 'netcoreapp1.0', 'testApp.dll']);
156+
});
157+
158+
test("Create launch.json for nested project opened in workspace", () => {
159+
let rootPath = path.resolve('testRoot');
160+
let info = createMSBuildWorkspaceInformation(path.join(rootPath, 'nested'), 'testApp.dll', 'netcoreapp1.0');
161+
let generator = new AssetGenerator(info, rootPath);
162+
let launchJson = generator.createLaunchJson(/*isWebProject*/ false);
163+
let programPath = launchJson.configurations[0].program;
164+
165+
// ${workspaceRoot}/nested/bin/Debug/netcoreapp1.0/testApp.dll
166+
let segments = programPath.split(path.sep);
167+
segments.should.deep.equal(['${workspaceRoot}', 'nested', 'bin', 'Debug', 'netcoreapp1.0', 'testApp.dll']);
168+
});
169+
170+
test("Create launch.json for web project opened in workspace", () => {
171+
let rootPath = path.resolve('testRoot');
172+
let info = createMSBuildWorkspaceInformation(rootPath, 'testApp.dll', 'netcoreapp1.0');
173+
let generator = new AssetGenerator(info, rootPath);
174+
let launchJson = generator.createLaunchJson(/*isWebProject*/ true);
175+
let programPath = launchJson.configurations[0].program;
176+
177+
// ${workspaceRoot}/bin/Debug/netcoreapp1.0/testApp.dll
178+
let segments = programPath.split(path.sep);
179+
segments.should.deep.equal(['${workspaceRoot}', 'bin', 'Debug', 'netcoreapp1.0', 'testApp.dll']);
180+
});
181+
182+
test("Create launch.json for nested web project opened in workspace", () => {
183+
let rootPath = path.resolve('testRoot');
184+
let info = createMSBuildWorkspaceInformation(path.join(rootPath, 'nested'), 'testApp.dll', 'netcoreapp1.0');
185+
let generator = new AssetGenerator(info, rootPath);
186+
let launchJson = generator.createLaunchJson(/*isWebProject*/ true);
187+
let programPath = launchJson.configurations[0].program;
188+
189+
// ${workspaceRoot}/nested/bin/Debug/netcoreapp1.0/testApp.dll
190+
let segments = programPath.split(path.sep);
191+
segments.should.deep.equal(['${workspaceRoot}', 'nested', 'bin', 'Debug', 'netcoreapp1.0', 'testApp.dll']);
192+
});
193+
});
194+
195+
function createMSBuildWorkspaceInformation(projectPath: string, compilationOutputAssemblyFile: string, targetFrameworkShortName: string, emitEntryPoint: boolean = true) : protocol.WorkspaceInformationResponse {
196+
return {
197+
DotNet: {
198+
Projects: [
199+
{
200+
Path: projectPath,
201+
Name: '',
202+
ProjectSearchPaths: [],
203+
Configurations: [
204+
{
205+
Name: 'Debug',
206+
CompilationOutputPath: '',
207+
CompilationOutputAssemblyFile: compilationOutputAssemblyFile,
208+
CompilationOutputPdbFile: '',
209+
EmitEntryPoint: emitEntryPoint
210+
}
211+
],
212+
Frameworks: [
213+
{
214+
Name: '',
215+
FriendlyName: '',
216+
ShortName: targetFrameworkShortName
217+
}
218+
],
219+
SourceFiles: []
220+
}
221+
],
222+
RuntimePath: ''
223+
}
224+
};
225+
}

0 commit comments

Comments
 (0)