Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solves the problem of getting project MSBuild information #34574

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 32 additions & 63 deletions src/dotnet-ef/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Text;
using System.Text.Json;
using Microsoft.EntityFrameworkCore.Tools.Properties;

namespace Microsoft.EntityFrameworkCore.Tools;
Expand Down Expand Up @@ -42,114 +44,81 @@ public Project(string file, string? framework, string? configuration, string? ru

public static Project FromFile(
string file,
string? buildExtensionsDir,
string? framework = null,
string? configuration = null,
string? runtime = null)
{
Debug.Assert(!string.IsNullOrEmpty(file), "file is null or empty.");

buildExtensionsDir ??= Path.Combine(Path.GetDirectoryName(file)!, "obj");

Directory.CreateDirectory(buildExtensionsDir);

byte[] efTargets;
using (var input = typeof(Resources).Assembly.GetManifestResourceStream(
"Microsoft.EntityFrameworkCore.Tools.Resources.EntityFrameworkCore.targets")!)
{
efTargets = new byte[input.Length];
input.ReadExactly(efTargets);
}

var efTargetsPath = Path.Combine(
buildExtensionsDir,
Path.GetFileName(file) + ".EntityFrameworkCore.targets");

bool FileMatches()
{
try
{
return File.ReadAllBytes(efTargetsPath).SequenceEqual(efTargets);
}
catch
{
return false;
}
}

// Avoid touching the targets file, if it matches what we need, to enable incremental builds
if (!File.Exists(efTargetsPath) || !FileMatches())
{
Reporter.WriteVerbose(Resources.WritingFile(efTargetsPath));
File.WriteAllBytes(efTargetsPath, efTargets);
}

IDictionary<string, string> metadata;
var metadataFile = Path.GetTempFileName();
try
{
var propertyArg = "/property:EFProjectMetadataFile=" + metadataFile;
var args = new List<string>
{
"msbuild",
};

if (framework != null)
{
propertyArg += ";TargetFramework=" + framework;
args.Add($"/property:TargetFramework={framework}");
}

if (configuration != null)
{
propertyArg += ";Configuration=" + configuration;
args.Add($"/property:Configuration={configuration}");
}

if (runtime != null)
{
propertyArg += ";RuntimeIdentifier=" + runtime;
args.Add($"/property:RuntimeIdentifier={runtime}");
}

var args = new List<string>
foreach (var property in typeof(Project).GetProperties())
{
"msbuild",
"/target:GetEFProjectMetadata",
propertyArg,
"/verbosity:quiet",
"/nologo"
};
args.Add($"/getProperty:{property.Name}");
}

args.Add("/getProperty:Platform");

args.Add(file);

var exitCode = Exe.Run("dotnet", args);
var output = new StringBuilder();

var exitCode = Exe.Run("dotnet", args, handleOutput: line => output.AppendLine(line));
if (exitCode != 0)
{
throw new CommandException(Resources.GetMetadataFailed);
}

metadata = File.ReadLines(metadataFile).Select(l => l.Split([':'], 2))
.ToDictionary(s => s[0], s => s[1].TrimStart());
metadata = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, string>>>(output.ToString())!["Properties"];
}
finally
{
File.Delete(metadataFile);
}

var platformTarget = metadata["PlatformTarget"];
var platformTarget = metadata[nameof(PlatformTarget)];
if (platformTarget.Length == 0)
{
platformTarget = metadata["Platform"];
}

return new Project(file, framework, configuration, runtime)
{
AssemblyName = metadata["AssemblyName"],
Language = metadata["Language"],
OutputPath = metadata["OutputPath"],
AssemblyName = metadata[nameof(AssemblyName)],
Language = metadata[nameof(Language)],
OutputPath = metadata[nameof(OutputPath)],
PlatformTarget = platformTarget,
ProjectAssetsFile = metadata["ProjectAssetsFile"],
ProjectDir = metadata["ProjectDir"],
RootNamespace = metadata["RootNamespace"],
RuntimeFrameworkVersion = metadata["RuntimeFrameworkVersion"],
TargetFileName = metadata["TargetFileName"],
TargetFrameworkMoniker = metadata["TargetFrameworkMoniker"],
Nullable = metadata["Nullable"],
TargetFramework = metadata["TargetFramework"],
TargetPlatformIdentifier = metadata["TargetPlatformIdentifier"]
ProjectAssetsFile = metadata[nameof(ProjectAssetsFile)],
ProjectDir = metadata[nameof(ProjectDir)],
RootNamespace = metadata[nameof(RootNamespace)],
RuntimeFrameworkVersion = metadata[nameof(RuntimeFrameworkVersion)],
TargetFileName = metadata[nameof(TargetFileName)],
TargetFrameworkMoniker = metadata[nameof(TargetFrameworkMoniker)],
Nullable = metadata[nameof(Nullable)],
TargetFramework = metadata[nameof(TargetFramework)],
TargetPlatformIdentifier = metadata[nameof(TargetPlatformIdentifier)]
};
}

Expand Down
2 changes: 0 additions & 2 deletions src/dotnet-ef/ProjectOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ internal class ProjectOptions
public CommandOption? Runtime { get; private set; }

// ReSharper disable once InconsistentNaming
public CommandOption? MSBuildProjectExtensionsPath { get; private set; }
public CommandOption? NoBuild { get; private set; }

public void Configure(CommandLineApplication command)
Expand All @@ -25,7 +24,6 @@ public void Configure(CommandLineApplication command)
Framework = command.Option("--framework <FRAMEWORK>", Resources.FrameworkDescription);
Configuration = command.Option("--configuration <CONFIGURATION>", Resources.ConfigurationDescription);
Runtime = command.Option("--runtime <RUNTIME_IDENTIFIER>", Resources.RuntimeDescription);
MSBuildProjectExtensionsPath = command.Option("--msbuildprojectextensionspath <PATH>", Resources.ProjectExtensionsDescription);
NoBuild = command.Option("--no-build", Resources.NoBuildDescription);
}
}
4 changes: 2 additions & 2 deletions src/dotnet-ef/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/dotnet-ef/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,13 @@
<value>The target framework. Defaults to the first one in the project.</value>
</data>
<data name="GetMetadataFailed" xml:space="preserve">
<value>Unable to retrieve project metadata. Ensure it's an SDK-style project. If you're using a custom BaseIntermediateOutputPath or MSBuildProjectExtensionsPath values, Use the --msbuildprojectextensionspath option.</value>
<value>Unable to retrieve project metadata. Ensure it's an SDK-style project.</value>
</data>
<data name="IdempotentDescription" xml:space="preserve">
<value>Generate a script that can be used on a database at any migration.</value>
</data>
<data name="JsonDescription" xml:space="preserve">
<value>Show JSON output. Use with --prefix-output to parse programatically.</value>
<value>Show JSON output. Use with --prefix-output to parse programmatically.</value>
</data>
<data name="MigrationDescription" xml:space="preserve">
<value>The target migration. If '0', all migrations will be reverted. Defaults to the last migration.</value>
Expand Down
28 changes: 0 additions & 28 deletions src/dotnet-ef/Resources/EntityFrameworkCore.targets

This file was deleted.

5 changes: 1 addition & 4 deletions src/dotnet-ef/RootCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ internal class RootCommand : CommandBase
private CommandOption? _framework;
private CommandOption? _configuration;
private CommandOption? _runtime;
private CommandOption? _msbuildprojectextensionspath;
private CommandOption? _noBuild;
private CommandOption? _help;
private IList<string>? _args;
Expand All @@ -38,7 +37,6 @@ public override void Configure(CommandLineApplication command)
_framework = options.Framework;
_configuration = options.Configuration;
_runtime = options.Runtime;
_msbuildprojectextensionspath = options.MSBuildProjectExtensionsPath;
_noBuild = options.NoBuild;

command.VersionOption("--version", GetVersion);
Expand Down Expand Up @@ -68,10 +66,9 @@ protected override int Execute(string[] _)
Reporter.WriteVerbose(Resources.UsingProject(projectFile));
Reporter.WriteVerbose(Resources.UsingStartupProject(startupProjectFile));

var project = Project.FromFile(projectFile, _msbuildprojectextensionspath!.Value());
var project = Project.FromFile(projectFile);
var startupProject = Project.FromFile(
startupProjectFile,
_msbuildprojectextensionspath.Value(),
_framework!.Value(),
_configuration!.Value(),
_runtime!.Value());
Expand Down
4 changes: 0 additions & 4 deletions src/dotnet-ef/dotnet-ef.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,6 @@ dotnet ef database update
<ProjectReference Include="..\ef\ef.csproj" ReferenceOutputAssembly="false" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="Resources\EntityFrameworkCore.targets" />
</ItemGroup>

<ItemGroup>
<None Update="Properties\Resources.Designer.tt">
<Generator>TextTemplatingFileGenerator</Generator>
Expand Down