Skip to content

Commit

Permalink
Solves the problem of getting project MSBuild information (#34574)
Browse files Browse the repository at this point in the history
This PR replaces the GetEFProjectMetadata target in EntityFrameworkCore.targets with Evaluate items and properties and display results of targets.

Fixes #23853
Fixes #30725
  • Loading branch information
paulomorgado authored Jan 17, 2025
1 parent 47e5d25 commit fbce1d5
Show file tree
Hide file tree
Showing 7 changed files with 37 additions and 105 deletions.
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

0 comments on commit fbce1d5

Please sign in to comment.