Use out-of-proc MSBuild evaluation (#674)

* Use out-of-proc MSBuild evaluation

This allows for support for new SDKs/TFMs without needing tye to target those TFMs

* Combine restore and metadata evaluation

* Batch process projects
This commit is contained in:
John Luo 2020-09-28 14:07:56 -07:00 коммит произвёл GitHub
Родитель f0c4f2f54d
Коммит bbef222754
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
18 изменённых файлов: 267 добавлений и 164 удалений

3
clean.cmd Normal file
Просмотреть файл

@ -0,0 +1,3 @@
@ECHO OFF
SETLOCAL
PowerShell -NoProfile -NoLogo -ExecutionPolicy ByPass -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = ''; try { & '%~dp0clean.ps1' %*; exit $LASTEXITCODE } catch { write-host $_; exit 1 }"

42
clean.ps1 Normal file
Просмотреть файл

@ -0,0 +1,42 @@
#requires -version 5
<#
.SYNOPSIS
Clean this repository.
.DESCRIPTION
This script cleans this repository interactively, leaving downloaded infrastructure untouched.
Clean operation is interactive to avoid losing new but unstaged files. Press 'c' then [Enter]
to perform the proposed deletions.
.EXAMPLE
Perform default clean operation.
clean.ps1
.EXAMPLE
Clean everything but downloaded infrastructure and VS / VS Code folders.
clean.ps1 -e .vs/ -e .vscode/
#>
[CmdletBinding(PositionalBinding = $false)]
param(
# Other lifecycle targets
[switch]$Help, # Show help
# Capture the rest
[Parameter(ValueFromRemainingArguments = $true)]
[string[]]$GitArguments
)
Set-StrictMode -Version 2
$ErrorActionPreference = 'Stop'
if ($Help) {
Get-Help $PSCommandPath
exit 0
}
git clean -dix -e .dotnet/ -e .tools/ @GitArguments
git checkout -- $(git ls-files -d)

38
clean.sh Normal file
Просмотреть файл

@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -euo pipefail
#
# Functions
#
__usage() {
echo "Usage: $(basename "${BASH_SOURCE[0]}") <Arguments>
Arguments:
<Arguments>... Arguments passed to the 'git' command. Any number of arguments allowed.
Description:
This script cleans the repository interactively, leaving downloaded infrastructure untouched.
Clean operation is interactive to avoid losing new but unstaged files. Press 'c' then [Enter]
to perform the proposed deletions.
"
}
git_args=()
while [[ $# -gt 0 ]]; do
case $1 in
-\?|-h|--help)
__usage
exit 0
;;
*)
git_args[${#git_args[*]}]="$1"
;;
esac
shift
done
# This incantation avoids unbound variable issues if git_args is empty
# https://stackoverflow.com/questions/7577052/bash-empty-array-expansion-with-set-u
git clean -dix -e .dotnet/ -e .tools/ ${git_args[@]+"${git_args[@]}"}

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

@ -4,9 +4,12 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Tye.ConfigModel;
@ -74,6 +77,88 @@ namespace Microsoft.Tye
config.Services.Where(filter.ServicesFilter).ToList() :
config.Services;
var sw = Stopwatch.StartNew();
// Project services will be restored and evaluated before resolving all other services.
// This batching will mitigate the performance cost of running MSBuild out of process.
var projectServices = services.Where(s => !string.IsNullOrEmpty(s.Project));
var projectMetadata = new Dictionary<string, string>();
using (var directory = TempDirectory.Create())
{
var projectPath = Path.Combine(directory.DirectoryPath, Path.GetRandomFileName() + ".proj");
var sb = new StringBuilder();
sb.AppendLine("<Project>");
sb.AppendLine(" <ItemGroup>");
foreach (var project in projectServices)
{
var expandedProject = Environment.ExpandEnvironmentVariables(project.Project!);
project.ProjectFullPath = Path.Combine(config.Source.DirectoryName!, expandedProject);
if (!File.Exists(project.ProjectFullPath))
{
throw new CommandException($"Failed to locate project: '{project.ProjectFullPath}'.");
}
sb.AppendLine($" <MicrosoftTye_ProjectServices " +
$"Include=\"{project.ProjectFullPath}\" " +
$"Name=\"{project.Name}\" " +
$"BuildProperties=\"{(project.BuildProperties.Any() ? project.BuildProperties.Select(kvp => $"{kvp.Name}={kvp.Value}").Aggregate((a, b) => a + ";" + b) : string.Empty)}\" />");
}
sb.AppendLine(@" </ItemGroup>");
sb.AppendLine($@" <Target Name=""MicrosoftTye_EvaluateProjects"">");
sb.AppendLine($@" <MsBuild Projects=""@(MicrosoftTye_ProjectServices)"" "
+ $@"Properties=""%(BuildProperties);"
+ $@"MicrosoftTye_ProjectName=%(Name)"" "
+ $@"Targets=""MicrosoftTye_GetProjectMetadata"" BuildInParallel=""true"" />");
sb.AppendLine(" </Target>");
sb.AppendLine("</Project>");
File.WriteAllText(projectPath, sb.ToString());
output.WriteDebugLine("Restoring and evaluating projects");
var msbuildEvaluationResult = await ProcessUtil.RunAsync(
"dotnet",
$"build " +
$"\"{projectPath}\" " +
$"/p:CustomAfterMicrosoftCommonTargets={Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!, "ProjectEvaluation.targets")} " +
$"/nologo",
throwOnError: false,
workingDirectory: directory.DirectoryPath);
// If the build fails, we're not really blocked from doing our work.
// For now we just log the output to debug. There are errors that occur during
// running these targets we don't really care as long as we get the data.
if (msbuildEvaluationResult.ExitCode != 0)
{
output.WriteDebugLine($"Evaluating project failed with exit code {msbuildEvaluationResult.ExitCode}:" +
$"{Environment.NewLine}Ouptut: {msbuildEvaluationResult.StandardOutput}" +
$"{Environment.NewLine}Error: {msbuildEvaluationResult.StandardError}");
}
var msbuildEvaluationOutput = msbuildEvaluationResult
.StandardOutput
.Split(Environment.NewLine);
foreach (var line in msbuildEvaluationOutput)
{
if (line.Trim().StartsWith("Microsoft.Tye metadata: "))
{
var values = line.Split(':', 3);
var projectName = values[1].Trim();
var metadataPath = values[2].Trim();
projectMetadata.Add(projectName, metadataPath);
output.WriteDebugLine($"Resolved metadata for service {projectName} at {metadataPath}");
}
}
output.WriteDebugLine($"Restore and project evaluation took: {sw.Elapsed.TotalMilliseconds}ms");
}
foreach (var configService in services)
{
ServiceBuilder service;
@ -87,9 +172,7 @@ namespace Microsoft.Tye
if (!string.IsNullOrEmpty(configService.Project))
{
var expandedProject = Environment.ExpandEnvironmentVariables(configService.Project);
var projectFile = new FileInfo(Path.Combine(config.Source.DirectoryName!, expandedProject));
var project = new DotnetProjectServiceBuilder(configService.Name!, projectFile);
var project = new DotnetProjectServiceBuilder(configService.Name!, new FileInfo(configService.ProjectFullPath));
service = project;
project.Build = configService.Build ?? true;
@ -107,7 +190,13 @@ namespace Microsoft.Tye
// to prompt for the registry name.
project.ContainerInfo = new ContainerInfo() { UseMultiphaseDockerfile = false, };
await ProjectReader.ReadProjectDetailsAsync(output, project);
// If project evaluation is successful this should not happen, therefore an exception will be thrown.
if (!projectMetadata.ContainsKey(configService.Name))
{
throw new CommandException($"Evaluated project metadata file could not be found for service {configService.Name}");
}
ProjectReader.ReadProjectDetails(output, project, projectMetadata[configService.Name]);
// Do k8s by default.
project.ManifestInfo = new KubernetesManifestInfo();

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

@ -25,6 +25,7 @@ namespace Microsoft.Tye.ConfigModel
public Dictionary<string, string> DockerFileArgs { get; set; } = new Dictionary<string, string>();
public string? DockerFileContext { get; set; }
public string? Project { get; set; }
public string? ProjectFullPath { get; set; }
public string? Include { get; set; }
public string? Repository { get; set; }
public bool? Build { get; set; }

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

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Tye</RootNamespace>
<AssemblyName>Microsoft.Tye.Core</AssemblyName>
<PackageId>Microsoft.Tye.Core</PackageId>

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

@ -10,15 +10,9 @@ using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Loader;
using System.Threading.Tasks;
using Microsoft.Build.Construction;
using Microsoft.Build.Definition;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Build.Locator;
using Microsoft.Build.Logging;
using Semver;
namespace Microsoft.Tye
@ -61,7 +55,7 @@ namespace Microsoft.Tye
}
}
public static Task ReadProjectDetailsAsync(OutputContext output, DotnetProjectServiceBuilder project)
public static void ReadProjectDetails(OutputContext output, DotnetProjectServiceBuilder project, string metadataFile)
{
if (output is null)
{
@ -73,14 +67,12 @@ namespace Microsoft.Tye
throw new ArgumentNullException(nameof(project));
}
if (!Directory.Exists(project.ProjectFile.DirectoryName))
if (project is null)
{
throw new CommandException($"Failed to locate directory: '{project.ProjectFile.DirectoryName}'.");
throw new ArgumentNullException(nameof(metadataFile));
}
EnsureMSBuildRegistered(output, project.ProjectFile);
EvaluateProject(output, project);
EvaluateProject(output, project, metadataFile);
if (!SemVersion.TryParse(project.Version, out var version))
{
@ -88,8 +80,6 @@ namespace Microsoft.Tye
version = new SemVersion(0, 1, 0);
project.Version = version.ToString();
}
return Task.CompletedTask;
}
private static void EnsureMSBuildRegistered(OutputContext? output, FileInfo projectFile)
@ -138,118 +128,40 @@ namespace Microsoft.Tye
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void LogIt(OutputContext output)
{
output.WriteDebugLine("Loaded: " + typeof(ProjectInstance).Assembly.FullName);
output.WriteDebugLine("Loaded From: " + typeof(ProjectInstance).Assembly.Location);
}
// Do not load MSBuild types before using EnsureMSBuildRegistered.
[MethodImpl(MethodImplOptions.NoInlining)]
private static void EvaluateProject(OutputContext output, DotnetProjectServiceBuilder project)
private static void EvaluateProject(OutputContext output, DotnetProjectServiceBuilder project, string metadataFile)
{
var sw = Stopwatch.StartNew();
// Currently we only log at debug level.
var logger = new ConsoleLogger(
verbosity: LoggerVerbosity.Normal,
write: message => output.WriteDebug(message),
colorSet: null,
colorReset: null);
var metadata = new Dictionary<string, string>();
var metadataKVPs = File.ReadLines(metadataFile).Select(l => l.Split(new[] { ':' }, 2));
// We need to isolate projects from each other for testing. MSBuild does not support
// loading the same project twice in the same collection.
var projectCollection = new ProjectCollection();
ProjectInstance projectInstance;
Microsoft.Build.Evaluation.Project msbuildProject;
try
foreach (var metadataKVP in metadataKVPs)
{
output.WriteDebugLine($"Loading project '{project.ProjectFile.FullName}'.");
msbuildProject = Microsoft.Build.Evaluation.Project.FromFile(project.ProjectFile.FullName, new ProjectOptions()
if (!string.IsNullOrEmpty(metadataKVP[1]))
{
ProjectCollection = projectCollection,
GlobalProperties = project.BuildProperties
});
projectInstance = msbuildProject.CreateProjectInstance();
output.WriteDebugLine($"Loaded project '{project.ProjectFile.FullName}'.");
}
catch (Exception ex)
{
throw new CommandException($"Failed to load project: '{project.ProjectFile.FullName}'.", ex);
}
try
{
AssemblyLoadContext.Default.Resolving += ResolveAssembly;
output.WriteDebugLine($"Restoring project '{project.ProjectFile.FullName}'.");
// Similar to what MSBuild does for restore:
// https://github.com/microsoft/msbuild/blob/3453beee039fb6f5ccc54ac783ebeced31fec472/src/MSBuild/XMake.cs#L1417
//
// We need to do restore as a separate operation
var restoreRequest = new BuildRequestData(
projectInstance,
targetsToBuild: new[] { "Restore" },
hostServices: null,
flags: BuildRequestDataFlags.ClearCachesAfterBuild | BuildRequestDataFlags.SkipNonexistentTargets | BuildRequestDataFlags.IgnoreMissingEmptyAndInvalidImports);
var parameters = new BuildParameters(projectCollection)
{
Loggers = new[] { logger, },
};
// We don't really look at the result, because it's not clear we should halt totally
// if restore fails.
var restoreResult = BuildManager.DefaultBuildManager.Build(parameters, restoreRequest);
output.WriteDebugLine($"Restored project '{project.ProjectFile.FullName}'.");
msbuildProject.MarkDirty();
projectInstance = msbuildProject.CreateProjectInstance();
var targets = new List<string>()
{
"ResolveReferences",
"ResolvePackageDependenciesDesignTime",
"PrepareResources",
"GetAssemblyAttributes",
};
var result = projectInstance.Build(
targets: targets.ToArray(),
loggers: new[] { logger, });
// If the build fails, we're not really blocked from doing our work.
// For now we just log the output to debug. There are errors that occur during
// running these targets we don't really care as long as we get the data.
}
finally
{
AssemblyLoadContext.Default.Resolving -= ResolveAssembly;
metadata.Add(metadataKVP[0], metadataKVP[1].Trim());
}
}
// Reading a few different version properties to be more resilient.
var version =
projectInstance.GetProperty("AssemblyInformationalVersion")?.EvaluatedValue ??
projectInstance.GetProperty("InformationalVersion")?.EvaluatedValue ??
projectInstance.GetProperty("Version").EvaluatedValue;
var version = GetMetadataValueOrNull("AssemblyInformationalVersion") ??
GetMetadataValueOrNull("InformationalVersion") ??
GetMetadataValueOrEmpty("Version");
project.Version = version;
output.WriteDebugLine($"Found application version: {version}");
var targetFrameworks = projectInstance.GetPropertyValue("TargetFrameworks");
project.TargetFrameworks = targetFrameworks.Split(';', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
project.TargetFrameworks = GetMetadataValueOrNull("TargetFrameworks")?.Split(';', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
// Figure out if functions app.
// If so, run app with function host.
project.RunCommand = projectInstance.GetPropertyValue("RunCommand");
project.RunArguments = projectInstance.GetPropertyValue("RunArguments");
project.TargetPath = projectInstance.GetPropertyValue("TargetPath");
project.PublishDir = projectInstance.GetPropertyValue("PublishDir");
project.AssemblyName = projectInstance.GetPropertyValue("AssemblyName");
project.IntermediateOutputPath = projectInstance.GetPropertyValue("IntermediateOutputPath");
project.RunCommand = GetMetadataValueOrEmpty("RunCommand");
project.RunArguments = GetMetadataValueOrEmpty("RunArguments");
project.TargetPath = GetMetadataValueOrEmpty("TargetPath");
project.PublishDir = GetMetadataValueOrEmpty("PublishDir");
project.AssemblyName = GetMetadataValueOrEmpty("AssemblyName");
project.IntermediateOutputPath = GetMetadataValueOrEmpty("IntermediateOutputPath");
output.WriteDebugLine($"RunCommand={project.RunCommand}");
output.WriteDebugLine($"RunArguments={project.RunArguments}");
@ -263,63 +175,37 @@ namespace Microsoft.Tye
project.TargetPath = Path.Combine(project.ProjectFile.DirectoryName!, NormalizePath(project.TargetPath));
project.PublishDir = Path.Combine(project.ProjectFile.DirectoryName!, NormalizePath(project.PublishDir));
var targetFramework = projectInstance.GetPropertyValue("TargetFramework");
var targetFramework = GetMetadataValueOrEmpty("TargetFramework");
project.TargetFramework = targetFramework;
output.WriteDebugLine($"Found target framework: {targetFramework}");
// TODO: Parse the name and version manually out of the TargetFramework field if it's non-null
project.TargetFrameworkName = projectInstance.GetPropertyValue("_ShortFrameworkIdentifier");
project.TargetFrameworkVersion = projectInstance.GetPropertyValue("_ShortFrameworkVersion") ?? projectInstance.GetPropertyValue("_TargetFrameworkVersionWithoutV");
project.TargetFrameworkName = GetMetadataValueOrEmpty("_ShortFrameworkIdentifier");
project.TargetFrameworkVersion = GetMetadataValueOrNull("_ShortFrameworkVersion") ?? GetMetadataValueOrEmpty("_TargetFrameworkVersionWithoutV");
var sharedFrameworks = projectInstance.GetItems("FrameworkReference").Select(i => i.EvaluatedInclude).ToList();
var sharedFrameworks = GetMetadataValueOrNull("FrameworkReference")?.Split(';') ?? Enumerable.Empty<string>();
project.Frameworks.AddRange(sharedFrameworks.Select(s => new Framework(s)));
output.WriteDebugLine($"Found shared frameworks: {string.Join(", ", sharedFrameworks)}");
// determine container base image
if (project.ContainerInfo != null)
{
project.ContainerInfo.BaseImageName = projectInstance.GetPropertyValue("ContainerBaseImage");
project.ContainerInfo.BaseImageTag = projectInstance.GetPropertyValue("ContainerBaseTag");
}
bool PropertyIsTrue(string property)
{
return projectInstance.GetPropertyValue(property) is string s && !string.IsNullOrEmpty(s) && bool.Parse(s);
project.ContainerInfo.BaseImageName = GetMetadataValueOrEmpty("ContainerBaseImage");
project.ContainerInfo.BaseImageTag = GetMetadataValueOrEmpty("ContainerBaseTag");
}
project.IsAspNet = project.Frameworks.Any(f => f.Name == "Microsoft.AspNetCore.App") ||
projectInstance.GetPropertyValue("MicrosoftNETPlatformLibrary") == "Microsoft.AspNetCore.App" ||
PropertyIsTrue("_AspNetCoreAppSharedFxIsEnabled") ||
PropertyIsTrue("UsingMicrosoftNETSdkWeb");
GetMetadataValueOrEmpty("MicrosoftNETPlatformLibrary") == "Microsoft.AspNetCore.App" ||
MetadataIsTrue("_AspNetCoreAppSharedFxIsEnabled") ||
MetadataIsTrue("UsingMicrosoftNETSdkWeb");
output.WriteDebugLine($"IsAspNet={project.IsAspNet}");
output.WriteDebugLine($"Evaluation Took: {sw.Elapsed.TotalMilliseconds}ms");
// The Microsoft.Build.Locator doesn't handle the loading of other assemblies
// that are shipped with MSBuild (ex NuGet).
//
// This means that the set of assemblies that need special handling depends on the targets
// that we run :(
//
// This is workaround for this limitation based on the targets we need to run
// to resolve references and versions.
//
// See: https://github.com/microsoft/MSBuildLocator/issues/86
Assembly? ResolveAssembly(AssemblyLoadContext context, AssemblyName assemblyName)
{
if (assemblyName.Name is object)
{
var msbuildDirectory = Environment.GetEnvironmentVariable("MSBuildExtensionsPath")!;
var assemblyFilePath = Path.Combine(msbuildDirectory, assemblyName.Name + ".dll");
if (File.Exists(assemblyFilePath))
{
return context.LoadFromAssemblyPath(assemblyFilePath);
}
}
return default;
}
string? GetMetadataValueOrNull(string key) => metadata!.TryGetValue(key, out var value) ? value : null;
string GetMetadataValueOrEmpty(string key) => metadata!.TryGetValue(key, out var value) ? value : string.Empty;
bool MetadataIsTrue(string key) => metadata!.TryGetValue(key, out var value) && bool.Parse(value);
}
private static string NormalizePath(string path)

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

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>

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

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<Description>Diagnostics collector and exporter for .NET Core applications.</Description>
<AssemblyName>Microsoft.Tye.Hosting.Diagnostics</AssemblyName>
<PackageId>Microsoft.Tye.Hosting.Diagnostics</PackageId>

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

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<Description>Orchestration host APIs.</Description>
<AssemblyName>Microsoft.Tye.Hosting</AssemblyName>
<PackageId>Microsoft.Tye.Hosting</PackageId>

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

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Microsoft.Tye</RootNamespace>
</PropertyGroup>

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

@ -0,0 +1,40 @@
<Project>
<Target Name="MicrosoftTye_GetProjectMetadata" DependsOnTargets="Restore;ResolveReferences;ResolvePackageDependenciesDesignTime;PrepareResources;GetAssemblyAttributes" >
<PropertyGroup>
<_MicrosoftTye_MetadataFile>$([System.IO.Path]::GetFullPath('$(IntermediateOutputPath)MicrosoftTye.ProjectMetadata.txt'))</_MicrosoftTye_MetadataFile>
<_MicrosoftTye_ProjectFrameworkReference>@(FrameworkReference, '%3B')</_MicrosoftTye_ProjectFrameworkReference>
</PropertyGroup>
<ItemGroup>
<_MicrosoftTye_ProjectMetadata Include="AssemblyInformationalVersion: $(AssemblyInformationalVersion)" />
<_MicrosoftTye_ProjectMetadata Include="InformationalVersion: $(InformationalVersion)" />
<_MicrosoftTye_ProjectMetadata Include="Version: $(Version)" />
<_MicrosoftTye_ProjectMetadata Include="TargetFrameworks: $(TargetFrameworks)" />
<_MicrosoftTye_ProjectMetadata Include="RunCommand: $(RunCommand)" />
<_MicrosoftTye_ProjectMetadata Include="RunArguments: $(RunArguments)" />
<_MicrosoftTye_ProjectMetadata Include="TargetPath: $(TargetPath)" />
<_MicrosoftTye_ProjectMetadata Include="PublishDir: $(PublishDir)" />
<_MicrosoftTye_ProjectMetadata Include="AssemblyName: $(AssemblyName)" />
<_MicrosoftTye_ProjectMetadata Include="IntermediateOutputPath: $(IntermediateOutputPath)" />
<_MicrosoftTye_ProjectMetadata Include="TargetFramework: $(TargetFramework)" />
<_MicrosoftTye_ProjectMetadata Include="_ShortFrameworkIdentifier: $(_ShortFrameworkIdentifier)" />
<_MicrosoftTye_ProjectMetadata Include="_ShortFrameworkVersion: $(_ShortFrameworkVersion)" />
<_MicrosoftTye_ProjectMetadata Include="_TargetFrameworkVersionWithoutV: $(_TargetFrameworkVersionWithoutV)" />
<_MicrosoftTye_ProjectMetadata Include="FrameworkReference: $(_MicrosoftTye_ProjectFrameworkReference)" />
<_MicrosoftTye_ProjectMetadata Include="ContainerBaseImage: $(ContainerBaseImage)" />
<_MicrosoftTye_ProjectMetadata Include="ContainerBaseTag: $(ContainerBaseTag)" />
<_MicrosoftTye_ProjectMetadata Include="MicrosoftNETPlatformLibrary: $(MicrosoftNETPlatformLibrary)" />
<_MicrosoftTye_ProjectMetadata Include="_AspNetCoreAppSharedFxIsEnabled: $(_AspNetCoreAppSharedFxIsEnabled)" />
<_MicrosoftTye_ProjectMetadata Include="UsingMicrosoftNETSdkWeb: $(UsingMicrosoftNETSdkWeb)" />
</ItemGroup>
<WriteLinesToFile
File="$(_MicrosoftTye_MetadataFile)"
Lines="@(_MicrosoftTye_ProjectMetadata)"
Overwrite="true"
WriteOnlyWhenDifferent="true"/>
<Message Text="Microsoft.Tye metadata: $(MicrosoftTye_ProjectName): $(_MicrosoftTye_MetadataFile)" Importance="High"/>
</Target>
</Project>

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

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Microsoft.Tye</RootNamespace>
<AssemblyName>tye</AssemblyName>
<PackageId>Microsoft.Tye</PackageId>
@ -10,6 +10,10 @@
<PackAsTool>true</PackAsTool>
</PropertyGroup>
<ItemGroup>
<None Include="ProjectEvaluation.targets" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\shared\KubectlDetector.cs" Link="KubectlDetector.cs" />
<Compile Include="..\shared\TempFile.cs" Link="TempFile.cs" />

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

@ -118,8 +118,8 @@ services:
var exception = await Assert.ThrowsAsync<CommandException>(async () =>
await ApplicationFactory.CreateAsync(outputContext, projectFile));
var wrongProjectPath = Path.Combine(projectDirectory.DirectoryPath, "backend1");
Assert.Equal($"Failed to locate directory: '{wrongProjectPath}'.", exception.Message);
var wrongProjectPath = Path.Combine(projectDirectory.DirectoryPath, "backend1/backend.csproj");
Assert.Equal($"Failed to locate project: '{wrongProjectPath}'.", exception.Message);
}
}
}

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

@ -2,7 +2,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>Microsoft.Tye.E2ETest</AssemblyName>
<IsTestProject>true</IsTestProject>
<IsUnitTestProject>true</IsUnitTestProject>

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

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

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

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<TestRunnerName>XUnit</TestRunnerName>
<Nullable>disable</Nullable>

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

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>Microsoft.Tye.UnitTests</AssemblyName>
<IsTestProject>true</IsTestProject>
<IsUnitTestProject>true</IsUnitTestProject>