Add API for multi-tfm projects (#184)

Project builder supports auto-detecting the framework
This commit is contained in:
Nate McMaster 2016-10-06 13:03:05 -07:00 коммит произвёл GitHub
Родитель 4214884b3e
Коммит 92a8d6d3bd
3 изменённых файлов: 165 добавлений и 50 удалений

Просмотреть файл

@ -35,7 +35,19 @@ namespace Microsoft.Extensions.ProjectModel
public string ProjectName => FindProperty("ProjectName") ?? _name;
public string Configuration { get; }
public NuGetFramework TargetFramework => NuGetFramework.Parse(FindProperty("NuGetTargetMoniker"));
public NuGetFramework TargetFramework
{
get
{
var tfm = FindProperty("NuGetTargetMoniker") ?? FindProperty("TargetFramework");
if (tfm == null)
{
return null;
}
return NuGetFramework.Parse(tfm);
}
}
public bool IsClassLibrary => FindProperty("OutputType").Equals("Library", StringComparison.OrdinalIgnoreCase);
// TODO get from actual properties according to TFM

Просмотреть файл

@ -14,6 +14,7 @@ using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.FileProviders.Physical;
using Microsoft.Extensions.ProjectModel.Internal;
using NuGet.Frameworks;
using System.Linq;
namespace Microsoft.Extensions.ProjectModel
{
@ -23,13 +24,37 @@ namespace Microsoft.Extensions.ProjectModel
private IFileInfo _fileInfo;
private string[] _buildTargets;
private Dictionary<string, string> _globalProperties = new Dictionary<string, string>();
private bool _explicitMsBuild;
private MsBuildContext _msbuildContext;
public MsBuildProjectContextBuilder()
{
Initialize();
}
public virtual MsBuildProjectContextBuilder Clone()
{
var builder = new MsBuildProjectContextBuilder()
.WithProperties(_globalProperties)
.WithBuildTargets(_buildTargets);
if (_msbuildContext != null)
{
builder.UseMsBuild(_msbuildContext);
}
if (_fileInfo != null)
{
builder.WithProjectFile(_fileInfo);
}
if (_configuration != null)
{
builder.WithConfiguration(_configuration);
}
return builder;
}
public MsBuildProjectContextBuilder WithBuildTargets(string[] targets)
{
if (targets == null)
@ -60,8 +85,27 @@ namespace Microsoft.Extensions.ProjectModel
// should be needed in most cases, but can be used to override
public MsBuildProjectContextBuilder UseMsBuild(MsBuildContext context)
{
_explicitMsBuild = true;
SetMsBuildContext(context);
_msbuildContext = context;
/*
Workaround https://github.com/Microsoft/msbuild/issues/999
Error: System.TypeInitializationException : The type initializer for 'BuildEnvironmentHelperSingleton' threw an exception.
Could not determine a valid location to MSBuild. Try running this process from the Developer Command Prompt for Visual Studio.
*/
Environment.SetEnvironmentVariable("MSBUILD_EXE_PATH", context.MsBuildExecutableFullPath);
WithProperty("MSBuildExtensionsPath", context.ExtensionsPath);
return this;
}
public MsBuildProjectContextBuilder WithProperties(IDictionary<string, string> properties)
{
foreach (var prop in properties)
{
_globalProperties[prop.Key] = prop.Value;
}
return this;
}
@ -84,11 +128,11 @@ namespace Microsoft.Extensions.ProjectModel
public MsBuildProjectContextBuilder WithProjectFile(IFileInfo fileInfo)
{
if (!_explicitMsBuild)
if (_msbuildContext == null)
{
var projectDir = Path.GetDirectoryName(fileInfo.PhysicalPath);
var sdk = DotNetCoreSdkResolver.DefaultResolver.ResolveProjectSdk(projectDir);
SetMsBuildContext(MsBuildContext.FromDotNetSdk(sdk));
UseMsBuild(MsBuildContext.FromDotNetSdk(sdk));
}
_fileInfo = fileInfo;
@ -105,12 +149,41 @@ namespace Microsoft.Extensions.ProjectModel
{
var projectCollection = CreateProjectCollection();
var project = CreateProject(_fileInfo, _configuration, _globalProperties, projectCollection);
if (project.GetProperty("TargetFramework") == null)
{
var frameworks = GetAvailableTargetFrameworks(project).ToList();
if (frameworks.Count > 1)
{
throw new InvalidOperationException($"Multiple frameworks are available. Either use {nameof(WithTargetFramework)} or {nameof(BuildAllTargetFrameworks)}");
}
if (frameworks.Count == 0)
{
throw new InvalidOperationException($"No frameworks are available. Either use {nameof(WithTargetFramework)} or {nameof(BuildAllTargetFrameworks)}");
}
project.SetGlobalProperty("TargetFramework", frameworks.Single());
}
var projectInstance = CreateProjectInstance(project, _buildTargets, ignoreBuildErrors);
var name = Path.GetFileNameWithoutExtension(_fileInfo.Name);
return new MsBuildProjectContext(name, _configuration, projectInstance);
}
public IEnumerable<MsBuildProjectContext> BuildAllTargetFrameworks()
{
var projectCollection = CreateProjectCollection();
var project = CreateProject(_fileInfo, _configuration, _globalProperties, projectCollection);
foreach (var framework in GetAvailableTargetFrameworks(project))
{
var builder = Clone();
builder.WithTargetFramework(framework);
yield return builder.Build();
}
}
protected virtual void Initialize()
{
WithBuildTargets(new[] { "ResolveReferences" });
@ -167,22 +240,20 @@ namespace Microsoft.Extensions.ProjectModel
throw new InvalidOperationException(sb.ToString());
}
private void SetMsBuildContext(MsBuildContext context)
private IEnumerable<string> GetAvailableTargetFrameworks(Project project)
{
/*
Workaround https://github.com/Microsoft/msbuild/issues/999
Error: System.TypeInitializationException : The type initializer for 'BuildEnvironmentHelperSingleton' threw an exception.
Could not determine a valid location to MSBuild. Try running this process from the Developer Command Prompt for Visual Studio.
*/
Environment.SetEnvironmentVariable("MSBUILD_EXE_PATH", context.MsBuildExecutableFullPath);
WithProperty("MSBuildExtensionsPath", context.ExtensionsPath);
var frameworks = project.GetProperty("TargetFrameworks")?.EvaluatedValue;
if (string.IsNullOrEmpty(frameworks))
{
return Enumerable.Empty<string>();
}
return frameworks.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
}
private class InMemoryLogger : ILogger
{
private readonly Stack<Action> _onShutdown = new Stack<Action>();
internal IList<BuildErrorEventArgs> Errors = new List<BuildErrorEventArgs>();
public string Parameters { get; set; }

Просмотреть файл

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Linq;
using Microsoft.DotNet.Cli.Utils;
@ -11,26 +12,59 @@ using Xunit.Abstractions;
namespace Microsoft.Extensions.ProjectModel
{
public class MsBuildProjectContextBuilderTest : IClassFixture<MsBuildFixture>
public class MsBuildProjectContextBuilderTest : IClassFixture<MsBuildFixture>, IDisposable
{
private const string SkipReason = "CI doesn't yet have a new enough version of .NET Core SDK";
private readonly MsBuildFixture _fixture;
private readonly ITestOutputHelper _output;
private readonly TemporaryFileProvider _files;
public MsBuildProjectContextBuilderTest(MsBuildFixture fixture, ITestOutputHelper output)
{
_fixture = fixture;
_output = output;
_files = new TemporaryFileProvider();
}
public void Dispose()
{
_files.Dispose();
}
[Fact(Skip = SkipReason)]
public void BuildsAllTargetFrameworks()
{
_files.Add("test.proj", @"
<Project ToolsVersion=""14.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
<PropertyGroup>
<TargetFrameworks>net451;netstandard1.3</TargetFrameworks>
</PropertyGroup>
</Project>
");
var contexts = new MsBuildProjectContextBuilder()
.WithBuildTargets(Array.Empty<string>())
.WithProjectFile(_files.GetFileInfo("test.proj"))
.BuildAllTargetFrameworks()
.ToList();
Assert.Collection(contexts,
context =>
{
Assert.Equal(FrameworkConstants.CommonFrameworks.Net451, context.TargetFramework);
},
context =>
{
Assert.Equal(FrameworkConstants.CommonFrameworks.NetStandard13, context.TargetFramework);
});
}
[Fact(Skip = SkipReason)]
public void ExecutesDesignTimeBuild()
{
using (var fileProvider = new TemporaryFileProvider())
{
// TODO remove when SDK becomes available on other feeds
fileProvider.Add("NuGet.config", @"
// TODO remove when SDK becomes available on other feeds
_files.Add("NuGet.config", @"
<configuration>
<packageSources>
<clear />
@ -40,7 +74,7 @@ namespace Microsoft.Extensions.ProjectModel
</packageSources>
</configuration>");
fileProvider.Add("test.csproj", @"
_files.Add("test.csproj", @"
<Project ToolsVersion=""14.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
<Import Project=""$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"" />
@ -68,39 +102,37 @@ namespace Microsoft.Extensions.ProjectModel
<Import Project=""$(MSBuildToolsPath)\Microsoft.CSharp.targets"" />
</Project>
");
fileProvider.Add("One.cs", "public class Abc {}");
fileProvider.Add("Two.cs", "public class Abc2 {}");
fileProvider.Add("Excluded.cs", "public class Abc {}");
_files.Add("One.cs", "public class Abc {}");
_files.Add("Two.cs", "public class Abc2 {}");
_files.Add("Excluded.cs", "public class Abc {}");
var testContext = _fixture.GetMsBuildContext();
var testContext = _fixture.GetMsBuildContext();
var muxer = Path.Combine(testContext.ExtensionsPath, "../..", "dotnet.exe");
var result = Command
.Create(muxer, new[] { "restore3", Path.Combine(fileProvider.Root, "test.csproj") })
.OnErrorLine(l => _output.WriteLine(l))
.OnOutputLine(l => _output.WriteLine(l))
.Execute();
Assert.Equal(0, result.ExitCode);
var muxer = Path.Combine(testContext.ExtensionsPath, "../..", "dotnet.exe");
var result = Command
.Create(muxer, new[] { "restore3", Path.Combine(_files.Root, "test.csproj") })
.OnErrorLine(l => _output.WriteLine(l))
.OnOutputLine(l => _output.WriteLine(l))
.Execute();
Assert.Equal(0, result.ExitCode);
var expectedCompileItems = new[] { "One.cs", "Two.cs" }.Select(p => Path.Combine(fileProvider.Root, p)).ToArray();
var builder = new MsBuildProjectContextBuilder()
.AsDesignTimeBuild()
.UseMsBuild(testContext)
.WithTargetFramework(FrameworkConstants.CommonFrameworks.NetCoreApp10)
.WithConfiguration("Debug")
.WithProjectFile(fileProvider.GetFileInfo("test.csproj"));
var expectedCompileItems = new[] { "One.cs", "Two.cs" }.Select(p => Path.Combine(_files.Root, p)).ToArray();
var context = builder.Build();
var context = new MsBuildProjectContextBuilder()
.AsDesignTimeBuild()
.UseMsBuild(testContext)
.WithConfiguration("Debug")
.WithProjectFile(_files.GetFileInfo("test.csproj"))
.Build();
Assert.False(fileProvider.GetFileInfo("bin").Exists);
Assert.False(fileProvider.GetFileInfo("obj").Exists);
Assert.Equal(expectedCompileItems, context.CompilationItems.OrderBy(i => i).ToArray());
Assert.Equal(Path.Combine(fileProvider.Root, "bin", "Debug", "netcoreapp1.0", "test.dll"), context.AssemblyFullPath);
Assert.True(context.IsClassLibrary);
Assert.Equal("TestProject", context.ProjectName);
Assert.Equal(FrameworkConstants.CommonFrameworks.NetCoreApp10, context.TargetFramework);
Assert.Equal("Microsoft.TestProject", context.RootNamespace);
}
Assert.False(_files.GetFileInfo("bin").Exists);
Assert.False(_files.GetFileInfo("obj").Exists);
Assert.Equal(expectedCompileItems, context.CompilationItems.OrderBy(i => i).ToArray());
Assert.Equal(Path.Combine(_files.Root, "bin", "Debug", "netcoreapp1.0", "test.dll"), context.AssemblyFullPath);
Assert.True(context.IsClassLibrary);
Assert.Equal("TestProject", context.ProjectName);
Assert.Equal(FrameworkConstants.CommonFrameworks.NetCoreApp10, context.TargetFramework);
Assert.Equal("Microsoft.TestProject", context.RootNamespace);
}
}
}