File: ReplaceFileContents.cs
Web Access
Project: ..\..\..\src\Tasks\sdk-tasks\sdk-tasks.csproj (sdk-tasks)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#nullable disable
 
using System.Text.RegularExpressions;
 
namespace Microsoft.DotNet.Build.Tasks
{
    /// <summary>
    /// Reads contents of an input file, and searches for each replacement passed in.
    ///
    /// When ReplacementItems is matched, it will replace the Include/ItemSpec with the corresponding
    /// ReplacementString metadata value. This can be useful if the ReplacementString is a value that
    /// cannot be represented by ITaskItem.ItemSpec (like string.Empty).
    ///
    /// When a ReplacementPattern is matched it will replace it with the string of the corresponding (by index)
    /// item in ReplacementStrings.
    ///
    /// For example, if 2 ReplacementPatterns are passed in, 2 ReplacementStrings must also passed in and the first
    /// pattern will be replaced with the first string, and the second pattern replaced with the second string.
    ///
    /// ReplacementPattern could easily be a regex, but it isn't needed for current use cases, so leaving this
    /// as just a string that will be replaced.
    /// </summary>
    public class ReplaceFileContents : Task
    {
        [Required]
        public ITaskItem[] InputFiles { get; set; }
 
        [Required]
        public ITaskItem[] DestinationFiles { get; set; }
 
        public ITaskItem[] ReplacementItems { get; set; }
 
        public ITaskItem[] ReplacementPatterns { get; set; }
 
        public ITaskItem[] ReplacementStrings { get; set; }
 
        /// <summary>
        /// Gets or sets a string that a file must contain for the replacement to be performed.
        /// </summary>
        public string FileMustContainText { get; set; }
 
        public override bool Execute()
        {
            if (ReplacementItems == null && ReplacementPatterns == null && ReplacementStrings == null)
            {
                throw new Exception($"ReplaceFileContents was called with no replacement values. Either pass ReplacementItems or ReplacementPatterns/ReplacementStrings properties.");
            }
 
            ReplacementItems = ReplacementItems ?? Array.Empty<ITaskItem>();
            ReplacementPatterns = ReplacementPatterns ?? Array.Empty<ITaskItem>();
            ReplacementStrings = ReplacementStrings ?? Array.Empty<ITaskItem>();
 
            if (ReplacementPatterns.Length != ReplacementStrings.Length)
            {
                throw new Exception($"Expected {nameof(ReplacementPatterns)}  (length {ReplacementPatterns.Length}) and {nameof(ReplacementStrings)} (length {ReplacementStrings.Length}) to have the same length.");
            }
 
            if (InputFiles.Length != DestinationFiles.Length)
            {
                throw new Exception($"Expected {nameof(InputFiles)}  (length {InputFiles.Length}) and {nameof(DestinationFiles)} (length {DestinationFiles.Length}) to have the same length.");
            }
 
            var filesNotFound = InputFiles.Where(i => !File.Exists(i.ItemSpec)).Select(i => i.ItemSpec);
            if (filesNotFound.Any())
            {
                var filesNotFoundString = string.Join(",", filesNotFound);
                throw new FileNotFoundException($"Expected files where not found: {filesNotFoundString}");
            }
 
            Log.LogMessage(MessageImportance.High, $"ReplacingContents for `{InputFiles.Length}` files.");
 
            for (var i = 0; i < InputFiles.Length; i++)
            {
                ReplaceContents(InputFiles[i].ItemSpec, DestinationFiles[i].ItemSpec);
            }
 
            return true;
        }
 
        public void ReplaceContents(string inputFile, string destinationFile)
        {
            string inputFileText = File.ReadAllText(inputFile);
            if (!string.IsNullOrEmpty(FileMustContainText) && !inputFileText.Contains(FileMustContainText))
            {
                Log.LogMessage(MessageImportance.Low, $"Skipping replacement on `{inputFile}` because it does not contain the text '{FileMustContainText}'.");
                return;
            }
 
            string outputFileText = ReplacePatterns(inputFileText);
 
            WriteOutputFile(destinationFile, outputFileText);
        }
 
        public string ReplacePatterns(string inputFileText)
        {
            var outText = inputFileText;
 
            foreach (var replacementItem in ReplacementItems)
            {
                var replacementPattern = replacementItem.ItemSpec;
                var replacementString = replacementItem.GetMetadata("ReplacementString");
 
                outText = outText.Replace(replacementPattern, replacementString);
            }
 
            for (int i = 0; i < ReplacementPatterns.Length; ++i)
            {
                var replacementPattern = ReplacementPatterns[i].ItemSpec;
                var replacementString = ReplacementStrings[i].ItemSpec;
 
                var regex = new Regex(replacementPattern);
                outText = regex.Replace(outText, replacementString);
            }
 
            return outText;
        }
 
        public void WriteOutputFile(string destinationFile, string outputFileText)
        {
            var destinationDirectory = Path.GetDirectoryName(destinationFile);
            Log.LogMessage(MessageImportance.High, $"Destination Directory: {destinationDirectory}");
            if (!Directory.Exists(destinationDirectory))
            {
                Log.LogMessage(MessageImportance.High, $"Destination Directory `{destinationDirectory}` does not exist. Creating...");
                Directory.CreateDirectory(destinationDirectory);
            }
 
            Log.LogMessage(MessageImportance.High, $"Writing file: {destinationFile}");
            File.WriteAllText(destinationFile, outputFileText);
        }
    }
}