Skip to content

Instantly share code, notes, and snippets.

@TheRealPiotrP
Last active November 23, 2016 19:43
Show Gist options
  • Select an option

  • Save TheRealPiotrP/9ab355cd48a5539c9fc31bafd98df5aa to your computer and use it in GitHub Desktop.

Select an option

Save TheRealPiotrP/9ab355cd48a5539c9fc31bafd98df5aa to your computer and use it in GitHub Desktop.

Goals

  1. Remove SDK PackageRef
  2. Remove pre & post imports
  3. Remove nuget restore from sdk acquisition

Impact on Templates

With the change in place, the .NET Core Library csproj template will become:

<Project ToolsVersion="15.0" Sdk="Microsoft.NET.Sdk/1.0.0-rc2">
  <PropertyGroup>
    <TargetFramework>netstandard1.4</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="**\*.cs" />
    <EmbeddedResource Include="**\*.resx" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="NETStandard.Library" Version="1.6" />
  </ItemGroup>
</Project>

Risks

It is imperative that implicit <Imports> do not get persisted to disk. Andy is tracking this concern.

Design

A new attribute is added to <Project> for representing SDKs: Sdk="Microsoft.NET.Sdk/1.0.0-RC2". In RC3 and beyond, the element is used to assist in the acquisition experience. In RC2 it is used to identify a pre-installed framework to be loaded. Specifically, when MSBuild discovers one of these elements it will inject implicit Imports before and after the current .proj file. An example:

This example .csproj file:

<Project Sdk="Microsoft.NET.Sdk/1.0.0-RC2" />
  <Target Name="SayHi">
    <Message Text="Hello, World" />
  </Target>
</Project>

Is interepreted by MSBuild as:

<Project Sdk="Microsoft.NET.Sdk/1.0.0-RC2" />
  <!-- Import all, in order listed -->
  <Import Project="$(MSBuildSDKsPath)\%(___MSBuildSDK.Name)\%(___MSBuildSDK.Version)\build\InitialImport.props" 
          Condition="Exists('$(MSBuildSDKsPath)\%(___MSBuildSDK.Name)\%(___MSBuildSDK.Version)\build\InitialImport.props')" />
  
  <Target Name="SayHi">
    <Message Text="Hello, World" />
  </Target>
  
  <!-- Import all, in order listed -->  
  <Import Project="$(MSBuildSDKsPath)\%(___MSBuildSDK.Name)\%(___MSBuildSDK.Version)\build\FinalImport.targets"
          Condition="Exists('$(MSBuildSDKsPath)\%(___MSBuildSDK.Name)\%(___MSBuildSDK.Version)\build\FinalImport.targets')" />
</Project>

Usage Examples

1. .NET Core Console App

<Project Sdk="Microsoft.NET.Sdk/1.0.0-RC2">
  ...
</Project>

2. .NET Core Web App

<Project Sdk="Microsoft.NET.Sdk/1.0.0-RC2;
              Microsoft.Web.Sdk/1.0.0-RC2">
  ...
</Project>

RC Work

MSBuild

  1. Enable <Project Sdk=""> attribute
  2. Enable InitialImport.props and FinalImport.targets injection based on the values in the SDK element
  3. Ensure we save the csproj correctly [the implicit imports remain implicit]

Core & Web SDKs

  1. Create InitialImport.props and FinalImport.targets
  2. Ship RC2 and beyond with 'clean' version numbers e.g: 1.0.0-RC2 vs. 1.0.0-alpha-20161104-2
  3. Add the SDK layouts to both VS and CLI

CLI & VS

  1. Add SDKs to installation layout
  2. Pass MSBuildSDKsPath Environment Variable, to be interpreted as $(MSBuildSDKsPath) for the sake of this document, to invocations of MSBuild as MSBuildSDKsPath

MSBuildSDKsPath Locations

  1. In Visual Studio: $(MSBuildExtensionsPath)\.dotnet\
  2. In CLI, [ProgramFiles]\dotnet\sdk\{cli_version}\Extensions\

Syntax Comparison

1. SDKs Attribute [Chosen]

<Project Sdk="Microsoft.NET.Sdk/1.0.0-RC2;
               FSharp/1.0.0-Beta;
               MySDK">
  <Target Name="SayHi">
    <Message Text="Hello, World" />
  </Target>
</Project>

Pros:

  • Because it is a project-level attribute, we get the immutability constraints desired.

Cons:

  • N/A

2. <SDK> element

<project>
  <SDK Name="Microsoft.NET.Sdk" Version="1.0.0-RC2" />
  <SDK Name="FSharp" Version="1.0.0-Beta" />
  
  <Message Text="Hello, World" />
</project>

Pros:

  • <SDK> elements include named Name and Version attributes
  • <SDK> elements can be listed separately, avoiding long value strings

Cons:

  • <SDK> cannot behave like other MSBuild elements. It must be statically evaluatable and so cannot accept $(Properties). We could put a bunch of special rules in place, but this would be counterintuitive for MSBuild developers.

3. Dynamic Attributes

<project Microsoft.NET.Sdk="1.0.0-RC2" 
         FSharp="1.0.0-Beta">
  <Message Text="Hello, World" />
</project>

Pros:

  • Not composing a single ; dilimited string

Cons:

  • Feels unnatural usage of XML
@AndyGerlicher
Copy link

Syntactic sugar:

  <Project Sdk="Microsoft.FSharp.Web/1.0.0.0" />  <!-- allows multiple SDK -->
  </Project>

Expanded form:

<Project>
  <prop here>
  <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk/1.0.0.0" />
...
  <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk/1.0.0.0" />
  <target here>
</Project>

Change: Sdk everywhere. No SDKs
Added expanded form.

@TheRealPiotrP
Copy link
Author

changed SDKs => Sdk
changed project to Project
added 'Target' around 'Message'

@TheRealPiotrP
Copy link
Author

@nick

  • for the condition this was what we discussed on Friday. Final impl. is with the MSBuild folks. I think you're making good points.
  • for install location, the key in the design is that we not use MSBuildExtensionsPath in target resolution as it takes away flexibility from acquisition story. We may [and likely will] be putting new SDKs in a per-user location and the idea was that using a new env var lets us redirect without affecting existing behavior. Andy was quite concerned about using MSBuildExtensionsPath at all, but it seemed convenient in VS until acquisition story is solved.

@andy, when we start installing Sdk's per-user we'll need to think about how we unify the VS-installed stuff with the per-user stuff. Might need multiple root dirs + search order?

@cdmihai
Copy link

cdmihai commented Nov 23, 2016

Made the MSBuild part public: dotnet/msbuild#1392

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment