internal ConditionEvaluationState(XmlAttribute conditionAttribute, Expander expanderToUse, Hashtable conditionedPropertiesInProject, string parsedCondition) { this.conditionAttribute = conditionAttribute; this.expanderToUse = expanderToUse; this.conditionedPropertiesInProject = conditionedPropertiesInProject; this.parsedCondition = parsedCondition; }
internal TargetExecutionWrapper ( Target targetClass, ArrayList taskElementList, List<string> targetParameters, XmlElement targetElement, Expander expander, BuildEventContext targetBuildEventContext ) { // Initialize the data about the target XML that has been calculated in the target class this.targetClass = targetClass; this.parentEngine = targetClass.ParentEngine; this.parentProject = targetClass.ParentProject; this.targetElement = targetElement; this.taskElementList = taskElementList; this.targetParameters = targetParameters; this.targetBuildEventContext = targetBuildEventContext; // Expand the list of depends on targets dependsOnTargetNames = expander.ExpandAllIntoStringList(targetClass.DependsOnTargets, targetClass.DependsOnTargetsAttribute); // Starting to build the target inProgressBuildState = InProgressBuildState.StartingBuild; // No messages have been logged loggedTargetStart = false; }
public void ExpandAllIntoTaskItems3() { BuildPropertyGroup pg = new BuildPropertyGroup(); BuildItemGroup ig = new BuildItemGroup(); ig.AddItem(new BuildItem("Compile", "foo.cs")); ig.AddItem(new BuildItem("Compile", "bar.cs")); BuildItemGroup ig2 = new BuildItemGroup(); ig2.AddItem(new BuildItem("Resource", "bing.resx")); Hashtable itemsByType = new Hashtable(StringComparer.OrdinalIgnoreCase); itemsByType["Compile"] = ig; itemsByType["Resource"] = ig2; Expander expander = new Expander(pg, itemsByType); List<TaskItem> itemsOut = expander.ExpandAllIntoTaskItems("foo;bar;@(compile);@(resource)", null); ObjectModelHelpers.AssertItemsMatch(@" foo bar foo.cs bar.cs bing.resx ", itemsOut.ToArray()); }
public void Metadata() { string content = @" <i Include='i1' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'> <m>$(p)</m> <n>n1</n> </i>"; XmlDocument doc = new XmlDocument(); doc.LoadXml(content); BuildItem item = CreateBuildItemFromXmlDocument(doc); Assertion.AssertEquals("i", item.Name); BuildPropertyGroup properties = new BuildPropertyGroup(); properties.SetProperty("p", "p1"); // Evaluated Expander expander = new Expander(properties, null, ExpanderOptions.ExpandAll); item.EvaluateAllItemMetadata(expander, ParserOptions.AllowPropertiesAndItemLists, null, null); Assertion.AssertEquals("p1", item.GetEvaluatedMetadata("m")); // Unevaluated Assertion.AssertEquals("$(p)", item.GetMetadata("m")); Assertion.AssertEquals("n1", item.GetMetadata("n")); // All custom metadata ArrayList metadataNames = new ArrayList(item.CustomMetadataNames); Assertion.Assert(metadataNames.Contains("n")); Assertion.Assert(metadataNames.Contains("m")); // Custom metadata count only Assertion.AssertEquals(2, item.CustomMetadataCount); // All metadata count Assertion.AssertEquals(2 + FileUtilities.ItemSpecModifiers.All.Length, item.MetadataCount); }
/// <summary> /// Helper method so we can keep the real Expander.ExpandMetadataAndProperties private. /// </summary> /// <param name="bucket"></param> /// <param name="expression"></param> /// <returns></returns> /// <owner>RGoel</owner> private static string ExpandMetadataAndProperties ( ItemBucket bucket, string expression ) { Expander itemExpander = new Expander(bucket.Expander, ExpanderOptions.ExpandPropertiesAndMetadata); return itemExpander.ExpandAllIntoString(expression, (new XmlDocument()).CreateAttribute("foo")); }
public void ExpandAllIntoTaskItems1() { BuildPropertyGroup pg = new BuildPropertyGroup(); Expander expander = new Expander(pg); List<TaskItem> itemsOut = expander.ExpandAllIntoTaskItems("foo", null); ObjectModelHelpers.AssertItemsMatch(@"foo", itemsOut.ToArray()); }
/// <summary> /// Helper method so we can keep the real Expander.ExpandItemsIntoString private. /// </summary> /// <param name="bucket"></param> /// <param name="expression"></param> /// <returns></returns> /// <owner>RGoel</owner> private static string ExpandItemsIntoString ( ItemBucket bucket, string expression ) { Expander itemExpander = new Expander(new ReadOnlyLookup(bucket.Lookup), null, ExpanderOptions.ExpandItems); return itemExpander.ExpandAllIntoString(expression, (new XmlDocument()).CreateAttribute("foo")); }
public void SimpleEvaluationTests() { Parser p = new Parser(); Expander expander = new Expander(new BuildPropertyGroup()); Hashtable conditionedProperties = null; ConditionEvaluationState state = new ConditionEvaluationState(DummyAttribute, expander, conditionedProperties, string.Empty); AssertParseEvaluate(p, "true", state, true); AssertParseEvaluate(p, "on", state, true); AssertParseEvaluate(p, "yes", state, true); AssertParseEvaluate(p, "false", state, false); AssertParseEvaluate(p, "off", state, false); AssertParseEvaluate(p, "no", state, false); }
/// <summary> /// Given a property bag, this method evaluates the current property, /// expanding any property references contained within. It stores this /// evaluated value in the "finalValue" member. /// </summary> /// <owner>RGoel</owner> internal void Evaluate ( Expander expander ) { ErrorUtilities.VerifyThrow(expander != null, "Expander required to evaluated property."); this.finalValueEscaped = expander.ExpandAllIntoStringLeaveEscaped(this.Value, this.propertyElement); }
/// <summary> /// Populate the lists of evaluated and unevaluated metadata with all metadata that have true conditions. /// </summary> /// <remarks> /// FUTURE: Currently this isn't done when the item is constructed; so for example HasMetadata will always return /// false until EvaluatedAllItemMetadata is explicitly called. The reason for this is that Metadata are /// not first class objects, they are merely string pairs produced by running over the child XML with a particular expander. /// When Metadata are first class objects this method can be changed to merely evaluate them, /// just as BuildItemGroup.Evaluate does for BuildItem, then methods like HasMetadata behave more sanely. Of course this /// could be a breaking change. /// </remarks> internal void EvaluateAllItemMetadata ( Expander expander, ParserOptions parserOptions, EngineLoggingServices loggingServices, BuildEventContext buildEventContext ) { ErrorUtilities.VerifyThrow(expander != null, "Null expander passed in."); // Cache all custom attributes on the item. For a persisted item, this will walk the item's child nodes, and // cache the custom attributes using a "last one wins" policy. For a virtual item, this method does nothing. // We only evaluate metadata by reading XML if (IsBackedByXml) { ErrorUtilities.VerifyThrow((this.evaluatedCustomMetadata != null) && (this.unevaluatedCustomMetadata != null), "Item is not initialized properly."); // We're evaluating from scratch, so clear out any old cached attributes. this.evaluatedCustomMetadata.Clear(); this.unevaluatedCustomMetadata.Clear(); // Let the expander know about our own item type, so it can // expand unqualified metadata expressions SpecificItemDefinitionLibrary specificItemDefinitionLibrary = new SpecificItemDefinitionLibrary(name, itemDefinitionLibrary); expander = new Expander(expander, specificItemDefinitionLibrary); List<XmlElement> metadataElements = xml.GetChildren(); // look at all the item's child nodes foreach (XmlElement metadataElement in metadataElements) { // confirm that the child node is not conditionally disabled bool condition = true; XmlAttribute conditionAttribute = ProjectXmlUtilities.GetConditionAttribute(metadataElement, true /*no other attributes allowed*/); if (conditionAttribute != null) { condition = Utilities.EvaluateCondition(conditionAttribute.Value, conditionAttribute, expander, null, parserOptions, loggingServices, buildEventContext); } if (condition) { // cache its value, both the evaluated and unevaluated. string unevaluatedMetadataValue = Utilities.GetXmlNodeInnerContents(metadataElement); unevaluatedCustomMetadata[metadataElement.Name] = unevaluatedMetadataValue; string evaluatedMetadataValue = expander.ExpandAllIntoStringLeaveEscaped(unevaluatedMetadataValue, metadataElement); evaluatedCustomMetadata[metadataElement.Name] = evaluatedMetadataValue; // Add this metadata to the running table we're using, so that one piece of metadata can refer to another one above expander.SetMetadataInMetadataTable(name, metadataElement.Name, evaluatedMetadataValue); } } } }
/// <summary> /// Determines if the new item spec that the user is trying to add to the project /// already matches an existing wildcarded item declared in the project. We only /// consider it a "match" in very specific circumstances... if there's anything /// weird or not-mainline about the new item spec or the existing item, we fail /// the match in order to "play it safe". /// </summary> internal bool NewItemSpecMatchesExistingWildcard(string newItemSpec) { Project parentProject = GetParentProject(); ErrorUtilities.VerifyThrow(parentProject != null, "This method should only get called on persisted items."); BuildPropertyGroup evaluatedProperties = parentProject.evaluatedProperties; if ( // The original item spec should have had at least one "*" or "?" in it. FileMatcher.HasWildcards(this.Include) && // The original item should not have a Condition. (this.Condition.Length == 0) && // The original item should not have an Exclude. (this.Exclude.Length == 0) && // The new item spec should NOT have any wildcards. !FileMatcher.HasWildcards(newItemSpec) ) { Expander propertyExpander = new Expander(evaluatedProperties); string newItemSpecExpandedEscaped = propertyExpander.ExpandAllIntoStringLeaveEscaped(newItemSpec, null); // New item spec should not have any unescaped semicolons ... this can really mess us up. if (-1 == newItemSpecExpandedEscaped.IndexOf(';')) { // Expand any properties in the new item spec that the user gave us. string newItemSpecExpandedUnescaped = EscapingUtilities.UnescapeAll(newItemSpecExpandedEscaped); // Loop over each piece separated by semicolons. List<string> itemIncludePieces = propertyExpander.ExpandAllIntoStringListLeaveEscaped(this.Include, this.IncludeAttribute); foreach (string itemIncludePiece in itemIncludePieces) { bool containsEscapedWildcards = EscapingUtilities.ContainsEscapedWildcards(itemIncludePiece); bool containsRealWildcards = FileMatcher.HasWildcards(itemIncludePiece); // If this is the piece that has the wildcards ... if (containsRealWildcards && !containsEscapedWildcards) { string itemIncludePieceContainingWildcardUnescaped = EscapingUtilities.UnescapeAll(itemIncludePiece); FileMatcher.Result match = FileMatcher.FileMatch(itemIncludePieceContainingWildcardUnescaped, newItemSpecExpandedUnescaped); if (match.isLegalFileSpec && match.isMatch) { // The wildcard in the original item spec will match the new item that // user is trying to add. return true; } } } } } return false; }
/// <summary> /// Sets the project's default targets from the given list of semi-colon-separated target names after expanding all /// embedded properties in the list. /// </summary> /// <owner>SumedhK</owner> /// <param name="defaultTargetsList"></param> /// <param name="propertyBag"></param> private void SetDefaultTargets(string defaultTargetsList, BuildPropertyGroup propertyBag) { Expander propertyExpander = new Expander(propertyBag); this.defaultTargetNames = propertyExpander.ExpandAllIntoStringListLeaveEscaped(defaultTargetsList, null).ToArray(); BuildProperty defaultTargetsProperty = new BuildProperty(ReservedPropertyNames.projectDefaultTargets, propertyExpander.ExpandAllIntoStringLeaveEscaped(defaultTargetsList, null), PropertyType.ReservedProperty); this.ReservedProperties.SetProperty(defaultTargetsProperty); // we also need to push this property directly into the evaluatedProperties bucket // since this property was computed "late", i.e. after the initial evaluation. this.evaluatedProperties.SetProperty(defaultTargetsProperty); }
/// <summary> /// Evaluates the item and returns a virtual group containing any resulting items. /// This allows an item to be evaluated without it belonging to an item group. /// </summary> internal BuildItemGroup Evaluate(Expander expander, string baseDirectory, bool expandMetadata, ParserOptions parserOptions, EngineLoggingServices loggingServices, BuildEventContext buildEventContext) { BuildItemGroup result = new BuildItemGroup(); bool itemCondition = Utilities.EvaluateCondition(Condition, ConditionAttribute, expander, parserOptions, loggingServices, buildEventContext); if (!itemCondition) { return result; } EvaluateAllItemMetadata(expander, parserOptions, loggingServices, buildEventContext); BuildItemGroup items = BuildItemGroup.ExpandItemIntoItems(baseDirectory, this, expander, expandMetadata); for (int i = 0; i < items.Count; i++) { BuildItem newItem = CreateClonedParentedItem(items[i], this); result.AddExistingItem(newItem); } return result; }
/// <summary> /// Builds this target if it has not already been built as part of its parent project. Before we actually execute the /// tasks for this target, though, we first call on all the dependent targets to build themselves. /// This function may throw InvalidProjectFileException /// </summary> internal void Build ( ProjectBuildState buildContext ) { // Depending on the build state, we may do different things. switch (buildState) { case BuildState.InProgress: // In single proc mode if the build state was already "in progress" // and somebody just told us to build ourselves, it means that there is // a loop (circular dependency) in the target dependency graph. In multi // proc mode we need to analyze the dependency graph before we can // tell if there a circular dependency or if two independent chains // of targets happen to need the result of this target. if (parentEngine.Router.SingleThreadedMode || buildContext.ContainsCycle(this.Name)) { ProjectErrorUtilities.VerifyThrowInvalidProject(false, TargetElement, "CircularDependency", targetName); } else { buildContext.CurrentBuildContextState = ProjectBuildState.BuildContextState.WaitingForTarget; this.executionState.AddWaitingBuildContext(buildContext); } break; case BuildState.CompletedSuccessfully: case BuildState.CompletedUnsuccessfully: // If this target has already been built as part of this project, // we're not going to build it again. Just return the result // from when it was built previously. Note: This condition // could really only ever hold true if the user specifically // requested us to build multiple targets and there existed // a direct or indirect dependency relationship between two or // more of those top-level targets. // Note: we aren't really entering the target in question here, so don't use the target // event context. Using the target ID for skipped messages would force us to // cache the individual target IDs for unloaded projects and it's not really worth the trouble. // Just use the parent event context. parentEngine.LoggingServices.LogComment(buildContext.ProjectBuildEventContext, ((buildState == BuildState.CompletedSuccessfully) ? "TargetAlreadyCompleteSuccess" : "TargetAlreadyCompleteFailure"), this.targetName); // Only contexts which are generated from an MSBuild task could need // the outputs of this target, such contexts have a non-null evaluation // request if ((buildState == BuildState.CompletedSuccessfully) && (buildContext.BuildRequest.OutputsByTarget != null && buildContext.NameOfBlockingTarget == null)) { error.VerifyThrow( String.Compare(EscapingUtilities.UnescapeAll(buildContext.NameOfTargetInProgress), this.Name, StringComparison.OrdinalIgnoreCase) == 0, "The name of the target in progress is inconsistent with the target being built"); error.VerifyThrow(targetOutputItems != null, "If the target built successfully, we must have its outputs."); buildContext.BuildRequest.OutputsByTarget[Name] = targetOutputItems.ToArray(); } if (buildContext.NameOfBlockingTarget == null) { buildContext.BuildRequest.ResultByTarget[Name] = buildState; } break; case BuildState.NotStarted: case BuildState.Skipped: { // Always have to create a new context in build as other projects or targets may try and build this target BuildEventContext targetBuildEventContext = new BuildEventContext ( buildContext.ProjectBuildEventContext.NodeId, this.id, buildContext.ProjectBuildEventContext.ProjectContextId, buildContext.ProjectBuildEventContext.TaskId ); Expander expander = new Expander(this.parentProject.evaluatedProperties, this.parentProject.evaluatedItemsByName); // We first make sure no batching was attempted with the target's condition. if (!conditionCheckedForInvalidMetadataReferences) { if (ExpressionShredder.ContainsMetadataExpressionOutsideTransform(this.Condition)) { ProjectErrorUtilities.ThrowInvalidProject(this.conditionAttribute, "TargetConditionHasInvalidMetadataReference", targetName, this.Condition); } conditionCheckedForInvalidMetadataReferences = true; } // If condition is false (based on propertyBag), set this target's state to // "Skipped" since we won't actually build it. if (!Utilities.EvaluateCondition(this.Condition, this.conditionAttribute, expander, null, ParserOptions.AllowProperties | ParserOptions.AllowItemLists, parentEngine.LoggingServices, targetBuildEventContext)) { buildState = BuildState.Skipped; if (buildContext.NameOfBlockingTarget == null) { buildContext.BuildRequest.ResultByTarget[Name] = buildState; } if (!parentEngine.LoggingServices.OnlyLogCriticalEvents) { // Expand the expression for the Log. string expanded = expander.ExpandAllIntoString(this.Condition, this.conditionAttribute); // By design: Not building dependencies. This is what NAnt does too. parentEngine.LoggingServices.LogComment(targetBuildEventContext, "TargetSkippedFalseCondition", this.targetName, this.Condition, expanded); } } else { // This target has not been built yet. So build it! // Change our state to "in progress". TargetParameters will need to be re-calculated if Inputs and Outputs attribute has changed. buildState = BuildState.InProgress; List<string> batchableTargetParameters = GetBatchableTargetParameters(); executionState = new TargetExecutionWrapper(this, taskElementList, batchableTargetParameters, targetElement, expander, targetBuildEventContext); ContinueBuild(buildContext, null); } } break; default: error.VerifyThrow(false, "Build state {0} not handled in Target.Build method", buildState); break; } }
public void PropertyFunctionConstructor1() { BuildPropertyGroup pg = new BuildPropertyGroup(); pg.SetProperty("ver1", @"1.2.3.4"); Expander expander = new Expander(pg); string result = expander.ExpandAllIntoStringLeaveEscaped(@"$([System.Version]::new($(ver1)))", null); Version v = new Version(result); Assertion.AssertEquals(1, v.Major); Assertion.AssertEquals(2, v.Minor); Assertion.AssertEquals(3, v.Build); Assertion.AssertEquals(4, v.Revision); }
/// <summary> /// Creates a list of targets to execute for the OnErrorClause /// </summary> private void InitializeOnErrorClauseExecution() { // Give default values; currentErrorTarget = 0; onErrorTargets = null; // Loop through each of the child nodes of the <target> element. List<XmlElement> childElements = ProjectXmlUtilities.GetValidChildElements(targetElement); foreach (XmlElement childElement in childElements) { switch (childElement.Name) { case XMakeElements.onError: ProjectXmlUtilities.VerifyThrowProjectNoChildElements(childElement); XmlAttribute condition = null; XmlAttribute executeTargets = null; foreach (XmlAttribute onErrorAttribute in childElement.Attributes) { switch (onErrorAttribute.Name) { case XMakeAttributes.condition: condition = childElement.Attributes[XMakeAttributes.condition]; break; case XMakeAttributes.executeTargets: executeTargets = childElement.Attributes[XMakeAttributes.executeTargets]; break; default: ProjectXmlUtilities.ThrowProjectInvalidAttribute(onErrorAttribute); break; } } ProjectErrorUtilities.VerifyThrowInvalidProject(executeTargets != null, childElement, "MissingRequiredAttribute", XMakeAttributes.executeTargets, XMakeElements.onError); Expander expander = new Expander(this.parentProject.evaluatedProperties, this.parentProject.evaluatedItemsByName); bool runErrorTargets = true; if (condition != null) { if ( !Utilities.EvaluateCondition ( condition.InnerText, condition, expander, null, ParserOptions.AllowProperties | ParserOptions.AllowItemLists, parentEngine.LoggingServices, targetBuildEventContext ) ) { runErrorTargets = false; } } if (runErrorTargets) { if (onErrorTargets == null) { onErrorTargets = expander.ExpandAllIntoStringList(executeTargets.InnerText, executeTargets); } else { onErrorTargets.AddRange(expander.ExpandAllIntoStringList(executeTargets.InnerText, executeTargets)); } } break; default: // Ignore break; } } }
public void MetadataIncludesItemDefinitionMetadata() { // Get an item of type "i" that has an item definition library // for type "i" that has default value "m1" for metadata "m" // and has value "n1" for metadata "n" BuildItem item = GetXmlBackedItemWithDefinitionLibrary(); // Evaluated Expander expander = new Expander(new BuildPropertyGroup(), null, ExpanderOptions.ExpandAll); item.EvaluateAllItemMetadata(expander, ParserOptions.AllowPropertiesAndItemLists, null, null); Assertion.AssertEquals("m1", item.GetEvaluatedMetadata("m")); Assertion.AssertEquals("n1", item.GetEvaluatedMetadata("n")); // Unevaluated Assertion.AssertEquals("m1", item.GetMetadata("m")); Assertion.AssertEquals("n1", item.GetMetadata("n")); // All custom metadata List<string> metadataNames = new List<string>((IList<string>)item.CustomMetadataNames); Assertion.AssertEquals("n", (string)metadataNames[0]); Assertion.AssertEquals("m", (string)metadataNames[1]); // Custom metadata count only Assertion.AssertEquals(3, item.CustomMetadataCount); // All metadata count Assertion.AssertEquals(item.CustomMetadataCount + FileUtilities.ItemSpecModifiers.All.Length, item.MetadataCount); }
internal Expander(Expander expander, SpecificItemDefinitionLibrary itemDefinitionLibrary) : this(expander.lookup, null , expander.options) { if (implicitMetadataItemType == null) { this.itemMetadata = expander.itemMetadata; } this.specificItemDefinitionLibrary = itemDefinitionLibrary; }
public UsingTaskInfo CopyAndExpand(Expander expander) { string newTaskName = expander.ExpandAllIntoString(UsingTask.TaskName, UsingTask.TaskNameAttribute); string newCondition = null; string newAssemblyName = null; string newAssemblyFile = null; if (UsingTask.Condition != null) { newCondition = expander.ExpandAllIntoString(UsingTask.Condition, UsingTask.ConditionAttribute); } if (UsingTask.AssemblyName != null) { newAssemblyName = expander.ExpandAllIntoString(UsingTask.AssemblyName, UsingTask.AssemblyNameAttribute); } if (UsingTask.AssemblyFile != null) { newAssemblyFile = expander.ExpandAllIntoString(UsingTask.AssemblyFile, UsingTask.AssemblyFileAttribute); } return new UsingTaskInfo(newTaskName, newAssemblyName, newAssemblyFile, newCondition); }
/// <summary> /// Create an expander from another expander, but with different /// options /// </summary> internal Expander(Expander expander, ExpanderOptions options) : this(expander.lookup, expander.itemMetadata, options) { }
/// <summary> /// Execute the function on the given instance /// </summary> public object Execute(Expander expander, object objectInstance, BuildPropertyGroup properties, ExpanderOptions options) { object functionResult = String.Empty; object[] args = null; try { // If there is no object instance, then the method invocation will be a static if (objectInstance == null) { // Check that the function that we're going to call is valid to call if (!IsStaticMethodAvailable(ObjectType, name)) { ProjectErrorUtilities.ThrowInvalidProject(null, "InvalidFunctionMethodUnavailable", name, ObjectType.FullName); } bindingFlags |= BindingFlags.Static; // For our intrinsic function we need to support calling of internal methods // since we don't want them to be public if (objectType == typeof(Microsoft.Build.BuildEngine.IntrinsicFunctions)) { bindingFlags |= BindingFlags.NonPublic; } } else { bindingFlags |= BindingFlags.Instance; } // We have a methodinfo match, need to plug in the arguments args = new object[arguments.Length]; // Assemble our arguments ready for passing to our method for (int n = 0; n < arguments.Length; n++) { object argument = expander.ExpandPropertiesLeaveTypedAndEscaped(this.arguments[n], null); string argumentValue = argument as string; if (argumentValue != null) { // remove our 'quotes' from the escaped string, leaving escaped quotes intact args[n] = EscapingUtilities.UnescapeAll(argumentValue.Trim('`', '"', '\'')); } else { args[n] = argument; } } // Handle special cases where the object type needs to affect the choice of method // The default binder and method invoke, often chooses the incorrect Equals and CompareTo and // fails the comparison, because what we have on the right is generally a string. // This special casing is to realize that its a comparison that is taking place and handle the // argument type coercion accordingly; effectively pre-preparing the argument type so // that it matches the left hand side ready for the default binder�s method invoke. if (objectInstance != null && args.Length == 1 && (String.Equals("Equals", this.name, StringComparison.OrdinalIgnoreCase) || String.Equals("CompareTo", this.name, StringComparison.OrdinalIgnoreCase))) { // change the type of the final unescaped string into the destination args[0] = Convert.ChangeType(args[0], objectInstance.GetType(), CultureInfo.InvariantCulture); } // If we've been asked for and instance to be constructed, then we // need to locate an appropriate constructor and invoke it if (String.Equals("new", this.name, StringComparison.OrdinalIgnoreCase)) { functionResult = LateBindExecute(null /* no previous exception */, BindingFlags.Public | BindingFlags.Instance, null /* no instance for a constructor */, args, true /* is constructor */); } else { // Execute the function given converted arguments // The only exception that we should catch to try a late bind here is missing method // otherwise there is the potential of running a function twice! try { // First use InvokeMember using the standard binder - this will match and coerce as needed functionResult = objectType.InvokeMember(this.name, bindingFlags, Type.DefaultBinder, objectInstance, args, CultureInfo.InvariantCulture); } catch (MissingMethodException ex) // Don't catch and retry on any other exception { // If we're invoking a method, then there are deeper attempts that // can be made to invoke the method if ((bindingFlags & BindingFlags.InvokeMethod) == BindingFlags.InvokeMethod) { // The standard binder failed, so do our best to coerce types into the arguments for the function // This may happen if the types need coercion, but it may also happen if the object represents a type that contains open type parameters, that is, ContainsGenericParameters returns true. functionResult = LateBindExecute(ex, bindingFlags, objectInstance, args, false /* is not constructor */); } else { // We were asked to get a property or field, and we found that we cannot // locate it. Since there is no further argument coersion possible // we'll throw right now. throw; } } } // If the result of the function call is a string, then we need to escape the result // so that we maintain the "engine contains escaped data" state. // The exception is that the user is explicitly calling MSBuild::Unescape or MSBuild::Escape if (functionResult is string && !String.Equals("Unescape", name, StringComparison.OrdinalIgnoreCase) && !String.Equals("Escape", name, StringComparison.OrdinalIgnoreCase)) { functionResult = EscapingUtilities.Escape((string)functionResult); } // There's nothing left to deal within the function expression, return the result from the execution if (String.IsNullOrEmpty(remainder)) { return functionResult; } // Recursively expand the remaining property body after execution return expander.ExpandPropertyBody(remainder, functionResult, properties, options); } // Exceptions coming from the actual function called are wrapped in a TargetInvocationException catch (TargetInvocationException ex) { // We ended up with something other than a function expression string partiallyEvaluated = GenerateStringOfMethodExecuted(expression, objectInstance, name, args); ProjectErrorUtilities.ThrowInvalidProject(null, "InvalidFunctionPropertyExpression", partiallyEvaluated, ex.InnerException.Message.Replace("\r\n", " ")); return null; } // Any other exception was thrown by trying to call it catch (Exception ex) { if (ExceptionHandling.NotExpectedFunctionException(ex)) { throw; } // We ended up with something other than a function expression string partiallyEvaluated = GenerateStringOfMethodExecuted(expression, objectInstance, name, args); ProjectErrorUtilities.ThrowInvalidProject(null, "InvalidFunctionPropertyExpression", partiallyEvaluated, ex.Message); return null; } }
/// <summary> /// This processes the <UsingTask> elements in the project file as well /// as the imported project files, by adding the necessary data to the /// task registry. /// </summary> /// <owner>RGoel</owner> private void EvaluateAllUsingTasks() { Expander expander = new Expander(evaluatedProperties, evaluatedItemsByName); foreach (UsingTask usingTask in this.usingTasks) { taskRegistry.RegisterTask(usingTask, expander, ParentEngine.LoggingServices, projectBuildEventContext); } }
/// <summary> /// Creates an instance of this class using the given bucket data. /// </summary> /// <param name="itemNames">Item types being batched on: null indicates no batching is occurring</param> /// <param name="itemMetadata">Hashtable of item metadata values: null indicates no batching is occurring</param> internal ItemBucket ( ICollection itemNames, Dictionary<string, string> itemMetadata, Lookup lookup, int bucketSequenceNumber ) { ErrorUtilities.VerifyThrow(lookup != null, "Need lookup."); // Create our own lookup just for this bucket this.lookup = lookup.Clone(); // Push down the items, so that item changes in this batch are not visible to parallel batches this.lookup.EnterScope(); // Add empty item groups for each of the item names, so that (unless items are added to this bucket) there are // no item types visible in this bucket among the item types being batched on if (itemNames != null) { foreach (string name in itemNames) { this.lookup.PopulateWithItems(name, new BuildItemGroup()); } } this.expander = new Expander(this.lookup.ReadOnlyLookup, itemMetadata); this.bucketSequenceNumber = bucketSequenceNumber; }
/// <summary> /// Given the properties and dictionary of previously encountered item definitions, evaluates /// this group of item definitions and adds to the dictionary as necessary. /// </summary> /// <exception cref="InvalidProjectFileException">If the item definitions are incorrectly defined</exception> internal void Evaluate(BuildPropertyGroup properties, ItemDefinitionsDictionary itemDefinitionsDictionary) { Expander expander = new Expander(properties); if (!Utilities.EvaluateCondition(condition, conditionAttribute, expander, ParserOptions.AllowProperties, parentProject)) { return; } List<XmlElement> childElements = ProjectXmlUtilities.GetValidChildElements(element); foreach (XmlElement child in childElements) { EvaluateItemDefinitionElement(child, properties, itemDefinitionsDictionary); } }
public void PropertyFunctionStaticMethodMakeRelative() { BuildPropertyGroup pg = new BuildPropertyGroup(); pg.SetProperty("ParentPath", @"c:\abc\def"); pg.SetProperty("FilePath", @"c:\abc\def\foo.cpp"); Expander expander = new Expander(pg); string result = expander.ExpandAllIntoStringLeaveEscaped(@"$([MSBuild]::MakeRelative($(ParentPath), `$(FilePath)`))", null); Assertion.AssertEquals(@"foo.cpp", result); }
/// <summary> /// Given the properties and dictionary of previously encountered item definitions, evaluates /// this specific item definition element and adds to the dictionary as necessary. /// </summary> /// <exception cref="InvalidProjectFileException">If the item definition is incorrectly defined</exception> private void EvaluateItemDefinitionElement(XmlElement itemDefinitionElement, BuildPropertyGroup properties, ItemDefinitionsDictionary itemDefinitionsDictionary) { ProjectXmlUtilities.VerifyThrowProjectValidNameAndNamespace(itemDefinitionElement); XmlAttribute conditionAttribute = ProjectXmlUtilities.GetConditionAttribute(itemDefinitionElement, /* sole attribute */ true); string condition = ProjectXmlUtilities.GetAttributeValue(conditionAttribute); MetadataDictionary metadataDictionary = null; string itemType = itemDefinitionElement.Name; itemDefinitionsDictionary.TryGetValue(itemType, out metadataDictionary); Expander expander = new Expander(properties, itemType, metadataDictionary); if (!Utilities.EvaluateCondition(condition, conditionAttribute, expander, ParserOptions.AllowPropertiesAndItemMetadata, parentProject)) { return; } List<XmlElement> childElements = ProjectXmlUtilities.GetValidChildElements(itemDefinitionElement); foreach (XmlElement child in childElements) { EvaluateItemDefinitionChildElement(child, properties, itemDefinitionsDictionary); } }
public void PropertyFunctionConstructor2() { BuildPropertyGroup pg = new BuildPropertyGroup(); pg.SetProperty("ver1", @"1.2.3.4"); pg.SetProperty("ver2", @"2.2.3.4"); Expander expander = new Expander(pg); string result = expander.ExpandAllIntoStringLeaveEscaped(@"$([System.Version]::new($(ver1)).CompareTo($([System.Version]::new($(ver2)))))", null); Assertion.AssertEquals(@"-1", result); }
/// <summary> /// Given the properties and dictionary of previously encountered item definitions, evaluates /// this specific item definition child element and adds to the dictionary as necessary. /// </summary> /// <exception cref="InvalidProjectFileException">If the item definition is incorrectly defined</exception> private void EvaluateItemDefinitionChildElement(XmlElement itemDefinitionChildElement, BuildPropertyGroup properties, ItemDefinitionsDictionary itemDefinitionsDictionary) { ProjectXmlUtilities.VerifyThrowProjectValidNameAndNamespace(itemDefinitionChildElement); ProjectErrorUtilities.VerifyThrowInvalidProject(!FileUtilities.IsItemSpecModifier(itemDefinitionChildElement.Name), itemDefinitionChildElement, "ItemSpecModifierCannotBeCustomMetadata", itemDefinitionChildElement.Name); ProjectErrorUtilities.VerifyThrowInvalidProject(XMakeElements.IllegalItemPropertyNames[itemDefinitionChildElement.Name] == null, itemDefinitionChildElement, "CannotModifyReservedItemMetadata", itemDefinitionChildElement.Name); XmlAttribute conditionAttribute = ProjectXmlUtilities.GetConditionAttribute(itemDefinitionChildElement, /* sole attribute */ true); string condition = ProjectXmlUtilities.GetAttributeValue(conditionAttribute); MetadataDictionary metadataDictionary = null; string itemType = itemDefinitionChildElement.ParentNode.Name; itemDefinitionsDictionary.TryGetValue(itemType, out metadataDictionary); Expander expander = new Expander(properties, itemType, metadataDictionary); if (!Utilities.EvaluateCondition(condition, conditionAttribute, expander, ParserOptions.AllowPropertiesAndItemMetadata, parentProject)) { return; } string unevaluatedMetadataValue = Utilities.GetXmlNodeInnerContents(itemDefinitionChildElement); bool containsItemVector = ItemExpander.ExpressionContainsItemVector(unevaluatedMetadataValue); // We don't allow expressions like @(foo) in the value, as no items exist at this point. ProjectErrorUtilities.VerifyThrowInvalidProject(!containsItemVector, itemDefinitionChildElement, "MetadataDefinitionCannotContainItemVectorExpression", unevaluatedMetadataValue, itemDefinitionChildElement.Name); string evaluatedMetadataValue = expander.ExpandAllIntoStringLeaveEscaped(unevaluatedMetadataValue, itemDefinitionChildElement); if (metadataDictionary == null) { metadataDictionary = new MetadataDictionary(StringComparer.OrdinalIgnoreCase); itemDefinitionsDictionary.Add(itemType, metadataDictionary); } // We only store the evaluated value; build items store the unevaluated value as well, but apparently only to // gather recursive portions (its re-evaluation always goes back to the XML). // Overwrite any existing default value for this particular metadata metadataDictionary[itemDefinitionChildElement.Name] = evaluatedMetadataValue; }
/// <summary> /// Parses the target's "Inputs" and "Outputs" attributes and gathers up referenced items. /// </summary> /// <param name="bucket"></param> /// <param name="itemVectorsInTargetInputs"></param> /// <param name="itemVectorTransformsInTargetInputs"></param> /// <param name="discreteItemsInTargetInputs"></param> /// <param name="itemVectorsInTargetOutputs"></param> /// <param name="discreteItemsInTargetOutputs"></param> /// <param name="targetOutputItemSpecs"></param> /// <owner>SumedhK</owner> private void ParseTargetInputOutputSpecifications ( ItemBucket bucket, out Hashtable itemVectorsInTargetInputs, out Hashtable itemVectorTransformsInTargetInputs, out Hashtable discreteItemsInTargetInputs, out Hashtable itemVectorsInTargetOutputs, out Hashtable discreteItemsInTargetOutputs, out ArrayList targetOutputItemSpecs ) { // break down the input/output specifications along the standard separator, after expanding all embedded properties // and item metadata Expander propertyAndMetadataExpander = new Expander(bucket.Expander, ExpanderOptions.ExpandPropertiesAndMetadata); List<string> targetInputs = propertyAndMetadataExpander.ExpandAllIntoStringListLeaveEscaped(TargetInputSpecification, this.targetInputsAttribute); List<string> targetOutputs = propertyAndMetadataExpander.ExpandAllIntoStringListLeaveEscaped(TargetOutputSpecification, this.targetOutputsAttribute); itemVectorTransformsInTargetInputs = new Hashtable(StringComparer.OrdinalIgnoreCase); // figure out which of the inputs are: // 1) item vectors // 2) item vectors with transforms // 3) "discrete" items i.e. items that do not reference item vectors SeparateItemVectorsFromDiscreteItems(this.targetInputsAttribute, targetInputs, bucket, out itemVectorsInTargetInputs, itemVectorTransformsInTargetInputs, out discreteItemsInTargetInputs); // figure out which of the outputs are: // 1) item vectors (with or without transforms) // 2) "discrete" items i.e. items that do not reference item vectors SeparateItemVectorsFromDiscreteItems(this.targetOutputsAttribute, targetOutputs, bucket, out itemVectorsInTargetOutputs, null /* don't want transforms separated */, out discreteItemsInTargetOutputs); // list out all the output item-specs targetOutputItemSpecs = GetItemSpecsFromItemVectors(itemVectorsInTargetOutputs); targetOutputItemSpecs.AddRange(discreteItemsInTargetOutputs.Values); }
/// <summary> /// Evaluates condition on property group, and if true, evaluates /// on each contained property. If that's true as well, adds property /// to evaluatedPropertyBag. /// </summary> /// <owner>DavidLe</owner> /// <param name="evaluatedPropertyBag"></param> /// <param name="conditionedPropertiesTable"></param> /// <param name="pass"></param> internal void Evaluate ( BuildPropertyGroup evaluatedPropertyBag, Hashtable conditionedPropertiesTable, ProcessingPass pass ) { ErrorUtilities.VerifyThrow(pass == ProcessingPass.Pass1, "Pass should be Pass1 for PropertyGroups."); Expander expander = new Expander(evaluatedPropertyBag); if (!Utilities.EvaluateCondition(this.Condition, this.ConditionAttribute, expander, conditionedPropertiesTable, ParserOptions.AllowProperties, ParentProject.ParentEngine.LoggingServices, ParentProject.ProjectBuildEventContext)) { return; } // Add all the properties to our project-level property bag. foreach (BuildProperty currentProperty in this.propertyList) { if (!Utilities.EvaluateCondition(currentProperty.Condition, currentProperty.ConditionAttribute, expander, conditionedPropertiesTable, ParserOptions.AllowProperties, ParentProject.ParentEngine.LoggingServices, parentProject.ProjectBuildEventContext)) { continue; } BuildProperty newProperty = currentProperty.Clone(false); newProperty.Evaluate(expander); evaluatedPropertyBag.SetProperty(newProperty); } }