context: //
  // TargetBatchingImpl.cs: Class that implements Target Batching Algorithm from the wiki.
  //
  // Author:
  //   Marek Sieradzki (marek.sieradzki@gmail.com)
  //   Ankit Jain (jankit@novell.com)
  //
  // (C) 2005 Marek Sieradzki
  // Copyright 2008 Novell, Inc (http://www.novell.com)
  // Copyright 2009 Novell, Inc (http://www.novell.com)
  //
  // Permission is hereby granted, free of charge, to any person obtaining
  // a copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to
  // permit persons to whom the Software is furnished to do so, subject to
  // the following conditions:
  //
  // The above copyright notice and this permission notice shall be
  // included in all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  
  using System;
  using System.IO;
  using System.Collections.Generic;
  using System.Xml;
  
  using Microsoft.Build.Framework;
  
  namespace Microsoft.Build.BuildEngine {
  	internal class TargetBatchingImpl : BatchingImplBase
  	{
  		string		inputs;
  		string		outputs;
  		string		name;
  
  		public TargetBatchingImpl (Project project, XmlElement targetElement)
  			: base (project)
  		{
  			if (targetElement == null)
  				throw new ArgumentNullException ("targetElement");
  
  			inputs = targetElement.GetAttribute ("Inputs");
  			outputs = targetElement.GetAttribute ("Outputs");
  			name = targetElement.GetAttribute ("Name");
  		}
  
  		public bool Build (Target target, out bool executeOnErrors)
  		{
  			executeOnErrors = false;
  			try {
  				Init ();
  
  				ParseTargetAttributes (target);
  				BatchAndPrepareBuckets ();
  				return Run (target, out executeOnErrors);
  			} finally {
  				consumedItemsByName = null;
  				consumedMetadataReferences = null;
  				consumedQMetadataReferences = null;
  				consumedUQMetadataReferences = null;
  				batchedItemsByName = null;
  				commonItemsByName = null;
  			}
  		}
  
  		bool Run (Target target, out bool executeOnErrors)
  		{
  			executeOnErrors = false;
  			if (buckets.Count > 0) {
  				foreach (Dictionary<string, BuildItemGroup> bucket in buckets)
  					if (!RunTargetWithBucket (bucket, target, out executeOnErrors))
  						return false;
  
  				return true;
  			} else {
  				return RunTargetWithBucket (null, target, out executeOnErrors);
  			}
  		}
  
  		bool RunTargetWithBucket (Dictionary<string, BuildItemGroup> bucket, Target target, out bool executeOnErrors)
  		{
  			bool target_result = true;
  			executeOnErrors = false;
  
  			LogTargetStarted (target);
  
  			if (bucket != null)
  				project.PushBatch (bucket, commonItemsByName);
  
  			try {
  				TaskExecutionMode taskExecutionMode;
  				string reason;
  				bool skip_completely;
  				if (!BuildTargetNeeded (out reason, out skip_completely)) {
  					LogTargetSkipped (target, reason);
  					if (skip_completely)
  						return true;
  
  					taskExecutionMode = TaskExecutionMode.SkipAndSetOutput;
  				} else {
  					taskExecutionMode = TaskExecutionMode.Complete;
  
  					if (!String.IsNullOrEmpty (reason))
  						target.Engine.LogMessage (MessageImportance.Low, reason);
  				}
  
  				for (int i = 0; i < target.BuildTasks.Count; i ++) {
  					//FIXME: parsing attributes repeatedly
  					IBuildTask bt = target.BuildTasks [i];
  
  					// HACK: need some form of cross references checks
  					var tem = taskExecutionMode;
  					if (tem == TaskExecutionMode.SkipAndSetOutput) {
  						var bti = bt as BuildTask;
  
  						//
  						// BuildTargetNeeded checks only files timestamps but ignores any metadata dependencies
  						// that way we can end up in the situation when output metadata are populated but from
  						// incomplete dependencies.
  						//
  						// E.g.
  						// <CreateItem Include="$(IntermediateOutputPath)%(_PngImage.LogicalName)" AdditionalMetadata="LogicalName=%(_PngImage.LogicalName)">
  						//		<Output TaskParameter="Include" />
  						// </CreateItem>
  						//
  						if (bti != null && bti.Name == "CreateItem")
  							tem = TaskExecutionMode.Complete;
  					}
  
  					TaskBatchingImpl batchingImpl = new TaskBatchingImpl (project);
  					bool task_result = batchingImpl.Build (bt, tem, out executeOnErrors);
  					if (task_result)
  						continue;
  
  					// task failed, if ContinueOnError,
  					// ignore failed state for target
  					target_result = bt.ContinueOnError;
  
  					if (!bt.ContinueOnError) {
  						executeOnErrors = true;
  						return false;
  					}
  
  				}
  			} finally {
  				if (bucket != null)
  					project.PopBatch ();
  
  				LogTargetFinished (target, target_result);
  			}
  
  			return target_result;
  		}
  
  		// Parse target's Input and Output attributes to get list of referenced
  		// metadata and items to determine batching
  		void ParseTargetAttributes (Target target)
  		{
  			if (!String.IsNullOrEmpty (inputs))
  				ParseAttribute (inputs);
  
  			if (!String.IsNullOrEmpty (outputs))
  				ParseAttribute (outputs);
  		}
  
  		bool BuildTargetNeeded (out string reason, out bool skipCompletely)
  		{
  			reason = String.Empty;
  			ITaskItem [] inputFiles;
  			ITaskItem [] outputFiles;
  			DateTime youngestInput, oldestOutput;
  			skipCompletely = false;
  
  			if (String.IsNullOrEmpty (inputs.Trim ())) {
  				return true;
  			}
  
  			if (String.IsNullOrEmpty (outputs.Trim ())) {
  				project.ParentEngine.LogError ("Target {0} has inputs but no outputs specified.", name);
  				return true;
  			}
  
  			Expression e = new Expression ();
  			e.Parse (inputs, ParseOptions.AllowItemsMetadataAndSplit);
  			inputFiles = (ITaskItem[]) e.ConvertTo (project, typeof (ITaskItem[]), ExpressionOptions.ExpandItemRefs);
  
  			e = new Expression ();
  			e.Parse (outputs, ParseOptions.AllowItemsMetadataAndSplit);
  			outputFiles = (ITaskItem[]) e.ConvertTo (project, typeof (ITaskItem[]), ExpressionOptions.ExpandItemRefs);
  
  			if (outputFiles == null || outputFiles.Length == 0) {
  				reason = String.Format ("No output files were specified for target {0}, skipping.", name);
  				return false;
  			}
  
  			if (inputFiles == null || inputFiles.Length == 0) {
  				skipCompletely = true;
  				reason = String.Format ("No input files were specified for target {0}, skipping.", name);
  				return false;
  			}
  
  			youngestInput = DateTime.MinValue;
  			oldestOutput = DateTime.MaxValue;
  
  			string youngestInputFile, oldestOutputFile;
  			youngestInputFile = oldestOutputFile = String.Empty;
  			foreach (ITaskItem item in inputFiles) {
  				string file = item.ItemSpec.Trim ();
  				if (file.Length == 0)
  					continue;
  
  				if (!File.Exists (file)) {
  					reason = String.Format ("Target {0} needs to be built as input file '{1}' does not exist.", name, file);
  					return true;
  				}
  
  
gt: 				DateTime lastWriteTime = File.GetLastWriteTime (file);