Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dotnet sln add does not work for projects with extension .msbuildproj #11292

Open
bash opened this issue Apr 15, 2020 · 4 comments
Open

dotnet sln add does not work for projects with extension .msbuildproj #11292

bash opened this issue Apr 15, 2020 · 4 comments
Milestone

Comments

@bash
Copy link

bash commented Apr 15, 2020

Steps to reproduce

  1. Create a new solution: dotnet new sln
  2. Create a new project with the extension .msbuildproj (Foo.msbuildproj):
    <?xml version="1.0" encoding="utf-8"?>
    <Project Sdk="Microsoft.Build.NoTargets/1.0.94">
        <PropertyGroup>
            <TargetFramework>netstandard2.1</TargetFramework>
        </PropertyGroup>
    </Project>
  3. Add the project to the solution: dotnet sln add Foo.msbuildproj

Expected behaviour

Project `Foo.csproj` added to the solution.

Actual behaviour

Project '/.../Foo.msbuildproj' has an unknown project type and cannot be added to the solution file. Contact your SDK provider for support.
@bash bash changed the title dotnet sln add does not work for .msbuildproj dotnet sln add does not work for projects with extension .msbuildproj Apr 15, 2020
@marcpopMSFT marcpopMSFT added the untriaged Request triage from a team member label Apr 16, 2020
@dsplaisted dsplaisted removed the untriaged Request triage from a team member label Jun 3, 2020
@dsplaisted dsplaisted removed their assignment Jun 3, 2020
@dsplaisted dsplaisted added this to the Backlog milestone Jun 3, 2020
@kkirkfield
Copy link

TLDR / Current Workaround

If you own the custom SDK you are importing and you want to have a custom file extension, then set the DefaultProjectTypeGuid property in your Sdk.props file like below.

<Project>

  <PropertyGroup>
    <DefaultProjectTypeGuid>{2F08BC15-189B-4804-B644-653F34C968A8}</DefaultProjectTypeGuid>
  </PropertyGroup>

</Project>

If you don't own the custom SDK and can't make changes to it, then add a ProjectTypeGuids property to your project with a random GUID like below. Then your project file extension can be whatever you want.

<Project Sdk="MyCustom.Sdk">

  <PropertyGroup>
    <ProjectTypeGuids>{2F08BC15-189B-4804-B644-653F34C968A8}</ProjectTypeGuids>
  </PropertyGroup>

</Project>

Research

Currently the dotnet CLI gives an error when trying to add a project to a solution that has an .msbuildproj file extension. This file extension is commonly used for projects using the Microsoft.Build.NoTargets SDK and other similar custom SDKs.

The error is being set at this location because the project type GUID is null.

if (string.IsNullOrEmpty(slnProject.TypeGuid))
{
Reporter.Error.WriteLine(
string.Format(
CommonLocalizableStrings.UnsupportedProjectType,
projectInstance.FullPath));
return;
}

The project type GUID is set to null here because the project root element does not have a ProjectTypeGuids property set, and there is no DefaultProjectTypeGuid property set. GetProjectTypeGuid() does not feel like the correct place to define a new value for msbuildproj files. A better option is to set a DefaultProjectTypeGuid property for msbuildproj files.

TypeGuid = rootElement.GetProjectTypeGuid() ?? projectInstance.GetDefaultProjectTypeGuid(),

public static class ProjectRootElementExtensions
{
public static string GetProjectTypeGuid(this ProjectRootElement rootElement)
{
return rootElement
.Properties
.FirstOrDefault(p => string.Equals(p.Name, "ProjectTypeGuids", StringComparison.OrdinalIgnoreCase))
?.Value
.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
.LastOrDefault(g => !string.IsNullOrWhiteSpace(g));
}
}

GetDefaultProjectTypeGuid() below is where the default value is being received.

public static string GetDefaultProjectTypeGuid(this ProjectInstance projectInstance)
{
return projectInstance.GetPropertyValue("DefaultProjectTypeGuid");
}

Different project SDKs in the dotnet SDK set the DefaultProjectTypeGuid property. To avoid projects using the Microsoft.Build.NoTargets SDK and other custom SDKs having to define a project type GUID, we can add a DefaultProjectTypeGuid property for projects using the msbuildproj extension. Custom SDKs should still opt to have their own file extension and set the DefaultProjectTypeGuid property, but this gives a fallback for msbuildproj files where the SDK doesn't define this.

See the below example of one place where the DefaultProjectTypeGuid property is set based on file extension. This specific example is to fix a cross targeting bug, but we can apply the same logic.

<!-- This exists as a workaround for https://github.com/Microsoft/msbuild/issues/3558 -->
<PropertyGroup Condition="'$(DefaultProjectTypeGuid)' == ''">
<DefaultProjectTypeGuid Condition="'$(MSBuildProjectExtension)' == '.csproj'">{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</DefaultProjectTypeGuid>
<DefaultProjectTypeGuid Condition="'$(MSBuildProjectExtension)' == '.vbproj'">{F184B08F-C81C-45F6-A57F-5ABD9991F28F}</DefaultProjectTypeGuid>
<!-- Note: F# sets DefaultProjectTypeGuid in the F# SDK -->
</PropertyGroup>

Originally I was going to do a pull request with the fix for this after line 42 below. That would allow it to fallback after the language targets and the cross targeting target referenced above would run.

<Import Project="$(LanguageTargets)"/>
<Import Project="$(MSBuildThisFileDirectory)..\targets\Microsoft.NET.Sdk.CrossTargeting.targets"
Condition="'$(IsCrossTargetingBuild)' == 'true'"/>
<Import Project="$(MSBuildThisFileDirectory)..\targets\Microsoft.NET.Sdk.targets"
Condition="'$(IsCrossTargetingBuild)' != 'true'"/>

While this did work for project files using a dotnet SDK, it didn't work for project files targeting custom SDKs like Microsoft.Build.NoTargets. For the fallback to apply to all projects regardless of SDK, I believe this would need to be added somewhere in MSBuild. I'm currently having trouble finding the best place to put this in MSBuild as the prop and target imports are hard to follow. If anyone has any ideas let me know below.

See also

@KalleOlaviNiemitalo
Copy link
Contributor

I see the sln file format requires a project type GUID. Does the choice of GUID have any effects on command-line tools such as MSBuild and NuGet?

@kkirkfield
Copy link

@KalleOlaviNiemitalo I looked through the DotNet SDK and MSBuild and didn't see anywhere that uses these project type GUIDs other than for the solution file, so I don't think it matters what GUID you choose as long as it is unique. I think Visual Studio may use these GUIDs in some way for example to enable/disable tooling for different workloads, but I can't be sure of that.

@kkirkfield
Copy link

@KalleOlaviNiemitalo If you use the Microsoft.Build.NoTargets SDK and need multitargeting, and you want to be able to open and build in Visual Studio, then the GUID you choose may be important. See this comment and thread for more info: microsoft/MSBuildSdks#155 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants