Skip to content

Commit d48f2ab

Browse files
author
Nate McMaster
committed
Use MSBuild project extensions instead of importing the users project
Implicit imports prevents using <Import> on a project file that has the Sdk attribute. This change instead generates a file in the MSBuildProjectExtensionsPath to inject targets require to find the UserSecretsId property in a project. Resolves #242
1 parent f277816 commit d48f2ab

File tree

7 files changed

+71
-84
lines changed

7 files changed

+71
-84
lines changed

src/Microsoft.Extensions.SecretManager.Tools/Internal/ProjectIdResolver.cs

Lines changed: 57 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,16 @@
55
using System.Collections.Generic;
66
using System.Diagnostics;
77
using System.IO;
8-
using System.Linq;
98
using System.Reflection;
109
using Microsoft.Extensions.Tools.Internal;
1110

1211
namespace Microsoft.Extensions.SecretManager.Tools.Internal
1312
{
14-
public class ProjectIdResolver : IDisposable
13+
public class ProjectIdResolver
1514
{
16-
private const string TargetsFileName = "FindUserSecretsProperty.targets";
1715
private const string DefaultConfig = "Debug";
1816
private readonly IReporter _reporter;
1917
private readonly string _workingDirectory;
20-
private readonly List<string> _tempFiles = new List<string>();
2118

2219
public ProjectIdResolver(IReporter reporter, string workingDirectory)
2320
{
@@ -29,83 +26,82 @@ public string Resolve(string project, string configuration)
2926
{
3027
var finder = new MsBuildProjectFinder(_workingDirectory);
3128
var projectFile = finder.FindMsBuildProject(project);
29+
EnsureProjectExtensionTargetsExist(projectFile);
3230

3331
_reporter.Verbose(Resources.FormatMessage_Project_File_Path(projectFile));
3432

35-
var targetFile = GetTargetFile();
36-
var outputFile = Path.GetTempFileName();
37-
_tempFiles.Add(outputFile);
38-
3933
configuration = !string.IsNullOrEmpty(configuration)
4034
? configuration
4135
: DefaultConfig;
4236

43-
var args = new[]
44-
{
45-
"msbuild",
46-
targetFile,
47-
"/nologo",
48-
"/t:_FindUserSecretsProperty",
49-
$"/p:Project={projectFile}",
50-
$"/p:OutputFile={outputFile}",
51-
$"/p:Configuration={configuration}"
52-
};
53-
var psi = new ProcessStartInfo
37+
var outputFile = Path.GetTempFileName();
38+
try
5439
{
55-
FileName = DotNetMuxer.MuxerPathOrDefault(),
56-
Arguments = ArgumentEscaper.EscapeAndConcatenate(args),
57-
RedirectStandardOutput = true,
58-
RedirectStandardError = true
59-
};
40+
var args = new[]
41+
{
42+
"msbuild",
43+
projectFile,
44+
"/nologo",
45+
"/t:_ExtractUserSecretsMetadata", // defined in ProjectIdResolverTargets.xml
46+
$"/p:_UserSecretsMetadataFile={outputFile}",
47+
$"/p:Configuration={configuration}"
48+
};
49+
var psi = new ProcessStartInfo
50+
{
51+
FileName = DotNetMuxer.MuxerPathOrDefault(),
52+
Arguments = ArgumentEscaper.EscapeAndConcatenate(args),
53+
RedirectStandardOutput = true,
54+
RedirectStandardError = true
55+
};
6056

6157
#if DEBUG
62-
_reporter.Verbose($"Invoking '{psi.FileName} {psi.Arguments}'");
58+
_reporter.Verbose($"Invoking '{psi.FileName} {psi.Arguments}'");
6359
#endif
6460

65-
var process = Process.Start(psi);
66-
process.WaitForExit();
61+
var process = Process.Start(psi);
62+
process.WaitForExit();
6763

68-
if (process.ExitCode != 0)
69-
{
70-
_reporter.Verbose(process.StandardOutput.ReadToEnd());
71-
_reporter.Verbose(process.StandardError.ReadToEnd());
72-
throw new InvalidOperationException(Resources.FormatError_ProjectFailedToLoad(projectFile));
73-
}
74-
75-
var id = File.ReadAllText(outputFile)?.Trim();
76-
if (string.IsNullOrEmpty(id))
77-
{
78-
throw new InvalidOperationException(Resources.FormatError_ProjectMissingId(projectFile));
79-
}
80-
81-
return id;
82-
}
64+
if (process.ExitCode != 0)
65+
{
66+
_reporter.Verbose(process.StandardOutput.ReadToEnd());
67+
_reporter.Verbose(process.StandardError.ReadToEnd());
68+
throw new InvalidOperationException(Resources.FormatError_ProjectFailedToLoad(projectFile));
69+
}
8370

84-
private string GetTargetFile()
85-
{
86-
var assemblyDir = Path.GetDirectoryName(GetType().GetTypeInfo().Assembly.Location);
71+
var id = File.ReadAllText(outputFile)?.Trim();
72+
if (string.IsNullOrEmpty(id))
73+
{
74+
throw new InvalidOperationException(Resources.FormatError_ProjectMissingId(projectFile));
75+
}
76+
return id;
8777

88-
// targets should be in one of these locations, depending on test setup and tools installation
89-
var searchPaths = new[]
78+
}
79+
finally
9080
{
91-
AppContext.BaseDirectory,
92-
assemblyDir, // next to assembly
93-
Path.Combine(assemblyDir, "../../toolassets"), // inside the nupkg
94-
Path.Combine(assemblyDir, "toolassets"), // for local builds
95-
Path.Combine(AppContext.BaseDirectory, "../../toolassets"), // relative to packaged deps.json
96-
};
97-
98-
return searchPaths
99-
.Select(dir => Path.Combine(dir, TargetsFileName))
100-
.Where(File.Exists)
101-
.First();
81+
TryDelete(outputFile);
82+
}
10283
}
10384

104-
public void Dispose()
85+
private void EnsureProjectExtensionTargetsExist(string projectFile)
10586
{
106-
foreach (var file in _tempFiles)
87+
// relies on MSBuildProjectExtensionsPath and Microsoft.Common.targets to import this file
88+
// into the target project
89+
var projectExtensionsPath = Path.Combine(
90+
Path.GetDirectoryName(projectFile),
91+
"obj",
92+
$"{Path.GetFileName(projectFile)}.usersecrets.targets");
93+
94+
Directory.CreateDirectory(Path.GetDirectoryName(projectExtensionsPath));
95+
96+
// should overwrite the file always. Hypothetically, another version of the user-secrets tool
97+
// could have already put a file here. We want to ensure the target file matches the currently
98+
// running tool
99+
using (var resource = GetType().GetTypeInfo().Assembly.GetManifestResourceStream("ProjectIdResolverTargets.xml"))
100+
using (var stream = new FileStream(projectExtensionsPath, FileMode.Create))
101+
using (var writer = new StreamWriter(stream))
107102
{
108-
TryDelete(file);
103+
writer.WriteLine("<!-- Auto-generated by dotnet-user-secrets. This file can be deleted and should not be commited to source control. -->");
104+
resource.CopyTo(stream);
109105
}
110106
}
111107

src/Microsoft.Extensions.SecretManager.Tools/Microsoft.Extensions.SecretManager.Tools.nuspec

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
</metadata>
2424
<files>
2525
<file src="prefercliruntime" target="\prefercliruntime" />
26-
<file src="toolassets\FindUserSecretsProperty.targets" target="toolassets\" />
2726
<file src="dotnet-user-secrets.dll" target="lib\netcoreapp1.0\" />
2827
<file src="dotnet-user-secrets.deps.json" target="lib\netcoreapp1.0\" />
2928
<file src="dotnet-user-secrets.runtimeconfig.json" target="lib\netcoreapp1.0\" />

src/Microsoft.Extensions.SecretManager.Tools/Program.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,8 @@ internal string ResolveId(CommandLineOptions options, IReporter reporter)
124124
return options.Id;
125125
}
126126

127-
using (var resolver = new ProjectIdResolver(reporter, _workingDirectory))
128-
{
129-
return resolver.Resolve(options.Project, options.Configuration);
130-
}
127+
var resolver = new ProjectIdResolver(reporter, _workingDirectory);
128+
return resolver.Resolve(options.Project, options.Configuration);
131129
}
132130
}
133131
}

src/Microsoft.Extensions.SecretManager.Tools/project.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@
55
"emitEntryPoint": true,
66
"warningsAsErrors": true,
77
"keyFile": "../../tools/Key.snk",
8-
"copyToOutput": "toolassets/*.targets",
8+
"embed": {
9+
"mappings": {
10+
"ProjectIdResolverTargets.xml": "./resources/ProjectIdResolverTargets.xml"
11+
}
12+
},
913
"compile": {
1014
"include": "../Shared/**/*.cs"
1115
}
1216
},
1317
"publishOptions": {
14-
"include": [
15-
"toolassets/*.targets",
16-
"prefercliruntime"
17-
]
18+
"include": "prefercliruntime"
1819
},
1920
"dependencies": {
2021
"Microsoft.Extensions.Configuration.UserSecrets": "1.0.0",
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<Project>
2+
<Target Name="_ExtractUserSecretsMetadata">
3+
<WriteLinesToFile File="$(_UserSecretsMetadataFile)" Lines="$(UserSecretsId)" />
4+
</Target>
5+
</Project>

src/Microsoft.Extensions.SecretManager.Tools/toolassets/FindUserSecretsProperty.targets

Lines changed: 0 additions & 6 deletions
This file was deleted.

test/Microsoft.Extensions.SecretManager.Tools.Tests/UserSecretsTestFixture.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@ public string GetTempSecretProject()
3232
return GetTempSecretProject(out userSecretsId);
3333
}
3434

35-
private const string ProjectTemplate = @"<Project ToolsVersion=""14.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
36-
<Import Project=""$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"" />
37-
35+
private const string ProjectTemplate = @"<Project ToolsVersion=""15.0"" Sdk=""Microsoft.NET.Sdk"">
3836
<PropertyGroup>
3937
<OutputType>Exe</OutputType>
4038
<TargetFrameworks>netcoreapp1.0</TargetFrameworks>
@@ -43,12 +41,8 @@ public string GetTempSecretProject()
4341
4442
<ItemGroup>
4543
<Compile Include=""**\*.cs"" Exclude=""Excluded.cs"" />
46-
47-
<PackageReference Include=""Microsoft.NET.Sdk"" Version=""1.0.0-*"" PrivateAssets=""All"" />
4844
<PackageReference Include=""Microsoft.NETCore.App"" Version=""1.0.1"" />
4945
</ItemGroup>
50-
51-
<Import Project=""$(MSBuildToolsPath)\Microsoft.CSharp.targets"" />
5246
</Project>";
5347

5448
public string GetTempSecretProject(out string userSecretsId)

0 commit comments

Comments
 (0)