Add API for multi-tfm projects (#184)
Project builder supports auto-detecting the framework
This commit is contained in:
parent
4214884b3e
commit
92a8d6d3bd
|
|
@ -35,7 +35,19 @@ namespace Microsoft.Extensions.ProjectModel
|
||||||
public string ProjectName => FindProperty("ProjectName") ?? _name;
|
public string ProjectName => FindProperty("ProjectName") ?? _name;
|
||||||
public string Configuration { get; }
|
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);
|
public bool IsClassLibrary => FindProperty("OutputType").Equals("Library", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
// TODO get from actual properties according to TFM
|
// TODO get from actual properties according to TFM
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ using Microsoft.Extensions.FileProviders;
|
||||||
using Microsoft.Extensions.FileProviders.Physical;
|
using Microsoft.Extensions.FileProviders.Physical;
|
||||||
using Microsoft.Extensions.ProjectModel.Internal;
|
using Microsoft.Extensions.ProjectModel.Internal;
|
||||||
using NuGet.Frameworks;
|
using NuGet.Frameworks;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.ProjectModel
|
namespace Microsoft.Extensions.ProjectModel
|
||||||
{
|
{
|
||||||
|
|
@ -23,13 +24,37 @@ namespace Microsoft.Extensions.ProjectModel
|
||||||
private IFileInfo _fileInfo;
|
private IFileInfo _fileInfo;
|
||||||
private string[] _buildTargets;
|
private string[] _buildTargets;
|
||||||
private Dictionary<string, string> _globalProperties = new Dictionary<string, string>();
|
private Dictionary<string, string> _globalProperties = new Dictionary<string, string>();
|
||||||
private bool _explicitMsBuild;
|
private MsBuildContext _msbuildContext;
|
||||||
|
|
||||||
public MsBuildProjectContextBuilder()
|
public MsBuildProjectContextBuilder()
|
||||||
{
|
{
|
||||||
Initialize();
|
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)
|
public MsBuildProjectContextBuilder WithBuildTargets(string[] targets)
|
||||||
{
|
{
|
||||||
if (targets == null)
|
if (targets == null)
|
||||||
|
|
@ -60,8 +85,27 @@ namespace Microsoft.Extensions.ProjectModel
|
||||||
// should be needed in most cases, but can be used to override
|
// should be needed in most cases, but can be used to override
|
||||||
public MsBuildProjectContextBuilder UseMsBuild(MsBuildContext context)
|
public MsBuildProjectContextBuilder UseMsBuild(MsBuildContext context)
|
||||||
{
|
{
|
||||||
_explicitMsBuild = true;
|
_msbuildContext = context;
|
||||||
SetMsBuildContext(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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,11 +128,11 @@ namespace Microsoft.Extensions.ProjectModel
|
||||||
|
|
||||||
public MsBuildProjectContextBuilder WithProjectFile(IFileInfo fileInfo)
|
public MsBuildProjectContextBuilder WithProjectFile(IFileInfo fileInfo)
|
||||||
{
|
{
|
||||||
if (!_explicitMsBuild)
|
if (_msbuildContext == null)
|
||||||
{
|
{
|
||||||
var projectDir = Path.GetDirectoryName(fileInfo.PhysicalPath);
|
var projectDir = Path.GetDirectoryName(fileInfo.PhysicalPath);
|
||||||
var sdk = DotNetCoreSdkResolver.DefaultResolver.ResolveProjectSdk(projectDir);
|
var sdk = DotNetCoreSdkResolver.DefaultResolver.ResolveProjectSdk(projectDir);
|
||||||
SetMsBuildContext(MsBuildContext.FromDotNetSdk(sdk));
|
UseMsBuild(MsBuildContext.FromDotNetSdk(sdk));
|
||||||
}
|
}
|
||||||
|
|
||||||
_fileInfo = fileInfo;
|
_fileInfo = fileInfo;
|
||||||
|
|
@ -105,12 +149,41 @@ namespace Microsoft.Extensions.ProjectModel
|
||||||
{
|
{
|
||||||
var projectCollection = CreateProjectCollection();
|
var projectCollection = CreateProjectCollection();
|
||||||
var project = CreateProject(_fileInfo, _configuration, _globalProperties, projectCollection);
|
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 projectInstance = CreateProjectInstance(project, _buildTargets, ignoreBuildErrors);
|
||||||
|
|
||||||
var name = Path.GetFileNameWithoutExtension(_fileInfo.Name);
|
var name = Path.GetFileNameWithoutExtension(_fileInfo.Name);
|
||||||
return new MsBuildProjectContext(name, _configuration, projectInstance);
|
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()
|
protected virtual void Initialize()
|
||||||
{
|
{
|
||||||
WithBuildTargets(new[] { "ResolveReferences" });
|
WithBuildTargets(new[] { "ResolveReferences" });
|
||||||
|
|
@ -167,22 +240,20 @@ namespace Microsoft.Extensions.ProjectModel
|
||||||
throw new InvalidOperationException(sb.ToString());
|
throw new InvalidOperationException(sb.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetMsBuildContext(MsBuildContext context)
|
private IEnumerable<string> GetAvailableTargetFrameworks(Project project)
|
||||||
{
|
{
|
||||||
/*
|
var frameworks = project.GetProperty("TargetFrameworks")?.EvaluatedValue;
|
||||||
Workaround https://github.com/Microsoft/msbuild/issues/999
|
if (string.IsNullOrEmpty(frameworks))
|
||||||
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.
|
return Enumerable.Empty<string>();
|
||||||
*/
|
}
|
||||||
|
return frameworks.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
Environment.SetEnvironmentVariable("MSBUILD_EXE_PATH", context.MsBuildExecutableFullPath);
|
|
||||||
WithProperty("MSBuildExtensionsPath", context.ExtensionsPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class InMemoryLogger : ILogger
|
private class InMemoryLogger : ILogger
|
||||||
{
|
{
|
||||||
private readonly Stack<Action> _onShutdown = new Stack<Action>();
|
private readonly Stack<Action> _onShutdown = new Stack<Action>();
|
||||||
|
|
||||||
internal IList<BuildErrorEventArgs> Errors = new List<BuildErrorEventArgs>();
|
internal IList<BuildErrorEventArgs> Errors = new List<BuildErrorEventArgs>();
|
||||||
|
|
||||||
public string Parameters { get; set; }
|
public string Parameters { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.DotNet.Cli.Utils;
|
using Microsoft.DotNet.Cli.Utils;
|
||||||
|
|
@ -11,26 +12,59 @@ using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.ProjectModel
|
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 const string SkipReason = "CI doesn't yet have a new enough version of .NET Core SDK";
|
||||||
|
|
||||||
private readonly MsBuildFixture _fixture;
|
private readonly MsBuildFixture _fixture;
|
||||||
private readonly ITestOutputHelper _output;
|
private readonly ITestOutputHelper _output;
|
||||||
|
private readonly TemporaryFileProvider _files;
|
||||||
|
|
||||||
public MsBuildProjectContextBuilderTest(MsBuildFixture fixture, ITestOutputHelper output)
|
public MsBuildProjectContextBuilderTest(MsBuildFixture fixture, ITestOutputHelper output)
|
||||||
{
|
{
|
||||||
_fixture = fixture;
|
_fixture = fixture;
|
||||||
_output = output;
|
_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)]
|
[Fact(Skip = SkipReason)]
|
||||||
public void ExecutesDesignTimeBuild()
|
public void ExecutesDesignTimeBuild()
|
||||||
{
|
{
|
||||||
using (var fileProvider = new TemporaryFileProvider())
|
// TODO remove when SDK becomes available on other feeds
|
||||||
{
|
_files.Add("NuGet.config", @"
|
||||||
// TODO remove when SDK becomes available on other feeds
|
|
||||||
fileProvider.Add("NuGet.config", @"
|
|
||||||
<configuration>
|
<configuration>
|
||||||
<packageSources>
|
<packageSources>
|
||||||
<clear />
|
<clear />
|
||||||
|
|
@ -40,7 +74,7 @@ namespace Microsoft.Extensions.ProjectModel
|
||||||
</packageSources>
|
</packageSources>
|
||||||
</configuration>");
|
</configuration>");
|
||||||
|
|
||||||
fileProvider.Add("test.csproj", @"
|
_files.Add("test.csproj", @"
|
||||||
<Project ToolsVersion=""14.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
|
<Project ToolsVersion=""14.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
|
||||||
<Import Project=""$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"" />
|
<Import Project=""$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"" />
|
||||||
|
|
||||||
|
|
@ -68,39 +102,37 @@ namespace Microsoft.Extensions.ProjectModel
|
||||||
<Import Project=""$(MSBuildToolsPath)\Microsoft.CSharp.targets"" />
|
<Import Project=""$(MSBuildToolsPath)\Microsoft.CSharp.targets"" />
|
||||||
</Project>
|
</Project>
|
||||||
");
|
");
|
||||||
fileProvider.Add("One.cs", "public class Abc {}");
|
_files.Add("One.cs", "public class Abc {}");
|
||||||
fileProvider.Add("Two.cs", "public class Abc2 {}");
|
_files.Add("Two.cs", "public class Abc2 {}");
|
||||||
fileProvider.Add("Excluded.cs", "public class Abc {}");
|
_files.Add("Excluded.cs", "public class Abc {}");
|
||||||
|
|
||||||
var testContext = _fixture.GetMsBuildContext();
|
var testContext = _fixture.GetMsBuildContext();
|
||||||
|
|
||||||
var muxer = Path.Combine(testContext.ExtensionsPath, "../..", "dotnet.exe");
|
var muxer = Path.Combine(testContext.ExtensionsPath, "../..", "dotnet.exe");
|
||||||
var result = Command
|
var result = Command
|
||||||
.Create(muxer, new[] { "restore3", Path.Combine(fileProvider.Root, "test.csproj") })
|
.Create(muxer, new[] { "restore3", Path.Combine(_files.Root, "test.csproj") })
|
||||||
.OnErrorLine(l => _output.WriteLine(l))
|
.OnErrorLine(l => _output.WriteLine(l))
|
||||||
.OnOutputLine(l => _output.WriteLine(l))
|
.OnOutputLine(l => _output.WriteLine(l))
|
||||||
.Execute();
|
.Execute();
|
||||||
Assert.Equal(0, result.ExitCode);
|
Assert.Equal(0, result.ExitCode);
|
||||||
|
|
||||||
var expectedCompileItems = new[] { "One.cs", "Two.cs" }.Select(p => Path.Combine(fileProvider.Root, p)).ToArray();
|
var expectedCompileItems = new[] { "One.cs", "Two.cs" }.Select(p => Path.Combine(_files.Root, p)).ToArray();
|
||||||
var builder = new MsBuildProjectContextBuilder()
|
|
||||||
.AsDesignTimeBuild()
|
|
||||||
.UseMsBuild(testContext)
|
|
||||||
.WithTargetFramework(FrameworkConstants.CommonFrameworks.NetCoreApp10)
|
|
||||||
.WithConfiguration("Debug")
|
|
||||||
.WithProjectFile(fileProvider.GetFileInfo("test.csproj"));
|
|
||||||
|
|
||||||
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(_files.GetFileInfo("bin").Exists);
|
||||||
Assert.False(fileProvider.GetFileInfo("obj").Exists);
|
Assert.False(_files.GetFileInfo("obj").Exists);
|
||||||
Assert.Equal(expectedCompileItems, context.CompilationItems.OrderBy(i => i).ToArray());
|
Assert.Equal(expectedCompileItems, context.CompilationItems.OrderBy(i => i).ToArray());
|
||||||
Assert.Equal(Path.Combine(fileProvider.Root, "bin", "Debug", "netcoreapp1.0", "test.dll"), context.AssemblyFullPath);
|
Assert.Equal(Path.Combine(_files.Root, "bin", "Debug", "netcoreapp1.0", "test.dll"), context.AssemblyFullPath);
|
||||||
Assert.True(context.IsClassLibrary);
|
Assert.True(context.IsClassLibrary);
|
||||||
Assert.Equal("TestProject", context.ProjectName);
|
Assert.Equal("TestProject", context.ProjectName);
|
||||||
Assert.Equal(FrameworkConstants.CommonFrameworks.NetCoreApp10, context.TargetFramework);
|
Assert.Equal(FrameworkConstants.CommonFrameworks.NetCoreApp10, context.TargetFramework);
|
||||||
Assert.Equal("Microsoft.TestProject", context.RootNamespace);
|
Assert.Equal("Microsoft.TestProject", context.RootNamespace);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue