/// <summary>
        /// Returns a string indicating why an incremental build is occurring.
        /// </summary>
        private static string GetIncrementalBuildReason(DependencyAnalysisLogDetail logDetail)
        {
            string reason = null;

            if (logDetail.Reason == OutofdateReason.NewerInput)
            {
                // One of the inputs was newer than its corresponding output
                reason = ResourceUtilities.FormatResourceString("BuildTargetPartiallyInputNewer", logDetail.InputItemName, logDetail.Input, logDetail.Output);
            }
            else if (logDetail.Reason == OutofdateReason.MissingOutput)
            {
                // One of the outputs was missing
                reason = ResourceUtilities.FormatResourceString("BuildTargetPartiallyOutputDoesntExist", logDetail.OutputItemName, logDetail.Input, logDetail.Output);
            }
            else if (logDetail.Reason == OutofdateReason.MissingInput)
            {
                // One of the inputs was missing
                reason = ResourceUtilities.FormatResourceString("BuildTargetPartiallyInputDoesntExist", logDetail.InputItemName, logDetail.Input, logDetail.Output);
            }

            return reason;
        }
        /// <summary>
        /// Compares the set of files/directories designated as "inputs" against the set of files/directories designated as
        /// "outputs", and indicates if any "output" file/directory is out-of-date w.r.t. any "input" file/directory.
        /// </summary>
        /// <remarks>
        /// NOTE: Internal for unit test purposes only.
        /// </remarks>
        /// <owner>danmose</owner>
        /// <param name="inputs"></param>
        /// <param name="outputs"></param>
        /// <returns>true, if any "input" is newer than any "output", or if any input or output does not exist.</returns>
        internal static bool IsAnyOutOfDate(out DependencyAnalysisLogDetail dependencyAnalysisDetailEntry, string projectDirectory, IList inputs, IList outputs)
        {
            ErrorUtilities.VerifyThrow((inputs.Count > 0) && (outputs.Count > 0), "Need to specify inputs and outputs.");

             // Algorithm: walk through all the outputs to find the oldest output
            //            walk through the inputs as far as we need to until we find one that's newer (if any)

            // PERF -- we could change this to ensure that we walk the shortest list first (because we walk that one entirely): 
            //         possibly the outputs list isn't actually the shortest list. However it always is the shortest
            //         in the cases I've seen, and adding this optimization would make the code hard to read.

            string oldestOutput = EscapingUtilities.UnescapeAll((string)outputs[0]);

            FileInfo oldestOutputInfo = null;
            try
            {
                string oldestOutputFullPath = Path.Combine(projectDirectory, oldestOutput);
                oldestOutputInfo = FileUtilities.GetFileInfoNoThrow(oldestOutputFullPath);
            }
            catch (Exception e)
            {
                if (ExceptionHandling.NotExpectedException(e))
                {
                    throw;
                }
                // Output does not exist
                oldestOutputInfo = null;
            }

            if (oldestOutputInfo == null)
            {
                // First output is missing: we must build the target
                string arbitraryInput = EscapingUtilities.UnescapeAll((string)inputs[0]);
                dependencyAnalysisDetailEntry = new DependencyAnalysisLogDetail(arbitraryInput, oldestOutput, null, null, OutofdateReason.MissingOutput);
                return true;
            }

            for (int i = 1; i < outputs.Count; i++)
            {
                string candidateOutput = EscapingUtilities.UnescapeAll((string)outputs[i]);

                FileInfo candidateOutputInfo = null;
                try
                {
                    string candidateOutputFullPath = Path.Combine(projectDirectory, candidateOutput);
                    candidateOutputInfo = FileUtilities.GetFileInfoNoThrow(candidateOutputFullPath);
                }
                catch (Exception e)
                {
                    if (ExceptionHandling.NotExpectedException(e))
                    {
                        throw;
                    }
                    // Output does not exist
                    candidateOutputInfo = null;
                }

                if (candidateOutputInfo == null)
                {
                    // An output is missing: we must build the target
                    string arbitraryInput = EscapingUtilities.UnescapeAll((string)inputs[0]);
                    dependencyAnalysisDetailEntry = new DependencyAnalysisLogDetail(arbitraryInput, candidateOutput, null, null, OutofdateReason.MissingOutput);
                    return true;
                }

                if (oldestOutputInfo.LastWriteTime > candidateOutputInfo.LastWriteTime)
                {
                    // This output is older than the previous record holder
                    oldestOutputInfo = candidateOutputInfo;
                    oldestOutput = candidateOutput;
                }
            }

            // Now compare the oldest output with each input and break out if we find one newer.
            foreach (string input in inputs)
            {
                string unescapedInput = EscapingUtilities.UnescapeAll(input);

                FileInfo inputInfo = null;
                try
                {
                    string unescapedInputFullPath = Path.Combine(projectDirectory, unescapedInput);
                    inputInfo = FileUtilities.GetFileInfoNoThrow(unescapedInputFullPath);
                }
                catch (Exception e)
                {
                    if (ExceptionHandling.NotExpectedException(e))
                    {
                        throw;
                    }
                    // Output does not exist
                    inputInfo = null;
                }

                if (inputInfo == null)
                {
                    // An input is missing: we must build the target
                    dependencyAnalysisDetailEntry = new DependencyAnalysisLogDetail(unescapedInput, oldestOutput, null, null, OutofdateReason.MissingInput);
                    return true;
                }
                else
                {
                    if (inputInfo.LastWriteTime > oldestOutputInfo.LastWriteTime)
                    {
                        // This input is newer than the oldest output: we must build the target
                        dependencyAnalysisDetailEntry = new DependencyAnalysisLogDetail(unescapedInput, oldestOutput, null, null, OutofdateReason.NewerInput);
                        return true;
                    }
                }
            }

            // All exist and no inputs are newer than any outputs; up to date
            dependencyAnalysisDetailEntry = null;
            return false;
        }
        /// <summary>
        /// Returns a string indicating why a full build is occurring.
        /// </summary>
        internal static string GetFullBuildReason(DependencyAnalysisLogDetail logDetail)
        {
            string reason = null;

            if (logDetail.Reason == OutofdateReason.NewerInput)
            {
                // One of the inputs was newer than all of the outputs
                reason = ResourceUtilities.FormatResourceString("BuildTargetCompletelyInputNewer", logDetail.Input, logDetail.Output);
            }
            else if (logDetail.Reason == OutofdateReason.MissingOutput)
            {
                // One of the outputs was missing
                reason = ResourceUtilities.FormatResourceString("BuildTargetCompletelyOutputDoesntExist", logDetail.Output);
            }
            else if (logDetail.Reason == OutofdateReason.MissingInput)
            {
                // One of the inputs was missing
                reason = ResourceUtilities.FormatResourceString("BuildTargetCompletelyInputDoesntExist", logDetail.Input);
            }

            return reason;
        }