66using System . Collections . ObjectModel ;
77using System . Diagnostics ;
88using System . IO ;
9+ using System . Linq ;
910using System . Reflection ;
11+ using System . Threading ;
1012using Microsoft . Build . BackEnd ;
1113using Microsoft . Build . Collections ;
1214using Microsoft . Build . Construction ;
@@ -122,6 +124,11 @@ internal sealed class TaskRegistry : ITranslatable
122124 /// </summary>
123125 private static string s_potentialTasksCoreLocation = Path . Combine ( BuildEnvironmentHelper . Instance . CurrentMSBuildToolsDirectory , s_tasksCoreFilename ) ;
124126
127+ /// <summary>
128+ /// Monotonically increasing counter for registered tasks.
129+ /// </summary>
130+ private int _nextRegistrationOrderId = 0 ;
131+
125132 /// <summary>
126133 /// Cache of tasks already found using exact matching,
127134 /// keyed by the task identity requested.
@@ -192,6 +199,12 @@ internal Toolset Toolset
192199 { return _toolset ; }
193200 }
194201
202+ /// <summary>
203+ /// Access the next registration sequence id.
204+ /// FOR UNIT TESTING ONLY.
205+ /// </summary>
206+ internal int NextRegistrationOrderId => _nextRegistrationOrderId ;
207+
195208 /// <summary>
196209 /// Access list of task registrations.
197210 /// FOR UNIT TESTING ONLY.
@@ -525,23 +538,11 @@ internal RegisteredTaskRecord GetTaskRegistrationRecord(
525538 }
526539 }
527540
528- Dictionary < RegisteredTaskIdentity , List < RegisteredTaskRecord > > registrations = GetRelevantRegistrations ( taskIdentity , exactMatchRequired ) ;
541+ IEnumerable < RegisteredTaskRecord > registrations = GetRelevantOrderedRegistrations ( taskIdentity , exactMatchRequired ) ;
529542
530543 // look for the given task name in the registry; if not found, gather all registered task names that partially
531544 // match the given name
532- foreach ( KeyValuePair < RegisteredTaskIdentity , List < RegisteredTaskRecord > > registration in registrations )
533- {
534- // if the given task name is longer than the registered task name
535- // we will use the longer name to help disambiguate between multiple matches
536- string mostSpecificTaskName = ( taskName . Length > registration . Key . Name . Length ) ? taskName : registration . Key . Name ;
537-
538- taskRecord = GetMatchingRegistration ( mostSpecificTaskName , registration . Value , taskProjectFile , taskIdentityParameters , targetLoggingContext , elementLocation ) ;
539-
540- if ( taskRecord != null )
541- {
542- break ;
543- }
544- }
545+ taskRecord = GetMatchingRegistration ( taskName , registrations , taskProjectFile , taskIdentityParameters , targetLoggingContext , elementLocation ) ;
545546 }
546547
547548 // If we didn't find the task but we have a fallback registry in the toolset state, try that one.
@@ -603,36 +604,24 @@ private static bool IsTaskFactoryClass(Type type, object unused)
603604 /// If no exact match is found, looks for partial matches.
604605 /// A task name that is not fully qualified may produce several partial matches.
605606 /// </summary>
606- private Dictionary < RegisteredTaskIdentity , List < RegisteredTaskRecord > > GetRelevantRegistrations ( RegisteredTaskIdentity taskIdentity , bool exactMatchRequired )
607+ private IEnumerable < RegisteredTaskRecord > GetRelevantOrderedRegistrations ( RegisteredTaskIdentity taskIdentity , bool exactMatchRequired )
607608 {
608- Dictionary < RegisteredTaskIdentity , List < RegisteredTaskRecord > > relevantTaskRegistrations =
609- new Dictionary < RegisteredTaskIdentity , List < RegisteredTaskRecord > > ( RegisteredTaskIdentity . RegisteredTaskIdentityComparer . Exact ) ;
610-
611- List < RegisteredTaskRecord > taskAssemblies ;
612-
613- // if we find an exact match
614- if ( _taskRegistrations . TryGetValue ( taskIdentity , out taskAssemblies ) )
609+ if ( _taskRegistrations . TryGetValue ( taskIdentity , out List < RegisteredTaskRecord > taskAssemblies ) )
615610 {
616- // we're done
617- relevantTaskRegistrations [ taskIdentity ] = taskAssemblies ;
618- return relevantTaskRegistrations ;
611+ // (records for single key should be ordered by order of registrations - as they are inserted into the list)
612+ return taskAssemblies ;
619613 }
620614
621615 if ( exactMatchRequired )
622616 {
623- return relevantTaskRegistrations ;
617+ return Enumerable . Empty < RegisteredTaskRecord > ( ) ;
624618 }
625619
626620 // look through all task declarations for partial matches
627- foreach ( KeyValuePair < RegisteredTaskIdentity , List < RegisteredTaskRecord > > taskRegistration in _taskRegistrations )
628- {
629- if ( RegisteredTaskIdentity . RegisteredTaskIdentityComparer . IsPartialMatch ( taskIdentity , taskRegistration . Key ) )
630- {
631- relevantTaskRegistrations [ taskRegistration . Key ] = taskRegistration . Value ;
632- }
633- }
634-
635- return relevantTaskRegistrations ;
621+ return _taskRegistrations
622+ . Where ( tp => RegisteredTaskIdentity . RegisteredTaskIdentityComparer . IsPartialMatch ( taskIdentity , tp . Key ) )
623+ . SelectMany ( tp => tp . Value )
624+ . OrderBy ( r => r . RegistrationOrderId ) ;
636625 }
637626
638627 // Create another set containing architecture-specific task entries.
@@ -673,7 +662,13 @@ private void RegisterTask(
673662 _taskRegistrations [ taskIdentity ] = registeredTaskEntries ;
674663 }
675664
676- RegisteredTaskRecord newRecord = new RegisteredTaskRecord ( taskName , assemblyLoadInfo , taskFactory , taskFactoryParameters , inlineTaskRecord ) ;
665+ RegisteredTaskRecord newRecord = new RegisteredTaskRecord (
666+ taskName ,
667+ assemblyLoadInfo ,
668+ taskFactory ,
669+ taskFactoryParameters ,
670+ inlineTaskRecord ,
671+ Interlocked . Increment ( ref _nextRegistrationOrderId ) ) ;
677672
678673 if ( overrideTask )
679674 {
@@ -721,23 +716,21 @@ private static Dictionary<RegisteredTaskIdentity, List<RegisteredTaskRecord>> Cr
721716 /// </summary>
722717 private RegisteredTaskRecord GetMatchingRegistration (
723718 string taskName ,
724- List < RegisteredTaskRecord > taskRecords ,
719+ IEnumerable < RegisteredTaskRecord > taskRecords ,
725720 string taskProjectFile ,
726721 IDictionary < string , string > taskIdentityParameters ,
727722 TargetLoggingContext targetLoggingContext ,
728723 ElementLocation elementLocation )
729- {
730- foreach ( RegisteredTaskRecord record in taskRecords )
731- {
732- if ( record . CanTaskBeCreatedByFactory ( taskName , taskProjectFile , taskIdentityParameters , targetLoggingContext , elementLocation ) )
733- {
734- return record ;
735- }
736- }
737-
738- // Cannot find the task in any of the records
739- return null ;
740- }
724+ =>
725+ taskRecords . FirstOrDefault ( r =>
726+ r . CanTaskBeCreatedByFactory (
727+ // if the given task name is longer than the registered task name
728+ // we will use the longer name to help disambiguate between multiple matches
729+ ( taskName . Length > r . TaskIdentity . Name . Length ) ? taskName : r . TaskIdentity . Name ,
730+ taskProjectFile ,
731+ taskIdentityParameters ,
732+ targetLoggingContext ,
733+ elementLocation ) ) ;
741734
742735 /// <summary>
743736 /// An object representing the identity of a task -- not just task name, but also
@@ -1115,17 +1108,23 @@ internal class RegisteredTaskRecord : ITranslatable
11151108 /// </summary>
11161109 private ParameterGroupAndTaskElementRecord _parameterGroupAndTaskBody ;
11171110
1111+ /// <summary>
1112+ /// The registration order id for this task. This is used to determine the order in which tasks are registered.
1113+ /// </summary>
1114+ private int _registrationOrderId ;
1115+
11181116 /// <summary>
11191117 /// Constructor
11201118 /// </summary>
1121- internal RegisteredTaskRecord ( string registeredName , AssemblyLoadInfo assemblyLoadInfo , string taskFactory , Dictionary < string , string > taskFactoryParameters , ParameterGroupAndTaskElementRecord inlineTask )
1119+ internal RegisteredTaskRecord ( string registeredName , AssemblyLoadInfo assemblyLoadInfo , string taskFactory , Dictionary < string , string > taskFactoryParameters , ParameterGroupAndTaskElementRecord inlineTask , int registrationOrderId )
11221120 {
11231121 ErrorUtilities . VerifyThrowArgumentNull ( assemblyLoadInfo , "AssemblyLoadInfo" ) ;
11241122 _registeredName = registeredName ;
11251123 _taskFactoryAssemblyLoadInfo = assemblyLoadInfo ;
11261124 _taskFactoryParameters = taskFactoryParameters ;
11271125 _taskIdentity = new RegisteredTaskIdentity ( registeredName , taskFactoryParameters ) ;
11281126 _parameterGroupAndTaskBody = inlineTask ;
1127+ _registrationOrderId = registrationOrderId ;
11291128
11301129 if ( String . IsNullOrEmpty ( taskFactory ) )
11311130 {
@@ -1206,6 +1205,11 @@ internal ParameterGroupAndTaskElementRecord ParameterGroupAndTaskBody
12061205 /// </summary>
12071206 internal RegisteredTaskIdentity TaskIdentity => _taskIdentity ;
12081207
1208+ /// <summary>
1209+ /// The registration order id for this task. This is used to determine the order in which tasks are registered.
1210+ /// </summary>
1211+ internal int RegistrationOrderId => _registrationOrderId ;
1212+
12091213 /// <summary>
12101214 /// Ask the question, whether or not the task name can be created by the task factory.
12111215 /// To answer this question we need to instantiate and initialize the task factory and ask it if it can create the given task name.
@@ -1765,6 +1769,7 @@ public void Translate(ITranslator translator)
17651769 translator . Translate ( ref _taskFactoryAssemblyLoadInfo , AssemblyLoadInfo . FactoryForTranslation ) ;
17661770 translator . Translate ( ref _taskFactory ) ;
17671771 translator . Translate ( ref _parameterGroupAndTaskBody ) ;
1772+ translator . Translate ( ref _registrationOrderId ) ;
17681773
17691774 IDictionary < string , string > localParameters = _taskFactoryParameters ;
17701775 translator . TranslateDictionary ( ref localParameters , count => CreateTaskFactoryParametersDictionary ( count ) ) ;
@@ -1787,7 +1792,7 @@ internal static RegisteredTaskRecord FactoryForDeserialization(ITranslator trans
17871792 public void Translate ( ITranslator translator )
17881793 {
17891794 translator . Translate ( ref _toolset , Toolset . FactoryForDeserialization ) ;
1790-
1795+ translator . Translate ( ref _nextRegistrationOrderId ) ;
17911796 IDictionary < RegisteredTaskIdentity , List < RegisteredTaskRecord > > copy = _taskRegistrations ;
17921797 translator . TranslateDictionary ( ref copy , TranslateTaskRegistrationKey , TranslateTaskRegistrationValue , count => CreateRegisteredTaskDictionary ( count ) ) ;
17931798
0 commit comments