File: FindAppConfigFile.cs
Web Access
Project: ..\..\..\src\Tasks\Microsoft.Build.Tasks.csproj (Microsoft.Build.Tasks.Core)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
 
#nullable disable
 
namespace Microsoft.Build.Tasks
{
    /// <summary>
    /// Finds the app.config file, if any, in the provided lists.
    /// For compat reasons, it has to follow a particular arbitrary algorithm.
    /// It also adds the TargetPath metadata.
    /// </summary>
    public class FindAppConfigFile : TaskExtension
    {
        // The list to search through
        private ITaskItem[] _primaryList;
        private ITaskItem[] _secondaryList;
 
        // The target path metadata value to add to the found item
 
        // The item found, if any
 
        // What we're looking for
        private const string appConfigFile = "app.config";
 
        /// <summary>
        /// The primary list to search through
        /// </summary>
        [Required]
        public ITaskItem[] PrimaryList
        {
            get
            {
                ErrorUtilities.VerifyThrowArgumentNull(_primaryList, nameof(PrimaryList));
                return _primaryList;
            }
            set => _primaryList = value;
        }
 
        /// <summary>
        /// The secondary list to search through
        /// </summary>
        [Required]
        public ITaskItem[] SecondaryList
        {
            get
            {
                ErrorUtilities.VerifyThrowArgumentNull(_secondaryList, nameof(SecondaryList));
                return _secondaryList;
            }
            set => _secondaryList = value;
        }
 
        /// <summary>
        /// The value to add as TargetPath metadata
        /// </summary>
        [Required]
        public string TargetPath { get; set; }
 
        /// <summary>
        /// The first matching item found in the list, if any
        /// </summary>
        [Output]
        public ITaskItem AppConfigFile { get; set; }
 
        /// <summary>
        /// Find the app config
        /// </summary>
        public override bool Execute()
        {
            // Look at the whole item spec first -- ie,
            // we want to prefer app.config files that are directly in the project folder.
            if (ConsultLists(true))
            {
                return true;
            }
 
            // If that fails, fall back to app.config files anywhere in the project cone.
            if (ConsultLists(false))
            {
                return true;
            }
 
            // Not found
            return true;
        }
 
        private bool ConsultLists(bool matchWholeItemSpec)
        {
            // Look at primary list first, then secondary list
            // We walk backwards on the list to find the last match (for historical reasons)
            for (int i = PrimaryList.Length - 1; i >= 0; i--)
            {
                if (IsMatchingItem(PrimaryList[i], matchWholeItemSpec))
                {
                    return true;
                }
            }
 
            for (int i = SecondaryList.Length - 1; i >= 0; i--)
            {
                if (IsMatchingItem(SecondaryList[i], matchWholeItemSpec))
                {
                    return true;
                }
            }
 
            return false;
        }
 
        /// <summary>
        /// Examines the item to see if it matches what we are looking for.
        /// If it does, returns true.
        /// </summary>
        private bool IsMatchingItem(ITaskItem item, bool matchWholeItemSpec)
        {
            try
            {
                string filename = (matchWholeItemSpec ? item.ItemSpec : Path.GetFileName(item.ItemSpec));
 
                if (String.Equals(filename, appConfigFile, StringComparison.OrdinalIgnoreCase))
                {
                    AppConfigFile = item;
 
                    // Originally the app.config was found in such a way that it's "OriginalItemSpec"
                    // metadata was cleared out. Although it doesn't really matter, for compatibility,
                    // we'll clear it out here.
                    AppConfigFile.SetMetadata("OriginalItemSpec", item.ItemSpec);
 
                    AppConfigFile.SetMetadata(ItemMetadataNames.targetPath, TargetPath);
 
                    Log.LogMessageFromResources(MessageImportance.Low, "FindInList.Found", AppConfigFile.ItemSpec);
                    return true;
                }
            }
            catch (ArgumentException ex)
            {
                // Just log this: presumably this item spec is not intended to be
                // a file path
                Log.LogMessageFromResources(MessageImportance.Low, "FindInList.InvalidPath", item.ItemSpec, ex.Message);
            }
            return false;
        }
    }
}