Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ artifacts/
.dotnet/
.tools/
.packages/
BenchmarkDotNet.Artifacts/

# Visual Studio 2015 cache/options directory
.vs/
Expand Down
1 change: 1 addition & 0 deletions MSBuild.Dev.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"src\\Framework\\Microsoft.Build.Framework.csproj",
"src\\MSBuild.UnitTests\\Microsoft.Build.CommandLine.UnitTests.csproj",
"src\\MSBuild\\MSBuild.csproj",
"src\\MSBuild.Benchmarks\\MSBuild.Benchmarks.csproj",
"src\\StringTools\\StringTools.csproj",
"src\\Tasks.UnitTests\\Microsoft.Build.Tasks.UnitTests.csproj",
"src\\Tasks\\Microsoft.Build.Tasks.csproj",
Expand Down
4 changes: 4 additions & 0 deletions MSBuild.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@
<Project Path="src/Framework/Microsoft.Build.Framework.csproj">
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/MSBuild.Benchmarks/MSBuild.Benchmarks.csproj" Id="7ab529db-7954-4d4f-a186-7544008a3b46">
<Platform Solution="*|ARM64" Project="arm64" />
<Platform Solution="*|x64" Project="x64" />
</Project>
<Project Path="src/MSBuild.Bootstrap/MSBuild.Bootstrap.csproj">
<Platform Solution="*|x64" Project="x64" />
</Project>
Expand Down
5 changes: 4 additions & 1 deletion eng/dependabot/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@
these properties to override package versions if necessary. -->

<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.13.10" />
<PackageVersion Include="BenchmarkDotNet" Version="0.13.12" />
<PackageVersion Update="BenchmarkDotNet" Condition="'$(BenchmarkDotNetVersion)' != ''" Version="$(BenchmarkDotNetVersion)" />

<PackageVersion Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.13.12" />
<PackageVersion Update="BenchmarkDotNet.Diagnostics.Windows" Condition="'$(BenchmarkDotNetDiagnosticsWindowsVersion)' != ''" Version="$(BenchmarkDotNetDiagnosticsWindowsVersion)" />

<PackageVersion Include="AwesomeAssertions" Version="8.0.2" />
<PackageVersion Update="AwesomeAssertions" Condition="'$(AwesomeAssertionsVersion)' != ''" Version="$(AwesomeAssertionsVersion)" />

Expand Down
1 change: 1 addition & 0 deletions src/Build/BackEnd/BuildManager/BuildManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1105,6 +1105,7 @@ public void EndBuild()
}

TaskRouter.ClearCache();
ItemSpecModifiers.ClearDefiningProjectCache();
}
catch (Exception e)
{
Expand Down
80 changes: 39 additions & 41 deletions src/Build/Definition/BuiltInMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
Expand All @@ -19,22 +19,12 @@ internal static class BuiltInMetadata
/// <summary>
/// Retrieves the count of built-in metadata.
/// </summary>
internal static int MetadataCount
{
[DebuggerStepThrough]
get
{ return ItemSpecModifiers.All.Length; }
}
internal static int MetadataCount => ItemSpecModifiers.All.Length;

/// <summary>
/// Retrieves the list of metadata names.
/// </summary>
internal static ICollection<string> MetadataNames
{
[DebuggerStepThrough]
get
{ return ItemSpecModifiers.All; }
}
internal static ImmutableArray<string> MetadataNames => ItemSpecModifiers.All;

/// <summary>
/// Retrieves a built-in metadata value and caches it.
Expand All @@ -48,45 +38,53 @@ internal static ICollection<string> MetadataNames
/// <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="fullPath">The generated full path, for caching</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 string fullPath)
{
return EscapingUtilities.UnescapeAll(GetMetadataValueEscaped(currentDirectory, evaluatedIncludeBeforeWildcardExpansionEscaped, evaluatedIncludeEscaped, definingProjectEscaped, name, ref fullPath));
}
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 and caches it.
/// Retrieves a built-in metadata value, caching derivable results in the provided per-item cache.
/// If value is not available, returns empty string.
/// </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="fullPath">The generated full path, for caching</param>
/// <returns>The escaped as necessary metadata value.</returns>
internal static string GetMetadataValueEscaped(string currentDirectory, string evaluatedIncludeBeforeWildcardExpansionEscaped, string evaluatedIncludeEscaped, string definingProjectEscaped, string name, ref string fullPath)
internal static string GetMetadataValueEscaped(
string currentDirectory,
string evaluatedIncludeBeforeWildcardExpansionEscaped,
string evaluatedIncludeEscaped,
string definingProjectEscaped,
string name,
ref ItemSpecModifiers.Cache cache)
{
// This is an assert, not a VerifyThrow, because the caller should already have done this check, and it's slow/hot.
Debug.Assert(ItemSpecModifiers.IsItemSpecModifier(name));

string value;
if (String.Equals(name, ItemSpecModifiers.RecursiveDir, StringComparison.OrdinalIgnoreCase))
if (ItemSpecModifiers.TryGetModifierKind(name, out ItemSpecModifierKind modifierKind))
{
value = GetRecursiveDirValue(evaluatedIncludeBeforeWildcardExpansionEscaped, evaluatedIncludeEscaped);
}
else
{
value = ItemSpecModifiers.GetItemSpecModifier(currentDirectory, evaluatedIncludeEscaped, definingProjectEscaped, name, ref fullPath);
return GetMetadataValueEscaped(currentDirectory, evaluatedIncludeBeforeWildcardExpansionEscaped, evaluatedIncludeEscaped, definingProjectEscaped, modifierKind, ref cache);
}

return value;
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.
Expand Down
26 changes: 7 additions & 19 deletions src/Build/Definition/ProjectItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ public class ProjectItem : IItem<ProjectMetadata>, IProjectMetadataParent, IItem
private PropertyDictionary<ProjectMetadata> _directMetadata;

/// <summary>
/// Cached value of the fullpath metadata. All other metadata are computed on demand.
/// Cached values of derivable item-spec modifiers. All time-based metadata are computed on demand.
/// </summary>
private string _fullPath;
private ItemSpecModifiers.Cache _cachedModifiers;

/// <summary>
/// External projects support
Expand Down Expand Up @@ -299,12 +299,7 @@ public ICollection<ProjectMetadata> Metadata
/// Includes any metadata inherited from item definitions.
/// Includes both custom and built-in metadata.
/// </summary>
public int MetadataCount
{
[DebuggerStepThrough]
get
{ return Metadata.Count + ItemSpecModifiers.All.Length; }
}
public int MetadataCount => Metadata.Count + ItemSpecModifiers.All.Length;

/// <summary>
/// Implementation of IKeyed exposing the item type, so items
Expand Down Expand Up @@ -700,7 +695,7 @@ public void Rename(string name)
return;
}

_fullPath = null; // Clear cached value
_cachedModifiers.Clear(); // Clear cached values

if (_xml.Count == 0 /* no metadata */ && _project.IsSuitableExistingItemXml(_xml, name, null /* no metadata */) && !FileMatcher.HasWildcardsSemicolonItemOrPropertyReferences(name))
{
Expand Down Expand Up @@ -854,16 +849,9 @@ internal void SplitOwnItemElement()
/// the specified name, if any.
/// </summary>
private string GetBuiltInMetadataEscaped(string name)
{
string value = null;

if (ItemSpecModifiers.IsItemSpecModifier(name))
{
value = BuiltInMetadata.GetMetadataValueEscaped(_project.DirectoryPath, _evaluatedIncludeBeforeWildcardExpansionEscaped, _evaluatedIncludeEscaped, this.Xml.ContainingProject.FullPath, name, ref _fullPath);
}

return value;
}
=> ItemSpecModifiers.TryGetModifierKind(name, out ItemSpecModifierKind modifierKind)
? BuiltInMetadata.GetMetadataValueEscaped(_project.DirectoryPath, _evaluatedIncludeBeforeWildcardExpansionEscaped, _evaluatedIncludeEscaped, Xml.ContainingProject.FullPath, modifierKind, ref _cachedModifiers)
: null;

/// <summary>
/// Retrieves the named metadata from the item definition, if any.
Expand Down
4 changes: 2 additions & 2 deletions src/Build/Evaluation/Expander.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2560,7 +2560,7 @@ internal static void ItemSpecModifierFunction(IElementLocation elementLocation,
string directoryToUse = item.Value.ProjectDirectory ?? FileUtilities.CurrentThreadWorkingDirectory ?? Directory.GetCurrentDirectory();
string definingProjectEscaped = item.Value.GetMetadataValueEscaped(ItemSpecModifiers.DefiningProjectFullPath);

result = ItemSpecModifiers.GetItemSpecModifier(directoryToUse, item.Key, definingProjectEscaped, functionName);
result = ItemSpecModifiers.GetItemSpecModifier(item.Key, functionName, directoryToUse, definingProjectEscaped);
}
// InvalidOperationException is how GetItemSpecModifier communicates invalid conditions upwards, so
// we do not want to rethrow in that case.
Expand Down Expand Up @@ -3328,7 +3328,7 @@ private static string GetMetadataValueFromMatch(
string directoryToUse = sourceOfMetadata.ProjectDirectory ?? FileUtilities.CurrentThreadWorkingDirectory ?? Directory.GetCurrentDirectory();
string definingProjectEscaped = sourceOfMetadata.GetMetadataValueEscaped(ItemSpecModifiers.DefiningProjectFullPath);

value = ItemSpecModifiers.GetItemSpecModifier(directoryToUse, itemSpec, definingProjectEscaped, match.Name);
value = ItemSpecModifiers.GetItemSpecModifier(itemSpec, match.Name, directoryToUse, definingProjectEscaped);
}
else
{
Expand Down
28 changes: 12 additions & 16 deletions src/Build/Instance/ProjectItemInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -817,9 +817,9 @@ internal sealed class TaskItem :
private IReadOnlyDictionary<string, string> _directMetadata;

/// <summary>
/// Cached value of the fullpath metadata. All other metadata are computed on demand.
/// Cached values of derivable item-spec modifiers. All time-based metadata are computed on demand.
/// </summary>
private string _fullPath;
private ItemSpecModifiers.Cache _cachedModifiers;

/// <summary>
/// All the item definitions that apply to this item, in order of
Expand Down Expand Up @@ -893,7 +893,7 @@ private TaskItem(TaskItem source, bool addOriginalItemSpec)
_includeEscaped = source._includeEscaped;
_includeBeforeWildcardExpansionEscaped = source._includeBeforeWildcardExpansionEscaped;
source.CopyMetadataTo(this, addOriginalItemSpec);
_fullPath = source._fullPath;
_cachedModifiers = source._cachedModifiers;
_definingFileEscaped = source._definingFileEscaped;
}

Expand Down Expand Up @@ -936,7 +936,7 @@ public string ItemSpec
ErrorUtilities.VerifyThrowArgumentNull(value, "ItemSpec");

_includeEscaped = value;
_fullPath = null; // Clear cached value
_cachedModifiers.Clear(); // Clear cached values
}
}

Expand Down Expand Up @@ -981,7 +981,10 @@ public ICollection MetadataNames
names.Add(metadatum.Key);
}

names.AddRange(ItemSpecModifiers.All);
foreach (string name in ItemSpecModifiers.All)
{
names.Add(name);
}

return names;
}
Expand Down Expand Up @@ -1054,7 +1057,7 @@ internal string IncludeEscaped

ErrorUtilities.VerifyThrowArgumentLength(value, "IncludeEscaped");
_includeEscaped = value;
_fullPath = null; // Clear cached value
_cachedModifiers.Clear(); // Clear cached values
}
}

Expand Down Expand Up @@ -2081,16 +2084,9 @@ internal TaskItem DeepClone(bool isImmutable)
/// If value is not available, returns empty string.
/// </summary>
private string GetBuiltInMetadataEscaped(string name)
{
string value = String.Empty;

if (ItemSpecModifiers.IsItemSpecModifier(name))
{
value = BuiltInMetadata.GetMetadataValueEscaped(_projectDirectory, _includeBeforeWildcardExpansionEscaped, _includeEscaped, _definingFileEscaped, name, ref _fullPath);
}

return value;
}
=> ItemSpecModifiers.TryGetModifierKind(name, out ItemSpecModifierKind modifierKind)
? BuiltInMetadata.GetMetadataValueEscaped(_projectDirectory, _includeBeforeWildcardExpansionEscaped, _includeEscaped, _definingFileEscaped, modifierKind, ref _cachedModifiers)
: string.Empty;

/// <summary>
/// Retrieves the named metadata from the item definition, if any.
Expand Down
8 changes: 3 additions & 5 deletions src/Build/Instance/ProjectMetadataInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -241,12 +241,10 @@ internal static void VerifyThrowReservedName(string name)
// PERF: This sequence of checks is faster than a full HashSet lookup since finding a match is an error case.
// Otherwise, many keys would still match to a bucket and begin a string comparison.
VerifyThrowReservedNameAllowItemSpecModifiers(name);
foreach (string itemSpecModifier in ItemSpecModifiers.All)

if (ItemSpecModifiers.IsItemSpecModifier(name))
{
if (itemSpecModifier.Length == name.Length && itemSpecModifier[0] == char.ToUpperInvariant(name[0]))
{
ErrorUtilities.VerifyThrowArgument(!MSBuildNameIgnoreCaseComparer.Default.Equals(itemSpecModifier, name), "OM_ReservedName", name);
}
ErrorUtilities.ThrowArgument("OM_ReservedName", name);
}
}

Expand Down
Loading