File: Definition\BuiltInMetadata.cs
Web Access
Project: ..\..\..\src\Build\Microsoft.Build.csproj (Microsoft.Build)
// 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.Collections.Immutable;
using System.Diagnostics;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
 
namespace Microsoft.Build.Evaluation
{
    /// <summary>
    /// This class encapsulates the behavior and collection of built-in metadata.  These metadatum
    /// are inferred from the content of the include and sometimes the context of the project or
    /// current directory.
    /// </summary>
    internal static class BuiltInMetadata
    {
        /// <summary>
        /// Retrieves the count of built-in metadata.
        /// </summary>
        internal static int MetadataCount => ItemSpecModifiers.All.Length;
 
        /// <summary>
        /// Retrieves the list of metadata names.
        /// </summary>
        internal static ImmutableArray<string> MetadataNames => ItemSpecModifiers.All;
 
        /// <summary>
        /// Retrieves a built-in metadata value and caches it.
        /// Never returns null.
        /// </summary>
        /// <param name="currentDirectory">
        /// The current directory for evaluation.  Null if this is being called from a task, otherwise
        /// it should be the project's directory.
        /// </param>
        /// <param name="evaluatedIncludeBeforeWildcardExpansionEscaped">The evaluated include prior to wildcard expansion.</param>
        /// <param name="evaluatedIncludeEscaped">The evaluated include for the item.</param>
        /// <param name="definingProjectEscaped">The path to the project that defined this item</param>
        /// <param name="name">The name of the metadata.</param>
        /// <param name="cache">The generated full path, for caching</param>
        /// <returns>The unescaped metadata value.</returns>
        internal static string GetMetadataValue(
            string currentDirectory,
            string evaluatedIncludeBeforeWildcardExpansionEscaped,
            string evaluatedIncludeEscaped,
            string definingProjectEscaped,
            string name,
            ref ItemSpecModifiers.Cache cache)
            => EscapingUtilities.UnescapeAll(GetMetadataValueEscaped(currentDirectory, evaluatedIncludeBeforeWildcardExpansionEscaped, evaluatedIncludeEscaped, definingProjectEscaped, name, ref cache));
 
        /// <summary>
        /// Retrieves a built-in metadata value, caching derivable results in the provided per-item cache.
        /// If value is not available, returns empty string.
        /// </summary>
        internal static string GetMetadataValueEscaped(
            string currentDirectory,
            string evaluatedIncludeBeforeWildcardExpansionEscaped,
            string evaluatedIncludeEscaped,
            string definingProjectEscaped,
            string name,
            ref ItemSpecModifiers.Cache cache)
        {
            if (ItemSpecModifiers.TryGetModifierKind(name, out ItemSpecModifierKind modifierKind))
            {
                return GetMetadataValueEscaped(currentDirectory, evaluatedIncludeBeforeWildcardExpansionEscaped, evaluatedIncludeEscaped, definingProjectEscaped, modifierKind, ref cache);
            }
 
            Debug.Fail($"Expected a valid item-spec modifier, got \"{name}\".");
            return string.Empty;
        }
 
        /// <summary>
        /// Retrieves a built-in metadata value, caching derivable results in the provided per-item cache.
        /// If value is not available, returns empty string.
        /// </summary>
        internal static string GetMetadataValueEscaped(
            string currentDirectory,
            string evaluatedIncludeBeforeWildcardExpansionEscaped,
            string evaluatedIncludeEscaped,
            string definingProjectEscaped,
            ItemSpecModifierKind modifierKind,
            ref ItemSpecModifiers.Cache cache)
            => modifierKind is ItemSpecModifierKind.RecursiveDir
                ? GetRecursiveDirValue(evaluatedIncludeBeforeWildcardExpansionEscaped, evaluatedIncludeEscaped)
                : ItemSpecModifiers.GetItemSpecModifier(evaluatedIncludeEscaped, modifierKind, currentDirectory, definingProjectEscaped, ref cache);
 
        /// <summary>
        /// Extract the value for "RecursiveDir", if any, from the Include.
        /// If there is none, returns an empty string.
        /// </summary>
        /// <remarks>
        /// Inputs to and outputs of this function are all escaped.
        /// </remarks>
        private static string GetRecursiveDirValue(string evaluatedIncludeBeforeWildcardExpansionEscaped, string evaluatedIncludeEscaped)
        {
            // If there were no wildcards, the two strings will be the same, and there is no recursivedir part.
            if (String.Equals(evaluatedIncludeBeforeWildcardExpansionEscaped, evaluatedIncludeEscaped, StringComparison.OrdinalIgnoreCase))
            {
                return String.Empty;
            }
 
            // we're going to the file system, so unescape the include value here:
            string evaluatedIncludeBeforeWildcardExpansion = EscapingUtilities.UnescapeAll(evaluatedIncludeBeforeWildcardExpansionEscaped);
            string evaluatedInclude = EscapingUtilities.UnescapeAll(evaluatedIncludeEscaped);
 
            FileMatcher.Result match = FileMatcher.Default.FileMatch(evaluatedIncludeBeforeWildcardExpansion, evaluatedInclude);
 
            if (match.isLegalFileSpec && match.isMatch)
            {
                return EscapingUtilities.Escape(match.wildcardDirectoryPart);
            }
 
            return String.Empty;
        }
    }
}