Clean up unused MSBuild tasks in SB (#15450)

This commit is contained in:
Matt Thalman 2023-02-07 13:42:50 -06:00 committed by GitHub
parent f05478ba9a
commit 4e601e7fc6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 0 additions and 1004 deletions

View file

@ -4,7 +4,6 @@
<UsingTask AssemblyFile="$(LeakDetectionTasksAssembly)" TaskName="CheckForPoison" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="WriteUsageBurndownData" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ReplaceTextInFile" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="DownloadFileSB" />
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />

View file

@ -6,7 +6,6 @@
<Import Project="$(GitInfoAllRepoPropsFile)" />
<UsingTask AssemblyFile="$(LeakDetectionTasksAssembly)" TaskName="MarkAndCatalogPackages" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="NuGetPack" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ZipFileExtractToDirectory" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ReplaceTextInFile" />

View file

@ -1,409 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// This task is sourced from https://github.com/microsoft/msbuild/blob/04e508c36f9c1fe826264aef7c26ffb8f16e9bdc/src/Tasks/DownloadFile.cs
// Contains further modifications in followup commits.
// It alleviates the problem of time outs on DownloadFile Task. We are not the version of msbuild that has this fix, hence we have to locally
// build it to get rid of the issue.
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.DotNet.Build.Tasks;
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Task = System.Threading.Tasks.Task;
namespace Microsoft.Build.Tasks
{
/// <summary>
/// Represents a task that can download a file.
/// </summary>
public sealed class DownloadFileSB : BuildTask, ICancelableTask
{
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
/// <summary>
/// Gets or sets an optional filename for the destination file. By default, the filename is derived from the <see cref="SourceUrl"/> if possible.
/// </summary>
public ITaskItem DestinationFileName { get; set; }
/// <summary>
/// Gets or sets a <see cref="ITaskItem"/> that specifies the destination folder to download the file to.
/// </summary>
[Required]
public ITaskItem DestinationFolder { get; set; }
/// <summary>
/// Gets or sets a <see cref="ITaskItem"/> that contains details about the downloaded file.
/// </summary>
[Output]
public ITaskItem DownloadedFile { get; set; }
/// <summary>
/// Gets or sets an optional number of times to retry if possible.
/// </summary>
public int Retries { get; set; }
/// <summary>
/// Gets or sets the number of milliseconds to wait before retrying.
/// </summary>
public int RetryDelayMilliseconds { get; set; } = 5 * 1000;
/// <summary>
/// Gets or sets an optional value indicating whether or not the download should be skipped if the file is up-to-date.
/// </summary>
public bool SkipUnchangedFiles { get; set; } = true;
/// <summary>
/// Gets or sets the URL to download.
/// </summary>
[Required]
public string SourceUrl { get; set; }
/// <summary>
/// Gets or sets the timeout for a successful download. If exceeded, the download continues
/// for another two timeout durations before failing. This makes it sometimes possible to
/// determine whether the timeout is just a little too short, or if the download would never
/// have finished.
/// </summary>
public string TimeoutSeconds { get; set; }
/// <summary>
/// Gets or sets a <see cref="HttpMessageHandler"/> to use. This is used by unit tests to mock a connection to a remote server.
/// </summary>
internal HttpMessageHandler HttpMessageHandler { get; set; }
/// <inheritdoc cref="ICancelableTask.Cancel"/>
public void Cancel()
{
_cancellationTokenSource.Cancel();
}
public override bool Execute()
{
return ExecuteAsync().GetAwaiter().GetResult();
}
private async Task<bool> ExecuteAsync()
{
if (!Uri.TryCreate(SourceUrl, UriKind.Absolute, out Uri uri))
{
Log.LogError($"DownloadFileSB.ErrorInvalidUrl {SourceUrl}");
return false;
}
int retryAttemptCount = 0;
CancellationToken cancellationToken = _cancellationTokenSource.Token;
var startTime = DateTime.UtcNow;
// Use the same API for the "success timeout" and the "would it ever succeed" timeout.
var timeout = TimeSpan.Zero;
var successCancellationTokenSource = new CancellationTokenSource();
if (double.TryParse(TimeoutSeconds, out double timeoutSeconds))
{
timeout = TimeSpan.FromSeconds(timeoutSeconds);
Log.LogMessage(MessageImportance.High, $"DownloadFileSB timeout set to {timeout}");
successCancellationTokenSource.CancelAfter(timeout);
_cancellationTokenSource.CancelAfter((int)(timeout.TotalMilliseconds * 3));
}
while (true)
{
try
{
await DownloadAsync(uri, cancellationToken);
break;
}
catch (OperationCanceledException e) when (e.CancellationToken == cancellationToken)
{
// This task is being cancelled. Exit the loop.
break;
}
catch (Exception e)
{
bool canRetry = IsRetriable(e, out Exception actualException) && retryAttemptCount++ < Retries;
if (canRetry)
{
Log.LogWarning($"DownloadFileSB.Retrying {SourceUrl} {retryAttemptCount + 1} {RetryDelayMilliseconds} {actualException}");
try
{
await Task.Delay(RetryDelayMilliseconds, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException delayException) when (delayException.CancellationToken == cancellationToken)
{
// This task is being cancelled, exit the loop
break;
}
}
else
{
Log.LogError($"DownloadFileSB.ErrorDownloading {SourceUrl} {actualException}");
break;
}
}
}
var finishTime = DateTime.UtcNow;
if (successCancellationTokenSource.IsCancellationRequested)
{
string error = $"{TimeoutSeconds} second timeout exceeded";
if (!_cancellationTokenSource.IsCancellationRequested)
{
error +=
$", but download completed after {finishTime - startTime}. " +
$"Try increasing timeout from {TimeoutSeconds} if this is acceptable.";
}
else
{
error +=
$", and didn't complete within leeway after {finishTime - startTime}. " +
$"The download was likely never going to terminate. Investigate logs and " +
$"add additional logging if necessary.";
}
Log.LogError(error);
}
else
{
Log.LogMessage(
MessageImportance.High,
$"DownloadFileSB.Downloading Complete! Elapsed: {finishTime - startTime}");
}
return !_cancellationTokenSource.IsCancellationRequested && !Log.HasLoggedErrors;
}
/// <summary>
/// Attempts to download the file.
/// </summary>
/// <param name="uri">The parsed <see cref="Uri"/> of the request.</param>
private async Task DownloadAsync(Uri uri, CancellationToken cancellationToken)
{
// The main reason to use HttpClient vs WebClient is because we can pass a message handler for unit tests to mock
using (var client = new HttpClient(HttpMessageHandler ?? new HttpClientHandler(), disposeHandler: true))
{
// Only get the response without downloading the file so we can determine if the file is already up-to-date
using (HttpResponseMessage response = await client.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false))
{
try
{
response.EnsureSuccessStatusCode();
}
catch (HttpRequestException e)
{
// HttpRequestException does not have the status code so its wrapped and thrown here so that later on we can determine
// if a retry is possible based on the status code
throw new CustomHttpRequestException(e.Message, e.InnerException, response.StatusCode);
}
if (!TryGetFileName(response, out string filename))
{
Log.LogError($"DownloadFileSB.ErrorUnknownFileName {SourceUrl} {nameof(DestinationFileName)}");
return;
}
DirectoryInfo destinationDirectory = Directory.CreateDirectory(DestinationFolder.ItemSpec);
var destinationFile = new FileInfo(Path.Combine(destinationDirectory.FullName, filename));
// The file is considered up-to-date if its the same length. This could be inaccurate, we can consider alternatives in the future
if (ShouldSkip(response, destinationFile))
{
Log.LogMessage(MessageImportance.Normal, $"DownloadFileSB.DidNotDownloadBecauseOfFileMatch {SourceUrl}", destinationFile.FullName, nameof(SkipUnchangedFiles), "true");
DownloadedFile = new TaskItem(destinationFile.FullName);
return;
}
var progressMonitorCancellationTokenSource = new CancellationTokenSource();
CancellationToken progressMonitorToken = progressMonitorCancellationTokenSource.Token;
try
{
cancellationToken.ThrowIfCancellationRequested();
var startTime = DateTime.UtcNow;
var progressMonitor = Task.Run(
async () =>
{
while (!progressMonitorToken.IsCancellationRequested)
{
destinationFile.Refresh();
if (destinationFile.Exists)
{
long current = destinationFile.Length;
long total = response.Content.Headers.ContentLength ?? 1;
var elapsed = DateTime.UtcNow - startTime;
double kbytesPerSecond = current / elapsed.TotalSeconds / 1000.0;
Log.LogMessage(
MessageImportance.High,
$"Progress... {elapsed}, " +
$"current file size {current / (double)total:00.0%} " +
$"({destinationFile.Length:#,0} / {total:#,0}) " +
$"~ {kbytesPerSecond:#,0.00} kB/s");
}
await Task.Delay(TimeSpan.FromSeconds(5), progressMonitorToken);
}
},
progressMonitorToken)
.ConfigureAwait(false);
using (var target = new FileStream(destinationFile.FullName, FileMode.Create, FileAccess.Write, FileShare.None))
{
Log.LogMessage(
MessageImportance.High,
$"DownloadFileSB.Downloading {SourceUrl} to " +
$"{destinationFile.FullName}");
Log.LogMessage( MessageImportance.Low, $"All response headers:\n{response.Headers}");
Log.LogMessage( MessageImportance.Low, $"All content headers:\n{response.Content.Headers}");
using (Stream responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
{
await responseStream.CopyToAsync(target, 1024, cancellationToken).ConfigureAwait(false);
}
Log.LogMessage(MessageImportance.High, $"DownloadFileSB.StreamCopyComplete {SourceUrl}");
DownloadedFile = new TaskItem(destinationFile.FullName);
}
}
finally
{
if (DownloadedFile == null)
{
// Delete the file if anything goes wrong during download. This could be destructive but we don't want to leave
// partially downloaded files on disk either. Alternatively we could download to a temporary location and copy
// on success but we are concerned about the added I/O
destinationFile.Delete();
}
progressMonitorCancellationTokenSource.Cancel();
}
}
}
}
/// <summary>
/// Determines if the specified exception is considered retriable.
/// </summary>
/// <param name="exception">The originally thrown exception.</param>
/// <param name="actualException">The actual exception to be used for logging errors.</param>
/// <returns><code>true</code> if the exception is retriable, otherwise <code>false</code>.</returns>
private static bool IsRetriable(Exception exception, out Exception actualException)
{
actualException = exception;
// Get aggregate inner exception
if (actualException is AggregateException aggregateException && aggregateException.InnerException != null)
{
actualException = aggregateException.InnerException;
}
// Some HttpRequestException have an inner exception that has the real error
if (actualException is HttpRequestException httpRequestException && httpRequestException.InnerException != null)
{
actualException = httpRequestException.InnerException;
// An IOException inside of a HttpRequestException means that something went wrong while downloading
if (actualException is IOException)
{
return true;
}
}
if (actualException is CustomHttpRequestException customHttpRequestException)
{
// A wrapped CustomHttpRequestException has the status code from the error
switch (customHttpRequestException.StatusCode)
{
case HttpStatusCode.InternalServerError:
case HttpStatusCode.RequestTimeout:
return true;
}
}
if (actualException is WebException webException)
{
// WebException is thrown when accessing the Content of the response
switch (webException.Status)
{
// Don't retry on anything that cannot be compensated for
case WebExceptionStatus.TrustFailure:
case WebExceptionStatus.MessageLengthLimitExceeded:
case WebExceptionStatus.RequestProhibitedByCachePolicy:
case WebExceptionStatus.RequestProhibitedByProxy:
return false;
default:
// Retry on all other WebExceptions
return true;
}
}
return false;
}
/// <summary>
/// Attempts to get the file name to use when downloading the file.
/// </summary>
/// <param name="response">The <see cref="HttpResponseMessage"/> with information about the response.</param>
/// <param name="filename">Receives the name of the file.</param>
/// <returns><code>true</code> if a file name could be determined, otherwise <code>false</code>.</returns>
private bool TryGetFileName(HttpResponseMessage response, out string filename)
{
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
// Not all URIs contain a file name so users will have to specify one
// Example: http://www.download.com/file/1/
filename = !String.IsNullOrWhiteSpace(DestinationFileName?.ItemSpec)
? DestinationFileName.ItemSpec // Get the file name from what the user specified
: response.Content?.Headers?.ContentDisposition?.FileName // Attempt to get the file name from the content-disposition header value
?? Path.GetFileName(response.RequestMessage.RequestUri.LocalPath); // Otherwise attempt to get a file name from the URI
return !String.IsNullOrWhiteSpace(filename);
}
/// <summary>
/// Represents a wrapper around the <see cref="HttpRequestException"/> that also contains the <see cref="HttpStatusCode"/>.
/// </summary>
private sealed class CustomHttpRequestException : HttpRequestException
{
public CustomHttpRequestException(string message, Exception inner, HttpStatusCode statusCode)
: base(message, inner)
{
StatusCode = statusCode;
}
public HttpStatusCode StatusCode { get; }
}
private bool ShouldSkip(HttpResponseMessage response, FileInfo destinationFile)
{
return SkipUnchangedFiles
&& destinationFile.Exists
&& destinationFile.Length == response.Content.Headers.ContentLength
&& response.Content.Headers.LastModified.HasValue
&& destinationFile.LastWriteTimeUtc > response.Content.Headers.LastModified.Value.UtcDateTime;
}
}
}

View file

@ -1,376 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.Build.Framework;
using NuGet;
using NuGet.Versioning;
using NuGet.Packaging;
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using NuGet.Common;
namespace Microsoft.DotNet.Build.Tasks.Packaging
{
public class NuGetPack : PackagingTask
{
/// <summary>
/// Target file paths to exclude when building the lib package for symbol server scenario
/// Copied from https://github.com/NuGet/NuGet.Client/blob/59433c7bacaae435a2cfe343cd441ea710579304/src/NuGet.Core/NuGet.Commands/PackCommandRunner.cs#L48
/// </summary>
private static readonly string[] _libPackageExcludes = new[] {
@"**\*.pdb".Replace('\\', Path.DirectorySeparatorChar),
@"src\**\*".Replace('\\', Path.DirectorySeparatorChar)
};
/// <summary>
/// Target file paths to exclude when building the symbols package for symbol server scenario
/// </summary>
private static readonly string[] _symbolPackageExcludes = new[] {
@"content\**\*".Replace('\\', Path.DirectorySeparatorChar),
@"tools\**\*.ps1".Replace('\\', Path.DirectorySeparatorChar)
};
private static readonly string _defaultPackedPackagePrefix = "transport";
private static readonly string _symbolsPackageExtension = ".symbols.nupkg";
private static readonly string _packageExtension = ".nupkg";
[Required]
public ITaskItem[] Nuspecs
{
get;
set;
}
[Required]
public string OutputDirectory
{
get;
set;
}
public string BaseDirectory
{
get;
set;
}
public string PackageVersion
{
get;
set;
}
public bool ExcludeEmptyDirectories
{
get;
set;
}
// Create an additional ".symbols.nupkg" package
public bool CreateSymbolPackage
{
get;
set;
}
// Include symbols in standard package
public bool IncludeSymbolsInPackage
{
get;
set;
}
// Create an additional "packed package" that includes lib and src / symbols
public bool CreatePackedPackage
{
get;
set;
}
/// <summary>
/// Nuspec files can contain properties that are substituted with values at pack time
/// This task property passes through the nuspect properties.
/// Each item is a string with the syntax <key>=<value>
/// String validation for <key> and <value> is deffered to the Nuget APIs
/// </summary>
public ITaskItem[] NuspecProperties
{
get;
set;
}
public ITaskItem[] AdditionalLibPackageExcludes
{
get;
set;
}
public ITaskItem[] AdditionalSymbolPackageExcludes
{
get;
set;
}
/// <summary>
/// If set, the symbol package is placed in the given directory. Otherwise OutputDirectory is used.
/// </summary>
public string SymbolPackageOutputDirectory
{
get;
set;
}
public string PackedPackageNamePrefix
{
get;
set;
}
public override bool Execute()
{
if (Nuspecs == null || Nuspecs.Length == 0)
{
Log.LogError("Nuspecs argument must be specified");
return false;
}
if (String.IsNullOrEmpty(OutputDirectory))
{
Log.LogError("OuputDirectory argument must be specified");
return false;
}
if (!Directory.Exists(OutputDirectory))
{
Directory.CreateDirectory(OutputDirectory);
}
Func<string, string> nuspecPropertyProvider = GetNuspecPropertyProviderFunction(NuspecProperties);
foreach (var nuspec in Nuspecs)
{
string nuspecPath = nuspec.GetMetadata("FullPath");
if (!File.Exists(nuspecPath))
{
Log.LogError($"Nuspec {nuspecPath} does not exist");
continue;
}
Manifest manifest = GetManifest(nuspecPath, nuspecPropertyProvider, false);
string nupkgPath = GetPackageOutputPath(nuspecPath, manifest, false, false);
Pack(nuspecPath, nupkgPath, manifest, IncludeSymbolsInPackage);
bool packSymbols = CreateSymbolPackage || CreatePackedPackage;
if (CreateSymbolPackage)
{
Manifest symbolsManifest = GetManifest(nuspecPath, nuspecPropertyProvider, false);
nupkgPath = GetPackageOutputPath(nuspecPath, symbolsManifest, true, false);
Pack(nuspecPath, nupkgPath, symbolsManifest, packSymbols);
}
if (CreatePackedPackage)
{
Manifest packedManifest = GetManifest(nuspecPath, nuspecPropertyProvider, true);
nupkgPath = GetPackageOutputPath(nuspecPath, packedManifest, false, true);
Pack(nuspecPath, nupkgPath, packedManifest, packSymbols);
}
}
return !Log.HasLoggedErrors;
}
private static Func<string, string> GetNuspecPropertyProviderFunction(ITaskItem[] nuspecProperties)
{
return nuspecProperties == null ? null : NuspecPropertyStringProvider.GetNuspecPropertyProviderFunction(nuspecProperties.Select(p => p.ItemSpec).ToArray());
}
private Manifest GetManifest(string nuspecPath, Func<string, string> nuspecPropertyProvider, bool isPackedPackage)
{
using (var nuspecFile = File.Open(nuspecPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete))
{
string baseDirectoryPath = (string.IsNullOrEmpty(BaseDirectory)) ? Path.GetDirectoryName(nuspecPath) : BaseDirectory;
Manifest manifest = Manifest.ReadFrom(nuspecFile, nuspecPropertyProvider, false);
if (isPackedPackage)
{
manifest = TransformManifestToPackedPackageManifest(manifest);
}
return manifest;
}
}
private string GetPackageOutputPath(string nuspecPath, Manifest manifest, bool isSymbolsPackage, bool applyPrefix)
{
string id = manifest.Metadata.Id;
if (String.IsNullOrEmpty(id))
{
Log.LogError($"Nuspec {nuspecPath} does not contain a valid Id");
return string.Empty;
}
// Overriding the Version from the Metadata if one gets passed in.
if (!string.IsNullOrEmpty(PackageVersion))
{
NuGetVersion overrideVersion;
if (NuGetVersion.TryParse(PackageVersion, out overrideVersion))
{
manifest.Metadata.Version = overrideVersion;
}
else
{
Log.LogError($"Failed to parse Package Version: '{PackageVersion}' is not a valid version.");
}
}
string version = manifest.Metadata.Version.ToString();
if (String.IsNullOrEmpty(version))
{
Log.LogError($"Nuspec {nuspecPath} does not contain a valid version");
return string.Empty;
}
string nupkgOutputDirectory = OutputDirectory;
if (isSymbolsPackage && !string.IsNullOrEmpty(SymbolPackageOutputDirectory))
{
nupkgOutputDirectory = SymbolPackageOutputDirectory;
}
string nupkgExtension = isSymbolsPackage ? _symbolsPackageExtension : _packageExtension;
return Path.Combine(nupkgOutputDirectory, $"{id}.{version}{nupkgExtension}");
}
public void Pack(string nuspecPath, string nupkgPath, Manifest manifest, bool packSymbols)
{
bool creatingSymbolsPackage = packSymbols && (Path.GetExtension(nupkgPath) == _symbolsPackageExtension);
try
{
PackageBuilder builder = new PackageBuilder();
string baseDirectoryPath = (string.IsNullOrEmpty(BaseDirectory)) ? Path.GetDirectoryName(nuspecPath) : BaseDirectory;
builder.Populate(manifest.Metadata);
builder.PopulateFiles(baseDirectoryPath, manifest.Files);
if (creatingSymbolsPackage)
{
// For symbols packages, filter out excludes
PathResolver.FilterPackageFiles(
builder.Files,
file => file.Path,
SymbolPackageExcludes);
// Symbol packages are only valid if they contain both symbols and sources.
Dictionary<string, bool> pathHasMatches = LibPackageExcludes.ToDictionary(
path => path,
path => PathResolver.GetMatches(builder.Files, file => file.Path, new[] { path }).Any());
if (!pathHasMatches.Values.Any(i => i))
{
Log.LogMessage(LogImportance.Low, $"Nuspec {nuspecPath} does not contain symbol or source files. Not creating symbol package.");
return;
}
foreach (var pathPair in pathHasMatches.Where(pathMatchPair => !pathMatchPair.Value))
{
Log.LogMessage(LogImportance.Low, $"Nuspec {nuspecPath} does not contain any files matching {pathPair.Key}. Not creating symbol package.");
return;
}
}
else if(!packSymbols)
{
// for packages which do not include symbols (not symbols or packed packages), filter lib excludes
PathResolver.FilterPackageFiles(
builder.Files,
file => file.Path,
LibPackageExcludes);
}
var directory = Path.GetDirectoryName(nupkgPath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
using (var fileStream = File.Create(nupkgPath))
{
builder.Save(fileStream);
}
Log.LogMessage($"Created '{nupkgPath}'");
}
catch (Exception e)
{
string packageType = "lib";
if (creatingSymbolsPackage)
{
packageType = "symbol";
}
else if (packSymbols)
{
packageType = "packed";
}
Log.LogError($"Error when creating nuget {packageType} package from {nuspecPath}. {e}");
}
}
private Manifest TransformManifestToPackedPackageManifest(Manifest manifest)
{
ManifestMetadata manifestMetadata = manifest.Metadata;
// Update Id
string _packageNamePrefix = PackedPackageNamePrefix != null ? PackedPackageNamePrefix : _defaultPackedPackagePrefix;
manifestMetadata.Id = $"{_packageNamePrefix}.{manifestMetadata.Id}";
// Update dependencies
List<PackageDependencyGroup> packedPackageDependencyGroups = new List<PackageDependencyGroup>();
foreach(var dependencyGroup in manifestMetadata.DependencyGroups)
{
List<NuGet.Packaging.Core.PackageDependency> packages = new List<NuGet.Packaging.Core.PackageDependency>();
foreach(var dependency in dependencyGroup.Packages)
{
NuGet.Packaging.Core.PackageDependency package = new NuGet.Packaging.Core.PackageDependency($"{_packageNamePrefix}.{dependency.Id}", dependency.VersionRange, dependency.Include, dependency.Exclude);
packages.Add(package);
}
PackageDependencyGroup packageDependencyGroup = new PackageDependencyGroup(dependencyGroup.TargetFramework, packages);
packedPackageDependencyGroups.Add(packageDependencyGroup);
}
manifestMetadata.DependencyGroups = packedPackageDependencyGroups;
// Update runtime.json
List<ManifestFile> manifestFiles = new List<ManifestFile>();
foreach(ManifestFile file in manifest.Files)
{
string fileName = file.Source;
if(Path.GetFileName(fileName) == "runtime.json" && file.Target == "")
{
string packedPackageSourcePath = Path.Combine(Path.GetDirectoryName(fileName), string.Join(".", _packageNamePrefix, Path.GetFileName(fileName)));
file.Source = File.Exists(packedPackageSourcePath) ? packedPackageSourcePath : fileName;
file.Target = "runtime.json";
}
manifestFiles.Add(file);
}
Manifest packedPackageManifest = new Manifest(manifestMetadata, manifestFiles);
return manifest;
}
private IEnumerable<string> LibPackageExcludes
{
get
{
return _libPackageExcludes
.Concat(AdditionalLibPackageExcludes?.Select(item => item.ItemSpec) ?? Enumerable.Empty<string>());
}
}
private IEnumerable<string> SymbolPackageExcludes
{
get
{
return _symbolPackageExcludes
.Concat(AdditionalSymbolPackageExcludes?.Select(item => item.ItemSpec) ?? Enumerable.Empty<string>());
}
}
}
}

View file

@ -1,72 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.Collections.Generic;
namespace Microsoft.DotNet.Build.Tasks.Packaging
{
public class NuspecPropertyStringProvider
{
public static Dictionary<string, string> GetNuspecPropertyDictionary(string[] nuspecProperties)
{
if (nuspecProperties == null)
{
return null;
}
var propertyDictionary = new Dictionary<string, string>();
foreach (var propertyString in nuspecProperties)
{
var property = GetKeyValuePair(propertyString);
propertyDictionary[property.Item1] = property.Item2;
}
return propertyDictionary;
}
public static Func<string, string> GetNuspecPropertyProviderFunction(string[] nuspecPropertyStrings)
{
var propertyDictionary = GetNuspecPropertyDictionary(nuspecPropertyStrings);
if (propertyDictionary == null)
{
return null;
}
return k => propertyDictionary[k];
}
private static Tuple<string, string> GetKeyValuePair(string propertyString)
{
propertyString = propertyString.Trim();
var indexOfEquals = propertyString.IndexOf("=", StringComparison.Ordinal);
if (indexOfEquals == -1)
{
throw new InvalidDataException($"Nuspec property {propertyString} does not have an \'=\' character in it");
}
if (indexOfEquals == propertyString.Length - 1)
{
throw new InvalidDataException($"Nuspec property {propertyString} does not have a value");
}
if (indexOfEquals == 0)
{
throw new InvalidDataException($"Nuspec property {propertyString} does not have a key");
}
var key = propertyString.Substring(0, indexOfEquals);
var valueStartIndex = indexOfEquals + 1;
var valueLength = propertyString.Length - valueStartIndex;
var value = propertyString.Substring(valueStartIndex, valueLength);
return new Tuple<string, string>(key, value);
}
}
}

View file

@ -1,54 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace Microsoft.DotNet.Build.Tasks
{
/// <summary>
/// Replaces feeds in a NuGet.Config file given a mapping
/// of old feeds to new feeds.
/// </summary>
public class ReplaceFeedsInNuGetConfig : Task
{
/// <summary>
/// The NuGet.Config file in which to replace feeds.
/// </summary>
[Required]
public string InputFile { get; set; }
/// <summary>
/// An item group of feeds to update.
/// %(Identity): The feed URL to find in the NuGet.Config.
/// %(NewFeed): The feed URL to replace %(Identity) with.
/// </summary>
[Required]
public ITaskItem[] FeedMapping { get; set; }
public override bool Execute()
{
string fileContents = File.ReadAllText(InputFile);
bool updated = false;
foreach (var feed in FeedMapping)
{
string oldFeed = feed.ItemSpec;
string newFeed = feed.GetMetadata("NewFeed");
if (fileContents.Contains(oldFeed))
{
fileContents = fileContents.Replace(oldFeed, newFeed);
updated = true;
}
}
if (updated) File.WriteAllText(InputFile, fileContents);
return true;
}
}
}

View file

@ -1,39 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace Microsoft.DotNet.Build.Tasks
{
public class ReplaceRegexInFiles : Task
{
[Required]
public string[] InputFiles { get; set; }
[Required]
public string OldTextRegex { get; set; }
[Required]
public string NewText { get; set; }
public override bool Execute()
{
Log.LogMessage($"Replacing '{OldTextRegex}' with '{NewText}'");
foreach (string file in InputFiles)
{
string fileContents = File.ReadAllText(file);
fileContents = Regex.Replace(fileContents, OldTextRegex, NewText);
File.WriteAllText(file, fileContents);
}
return true;
}
}
}

View file

@ -1,45 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.Text;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using NuGet.Packaging;
using NuGet.Packaging.Core;
namespace Microsoft.DotNet.Build.Tasks
{
public class WriteVersionsFile : Task
{
[Required]
public ITaskItem[] NugetPackages { get; set; }
[Required]
public string OutputPath { get; set; }
public override bool Execute()
{
Directory.CreateDirectory(Path.GetDirectoryName(OutputPath));
using (Stream outStream = File.Open(OutputPath, FileMode.Create))
{
using (StreamWriter sw = new StreamWriter(outStream, new UTF8Encoding(false)))
{
foreach (ITaskItem nugetPackage in NugetPackages)
{
using (PackageArchiveReader par = new PackageArchiveReader(nugetPackage.GetMetadata("FullPath")))
{
PackageIdentity packageIdentity = par.GetIdentity();
sw.WriteLine($"{packageIdentity.Id} {packageIdentity.Version}");
}
}
}
}
return true;
}
}
}

View file

@ -6,20 +6,16 @@
</ItemGroup>
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="AddSourceToNuGetConfig" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="GetSourceBuiltNupkgCacheConflicts" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ReadNuGetPackageInfos" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="RemoveInternetSourcesFromNuGetConfig" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ReplaceFeedsInNuGetConfig" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="UpdateJson" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="UpdateNuGetConfigPackageSourcesMappings" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ValidateUsageAgainstBaseline" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="WritePackageVersionsProps" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="WritePackageUsageData" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="WriteUsageReports" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="WriteVersionsFile" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ZipFileExtractToDirectory" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ReplaceTextInFile" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ReplaceRegexInFiles" />
<!--
Central property to define that a repo doesn't implement any of the Repo API. If a repo adds an

View file

@ -67,7 +67,5 @@
<UseSourceBuiltSdkOverride Include="@(ArcadeSdkOverride)" />
</ItemGroup>
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ReplaceRegexInFiles" />
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>

View file

@ -63,7 +63,6 @@
</ItemGroup>
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="AddRidToRuntimeJson" />
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="PublishCoreSetupBinaries" />
<Target Name="SetOutputList" AfterTargets="Package" BeforeTargets="GatherBuiltPackages">
<ItemGroup>