Skip to content

Commit

Permalink
Fix issue with C# projects referencing F# projects
Browse files Browse the repository at this point in the history
This fixes a long-standing problem where C# projects would attempt to load project references
to non-C# projects. This is related to the problem on not adding file references for C# project references
to avoid duplicate references. Now, we're a bit more selective when we decide to throw out a project or file
reference: We check the file path and see if it ends with ".csproj".
  • Loading branch information
DustinCampbell committed Nov 4, 2017
1 parent 4a17dc9 commit 6d8813f
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 9 deletions.
3 changes: 2 additions & 1 deletion build.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
"ProjectAndSolution",
"ProjectAndSolutionWithProjectSection",
"TwoProjectsWithSolution",
"ProjectWithGeneratedFile"
"ProjectWithGeneratedFile",
"CSharpAndFSharp"
],
"LegacyTestAssets": [
"LegacyNUnitTestProject",
Expand Down
3 changes: 2 additions & 1 deletion src/OmniSharp.MSBuild/MSBuildProjectSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,8 @@ private void UpdateProjectReferences(Project project, ImmutableArray<string> pro
{
if (!_projects.TryGetValue(projectReferencePath, out var referencedProject))
{
if (File.Exists(projectReferencePath))
if (File.Exists(projectReferencePath) &&
projectReferencePath.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase))
{
_logger.LogInformation($"Found referenced project outside root directory: {projectReferencePath}");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ private static class MetadataNames
public const string FullPath = nameof(FullPath);
public const string IsImplicitlyDefined = nameof(IsImplicitlyDefined);
public const string Project = nameof(Project);
public const string OriginalItemSpec = nameof(OriginalItemSpec);
public const string ReferenceSourceTarget = nameof(ReferenceSourceTarget);
public const string Version = nameof(Version);
}
Expand Down
43 changes: 36 additions & 7 deletions src/OmniSharp.MSBuild/ProjectFile/ProjectFileInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,38 @@ private static ProjectData CreateProjectData(ProjectInstance projectInstance, Im

var sourceFiles = GetFullPaths(
projectInstance.GetItems(ItemNames.Compile), filter: FileNameIsNotGenerated);
var projectReferences = GetFullPaths(projectInstance.GetItems(ItemNames.ProjectReference));
var references = GetFullPaths(
projectInstance.GetItems(ItemNames.ReferencePath).Where(ReferenceSourceTargetIsNotProjectReference));

var projectReferences = GetFullPaths(
projectInstance.GetItems(ItemNames.ProjectReference), filter: IsCSharpProject);

var references = ImmutableArray.CreateBuilder<string>();
foreach (var referencePathItem in projectInstance.GetItems(ItemNames.ReferencePath))
{
var referenceSourceTarget = referencePathItem.GetMetadataValue(MetadataNames.ReferenceSourceTarget);

if (StringComparer.OrdinalIgnoreCase.Equals(referenceSourceTarget, ItemNames.ProjectReference))
{
// If the reference was sourced from a project reference, we have two choices:
//
// 1. If the reference is a C# project reference, we shouldn't add it because it'll just duplicate
// the project reference.
// 2. If the reference is *not* a C# project reference, we should keep this reference because the
// project reference was already removed.

var originalItemSpec = referencePathItem.GetMetadataValue(MetadataNames.OriginalItemSpec);
if (originalItemSpec.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase))
{
continue;
}
}

var fullPath = referencePathItem.GetMetadataValue(MetadataNames.FullPath);
if (!string.IsNullOrEmpty(fullPath))
{
references.Add(fullPath);
}
}

var packageReferences = GetPackageReferences(projectInstance.GetItems(ItemNames.PackageReference));
var analyzers = GetFullPaths(projectInstance.GetItems(ItemNames.Analyzer));

Expand All @@ -216,7 +245,7 @@ private static ProjectData CreateProjectData(ProjectInstance projectInstance, Im
targetFramework, targetFrameworks,
outputKind, languageVersion, allowUnsafeCode, documentationFile, preprocessorSymbolNames, suppressDiagnosticIds,
signAssembly, assemblyOriginatorKeyFile,
sourceFiles, projectReferences, references, packageReferences, analyzers);
sourceFiles, projectReferences, references.ToImmutable(), packageReferences, analyzers);
}

public ProjectFileInfo Reload(
Expand Down Expand Up @@ -273,11 +302,11 @@ private static Dictionary<string, string> GetGlobalProperties(MSBuildInstance ms
return globalProperties;
}

private static bool ReferenceSourceTargetIsNotProjectReference(ProjectItemInstance item)
=> item.GetMetadataValue(MetadataNames.ReferenceSourceTarget) != ItemNames.ProjectReference;
private static bool IsCSharpProject(string filePath)
=> filePath.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase);

private static bool FileNameIsNotGenerated(string filePath)
=> !Path.GetFileName(filePath).StartsWith("TemporaryGeneratedFile_");
=> !Path.GetFileName(filePath).StartsWith("TemporaryGeneratedFile_", StringComparison.OrdinalIgnoreCase);

private static ImmutableArray<string> GetFullPaths(IEnumerable<ProjectItemInstance> items, Func<string, bool> filter = null)
{
Expand Down
48 changes: 48 additions & 0 deletions test-assets/test-projects/CSharpAndFSharp/CSharpAndFSharp.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26124.0
MinimumVisualStudioVersion = 15.0.26124.0
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "csharp-console", "csharp-console\csharp-console.csproj", "{ED247A90-AFBE-4717-8F32-AA8BFC2C8627}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "fsharp-lib", "fsharp-lib\fsharp-lib.fsproj", "{4FCFD6A3-2860-42C4-B98E-ADAEC268B928}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{ED247A90-AFBE-4717-8F32-AA8BFC2C8627}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ED247A90-AFBE-4717-8F32-AA8BFC2C8627}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ED247A90-AFBE-4717-8F32-AA8BFC2C8627}.Debug|x64.ActiveCfg = Debug|x64
{ED247A90-AFBE-4717-8F32-AA8BFC2C8627}.Debug|x64.Build.0 = Debug|x64
{ED247A90-AFBE-4717-8F32-AA8BFC2C8627}.Debug|x86.ActiveCfg = Debug|x86
{ED247A90-AFBE-4717-8F32-AA8BFC2C8627}.Debug|x86.Build.0 = Debug|x86
{ED247A90-AFBE-4717-8F32-AA8BFC2C8627}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ED247A90-AFBE-4717-8F32-AA8BFC2C8627}.Release|Any CPU.Build.0 = Release|Any CPU
{ED247A90-AFBE-4717-8F32-AA8BFC2C8627}.Release|x64.ActiveCfg = Release|x64
{ED247A90-AFBE-4717-8F32-AA8BFC2C8627}.Release|x64.Build.0 = Release|x64
{ED247A90-AFBE-4717-8F32-AA8BFC2C8627}.Release|x86.ActiveCfg = Release|x86
{ED247A90-AFBE-4717-8F32-AA8BFC2C8627}.Release|x86.Build.0 = Release|x86
{4FCFD6A3-2860-42C4-B98E-ADAEC268B928}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4FCFD6A3-2860-42C4-B98E-ADAEC268B928}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FCFD6A3-2860-42C4-B98E-ADAEC268B928}.Debug|x64.ActiveCfg = Debug|x64
{4FCFD6A3-2860-42C4-B98E-ADAEC268B928}.Debug|x64.Build.0 = Debug|x64
{4FCFD6A3-2860-42C4-B98E-ADAEC268B928}.Debug|x86.ActiveCfg = Debug|x86
{4FCFD6A3-2860-42C4-B98E-ADAEC268B928}.Debug|x86.Build.0 = Debug|x86
{4FCFD6A3-2860-42C4-B98E-ADAEC268B928}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FCFD6A3-2860-42C4-B98E-ADAEC268B928}.Release|Any CPU.Build.0 = Release|Any CPU
{4FCFD6A3-2860-42C4-B98E-ADAEC268B928}.Release|x64.ActiveCfg = Release|x64
{4FCFD6A3-2860-42C4-B98E-ADAEC268B928}.Release|x64.Build.0 = Release|x64
{4FCFD6A3-2860-42C4-B98E-ADAEC268B928}.Release|x86.ActiveCfg = Release|x86
{4FCFD6A3-2860-42C4-B98E-ADAEC268B928}.Release|x86.Build.0 = Release|x86
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace csharp_console
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<ProjectReference Include="..\fsharp-lib\fsharp-lib.fsproj" />
</ItemGroup>

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace lib

module Say =
let hello name =
printfn "Hello %s" name
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Compile Include="Library.fs" />
</ItemGroup>

</Project>
17 changes: 17 additions & 0 deletions tests/OmniSharp.MSBuild.Tests/WorkspaceInformationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,23 @@ public async Task ProjectWithSdkProperty()
}
}

[Fact]
public async Task CSharpAndFSharp()
{
using (var testProject = await TestAssets.Instance.GetTestProjectAsync("CSharpAndFSharp"))
using (var host = CreateOmniSharpHost(testProject.Directory))
{
var workspaceInfo = await GetWorkspaceInfoAsync(host);

Assert.NotNull(workspaceInfo.Projects);
Assert.Equal(1, workspaceInfo.Projects.Count);

var project = workspaceInfo.Projects[0];
Assert.Equal("csharp-console.csproj", Path.GetFileName(project.Path));
Assert.Equal(3, project.SourceFiles.Count);
}
}

[ConditionalFact(typeof(WindowsOnly))]
public async Task AntlrGeneratedFiles()
{
Expand Down

0 comments on commit 6d8813f

Please sign in to comment.