Example #1
0
        /// <summary>
        /// Indicates to the EngineProxy that it is no longer needed.
        /// Called by TaskEngine when the task using the EngineProxy is done.
        /// </summary>
        internal void MarkAsInActive()
        {
            activeProxy = false;

            // Since the task has a pointer to this class it may store it in a static field. Null out
            // internal data so the leak of this object doesn't lead to a major memory leak.
            loggingServices   = null;
            parentModule      = null;
            buildEventContext = null;

            // Clear out the sponsor (who is responsible for keeping the EngineProxy remoting lease alive until the task is done)
            // this will be null if the engineproxy was never sent accross an appdomain boundry.
            if (sponsor != null)
            {
                ILease lease = (ILease)RemotingServices.GetLifetimeService(this);

                if (lease != null)
                {
                    lease.Unregister(sponsor);
                }

                sponsor.Close();
                sponsor = null;
            }
        }
Example #2
0
        /// <summary>
        /// Evaluates a string representing a condition from a "condition" attribute.
        /// If the condition is a malformed string, it throws an InvalidProjectFileException.
        /// This method uses cached expression trees to avoid generating them from scratch every time it's called.
        /// This method is thread safe and is called from engine and task execution module threads
        /// </summary>
        /// <param name="condition">Can be null</param>
        /// <param name="conditionAttribute">XML attribute on which the condition is evaluated</param>
        /// <param name="expander">All the data available for expanding embedded properties, metadata, and items</param>
        /// <param name="conditionedPropertiesTable">Can be null</param>
        /// <param name="itemListOptions"></param>
        /// <param name="loggingServices">Can be null</param>
        /// <param name="buildEventContext"> contains contextual information for logging events</param>
        /// <returns>true, if the expression evaluates to true, otherwise false</returns>
        internal static bool EvaluateCondition
        (
            string condition,
            XmlAttribute conditionAttribute,
            Expander expander,
            Hashtable conditionedPropertiesTable,
            ParserOptions itemListOptions,
            EngineLoggingServices loggingServices,
            BuildEventContext buildEventContext
        )
        {
            ErrorUtilities.VerifyThrow((conditionAttribute != null) || (string.IsNullOrEmpty(condition)),
                                       "If condition is non-empty, you must provide the XML node representing the condition.");

            // An empty condition is equivalent to a "true" condition.
            if (string.IsNullOrEmpty(condition))
            {
                return(true);
            }

            Hashtable cachedExpressionTreesForCurrentOptions = cachedExpressionTrees[(int)itemListOptions];

            // Try and see if we have an expression tree for this condition already
            GenericExpressionNode parsedExpression = (GenericExpressionNode)cachedExpressionTreesForCurrentOptions[condition];

            if (parsedExpression == null)
            {
                Parser conditionParser = new Parser();

                #region REMOVE_COMPAT_WARNING
                conditionParser.LoggingServices      = loggingServices;
                conditionParser.LogBuildEventContext = buildEventContext;
                #endregion

                parsedExpression = conditionParser.Parse(condition, conditionAttribute, itemListOptions);

                // It's possible two threads will add a different tree to the same entry in the hashtable,
                // but it should be rare and it's not a problem - the previous entry will be thrown away.
                // We could ensure no dupes with double check locking but it's not really necessary here.
                // Also, we don't want to lock on every read.
                lock (cachedExpressionTreesForCurrentOptions)
                {
                    cachedExpressionTreesForCurrentOptions[condition] = parsedExpression;
                }
            }

            ConditionEvaluationState state = new ConditionEvaluationState(conditionAttribute, expander, conditionedPropertiesTable, condition);
            bool result;

            // We are evaluating this expression now and it can cache some state for the duration,
            // so we don't want multiple threads working on the same expression
            lock (parsedExpression)
            {
                result = parsedExpression.Evaluate(state);
                parsedExpression.ResetState();
            }

            return(result);
        }
Example #3
0
        /// <summary>
        /// Given a task name and a list of assemblies, this helper method checks if the task exists in any of the assemblies.
        /// </summary>
        /// <remarks>
        /// If the task name is fully qualified, then a match (if any) is unambiguous; otherwise, if there are multiple tasks with
        /// the same name in different namespaces/assemblies, the first task found will be returned.
        /// </remarks>
        /// <param name="taskName"></param>
        /// <param name="taskAssemblies"></param>
        /// <param name="taskProjectFile"></param>
        /// <param name="taskNode"></param>
        /// <param name="loggingServices"></param>
        /// <param name="buildEventContext"></param>
        /// <param name="taskClass"></param>
        /// <returns>true, if task is successfully loaded</returns>
        private bool GetTaskFromAssembly
        (
            string taskName,
            ArrayList taskAssemblies,
            string taskProjectFile,
            XmlNode taskNode,
            EngineLoggingServices loggingServices,
            BuildEventContext buildEventContext,
            out LoadedType taskClass
        )
        {
            taskClass = null;

            foreach (AssemblyLoadInfo assembly in taskAssemblies)
            {
                try
                {
                    taskClass = typeLoader.Load(taskName, assembly);
                }
                catch (TargetInvocationException e)
                {
                    // Exception thrown by the called code itself
                    // Log the stack, so the task vendor can fix their code
                    ProjectErrorUtilities.VerifyThrowInvalidProject(false, taskNode, "TaskLoadFailure", taskName, assembly.ToString(), Environment.NewLine + e.InnerException.ToString());
                }
                catch (ReflectionTypeLoadException e)
                {
                    // ReflectionTypeLoadException.LoaderExceptions may contain nulls
                    foreach (Exception exception in e.LoaderExceptions)
                    {
                        if (exception != null)
                        {
                            loggingServices.LogError(buildEventContext, new BuildEventFileInfo(taskProjectFile), "TaskLoadFailure", taskName, assembly.ToString(), exception.Message);
                        }
                    }

                    ProjectErrorUtilities.VerifyThrowInvalidProject(false, taskNode, "TaskLoadFailure", taskName, assembly.ToString(), e.Message);
                }
                catch (Exception e) // Catching Exception, but rethrowing unless it's a well-known exception.
                {
                    if (ExceptionHandling.NotExpectedReflectionException(e))
                    {
                        throw;
                    }

                    ProjectErrorUtilities.VerifyThrowInvalidProject(false, taskNode, "TaskLoadFailure", taskName, assembly.ToString(), e.Message);
                }

                if (taskClass != null)
                {
                    return(true);
                }
            }

            return(false);
        }
Example #4
0
        internal TargetCycleDetector(EngineLoggingServices engineLoggingService, EngineCallback engineCallback)
        {
            this.engineLoggingService = engineLoggingService;
            this.engineCallback       = engineCallback;

            dependencyGraph             = new Hashtable();
            outstandingExternalRequests = new Hashtable();
            cycleParent = null;
            cycleChild  = null;
        }
Example #5
0
 /// <summary>
 /// Evaluates a string representing a condition from a "condition" attribute.
 /// If the condition is a malformed string, it throws an InvalidProjectFileException.
 /// This method uses cached expression trees to avoid generating them from scratch every time it's called.
 /// This method is thread safe and is called from engine and task execution module threads
 /// </summary>
 /// <param name="condition">Can be null</param>
 /// <param name="conditionAttribute">XML attribute on which the condition is evaluated</param>
 /// <param name="expander">All the data available for expanding embedded properties, metadata, and items</param>
 /// <param name="itemListOptions"></param>
 /// <param name="loggingServices">Can be null</param>
 /// <param name="eventContext"> contains contextual information for logging events</param>
 /// <returns>true, if the expression evaluates to true, otherwise false</returns>
 internal static bool EvaluateCondition
 (
     string condition,
     XmlAttribute conditionAttribute,
     Expander expander,
     ParserOptions itemListOptions,
     EngineLoggingServices loggingServices,
     BuildEventContext buildEventContext
 )
 {
     return(EvaluateCondition(condition,
                              conditionAttribute,
                              expander,
                              null,
                              itemListOptions,
                              loggingServices,
                              buildEventContext));
 }
Example #6
0
        /// <summary>
        /// Creates an IntrinsicTask object around a "task" node
        /// </summary>
        internal IntrinsicTask(XmlElement taskNodeXmlElement, EngineLoggingServices loggingServices, BuildEventContext eventContext, string executionDirectory, ItemDefinitionLibrary itemDefinitionLibrary)
        {
            this.taskNodeXmlElement    = taskNodeXmlElement;
            this.conditionAttribute    = taskNodeXmlElement.Attributes[XMakeAttributes.condition];
            this.loggingServices       = loggingServices;
            this.buildEventContext     = eventContext;
            this.executionDirectory    = executionDirectory;
            this.itemDefinitionLibrary = itemDefinitionLibrary;

            ErrorUtilities.VerifyThrow(IsIntrinsicTaskName(taskNodeXmlElement.Name), "Only PropertyGroup and ItemGroup are known intrinsic tasks");

            switch (taskNodeXmlElement.Name)
            {
            case XMakeElements.propertyGroup:
                backingType = BackingType.PropertyGroup;
                // If the backing type is a property group, we can just use a property group object; its semantics aren't
                // tangled up with the project object. Put another way, we only really need the code that understands the XML
                // format of a property group, and we can get that without the rest of BuildPropertyGroup getting in the way.
                // Specify that these properties are output properties, so they get reverted when the project is reset.
                backingPropertyGroup = new BuildPropertyGroup(null /* no parent project */, taskNodeXmlElement, PropertyType.OutputProperty);
                break;

            case XMakeElements.itemGroup:
                backingType = BackingType.ItemGroup;
                // If the backing type is an item group, we just re-use the code that understands the XML format of an item group;
                // the semantics of BuildItemGroup are too coupled to its current use in the Project object for us to re-use it.
                backingItemGroupXml = new BuildItemGroupXml(taskNodeXmlElement);
                List <XmlElement> children = backingItemGroupXml.GetChildren();
                backingBuildItemGroupChildren = new List <BuildItemGroupChildXml>(children.Count);

                foreach (XmlElement child in children)
                {
                    BuildItemGroupChildXml childXml = new BuildItemGroupChildXml(child, ChildType.Any);
                    backingBuildItemGroupChildren.Add(childXml);
                }
                break;
            }
        }
Example #7
0
        /// <summary>
        /// Create an instance of this class to represent the IBuildEngine2 interface to the task
        /// including the event location where the log messages are raised
        /// </summary>
        /// <param name="parentModule">Parent Task Execution Module</param>
        /// <param name="handleId"></param>
        /// <param name="parentProjectFullFileName">the full path to the currently building project</param>
        /// <param name="projectFileOfTaskNode">the path to the actual file (project or targets) where the task invocation is located</param>
        /// <param name="loggingServices"></param>
        /// <param name="buildEventContext">Event Context where events will be seen to be raised from. Task messages will get this as their event context</param>
        internal EngineProxy
        (
            TaskExecutionModule parentModule,
            int handleId,
            string parentProjectFullFileName,
            string projectFileOfTaskNode,
            EngineLoggingServices loggingServices,
            BuildEventContext buildEventContext
        )
        {
            ErrorUtilities.VerifyThrow(parentModule != null, "No parent module.");
            ErrorUtilities.VerifyThrow(loggingServices != null, "No logging services.");
            ErrorUtilities.VerifyThrow(projectFileOfTaskNode != null, "Need project file path string");

            this.parentModule = parentModule;
            this.handleId     = handleId;
            this.parentProjectFullFileName = parentProjectFullFileName;
            this.projectFileOfTaskNode     = projectFileOfTaskNode;
            this.loggingServices           = loggingServices;
            this.buildEventContext         = buildEventContext;
            this.callbackMonitor           = new object();

            activeProxy = true;
        }
Example #8
0
 /// <summary>
 /// Initalize this class with a central logger id identifying the central logger to which
 /// these events should be forwarded and a logging service that will do the forwarding
 /// </summary>
 /// <param name="loggerId">central logger id</param>
 /// <param name="loggingService">engine logging service</param>
 internal EventRedirector(int loggerId, EngineLoggingServices loggingService)
 {
     this.loggerId       = loggerId;
     this.loggingService = loggingService;
 }
 /// <summary>
 /// This constructor initializes all required data.
 /// </summary>
 /// <owner>JomoF</owner>
 /// <param name="loggingServices"></param>
 /// <param name="binPath"></param>
 internal ProjectSchemaValidationHandler(BuildEventContext buildEventContext, EngineLoggingServices loggingServices, string binPath)
 {
     this.engineLoggingServices = loggingServices;
     this.binPath           = binPath;
     this.buildEventContext = buildEventContext;
 }
Example #10
0
        /// <summary>
        /// This function implements the callback via the IBuildEngine interface
        /// </summary>
        /// <returns>result of call to engine</returns>
        virtual internal bool BuildProjectFile
        (
            int handleId,
            string[] projectFileNames,
            string[] targetNames,
            IDictionary[] globalPropertiesPerProject,
            IDictionary[] targetOutputsPerProject,
            EngineLoggingServices loggingServices,
            string [] toolsVersions,
            bool useResultsCache,
            bool unloadProjectsOnCompletion,
            BuildEventContext taskContext
        )
        {
            if (projectFileNames.Length == 0)
            {
                // Nothing to do, just return success
                return(true);
            }

            string currentDir = FileUtilities.GetCurrentDirectoryStaticBuffer(currentDirectoryBuffer);

            if (Engine.debugMode)
            {
                string targetName = targetNames == null ? "null" : targetNames[0];

                bool remoteNode = false;
                for (int r = 0; r < projectFileNames.Length; r++)
                {
                    string fullProjectName = projectFileNames[r] != null ?
                                             projectFileNames[r] : "null";
                    Console.WriteLine("RemoteNode: " + remoteNode + " Project " + fullProjectName + " T:" + targetName + " NodeProdyId# " + handleId + " Time " + DateTime.Now.ToLongTimeString());
                    if (globalPropertiesPerProject[r] != null)
                    {
                        foreach (DictionaryEntry entry in globalPropertiesPerProject[r])
                        {
                            Console.WriteLine(currentDir + " :GLOBAL " + entry.Key + "=" + entry.Value.ToString());
                        }
                    }
                }
            }

            BuildRequest[] buildRequests = new BuildRequest[projectFileNames.Length];
            for (int i = 0; i < buildRequests.Length; i++)
            {
                // We need to get the full path to the project before we call back
                // into the engine which has no control over current path
                string fullProjectName = projectFileNames[i] != null?
                                         Path.GetFullPath(projectFileNames[i]) : null;

                buildRequests[i] = new BuildRequest(handleId, fullProjectName, targetNames, globalPropertiesPerProject[i],
                                                    toolsVersions[i], i, useResultsCache, unloadProjectsOnCompletion);
                ErrorUtilities.VerifyThrow(buildRequests[i].IsGeneratedRequest == true, "Should not be sending non generated requests from TEM to engine");
                buildRequests[i].ParentBuildEventContext = taskContext;
            }

            BuildResult[] buildResultsLocal = new BuildResult[projectFileNames.Length];

            if (moduleMode == TaskExecutionModuleMode.SingleProcMode)
            {
                for (int i = 0; i < projectFileNames.Length; i++)
                {
                    // If we are running in a single threaded mode we need to
                    // re-enter the main build loop on the current thread in order
                    // to build the requested project, because the main build is below
                    // us on the stack
                    engineCallback.PostBuildRequestsToHost(new BuildRequest[] { buildRequests[i] });
                    buildResultsLocal[i] = engineCallback.GetParentEngine().EngineBuildLoop(buildRequests[i]);
                    buildResultsLocal[i].ConvertToTaskItems();
                }
            }
            else
            {
                WaitForBuildResults(handleId, buildResultsLocal, buildRequests);
            }

            // Store the outputs in the hashtables provided by the caller
            bool overallResult = true;

            for (int i = 0; i < buildResultsLocal.Length; i++)
            {
                // Users of the Object Model can pass in null targetOutputs for projects they do not want outputs for
                // therefore we need to make sure that there are targetoutputs and the users want the results
                if (buildResultsLocal[i] != null)
                {
                    if (buildResultsLocal[i].OutputsByTarget != null && targetOutputsPerProject[i] != null)
                    {
                        foreach (DictionaryEntry entry in buildResultsLocal[i].OutputsByTarget)
                        {
                            targetOutputsPerProject[i].Add(entry.Key, entry.Value);
                        }
                        overallResult = overallResult && buildResultsLocal[i].EvaluationResult;
                    }
                }
                else
                {
                    // The calculation was terminated prior to receiving the result
                    overallResult = false;
                }
            }

            // We're now returning from an IBuildEngine callback;
            // set the current directory back to what the tasks expect
            if (Directory.GetCurrentDirectory() != currentDir)
            {
                Directory.SetCurrentDirectory(currentDir);
            }

            if (Engine.debugMode)
            {
                bool   remoteNode = false;
                string targetName = targetNames == null ? "null" : targetNames[0];
                Console.WriteLine("RemoteNode: " + remoteNode + " T:" + targetName + " HandleId# " + handleId + " Result " + overallResult);
            }

            return(overallResult);
        }
Example #11
0
        /// <summary>
        /// Given a task name, this method retrieves the task class. If the task has been requested before, it will be found in
        /// the class cache; otherwise, &lt;UsingTask&gt; declarations will be used to search the appropriate assemblies.
        /// </summary>
        /// <param name="taskName"></param>
        /// <param name="taskProjectFile"></param>
        /// <param name="taskNode"></param>
        /// <param name="exactMatchRequired"></param>
        /// <param name="loggingServices"></param>
        /// <param name="buildEventContext"></param>
        /// <param name="taskClass"></param>
        /// <returns>true, if task is found</returns>
        public bool GetRegisteredTask
        (
            string taskName,
            string taskProjectFile,
            XmlNode taskNode,
            bool exactMatchRequired,
            EngineLoggingServices loggingServices,
            BuildEventContext buildEventContext,
            out LoadedType taskClass
        )
        {
            taskClass = null;

            // If there are no using tags in the project don't bother caching or looking for tasks
            if (registeredTasks == null)
            {
                return(false);
            }

            Hashtable cachedTaskClasses = exactMatchRequired ? this.cachedTaskClassesWithExactMatch : this.cachedTaskClassesWithFuzzyMatch;

            if (cachedTaskClasses.Contains(taskName))
            {
                // Caller has asked us before for this same task name, and for the same value of "bool exactMatchRequired".
                // Return whatever the previous result was, even if it was null.   Why would the result be different than
                // it was before?  NOTE:  Hash tables CAN have "null" as their value, and this still returns "true" for Contains(...).
                taskClass = (LoadedType)cachedTaskClasses[taskName];
            }
            else
            {
                Hashtable registeredTasksFound;

                // look for the given task name in the registry; if not found, gather all registered task names that partially
                // match the given name
                if (FindRegisteredTasks(taskName, exactMatchRequired, out registeredTasksFound))
                {
                    foreach (DictionaryEntry registeredTaskFound in registeredTasksFound)
                    {
                        string mostSpecificTaskName = (string)registeredTaskFound.Key;

                        // if the given task name is longer than the registered task name
                        if (taskName.Length > ((string)registeredTaskFound.Key).Length)
                        {
                            // we will use the longer name to help disambiguate between multiple matches
                            mostSpecificTaskName = taskName;
                        }

                        if (GetTaskFromAssembly(mostSpecificTaskName, (ArrayList)registeredTaskFound.Value, taskProjectFile, taskNode, loggingServices, buildEventContext, out taskClass))
                        {
                            // Whilst we are within the processing of the task, we haven't actually started executing it, so
                            // our using task message needs to be in the context of the target. However any errors should be reported
                            // at the point where the task appears in the project.
                            BuildEventContext usingTaskContext = new BuildEventContext(buildEventContext.NodeId, buildEventContext.TargetId, buildEventContext.ProjectContextId, BuildEventContext.InvalidTaskId);
                            loggingServices.LogComment(usingTaskContext, "TaskFound", taskName, taskClass.Assembly.ToString());
                            break;
                        }
                    }
                }

                // Cache the result, even if it is null.  We should never again do the work we just did, for this task name.
                cachedTaskClasses[taskName] = taskClass;
            }

            return(taskClass != null);
        }
Example #12
0
        /// <summary>
        /// Reads the given &lt;UsingTask&gt; tag and saves the task information specified in it.
        /// </summary>
        /// <param name="usingTask"></param>
        /// <param name="expander"></param>
        /// <param name="loggingServices"></param>
        /// <param name="buildEventContext"></param>
        public void RegisterTask(UsingTask usingTask, Expander expander, EngineLoggingServices loggingServices, BuildEventContext buildEventContext)
        {
            if (
                // if the <UsingTask> tag doesn't have a condition on it
                (usingTask.Condition == null)
                ||
                // or if the condition holds
                Utilities.EvaluateCondition(usingTask.Condition, usingTask.ConditionAttribute, expander,
                                            null, ParserOptions.AllowProperties | ParserOptions.AllowItemLists, loggingServices, buildEventContext)
                )
            {
                // Lazily allocate the hashtables if they are needed
                if (registeredTasks == null)
                {
                    cachedTaskClassesWithExactMatch = new Hashtable(StringComparer.OrdinalIgnoreCase);
                    cachedTaskClassesWithFuzzyMatch = new Hashtable(StringComparer.OrdinalIgnoreCase);
                    registeredTasks = new Hashtable(StringComparer.OrdinalIgnoreCase);
                }

                string assemblyName = null;
                string assemblyFile = null;

                if (usingTask.AssemblyName != null)
                {
                    // expand out all embedded properties and items in the assembly name
                    assemblyName = expander.ExpandAllIntoString(usingTask.AssemblyName, usingTask.AssemblyNameAttribute);

                    ProjectErrorUtilities.VerifyThrowInvalidProject(assemblyName.Length > 0,
                                                                    usingTask.AssemblyNameAttribute, "InvalidEvaluatedAttributeValue", assemblyName, usingTask.AssemblyName, XMakeAttributes.assemblyName, XMakeElements.usingTask);
                }
                else
                {
                    // expand out all embedded properties and items in the assembly file/path
                    assemblyFile = expander.ExpandAllIntoString(usingTask.AssemblyFile, usingTask.AssemblyFileAttribute);

                    ProjectErrorUtilities.VerifyThrowInvalidProject(assemblyFile.Length > 0,
                                                                    usingTask.AssemblyFileAttribute, "InvalidEvaluatedAttributeValue", assemblyFile, usingTask.AssemblyFile, XMakeAttributes.assemblyFile, XMakeElements.usingTask);

                    // figure out the directory of the project in which this <UsingTask> node was defined
                    string projectFile = XmlUtilities.GetXmlNodeFile(usingTask.TaskNameAttribute.OwnerElement, String.Empty);
                    string projectDir  = (projectFile.Length > 0)
                        ? Path.GetDirectoryName(projectFile)
                        : String.Empty;

                    // ensure the assembly file/path is relative to the project in which this <UsingTask> node was defined -- we
                    // don't want paths from imported projects being interpreted relative to the main project file
                    try
                    {
                        assemblyFile = Path.Combine(projectDir, assemblyFile);
                    }
                    catch (ArgumentException ex)
                    {
                        // Invalid chars in AssemblyFile path
                        ProjectErrorUtilities.VerifyThrowInvalidProject(false, usingTask.AssemblyFileAttribute,
                                                                        "InvalidAttributeValueWithException", assemblyFile,
                                                                        XMakeAttributes.assemblyFile, XMakeElements.usingTask, ex.Message);
                    }
                }

                AssemblyLoadInfo taskAssembly = new AssemblyLoadInfo(assemblyName, assemblyFile);

                // expand out all embedded properties and items
                string taskName = expander.ExpandAllIntoString(usingTask.TaskName, usingTask.TaskNameAttribute);

                ProjectErrorUtilities.VerifyThrowInvalidProject(taskName.Length > 0,
                                                                usingTask.TaskNameAttribute, "InvalidEvaluatedAttributeValue", taskName, usingTask.TaskName, XMakeAttributes.taskName, XMakeElements.usingTask);

                // since more than one task can have the same name, we want to keep track of all assemblies that are declared to
                // contain tasks with a given name...
                ArrayList taskAssemblies = (ArrayList)registeredTasks[taskName];

                if (taskAssemblies == null)
                {
                    taskAssemblies            = new ArrayList();
                    registeredTasks[taskName] = taskAssemblies;
                }

                taskAssemblies.Add(taskAssembly);
            }
        }