public override bool Execute() { var filesToRemove = new List <ITaskItem>(); var assetCandidates = new List <ITaskItem>(); try { if (ProjectAssembly.Length != 1) { Log.LogError("Invalid number of project assemblies '{0}'", string.Join("," + Environment.NewLine, ProjectAssembly.Select(a => a.ItemSpec))); return(true); } if (ProjectDebugSymbols.Length > 1) { Log.LogError("Invalid number of symbol assemblies '{0}'", string.Join("," + Environment.NewLine, ProjectDebugSymbols.Select(a => a.ItemSpec))); return(true); } for (int i = 0; i < Candidates.Length; i++) { var candidate = Candidates[i]; if (ShouldFilterCandidate(candidate, TimeZoneSupport, InvariantGlobalization, CopySymbols, out var reason)) { Log.LogMessage("Skipping asset '{0}' because '{1}'", candidate.ItemSpec, reason); filesToRemove.Add(candidate); continue; } var satelliteAssembly = SatelliteAssemblies.FirstOrDefault(s => s.ItemSpec == candidate.ItemSpec); if (satelliteAssembly != null) { var inferredCulture = satelliteAssembly.GetMetadata("DestinationSubDirectory").Trim('\\', '/'); Log.LogMessage("Found satellite assembly '{0}' asset for candidate '{1}' with inferred culture '{2}'", satelliteAssembly.ItemSpec, candidate.ItemSpec, inferredCulture); var assetCandidate = new TaskItem(satelliteAssembly); assetCandidate.SetMetadata("AssetKind", "Build"); assetCandidate.SetMetadata("AssetRole", "Related"); assetCandidate.SetMetadata("AssetTraitName", "Culture"); assetCandidate.SetMetadata("AssetTraitValue", inferredCulture); assetCandidate.SetMetadata("RelativePath", $"_framework/{inferredCulture}/{satelliteAssembly.GetMetadata("FileName")}{satelliteAssembly.GetMetadata("Extension")}"); assetCandidate.SetMetadata("RelatedAsset", Path.GetFullPath(Path.Combine(OutputPath, "wwwroot", "_framework", Path.GetFileName(assetCandidate.GetMetadata("ResolvedFrom"))))); assetCandidates.Add(assetCandidate); continue; } var destinationSubPath = candidate.GetMetadata("DestinationSubPath"); if (candidate.GetMetadata("FileName") == "dotnet" && candidate.GetMetadata("Extension") == ".js") { var itemHash = FileHasher.GetFileHash(candidate.ItemSpec); var cacheBustedDotNetJSFileName = $"dotnet.{BundledNETCoreAppPackageVersion}.{itemHash}.js"; var originalFileFullPath = Path.GetFullPath(candidate.ItemSpec); var originalFileDirectory = Path.GetDirectoryName(originalFileFullPath); var cacheBustedDotNetJSFullPath = Path.Combine(originalFileDirectory, cacheBustedDotNetJSFileName); var newDotNetJs = new TaskItem(cacheBustedDotNetJSFullPath, candidate.CloneCustomMetadata()); newDotNetJs.SetMetadata("OriginalItemSpec", candidate.ItemSpec); var newRelativePath = $"_framework/{cacheBustedDotNetJSFileName}"; newDotNetJs.SetMetadata("RelativePath", newRelativePath); newDotNetJs.SetMetadata("AssetTraitName", "BlazorWebAssemblyResource"); newDotNetJs.SetMetadata("AssetTraitValue", "native"); assetCandidates.Add(newDotNetJs); continue; } else if (string.IsNullOrEmpty(destinationSubPath)) { var relativePath = candidate.GetMetadata("FileName") + candidate.GetMetadata("Extension"); candidate.SetMetadata("RelativePath", $"_framework/{relativePath}"); } else { candidate.SetMetadata("RelativePath", $"_framework/{destinationSubPath}"); } var culture = candidate.GetMetadata("Culture"); if (!string.IsNullOrEmpty(culture)) { candidate.SetMetadata("AssetKind", "Build"); candidate.SetMetadata("AssetRole", "Related"); candidate.SetMetadata("AssetTraitName", "Culture"); candidate.SetMetadata("AssetTraitValue", culture); var fileName = candidate.GetMetadata("FileName"); var suffixIndex = fileName.Length - ".resources".Length; var relatedAssetPath = Path.GetFullPath(Path.Combine( OutputPath, "wwwroot", "_framework", fileName.Substring(0, suffixIndex) + ProjectAssembly[0].GetMetadata("Extension"))); candidate.SetMetadata("RelatedAsset", relatedAssetPath); Log.LogMessage("Found satellite assembly '{0}' asset for inferred candidate '{1}' with culture '{2}'", candidate.ItemSpec, relatedAssetPath, culture); } assetCandidates.Add(candidate); } var intermediateAssembly = new TaskItem(ProjectAssembly[0]); intermediateAssembly.SetMetadata("RelativePath", $"_framework/{intermediateAssembly.GetMetadata("FileName")}{intermediateAssembly.GetMetadata("Extension")}"); assetCandidates.Add(intermediateAssembly); if (ProjectDebugSymbols.Length > 0) { var debugSymbols = new TaskItem(ProjectDebugSymbols[0]); debugSymbols.SetMetadata("RelativePath", $"_framework/{debugSymbols.GetMetadata("FileName")}{debugSymbols.GetMetadata("Extension")}"); assetCandidates.Add(debugSymbols); } for (int i = 0; i < ProjectSatelliteAssemblies.Length; i++) { var projectSatelliteAssembly = ProjectSatelliteAssemblies[i]; var candidateCulture = projectSatelliteAssembly.GetMetadata("Culture"); Log.LogMessage( "Found satellite assembly '{0}' asset for project '{1}' with culture '{2}'", projectSatelliteAssembly.ItemSpec, intermediateAssembly.ItemSpec, candidateCulture); var assetCandidate = new TaskItem(Path.GetFullPath(projectSatelliteAssembly.ItemSpec), projectSatelliteAssembly.CloneCustomMetadata()); var projectAssemblyAssetPath = Path.GetFullPath(Path.Combine( OutputPath, "wwwroot", "_framework", ProjectAssembly[0].GetMetadata("FileName") + ProjectAssembly[0].GetMetadata("Extension"))); var normalizedPath = assetCandidate.GetMetadata("TargetPath").Replace('\\', '/'); assetCandidate.SetMetadata("AssetKind", "Build"); assetCandidate.SetMetadata("AssetRole", "Related"); assetCandidate.SetMetadata("AssetTraitName", "Culture"); assetCandidate.SetMetadata("AssetTraitValue", candidateCulture); assetCandidate.SetMetadata("RelativePath", Path.Combine("_framework", normalizedPath)); assetCandidate.SetMetadata("RelatedAsset", projectAssemblyAssetPath); assetCandidates.Add(assetCandidate); } for (var i = 0; i < assetCandidates.Count; i++) { var candidate = assetCandidates[i]; ApplyUniqueMetadataProperties(candidate); } } catch (Exception ex) { Log.LogError(ex.ToString()); return(false); } FilesToRemove = filesToRemove.ToArray(); AssetCandidates = assetCandidates.ToArray(); return(!Log.HasLoggedErrors); }
private List <ITaskItem> ProcessNativeAssets( Dictionary <string, ITaskItem> nativeAssets, IDictionary <string, ITaskItem> resolvedPublishFilesToRemove, Dictionary <string, ITaskItem> resolvedNativeAssetToPublish, Dictionary <string, ITaskItem> compressedRepresentations, List <ITaskItem> filesToRemove) { var nativeStaticWebAssets = new List <ITaskItem>(); // Keep track of the updated assets to determine what compressed assets we can reuse var updateMap = new Dictionary <string, ITaskItem>(); foreach (var kvp in nativeAssets) { var key = kvp.Key; var asset = kvp.Value; var isDotNetJs = IsDotNetJs(key); var isDotNetWasm = IsDotNetWasm(key); if (!isDotNetJs && !isDotNetWasm) { if (resolvedNativeAssetToPublish.TryGetValue(Path.GetFileName(asset.GetMetadata("OriginalItemSpec")), out var existing)) { if (!resolvedPublishFilesToRemove.TryGetValue(existing.ItemSpec, out var removed)) { // This is a native asset like timezones.blat or similar that was not filtered and that needs to be updated // to a publish asset. var newAsset = new TaskItem(asset); ApplyPublishProperties(newAsset); nativeStaticWebAssets.Add(newAsset); filesToRemove.Add(existing); updateMap.Add(asset.ItemSpec, newAsset); Log.LogMessage(MessageImportance.Low, "Promoting asset '{0}' to Publish asset.", asset.ItemSpec); } else { // This was a file that was filtered, so just remove it, we don't need to add any publish static web asset filesToRemove.Add(removed); // Remove the file from the list to avoid double processing later when we process other files we filtered. resolvedPublishFilesToRemove.Remove(existing.ItemSpec); } } continue; } if (isDotNetJs) { var aotDotNetJs = WasmAotAssets.SingleOrDefault(a => $"{a.GetMetadata("FileName")}{a.GetMetadata("Extension")}" == "dotnet.js"); ITaskItem newDotNetJs = null; if (aotDotNetJs != null) { newDotNetJs = new TaskItem(Path.GetFullPath(aotDotNetJs.ItemSpec), asset.CloneCustomMetadata()); newDotNetJs.SetMetadata("OriginalItemSpec", aotDotNetJs.ItemSpec); newDotNetJs.SetMetadata("RelativePath", $"_framework/{$"dotnet.{DotNetJsVersion}.{FileHasher.GetFileHash(aotDotNetJs.ItemSpec)}.js"}"); updateMap.Add(asset.ItemSpec, newDotNetJs); Log.LogMessage(MessageImportance.Low, "Replacing asset '{0}' with AoT version '{1}'", asset.ItemSpec, newDotNetJs.ItemSpec); } else { newDotNetJs = new TaskItem(asset); Log.LogMessage(MessageImportance.Low, "Promoting asset '{0}' to Publish asset.", asset.ItemSpec); } ApplyPublishProperties(newDotNetJs); nativeStaticWebAssets.Add(newDotNetJs); if (resolvedNativeAssetToPublish.TryGetValue("dotnet.js", out var resolved)) { filesToRemove.Add(resolved); } continue; } if (isDotNetWasm) { var aotDotNetWasm = WasmAotAssets.SingleOrDefault(a => $"{a.GetMetadata("FileName")}{a.GetMetadata("Extension")}" == "dotnet.wasm"); ITaskItem newDotNetWasm = null; if (aotDotNetWasm != null) { newDotNetWasm = new TaskItem(Path.GetFullPath(aotDotNetWasm.ItemSpec), asset.CloneCustomMetadata()); newDotNetWasm.SetMetadata("OriginalItemSpec", aotDotNetWasm.ItemSpec); updateMap.Add(asset.ItemSpec, newDotNetWasm); Log.LogMessage(MessageImportance.Low, "Replacing asset '{0}' with AoT version '{1}'", asset.ItemSpec, newDotNetWasm.ItemSpec); } else { newDotNetWasm = new TaskItem(asset); Log.LogMessage(MessageImportance.Low, "Promoting asset '{0}' to Publish asset.", asset.ItemSpec); } ApplyPublishProperties(newDotNetWasm); nativeStaticWebAssets.Add(newDotNetWasm); if (resolvedNativeAssetToPublish.TryGetValue("dotnet.wasm", out var resolved)) { filesToRemove.Add(resolved); } continue; } } var compressedUpdatedFiles = ProcessCompressedAssets(compressedRepresentations, nativeAssets, updateMap); foreach (var f in compressedUpdatedFiles) { nativeStaticWebAssets.Add(f); } return(nativeStaticWebAssets);