Example #1
0
        /// <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;
            }
        }
Example #2
0
        /// <summary>
        /// Determines how many times the batchable object needs to be executed (each execution is termed a "batch"), and prepares
        /// buckets of items to pass to the object in each batch.
        /// </summary>
        /// <param name="parentNode"></param>
        /// <param name="batchableObjectParameters"></param>
        /// <param name="lookup"></param>
        /// <param name="implicitBatchableItemType">Any item type that can be considered an implicit input to this batchable object.
        /// This is useful for items inside targets, where the item name is plainly an item type that's an "input" to the object.</param>
        /// <returns>ArrayList containing ItemBucket objects, each one representing an execution batch.</returns>
        internal static ArrayList PrepareBatchingBuckets
        (
            XmlNode parentNode,
            List <string> batchableObjectParameters,
            Lookup lookup,
            string implicitBatchableItemType
        )
        {
            ErrorUtilities.VerifyThrow(parentNode != null, "Need the XML node that represents the batchable object.");
            ErrorUtilities.VerifyThrow(batchableObjectParameters != null, "Need the parameters of the batchable object to determine if it can be batched.");
            ErrorUtilities.VerifyThrow(lookup != null, "Need to specify the lookup.");

            ItemsAndMetadataPair pair = ExpressionShredder.GetReferencedItemNamesAndMetadata(batchableObjectParameters);

            // All the @(itemname) item list references in the tag, including transforms, etc.
            // The keys in the hashtable are the item names, and the values are all String.Empty (not used).
            Hashtable consumedItemReferences = pair.Items;

            // All the %(itemname.metadataname) references in the tag (not counting those embedded
            // inside item transforms), and note that the itemname portion is optional.
            // The keys in the returned hash table are the qualified metadata names (e.g. "EmbeddedResource.Culture"
            // or just "Culture").  The values are MetadataReference structs, which simply split out the item
            // name (possibly null) and the actual metadata name.
            Dictionary <string, MetadataReference> consumedMetadataReferences = pair.Metadata;

            ArrayList buckets = null;

            if (consumedMetadataReferences?.Count > 0)
            {
                // Add any item types that we were explicitly told to assume.
                if (implicitBatchableItemType != null)
                {
                    consumedItemReferences = Utilities.CreateTableIfNecessary(consumedItemReferences);
                    consumedItemReferences[implicitBatchableItemType] = String.Empty;
                }

                // This method goes through all the item list references and figures out which ones
                // will be participating in batching, and which ones won't.  We get back a hashtable
                // where the key is the item name that will be participating in batching.  The values
                // are all String.Empty (not used).  This method may return additional item names
                // that weren't represented in "consumedItemReferences"... this would happen if there
                // were qualified metadata references in the consumedMetadataReferences table, such as
                // %(EmbeddedResource.Culture).
                Hashtable itemListsToBeBatched = GetItemListsToBeBatched(parentNode, consumedMetadataReferences, consumedItemReferences, lookup);

                // At this point, if there were any metadata references in the tag, but no item
                // references to batch on, we've got a problem because we can't figure out which
                // item lists the user wants us to batch.
                if (itemListsToBeBatched.Count == 0)
                {
                    foreach (string unqualifiedMetadataName in consumedMetadataReferences.Keys)
                    {
                        // Of course, since this throws an exception, there's no way we're ever going
                        // to really loop here... it's just that the foreach is the only way I can
                        // figure out how to get data out of the hashtable without knowing any of the
                        // keys!
                        ProjectErrorUtilities.VerifyThrowInvalidProject(false,
                                                                        parentNode, "CannotReferenceItemMetadataWithoutItemName", unqualifiedMetadataName);
                    }
                }
                else
                {
                    // If the batchable object consumes item metadata as well as items to be batched,
                    // we need to partition the items consumed by the object.
                    buckets = BucketConsumedItems(parentNode, lookup, itemListsToBeBatched, consumedMetadataReferences);
                }
            }

            // if the batchable object does not consume any item metadata or items, or if the item lists it consumes are all
            // empty, then the object does not need to be batched
            if ((buckets == null) || (buckets.Count == 0))
            {
                // create a default bucket that references the project items and properties -- this way we always have a bucket
                buckets = new ArrayList(1);
                buckets.Add(new ItemBucket(null, null, lookup, buckets.Count));
            }

            return(buckets);
        }