diff --git a/src/Blazor.Compiler/ProjectReferenceUtil.cs b/src/Blazor.Compiler/ProjectReferenceUtil.cs
index b8381b1f..8875fcbd 100644
--- a/src/Blazor.Compiler/ProjectReferenceUtil.cs
+++ b/src/Blazor.Compiler/ProjectReferenceUtil.cs
@@ -10,86 +10,136 @@ namespace Blazor.Compiler
{
internal static class ProjectReferenceUtil
{
-
///
- /// Inspects the project.assets.json file in a .NET Core project and attempts to determine
+ /// Inspects the project.assets.json file and .csproj files in a .NET Core project and attempts to determine
/// which .dll files are referenced in its compilation
///
- public static string[] FindReferencedAssemblies(string copyFromProjectRoot, OptimizationLevel optimizationLevel)
- {
- var assetsJsonPath = Path.Combine(copyFromProjectRoot, "obj", "project.assets.json");
- if (!File.Exists(assetsJsonPath))
- {
+ public static string[] FindReferencedAssemblies(string projectRoot, OptimizationLevel optimizationLevel) {
+ return assembliesFromProjectAssets(projectRoot, optimizationLevel)
+ .Concat(assembliesFromCSProj(projectRoot, optimizationLevel))
+ .Select(path => path.Replace('/', Path.DirectorySeparatorChar))
+ .Distinct(new FilenameComparer()) // TODO: Make sure you pick the most recent version of each assembly, not just an arbitrary one
+ .ToArray();
+ }
+
+ private static IEnumerable assembliesFromProjectAssets(string projectRoot, OptimizationLevel optimizationLevel) {
+ var assetsJsonPath = Path.Combine(projectRoot, "obj", "project.assets.json");
+ if(!File.Exists(assetsJsonPath)) {
throw new FileNotFoundException($"Cannot local project assets file. Maybe the referenced project hasn't been restored yet. Searched for: {assetsJsonPath}");
}
var assetsInfo = (JsonDict)Json.Deserialize(File.ReadAllText(assetsJsonPath));
+
var packageFolders = ((JsonDict)assetsInfo["packageFolders"]).Keys.ToList();
- if (!packageFolders.Any())
- {
+ if(!packageFolders.Any()) {
throw new InvalidDataException($"Expected to find package folders, but found none.");
}
var targets = ((JsonDict)assetsInfo["targets"]);
- if (targets.Count != 1)
- {
+ if(targets.Count != 1) {
throw new InvalidDataException($"Expected to find exactly 1 target, but found {targets.Count}.");
}
var target = (Dictionary)targets.Values.Single();
var project = (JsonDict)assetsInfo["project"];
var frameworks = ((JsonDict)project["frameworks"]);
- if (frameworks.Count != 1)
- {
+ if(frameworks.Count != 1) {
throw new InvalidDataException($"Expected to find exactly 1 frameworks, but found {frameworks.Count}.");
}
var framework = frameworks.Single();
- var binDir = Path.Combine(copyFromProjectRoot, "bin", optimizationLevel.ToString(), framework.Key);
+ var binDir = Path.Combine(projectRoot, "bin", optimizationLevel.ToString(), framework.Key);
- var referenceAssemblies = target.SelectMany(referenceKvp =>
- {
- var reference = (JsonDict)referenceKvp.Value;
- if (reference.TryGetValue("compile", out var compileInfo))
- {
- var compileItems = ((JsonDict)compileInfo).Keys.Where(item => item.EndsWith(".dll"));
- var referenceType = (string)reference["type"];
- if (referenceType == "package")
- {
- var packageNameAndVersion = referenceKvp.Key;
-
- return compileItems.Select(item =>
- {
- var partialPath = Path.Combine(packageNameAndVersion, item);
- foreach (var packageFolder in packageFolders)
- {
- var candidateFilename = Path.Combine(packageFolder, partialPath);
- if (File.Exists(candidateFilename))
- {
- return candidateFilename;
- }
+ return compiledReferences(target, packageFolders, binDir);
+ }
+
+
+
+ private static IEnumerable compiledReferences(IDictionary references, IList packageFolders, string binDir) {
+ foreach(var (packageNameAndVersion, reference) in references) {
+ var referenceInfo = (JsonDict)reference;
+ if(referenceInfo.TryGetValue("compile", out var compileInfo)) {
+ var compileItems = ((JsonDict)compileInfo).Keys.Where(item => Path.GetExtension(item) == ".dll");
+ switch(referenceInfo["type"]) {
+ case "package":
+ foreach(var item in compileItems) {
+ var partialPath = Path.Combine(packageNameAndVersion, item);
+ yield return packageFolders
+ .Select(getCandidate)
+ .FirstOrDefault(File.Exists)
+ ?? throw new InvalidDataException($"Could not find {partialPath} in any of the package folders:\n" +
+ $"{String.Join('\n', packageFolders)}");
+
+ string getCandidate(string packageFolder) => Path.Combine(packageFolder, partialPath);
}
+ break;
+ case "project":
+ foreach(var item in compileItems) {
+ yield return item.Replace("bin/placeholder", binDir);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ private static IEnumerable assembliesFromCSProj(string projectRoot, OptimizationLevel optimizationLevel) {
+ string projectName = Path.GetFileName(projectRoot.TrimEnd(Path.DirectorySeparatorChar));
+ var csProjPath = Path.Combine(projectRoot, projectName + ".csproj");
+ if(!File.Exists(csProjPath)) {
+ throw new FileNotFoundException($"Could not find csproj for {projectRoot}");
+ }
+
+ var projInfo = new AngleSharp.Parser.Xml.XmlParser().Parse(File.ReadAllText(csProjPath));
+
+ var projectNode = elementsOf(projInfo, "Project").Single();
+
+ string binDir;
+ {
+ var propertyGroup = elementsOf(projectNode, "PropertyGroup").Single();
+ var frameworkNode = elementsOf(propertyGroup, "TargetFramework").Single();
+ var targetFramework = frameworkNode.TextContent;
+ binDir = Path.Combine(projectRoot, "bin", optimizationLevel.ToString(), targetFramework);
+ }
- throw new InvalidDataException($"Could not find {partialPath} in any of the package folders:\n{string.Join('\n', packageFolders)}");
- });
+ foreach(var itemGroup in elementsOf(projectNode, "ItemGroup")) {
+ foreach(var reference in elementsOf(itemGroup, "Reference")) {
+ var includeAttr = reference.Attributes.Single(x => x.Name == "Include");
+
+ //Look in bin dir first, else try looking for the hint
+ var assemblyPath = Path.Combine(binDir, includeAttr.Value + ".dll"); //TODO is it possible to reference others eg. .exe
+ if(File.Exists(assemblyPath)) {
+ yield return assemblyPath;
+ continue;
}
- else if (referenceType == "project")
- {
- return compileItems.Select(item => item.Replace("bin/placeholder", binDir));
+ else {
+ var hint = elementsOf(reference, "HintPath")
+ .SingleOrDefault()?.TextContent;
+ if(!String.IsNullOrWhiteSpace(hint)) {
+ assemblyPath = Path.Combine(projectRoot, hint);
+ if(File.Exists(assemblyPath)) {
+ yield return assemblyPath;
+ continue;
+ }
+ }
}
+ throw new InvalidDataException($"Could not find {includeAttr.Value} in the bin directory \"{binDir}\"");
}
- return Enumerable.Empty();
- });
+ //foreach(var projReference in elementsOf(itemGroup, "ProjectReference")) {
+ // The project references should be picked up from the project assets. The csproj is only needed for direct dll references.
+ //}
+ }
- return referenceAssemblies
- .Select(path => path.Replace('/', Path.DirectorySeparatorChar))
- .Distinct(new FilenameComparer()) // TODO: Make sure you pick the most recent version of each assembly, not just an arbitrary one
- .ToArray();
+
+ IEnumerable elementsOf(AngleSharp.Dom.INode node, string nodeName) {
+ return node.ChildNodes.OfType().Where(x => x.NodeName == nodeName);
+ }
}
+
private class FilenameComparer : IEqualityComparer
{
- public bool Equals(string x, string y) => string.Equals(
+ public bool Equals(string x, string y) => String.Equals(
Path.GetFileName(x),
Path.GetFileName(y),
StringComparison.OrdinalIgnoreCase);