Things (I think) you
should know
about project files and MSBuildDavid Wengier
Developer - .NET Project System
Microsoft
@davidwengier twitch.tv/davidwengier
@davidwengier twitch.tv/davidwengier
What is a Project System?
Project
System
Test Explorer
MSBuild
VS API (DTE)
Debugger
Solution Explorer
Compiler
@davidwengier twitch.tv/davidwengier
What is a project file?
@davidwengier twitch.tv/davidwengier
A project file is an MSBuild
file.
@davidwengier twitch.tv/davidwengier
Legacy vs SDK-style
@davidwengier twitch.tv/davidwengier
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net472</TargetFramework>
</PropertyGroup>
</Project>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import
Project="$(MSBuildExtensionsPath)$(MSBuildToolsVersion)Microsoft.Common.props"
Condition="Exists('$(MSBuildExtensionsPath)$(MSBuildToolsVersion)Microsoft.Common.props
')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{6656A70A-50E8-40A0-996B-81489FB455BE}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>ConsoleApp9</RootNamespace>
<AssemblyName>ConsoleApp9</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>binDebug</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>binRelease</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="PropertiesAssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)Microsoft.CSharp.targets" />
</Project>
@davidwengier twitch.tv/davidwengier
Imports and Sdks
@davidwengier twitch.tv/davidwengier
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net472</TargetFramework>
</PropertyGroup>
</Project>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import
Project="$(MSBuildExtensionsPath)$(MSBuildToolsVersion)Microsoft.Common.props"
Condition="Exists('$(MSBuildExtensionsPath)$(MSBuildToolsVersion)Microsoft.Common.props
')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{6656A70A-50E8-40A0-996B-81489FB455BE}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>ConsoleApp9</RootNamespace>
<AssemblyName>ConsoleApp9</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>binDebug</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>binRelease</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="PropertiesAssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)Microsoft.CSharp.targets" />
</Project>
@davidwengier twitch.tv/davidwengier
Implicit imports
@davidwengier twitch.tv/davidwengier
How does MSBuild work?
You can ignore this bit if you already know.
@davidwengier twitch.tv/davidwengier
The biggest simplification ever:
public class Project
{
public Dictionary<string, string> Properties { get; set; }
public Dictionary<string, Dictionary<string, string>> Items { get; set; }
}
@davidwengier twitch.tv/davidwengier
The biggest simplification ever, cont:
<Project>
<PropertyGroup>
<Foo>Bar</Foo>
</PropertyGroup>
<ItemGroup>
<Compile Include="Main.cs“ MyMetadata="Zam" />
</ItemGroup>
</Project>
<Project>
<PropertyGroup>
<Foo>Baz</Foo>
</PropertyGroup>
<ItemGroup>
<Compile Update="Main.cs" MyMetadata="Zim" />
</ItemGroup>
</Project>
@davidwengier twitch.tv/davidwengier
The biggest simplification ever, cont:
<Project>
<PropertyGroup>
<Foo>Baz</Foo>
</PropertyGroup>
<ItemGroup>
<Compile Update="Main.cs" MyMetadata="Zim" />
</ItemGroup>
</Project>
<Project>
<PropertyGroup>
<Foo>Bar</Foo>
</PropertyGroup>
<ItemGroup>
<Compile Include="Main.cs" MyMetadata="Zam" />
</ItemGroup>
</Project>
@davidwengier twitch.tv/davidwengier
Code reuse in project files
@davidwengier twitch.tv/davidwengier
Better package references
@davidwengier twitch.tv/davidwengier
What MSBuild sees
@davidwengier twitch.tv/davidwengier
Performance Summary
@davidwengier twitch.tv/davidwengier
Build logging
@davidwengier twitch.tv/davidwengier
VS Build logging
@davidwengier twitch.tv/davidwengier
Imports Tree
@davidwengier twitch.tv/davidwengier
In summary:
• Project files are MSBuild files
• Project files are code
• Code reuse (Import)
• Separation of concerns (Import, Include vs Update)
• Order is important
• Use /pp or /bl to see what is going on
• Stuff we didn’t cover:
• Function calls (Tasks)
• Variables (properties)
• Conditionals (condition)
Thank You
@davidwengier twitch.tv/davidwengier

A (very) opinionated guide to MSBuild and Project Files

  • 1.
    Things (I think)you should know about project files and MSBuildDavid Wengier Developer - .NET Project System Microsoft @davidwengier twitch.tv/davidwengier
  • 2.
    @davidwengier twitch.tv/davidwengier What isa Project System? Project System Test Explorer MSBuild VS API (DTE) Debugger Solution Explorer Compiler
  • 3.
  • 4.
  • 5.
  • 6.
    @davidwengier twitch.tv/davidwengier <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net472</TargetFramework> </PropertyGroup> </Project> <?xmlversion="1.0" encoding="utf-8"?> <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="$(MSBuildExtensionsPath)$(MSBuildToolsVersion)Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)$(MSBuildToolsVersion)Microsoft.Common.props ')" /> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <ProjectGuid>{6656A70A-50E8-40A0-996B-81489FB455BE}</ProjectGuid> <OutputType>Exe</OutputType> <RootNamespace>ConsoleApp9</RootNamespace> <AssemblyName>ConsoleApp9</AssemblyName> <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> <FileAlignment>512</FileAlignment> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <Deterministic>true</Deterministic> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PlatformTarget>AnyCPU</PlatformTarget> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>binDebug</OutputPath> <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PlatformTarget>AnyCPU</PlatformTarget> <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>binRelease</OutputPath> <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> </PropertyGroup> <ItemGroup> <Reference Include="System" /> <Reference Include="System.Core" /> <Reference Include="System.Xml.Linq" /> <Reference Include="System.Data.DataSetExtensions" /> <Reference Include="Microsoft.CSharp" /> <Reference Include="System.Data" /> <Reference Include="System.Net.Http" /> <Reference Include="System.Xml" /> </ItemGroup> <ItemGroup> <Compile Include="Program.cs" /> <Compile Include="PropertiesAssemblyInfo.cs" /> </ItemGroup> <ItemGroup> <None Include="App.config" /> </ItemGroup> <Import Project="$(MSBuildToolsPath)Microsoft.CSharp.targets" /> </Project>
  • 7.
  • 8.
    @davidwengier twitch.tv/davidwengier <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net472</TargetFramework> </PropertyGroup> </Project> <?xmlversion="1.0" encoding="utf-8"?> <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="$(MSBuildExtensionsPath)$(MSBuildToolsVersion)Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)$(MSBuildToolsVersion)Microsoft.Common.props ')" /> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <ProjectGuid>{6656A70A-50E8-40A0-996B-81489FB455BE}</ProjectGuid> <OutputType>Exe</OutputType> <RootNamespace>ConsoleApp9</RootNamespace> <AssemblyName>ConsoleApp9</AssemblyName> <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> <FileAlignment>512</FileAlignment> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <Deterministic>true</Deterministic> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PlatformTarget>AnyCPU</PlatformTarget> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <OutputPath>binDebug</OutputPath> <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PlatformTarget>AnyCPU</PlatformTarget> <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>binRelease</OutputPath> <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> </PropertyGroup> <ItemGroup> <Reference Include="System" /> <Reference Include="System.Core" /> <Reference Include="System.Xml.Linq" /> <Reference Include="System.Data.DataSetExtensions" /> <Reference Include="Microsoft.CSharp" /> <Reference Include="System.Data" /> <Reference Include="System.Net.Http" /> <Reference Include="System.Xml" /> </ItemGroup> <ItemGroup> <Compile Include="Program.cs" /> <Compile Include="PropertiesAssemblyInfo.cs" /> </ItemGroup> <ItemGroup> <None Include="App.config" /> </ItemGroup> <Import Project="$(MSBuildToolsPath)Microsoft.CSharp.targets" /> </Project>
  • 9.
  • 10.
    @davidwengier twitch.tv/davidwengier How doesMSBuild work? You can ignore this bit if you already know.
  • 11.
    @davidwengier twitch.tv/davidwengier The biggestsimplification ever: public class Project { public Dictionary<string, string> Properties { get; set; } public Dictionary<string, Dictionary<string, string>> Items { get; set; } }
  • 12.
    @davidwengier twitch.tv/davidwengier The biggestsimplification ever, cont: <Project> <PropertyGroup> <Foo>Bar</Foo> </PropertyGroup> <ItemGroup> <Compile Include="Main.cs“ MyMetadata="Zam" /> </ItemGroup> </Project> <Project> <PropertyGroup> <Foo>Baz</Foo> </PropertyGroup> <ItemGroup> <Compile Update="Main.cs" MyMetadata="Zim" /> </ItemGroup> </Project>
  • 13.
    @davidwengier twitch.tv/davidwengier The biggestsimplification ever, cont: <Project> <PropertyGroup> <Foo>Baz</Foo> </PropertyGroup> <ItemGroup> <Compile Update="Main.cs" MyMetadata="Zim" /> </ItemGroup> </Project> <Project> <PropertyGroup> <Foo>Bar</Foo> </PropertyGroup> <ItemGroup> <Compile Include="Main.cs" MyMetadata="Zam" /> </ItemGroup> </Project>
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
    @davidwengier twitch.tv/davidwengier In summary: •Project files are MSBuild files • Project files are code • Code reuse (Import) • Separation of concerns (Import, Include vs Update) • Order is important • Use /pp or /bl to see what is going on • Stuff we didn’t cover: • Function calls (Tasks) • Variables (properties) • Conditionals (condition)
  • 22.

Editor's Notes

  • #4 An MSBuild file
  • #5 An MSBuild file
  • #18 /clp:PerformanceSummary=true