Skip to content

Allow 'Add project reference' for existing projects #40133

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

Merged
merged 21 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"author": "Test Asset",
"classifications": [ "Test Asset" ],
"name": "TestAssets.PostActions.AddProjectReference.Existing",
"groupIdentity": "TestAssets.PostActions.AddProjectReference.Existing",
"precedence": "100",
"identity": "TestAssets.PostActions.AddProjectReference.Existing",
"shortName": "TestAssets.PostActions.AddProjectReference.Existing",
"primaryOutputs": [
{
"path": "Project1/Project1.csproj"
}
],
"postActions": [
{
"Description": "Add ProjectReference to ExistingProject/ExistingProject.csproj",
"ActionId": "B17581D1-C5C9-4489-8F0A-004BE667B814",
"ContinueOnError": "false",
"ManualInstructions": [
{
"Text": "Manually add the reference to Project1 in ExistingProject"
}
],
"args": {
"targetFiles": [
"ExistingProject/ExistingProject.csproj"
],
"referenceType": "project",
"reference": "Project1/Project1.csproj",
"projectFileExtensions": ".csproj"
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace Project1
{
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>$(CurrentTargetFramework)</TargetFramework>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"author": "Test Asset",
"classifications": [ "Test Asset" ],
"name": "TestAssets.PostActions.AddProjectReference.ExistingWithRename",
"groupIdentity": "TestAssets.PostActions.AddProjectReference.ExistingWithRename",
"precedence": "100",
"identity": "TestAssets.PostActions.AddProjectReference.ExistingWithRename",
"shortName": "TestAssets.PostActions.AddProjectReference.ExistingWithRename",
"primaryOutputs": [
{
"path": "Project1/Project1.csproj"
}
],
"symbols": {
"existingProject": {
"displayName": "Existing project relative path",
"description": "Path relative to the working directory",
"type": "parameter",
"datatype": "string",
"defaultValue": "ExistingProject/ExistingProject.csproj",
"fileRename": "ExistingProjectPath"
}
},
"postActions": [
{
"Description": "Add ProjectReference to ExistingProject/ExistingProject.csproj",
"ActionId": "B17581D1-C5C9-4489-8F0A-004BE667B814",
"ContinueOnError": "false",
"ManualInstructions": [
{
"Text": "Manually add the reference to Project1 in ExistingProject"
}
],
"applyFileRenamesToArgs": [
"targetFiles"
],
"args": {
"targetFiles": [
"ExistingProjectPath"
],
"referenceType": "project",
"reference": "Project1/Project1.csproj"
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace Project1
{
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>$(CurrentTargetFramework)</TargetFramework>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ protected static IReadOnlyList<string> GetTargetForSource(ICreationEffects2 crea
return results;
}

protected static IReadOnlyList<string>? GetConfiguredFiles(
protected static IReadOnlyList<string> GetConfiguredFiles(
IReadOnlyDictionary<string, string> postActionArgs,
ICreationEffects creationEffects,
string argName,
Expand All @@ -56,47 +56,20 @@ protected static IReadOnlyList<string> GetTargetForSource(ICreationEffects2 crea
{
if (creationEffects is not ICreationEffects2 creationEffects2)
{
return null;
return new List<string>();
}
if (!postActionArgs.TryGetValue(argName, out string? targetFiles))
{
return null;
return new List<string>();
}
if (string.IsNullOrWhiteSpace(targetFiles))
{
return null;
return new List<string>();
}

// try to parse the argument as json; if it is not valid json, use it as a string
if (targetFiles.TryParse(out JToken? config))
if (TryParseAsJson(targetFiles, out IReadOnlyList<string> paths))
{
if (config == null)
{
return null;
}

if (config.Type == JTokenType.String)
{
return ProcessPaths(config.ToString().Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries));
}
else if (config is JArray arr)
{
List<string> parts = new();

foreach (JToken token in arr)
{
if (token.Type != JTokenType.String)
{
continue;
}
parts.Add(token.ToString());
}

if (parts.Count > 0)
{
return ProcessPaths(parts);
}
}
return ProcessPaths(paths);
}

return ProcessPaths(targetFiles.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries));
Expand All @@ -111,11 +84,72 @@ IReadOnlyList<string> ProcessPaths(IReadOnlyList<string> paths)
}
}

protected static IReadOnlyList<string>? GetTargetFilesPaths(
IReadOnlyDictionary<string, string> postActionArgs,
string outputBasePath)
{
postActionArgs.TryGetValue("targetFiles", out string? targetFiles);
if (string.IsNullOrWhiteSpace(targetFiles))
{
return null;
}

// try to parse the argument as json; if it is not valid json, use it as a string
if (TryParseAsJson(targetFiles, out IReadOnlyList<string> paths))
{
return GetFullPaths(paths);
}

return GetFullPaths(targetFiles.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries));

IReadOnlyList<string> GetFullPaths(IEnumerable<string> paths)
{
var fullPaths = paths
.Select(p => Path.GetFullPath(p, outputBasePath))
.ToList();

return fullPaths.AsReadOnly();
}
}

protected abstract bool ProcessInternal(
IEngineEnvironmentSettings environment,
IPostAction action,
ICreationEffects creationEffects,
ICreationResult templateCreationResult,
string outputBasePath);

private static bool TryParseAsJson(string targetFiles, out IReadOnlyList<string> paths)
{
paths = new List<string>();
targetFiles.TryParse(out JToken? config);
if (config is null)
{
return false;
}

if (config.Type == JTokenType.String)
{
paths = config.ToString().Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
return true;
}

if (config is not JArray arr)
{
return false;
}

var parts = arr
.Where(token => token.Type == JTokenType.String)
.Select(token => token.ToString()).ToList();

if (parts.Count == 0)
{
return false;
}

paths = parts.AsReadOnly();
return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ protected override bool ProcessInternal(IEngineEnvironmentSettings environment,
{
IReadOnlyList<string>? projectsToProcess = GetConfiguredFiles(action.Args, creationEffects, "targetFiles", outputBasePath);

if (projectsToProcess is null)
if (!projectsToProcess.Any())
{
//If the author didn't opt in to the new behavior by specifying "targetFiles", search for project file in current output directory or above.
HashSet<string> extensionLimiters = new(StringComparer.Ordinal);
Expand All @@ -59,7 +59,7 @@ protected override bool ProcessInternal(IEngineEnvironmentSettings environment,

extensionLimiters.UnionWith(projectFileExtensions.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries));
}
projectsToProcess = FindProjFileAtOrAbovePath(environment.Host.FileSystem, outputBasePath, extensionLimiters);
projectsToProcess = TryFindProjects(environment, outputBasePath, extensionLimiters);
if (projectsToProcess.Count > 1)
{
// multiple projects at the same level. Error.
Expand All @@ -72,7 +72,13 @@ protected override bool ProcessInternal(IEngineEnvironmentSettings environment,
return false;
}
}
if (projectsToProcess is null || !projectsToProcess.Any())

if (!projectsToProcess.Any())
{
projectsToProcess = FindExistingTargetFiles(environment.Host.FileSystem, action.Args, outputBasePath);
}

if (!projectsToProcess.Any())
{
// no projects found. Error.
Reporter.Error.WriteLine(LocalizableStrings.PostAction_AddReference_Error_UnresolvedProjFile);
Expand All @@ -92,6 +98,27 @@ protected override bool ProcessInternal(IEngineEnvironmentSettings environment,
return true;
}

private static IReadOnlyList<string> TryFindProjects(IEngineEnvironmentSettings environment, string outputBasePath, HashSet<string> extensionLimiters)
{
try
{
return FindProjFileAtOrAbovePath(environment.Host.FileSystem, outputBasePath, extensionLimiters);
}
catch (DirectoryNotFoundException)
{
return [];
}
}

private static IReadOnlyList<string> FindExistingTargetFiles(IPhysicalFileSystem fileSystem, IReadOnlyDictionary<string, string> actionArgs, string outputBasePath)
{
var targetFiles = GetTargetFilesPaths(actionArgs, outputBasePath);
var foundFiles = targetFiles?
.Where(fileSystem.FileExists)
.ToList();
return foundFiles ?? [];
}

private bool AddReference(IPostAction actionConfig, string projectFile, string outputBasePath, ICreationEffects creationEffects)
{
if (actionConfig.Args == null || !actionConfig.Args.TryGetValue("reference", out string? referenceToAdd))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ public DotnetRestorePostActionProcessor(Func<string, bool>? restoreCallback = nu
protected override bool ProcessInternal(IEngineEnvironmentSettings environment, IPostAction actionConfig, ICreationEffects creationEffects, ICreationResult templateCreationResult, string outputBasePath)
{
bool allSucceeded = true;
IEnumerable<string>? targetFiles = GetConfiguredFiles(actionConfig.Args, creationEffects, "files", outputBasePath);
IEnumerable<string> targetFiles = GetConfiguredFiles(actionConfig.Args, creationEffects, "files", outputBasePath);

if (targetFiles is null || !targetFiles.Any())
if (!targetFiles.Any())
{
//If the author didn't opt in to the new behavior by specifying "projectFiles", use the old behavior - primary outputs
if (templateCreationResult.PrimaryOutputs.Count == 0)
Expand All @@ -38,7 +38,7 @@ protected override bool ProcessInternal(IEngineEnvironmentSettings environment,
targetFiles = templateCreationResult.PrimaryOutputs.Select(output => Path.GetFullPath(output.Path, outputBasePath));
}

if (targetFiles is null || !targetFiles.Any())
if (!targetFiles.Any())
{
Reporter.Error.WriteLine(string.Format(LocalizableStrings.PostAction_Restore_Error_FailedToDetermineProjectToRestore));
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ internal static IReadOnlyList<string> FindSolutionFilesAtOrAbovePath(IPhysicalFi

// The project files to add are a subset of the primary outputs, specifically the primary outputs indicated by the primaryOutputIndexes post action argument (semicolon separated)
// If any indexes are out of range or non-numeric, this method returns false and projectFiles is set to null.
internal static bool TryGetProjectFilesToAdd(IPostAction actionConfig, ICreationResult templateCreationResult, string outputBasePath, [NotNullWhen(true)] out IReadOnlyList<string>? projectFiles)
internal static bool TryGetProjectFilesToAdd(IPostAction actionConfig, ICreationResult templateCreationResult, string outputBasePath, [NotNullWhen(true)] out IReadOnlyList<string> projectFiles)
{
List<string> filesToAdd = new();
projectFiles = new List<string>();

if ((actionConfig.Args != null) && actionConfig.Args.TryGetValue("primaryOutputIndexes", out string? projectIndexes))
{
Expand All @@ -44,15 +45,13 @@ internal static bool TryGetProjectFilesToAdd(IPostAction actionConfig, ICreation
{
if (templateCreationResult.PrimaryOutputs.Count <= index || index < 0)
{
projectFiles = null;
return false;
}

filesToAdd.Add(Path.GetFullPath(templateCreationResult.PrimaryOutputs[index].Path, outputBasePath));
}
else
{
projectFiles = null;
return false;
}
}
Expand Down Expand Up @@ -81,8 +80,8 @@ protected override bool ProcessInternal(IEngineEnvironmentSettings environment,
return false;
}

IReadOnlyList<string>? projectFiles = GetConfiguredFiles(action.Args, creationEffects, "projectFiles", outputBasePath, (path) => Path.GetExtension(path).EndsWith("proj", StringComparison.OrdinalIgnoreCase));
if (projectFiles is null)
IReadOnlyList<string> projectFiles = GetConfiguredFiles(action.Args, creationEffects, "projectFiles", outputBasePath, (path) => Path.GetExtension(path).EndsWith("proj", StringComparison.OrdinalIgnoreCase));
if (!projectFiles.Any())
{
//If the author didn't opt in to the new behavior by specifying "projectFiles", use the old behavior
if (!TryGetProjectFilesToAdd(action, templateCreationResult, outputBasePath, out projectFiles))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
TestAssets.PostActions.AddPackageReference.B... TestAssets.PostActions.AddPackageReference.Basic Test Asset
TestAssets.PostActions.AddPackageReference.B... TestAssets.PostActions.AddPackageReference.BasicWithFiles Test Asset
TestAssets.PostActions.AddProjectReference.B... TestAssets.PostActions.AddProjectReference.Basic Test Asset
TestAssets.PostActions.AddProjectReference.E... TestAssets.PostActions.AddProjectReference.Existing Test Asset
TestAssets.PostActions.AddProjectReference.E... TestAssets.PostActions.AddProjectReference.ExistingWithRename Test Asset
TestAssets.PostActions.AddProjectToSolution.... TestAssets.PostActions.AddProjectToSolution.Basic Test Asset
TestAssets.PostActions.AddProjectToSolution.... TestAssets.PostActions.AddProjectToSolution.BasicInSolutionRoot Test Asset
TestAssets.PostActions.AddProjectToSolution.... TestAssets.PostActions.AddProjectToSolution.BasicWithFiles Test Asset
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
TestAssets.PostActions.AddPackageReference.B... TestAssets.PostActions.AddPackageReference.Basic Test Asset
TestAssets.PostActions.AddPackageReference.B... TestAssets.PostActions.AddPackageReference.BasicWithFiles Test Asset
TestAssets.PostActions.AddProjectReference.B... TestAssets.PostActions.AddProjectReference.Basic Test Asset
TestAssets.PostActions.AddProjectReference.E... TestAssets.PostActions.AddProjectReference.Existing Test Asset
TestAssets.PostActions.AddProjectReference.E... TestAssets.PostActions.AddProjectReference.ExistingWithRename Test Asset
TestAssets.PostActions.AddProjectToSolution.... TestAssets.PostActions.AddProjectToSolution.Basic Test Asset
TestAssets.PostActions.AddProjectToSolution.... TestAssets.PostActions.AddProjectToSolution.BasicInSolutionRoot Test Asset
TestAssets.PostActions.AddProjectToSolution.... TestAssets.PostActions.AddProjectToSolution.BasicWithFiles Test Asset
Expand Down
Loading
Loading