Clean up unused MSBuild tasks in SB (#15450)
This commit is contained in:
parent
f05478ba9a
commit
4e601e7fc6
11 changed files with 0 additions and 1004 deletions
|
@ -4,7 +4,6 @@
|
||||||
<UsingTask AssemblyFile="$(LeakDetectionTasksAssembly)" TaskName="CheckForPoison" />
|
<UsingTask AssemblyFile="$(LeakDetectionTasksAssembly)" TaskName="CheckForPoison" />
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="WriteUsageBurndownData" />
|
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="WriteUsageBurndownData" />
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ReplaceTextInFile" />
|
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ReplaceTextInFile" />
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="DownloadFileSB" />
|
|
||||||
|
|
||||||
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
|
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
<Import Project="$(GitInfoAllRepoPropsFile)" />
|
<Import Project="$(GitInfoAllRepoPropsFile)" />
|
||||||
|
|
||||||
<UsingTask AssemblyFile="$(LeakDetectionTasksAssembly)" TaskName="MarkAndCatalogPackages" />
|
<UsingTask AssemblyFile="$(LeakDetectionTasksAssembly)" TaskName="MarkAndCatalogPackages" />
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="NuGetPack" />
|
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ZipFileExtractToDirectory" />
|
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ZipFileExtractToDirectory" />
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ReplaceTextInFile" />
|
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ReplaceTextInFile" />
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,20 +6,16 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="AddSourceToNuGetConfig" />
|
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="AddSourceToNuGetConfig" />
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="GetSourceBuiltNupkgCacheConflicts" />
|
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ReadNuGetPackageInfos" />
|
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ReadNuGetPackageInfos" />
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="RemoveInternetSourcesFromNuGetConfig" />
|
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="RemoveInternetSourcesFromNuGetConfig" />
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ReplaceFeedsInNuGetConfig" />
|
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="UpdateJson" />
|
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="UpdateJson" />
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="UpdateNuGetConfigPackageSourcesMappings" />
|
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="UpdateNuGetConfigPackageSourcesMappings" />
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ValidateUsageAgainstBaseline" />
|
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ValidateUsageAgainstBaseline" />
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="WritePackageVersionsProps" />
|
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="WritePackageVersionsProps" />
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="WritePackageUsageData" />
|
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="WritePackageUsageData" />
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="WriteUsageReports" />
|
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="WriteUsageReports" />
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="WriteVersionsFile" />
|
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ZipFileExtractToDirectory" />
|
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ZipFileExtractToDirectory" />
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ReplaceTextInFile" />
|
<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
|
Central property to define that a repo doesn't implement any of the Repo API. If a repo adds an
|
||||||
|
|
|
@ -67,7 +67,5 @@
|
||||||
<UseSourceBuiltSdkOverride Include="@(ArcadeSdkOverride)" />
|
<UseSourceBuiltSdkOverride Include="@(ArcadeSdkOverride)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="ReplaceRegexInFiles" />
|
|
||||||
|
|
||||||
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
|
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -63,7 +63,6 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="AddRidToRuntimeJson" />
|
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="AddRidToRuntimeJson" />
|
||||||
<UsingTask AssemblyFile="$(XPlatSourceBuildTasksAssembly)" TaskName="PublishCoreSetupBinaries" />
|
|
||||||
|
|
||||||
<Target Name="SetOutputList" AfterTargets="Package" BeforeTargets="GatherBuiltPackages">
|
<Target Name="SetOutputList" AfterTargets="Package" BeforeTargets="GatherBuiltPackages">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue