public override IVerbWorker getWorker(WorkingDirectory workingDirectory)
        {
            Disposition disposition = new Fresh();

            if (this.verifyVerb != null)
            {
                VerificationResult verificationResult = VerificationResult.fromXmlFile(this.verifyVerb.getOutputs().Single());
                if (!verificationResult.pass)
                {
                    disposition = new Failed();
                }
            }

            if (!(disposition is Failed))
            {
                foreach (var o in this.buildVerb.getOutputs())
                {
                    if (o.getExtension() == ".exe")
                    {
                        File.Copy(workingDirectory.PathTo(o), workingDirectory.PathTo(this.exeOutput), overwrite: true);
                    }
                    else
                    {
                        var dest = this.RelocateBuildObjectToExeDirectory(o);
                        File.Copy(
                            workingDirectory.PathTo(o),
                            workingDirectory.PathTo(dest),
                            overwrite: true);
                    }
                }
            }

            return(new VerbSyncWorker(workingDirectory, disposition));
        }
Example #2
0
        public static new Disposition readXml(XmlReader xr)
        {
            List <string> messages = new List <string>();

            if (!xr.IsEmptyElement)
            {
                while (xr.Read())
                {
                    if (xr.NodeType == XmlNodeType.Element)
                    {
                        if (xr.Name.Equals(_xml_MessageTag))
                        {
                            messages.Add(xr.ReadElementContentAsString());
                        }
                        else
                        {
                            throw new Exception("Unrecognized Disposition::Failed tag " + xr.Name);
                        }
                    }
                    else if (xr.NodeType == XmlNodeType.EndElement)
                    {
                        Util.Assert(xr.Name.Equals(_xml_tag));
                        break;
                    }
                }
            }
            Failed f = new Failed();

            f.messages = messages;
            return(f);
        }
Example #3
0
        /// <summary>
        /// Performs the completion work for Async workers, and is called
        /// after the runAsync method returns.  Returns the ultimate
        /// disposition of the activity.
        /// </summary>
        /// <remarks>
        /// This method runs synchronously on the main thread.
        /// It can tidy up the state after async work, and store results
        /// in the Repository.
        /// Thou shalt not return Stale.
        /// </remarks>
        /// <returns>The disposition of this verb's worker's work.</returns>
        public Disposition Complete()
        {
            this.verb.RecordProcessInvokeCpuTime(this.pinv.CpuTime);

            string stdout = null;

            if (this.returnStandardOut)
            {
                stdout = this.pinv.GetStdout();
            }

            string stderr = null;

            if (this.returnStandardError)
            {
                stderr = this.pinv.GetStderr();
            }

            Disposition disposition;

            if (this.exitCodeHandling == ProcessExitCodeHandling.NonzeroIsOkay || this.pinv.ExitCode == 0)
            {
                disposition = new Fresh();
            }
            else
            {
                // Sheesh. Some tools emit error messages to stdout.
                // REVIEW: Provide full command line here rather than just executable (like old version did)?
                Failed f = new Failed(this.pinv.GetStdout() + this.pinv.GetStderr());
                f.AddError("Executable: " + this.executable + "\n");
                disposition = f;
            }

            return(this.verb.Complete(this.workingDirectory, this.pinv.CpuTime, stdout, stderr, disposition));
        }
Example #4
0
        void markFailed(IVerb verb)
        {
            //- at least one of verb's inputs has a permanent failure, so we didn't
            //- even try to execute it.
            Disposition         disposition = new Failed("upstream failure");
            ResultSummaryRecord summary     = new ResultSummaryRecord(verb, disposition, new BuildObjectValuePointer[] {});

            //- NB never store upstream failures to the persistent cache, because
            //- they depend on our knowledge this run that the upstream verb failed.
            //- If, in another run, the verb or its inputs are modified, produce the
            //- same outputs, but returns success, we'll be stuck pulling this
            //- upstream failure out of cache but calling this verb a failure.
            //-string inputHash = computeInputHash(verb, false);
            //-if (inputHash != null)
            //-{
            //-    Util.Assert(false);
            //-
            //-
            //-
            //-
            //-
            //-    resultCache.storeResult(inputHash, summary);
            //-}
            //-else
            //-{
            unrecordableFailures[verb] = disposition;
            //-}
            verbIsComplete(verb, disposition, BlessingRequest.Bless);
        }
Example #5
0
        public static Disposition readXml(XmlReader xr)
        {
            Util.Assert(xr.Name.Equals(_xml_tag));
            string value = xr.GetAttribute(_xml_value_attr);

            if (value.Equals(Fresh.Value))
            {
                return(new Fresh());
            }
            else if (value.Equals(Failed.Value))
            {
                return(Failed.readXml(xr));
            }
            else
            {
                throw new Exception("Invalid disposition value " + value);
            }
        }
Example #6
0
        /// <summary>
        /// Mark a verb as having failed.
        /// </summary>
        /// <param name="verb">The verb that failed.</param>
        private void markFailed(IVerb verb)
        {
            // At least one of verb's inputs has a permanent failure, so we didn't
            // even try to execute it.
            Disposition         disposition = new Failed("upstream failure");
            ResultSummaryRecord summary     = new ResultSummaryRecord(verb, disposition, new BuildObjectValuePointer[] { });

            // NB never store upstream failures to the persistent cache, because
            // they depend on our knowledge this run that the upstream verb failed.
            // If, in another run, the verb or its inputs are modified, produce the
            // same outputs, but returns success, we'll be stuck pulling this
            // upstream failure out of cache but calling this verb a failure.
            ////string inputHash = computeInputHash(verb, false);
            ////if (inputHash != null)
            ////{
            ////    Util.Assert(false);
            ////    // "Upstream failures" will never have a computable inputHash, because their inputs
            ////    // can't be considered known. Even if the upstream verb wrote something to disk,
            ////    // what if the upstream verb changes to no longer fail but still emit the same thing?
            ////    // We wouldn't want to conclude that, because the inputs hadn't changed, this
            ////    // verb still had an upstream failure.
            ////    repository.StoreResult(inputHash, summary);
            ////}
            ////else
            ////{
            this.unrecordableFailures[verb] = disposition;
            ////}

            // Mark all the verb's outputs as Failed in the repository.
            foreach (BuildObject obj in verb.getOutputs())
            {
                if (obj is VirtualBuildObject)
                {
                    this.repository.StoreVirtual(obj, disposition, null);
                }
                else
                {
                    this.repository.AddObject(obj, disposition, null);
                }
            }

            this.verbIsComplete(verb, disposition);
        }
Example #7
0
        public Disposition Complete(WorkingDirectory workingDirectory, double cpuTimeSeconds, string stdout, string stderr, Disposition disposition)
        {
            var dis = disposition;

            string cSharpPath = Path.ChangeExtension(workingDirectory.PathTo(this.expandedSource), CSharpExt);

            if (!File.Exists(cSharpPath))
            {
                // Dafny has a bug where compilation fails but result code is still 0.
                dis = new Failed();
            }

            if (dis is Fresh)
            {
                this.RewriteCSharpFile(cSharpPath, workingDirectory.PathTo(this.output));
            }

            return(dis);
        }
Example #8
0
        public static new Disposition readXml(XmlReader xr)
        {
            List<string> messages = new List<string>();
            if (!xr.IsEmptyElement)
            {
                while (xr.Read())
                {
                    if (xr.NodeType == XmlNodeType.Element)
                    {
                        if (xr.Name.Equals(_xml_MessageTag))
                        {
                            messages.Add(xr.ReadElementContentAsString());
                        }
                        else
                        {
                            throw new Exception("Unrecognized Disposition::Failed tag " + xr.Name);
                        }
                    }
                    else if (xr.NodeType == XmlNodeType.EndElement)
                    {
                        Util.Assert(xr.Name.Equals(Disposition._xml_tag));
                        break;
                    }
                }
            }

            Failed f = new Failed();
            f.messages = messages;
            return f;
        }
        /// <summary>
        /// Performs the completion work for Async workers, and is called
        /// after the runAsync method returns.  Returns the ultimate
        /// disposition of the activity.
        /// </summary>
        /// <remarks>
        /// This method runs synchronously on the main thread.
        /// It can tidy up the state after async work, and store results
        /// in the Repository.
        /// Thou shalt not return Stale.
        /// </remarks>
        /// <returns>The disposition of this verb's worker's work.</returns>
        public Disposition Complete()
        {
            this.verb.RecordProcessInvokeCpuTime(this.pinv.CpuTime);

            string stdout = null;
            if (this.returnStandardOut)
            {
                stdout = this.pinv.GetStdout();
            }

            string stderr = null;
            if (this.returnStandardError)
            {
                stderr = this.pinv.GetStderr();
            }

            Disposition disposition;
            if (this.exitCodeHandling == ProcessExitCodeHandling.NonzeroIsOkay || this.pinv.ExitCode == 0)
            {
                disposition = new Fresh();
            }
            else
            {
                // Sheesh. Some tools emit error messages to stdout.
                // REVIEW: Provide full command line here rather than just executable (like old version did)?
                Failed f = new Failed(this.pinv.GetStdout() + this.pinv.GetStderr());
                f.AddError("Executable: " + this.executable + "\n");
                disposition = f;
            }

            return this.verb.Complete(this.workingDirectory, this.pinv.CpuTime, stdout, stderr, disposition);
        }
Example #10
0
        /// <summary>
        /// Mark a verb as having failed.
        /// </summary>
        /// <param name="verb">The verb that failed.</param>
        private void markFailed(IVerb verb)
        {
            // At least one of verb's inputs has a permanent failure, so we didn't
            // even try to execute it.
            Disposition disposition = new Failed("upstream failure");
            ResultSummaryRecord summary = new ResultSummaryRecord(verb, disposition, new BuildObjectValuePointer[] { });

            // NB never store upstream failures to the persistent cache, because
            // they depend on our knowledge this run that the upstream verb failed.
            // If, in another run, the verb or its inputs are modified, produce the
            // same outputs, but returns success, we'll be stuck pulling this
            // upstream failure out of cache but calling this verb a failure.
            ////string inputHash = computeInputHash(verb, false);
            ////if (inputHash != null)
            ////{
            ////    Util.Assert(false);
            ////    // "Upstream failures" will never have a computable inputHash, because their inputs
            ////    // can't be considered known. Even if the upstream verb wrote something to disk,
            ////    // what if the upstream verb changes to no longer fail but still emit the same thing?
            ////    // We wouldn't want to conclude that, because the inputs hadn't changed, this
            ////    // verb still had an upstream failure.
            ////    repository.StoreResult(inputHash, summary);
            ////}
            ////else
            ////{
            this.unrecordableFailures[verb] = disposition;
            ////}

            // Mark all the verb's outputs as Failed in the repository.
            foreach (BuildObject obj in verb.getOutputs())
            {
                if (obj is VirtualBuildObject)
                {
                    this.repository.StoreVirtual(obj, disposition, null);
                }
                else
                {
                    this.repository.AddObject(obj, disposition, null);
                }
            }

            this.verbIsComplete(verb, disposition);
        }
Example #11
0
        Disposition recordResult(IVerb verb, Disposition executionDisposition)
        {
            //- record how each output appeared.

            List <BuildObjectValuePointer> outputs    = new List <BuildObjectValuePointer>();
            List <string>             missingOutputs  = new List <string>();
            IEnumerable <BuildObject> expectedOutputs = verb.getOutputs();

            if (executionDisposition is Failed)
            {
                expectedOutputs = expectedOutputs.Concat(verb.getFailureOutputs());
            }
            bool hasVirtualOutputs = false;

            foreach (BuildObject outobj in expectedOutputs)
            {
                if (!(outobj is VirtualBuildObject))
                {
                    if (File.Exists(outobj.getFilesystemPath()))
                    {
                        //- Try to catch accidental case mismatches that would burn us when we
                        //- try to fetch the file back in.
                        string fsname = PathNormalizer.dbg_normalizePath_nocache(outobj.getFilesystemPath(), false);
                        Util.Assert(Path.GetFileName(fsname).Equals(outobj.getFileName()));

                        outputs.Add(resultCache.storeObject(outobj));
                    }
                    else
                    {
                        missingOutputs.Add(String.Format("Missing expected output {0}", outobj.getRelativePath()));
                    }
                }
                else
                {
                    hasVirtualOutputs = true;
                    if (nuObjContents.getDisposition(outobj) is Fresh)
                    {
                        //- nothing to cache; virtual objects only survive in nuObjContents, the in-process cache
                    }
                    else
                    {
                        missingOutputs.Add(String.Format("Missing expected virtual {0}", outobj.getRelativePath()));
                    }
                }
            }
            if (!(executionDisposition is Failed) && missingOutputs.Count() > 0)
            {
                executionDisposition = new Failed(missingOutputs);
            }
            ResultSummaryRecord summary = new ResultSummaryRecord(verb, executionDisposition, outputs);
            string inputHash            = computeInputHash(verb, true);

            Util.Assert(inputHash != null);
            if (!hasVirtualOutputs)
            {
                resultCache.storeResult(inputHash, summary);
            }
            else
            {
                Say("Not caching verb persistently: " + verb);
            }

            verbIsComplete(verb, executionDisposition, BlessingRequest.Bless);
            return(executionDisposition);
        }
Example #12
0
        /// <summary>
        /// Record how each output of a task appeared.
        /// </summary>
        /// <param name="completion">The task completion notification.</param>
        /// <returns>Overall result of the verb execution.</returns>
        private Disposition recordResult(VerbRunner.TaskCompletion completion)
        {
            WorkingDirectory workingDirectory     = completion.workingDirectory;
            IVerb            verb                 = completion.verb;
            Disposition      executionDisposition = completion.disposition;

            List <BuildObjectValuePointer> outputs    = new List <BuildObjectValuePointer>();
            List <string>             missingOutputs  = new List <string>();
            IEnumerable <BuildObject> expectedOutputs = verb.getOutputs();

            if (executionDisposition is Failed)
            {
                expectedOutputs = expectedOutputs.Concat(verb.getFailureOutputs());
            }

            bool hasVirtualOutputs = false;

            foreach (BuildObject outobj in expectedOutputs)
            {
                if (!(outobj is VirtualBuildObject))
                {
                    // For expected file outputs, check for existence in working directory.
                    // REVIEW: Add method to WorkingDirectory which does this?
                    if (File.Exists(workingDirectory.PathTo(outobj)))
                    {
                        // Try to catch accidental case mismatches that would burn us when we
                        // try to fetch the file back in.
                        ////string fsname = PathNormalizer.dbg_normalizePath_nocache(outobj.deprecatedGetFilesystemPath(), false);
                        ////Util.Assert(Path.GetFileName(fsname).Equals(outobj.getFileName()));
                        // REVIEW: Do we need to worry about case mismatches anymore?  See comments above.
                        outputs.Add(this.repository.Store(workingDirectory, outobj, executionDisposition));

                        // Store a copy of this verb output as a file in the real nuobj directory.
                        Util.Assert(outobj.getRelativePath().StartsWith(BuildEngine.theEngine.getObjRoot(), StringComparison.Ordinal));
                        string nuobjPath = IronRootDirectory.PathTo(outobj);
                        Directory.CreateDirectory(Path.GetDirectoryName(nuobjPath));
                        File.Copy(workingDirectory.PathTo(outobj), nuobjPath, true);
                    }
                    else
                    {
                        missingOutputs.Add(string.Format("Missing expected output {0}", outobj.getRelativePath()));
                    }
                }
                else
                {
                    hasVirtualOutputs = true;
                    if (this.repository.GetDisposition(outobj) is Fresh)
                    {
                        // Nothing to cache; virtual objects only survive in the Repository, the in-process store.
                    }
                    else
                    {
                        missingOutputs.Add(string.Format("Missing expected virtual {0}", outobj.getRelativePath()));
                    }
                }
            }

            if (!(executionDisposition is Failed) && missingOutputs.Count() > 0)
            {
                executionDisposition = new Failed(missingOutputs);
            }

            ResultSummaryRecord summary = new ResultSummaryRecord(verb, executionDisposition, outputs);
            string inputHash            = this.computeInputHash(verb, true);

            Util.Assert(inputHash != null);
            if (!hasVirtualOutputs)
            {
                this.repository.StoreResult(inputHash, summary);
            }
            else
            {
                this.Say("Not caching verb persistently: " + verb);
            }

            this.verbIsComplete(verb, executionDisposition);
            return(executionDisposition);
        }
Example #13
0
        public override IVerbWorker getWorker(WorkingDirectory workingDirectory)
        {
            Disposition disposition = new Fresh();

            if (this.verifyVerb != null)
            {
                VerificationResult verificationResult = VerificationResult.fromXmlFile(this.verifyVerb.getOutputs().Single());
                if (!verificationResult.pass)
                {
                    disposition = new Failed();
                }
            }

            if (!(disposition is Failed))
            {
                foreach (var o in this.buildVerb.getOutputs())
                {
                    if (o.getExtension() == ".exe")
                    {
                        File.Copy(workingDirectory.PathTo(o), workingDirectory.PathTo(this.exeOutput), overwrite: true);
                    }
                    else
                    {
                        var dest = this.RelocateBuildObjectToExeDirectory(o);
                        File.Copy(
                            workingDirectory.PathTo(o),
                            workingDirectory.PathTo(dest),
                            overwrite: true);
                    }
                }
            }

            return new VerbSyncWorker(workingDirectory, disposition);
        }
Example #14
0
        public ProcessInvoker(
            string executable,
            string[] args,
            RcHandling rcHandling,
            BuildObject failureBase,
            string finalStdoutPath = null,
            string dbgText         = null,
            bool allowAbsoluteExe  = false,
            bool allowAbsoluteArgs = false,
            string workingDir      = null)
        {
            Util.Assert(allowAbsoluteExe || !executable.Contains(":"));  //- Hey, this looks like an absolute path! Use .getRelativePath() to tolerate crossing machine boundaries.
            foreach (string arg in args)
            {
                //- Pardon my distasteful heuristic to avoid flagging /flag:value args.
                Util.Assert(allowAbsoluteArgs || arg.Length < 2 || arg[1] != ':');  //- Hey, this looks like an absolute path! Use .getRelativePath() to tolerate crossing machine boundaries.
            }

            this.rcHandling = rcHandling;
            stdout          = new StringBuilder();
            stderr          = new StringBuilder();

            using (Job job = new Job())
            {
                using (Process proc = new Process())
                {
                    proc.StartInfo.FileName = Path.Combine(BuildEngine.theEngine.getIronRoot(), executable);
                    //- TODO Is there a better way to escape the args to avoid problems with spaces?
                    proc.StartInfo.Arguments = String.Join(" ", args);

                    proc.StartInfo.WorkingDirectory = workingDir == null?BuildEngine.theEngine.getIronRoot() : workingDir;

                    proc.StartInfo.RedirectStandardOutput = true;
                    if (finalStdoutPath != null)
                    {
                        _tmpStdoutPath           = finalStdoutPath + ".tmp";
                        stdoutFile               = new StreamWriter(_tmpStdoutPath);
                        proc.OutputDataReceived += new DataReceivedEventHandler(stdoutRedirectHandler);
                    }
                    else
                    {
                        //- collect stdout here for diagnostics.
                        proc.OutputDataReceived += new DataReceivedEventHandler(stdoutHandler);
                    }
                    proc.StartInfo.RedirectStandardError = true;
                    proc.ErrorDataReceived        += new DataReceivedEventHandler(stderrHandler);
                    proc.StartInfo.UseShellExecute = false;

                    string commandLine = proc.StartInfo.FileName + " " + proc.StartInfo.Arguments;
                    if (alwaysEmitDiagnostics)
                    {
                        //- In diagnostic mode, we emit the command line twice, once ahead in case Boogie decides
                        //- to run away and never come back.
                        BuildObject failureBatObj = failureBase.makeOutputObject(".bat");
                        failureBatObj.prepareObjDirectory();
                        File.WriteAllText(failureBatObj.getFilesystemPath(), commandLine);
                    }

                    proc.Start();
                    job.AddProcess(proc);
                    proc.BeginOutputReadLine();
                    proc.BeginErrorReadLine();
                    proc.WaitForExit();

                    cpuTime = job.GetCpuTime().TotalSeconds;

                    exitCode = proc.ExitCode;
                    if (stdoutFile != null)
                    {
                        stdoutFile.Close();
                    }

                    if (passed())
                    {
                        disposition = new Fresh();
                    }
                    else
                    {
                        //- sheesh. Some tools emit error messages to stdout.
                        Failed f = new Failed(getStdoutString() + stderr.ToString());
                        f.AddError("Command line: " + commandLine + "\n");
                        disposition = f;
                    }

#pragma warning disable 429 //- alwaysEmitDiagnostics is a compile-time constant that can hide expression !passed()
                    if (failureBase != null && (alwaysEmitDiagnostics || !passed()))
#pragma warning restore 429
                    {
                        failureBase.prepareObjDirectory();
                        File.WriteAllText(failureBase.makeOutputObject(".bat").getFilesystemPath(), commandLine);
                        File.WriteAllText(failureBase.makeOutputObject(".txt").getFilesystemPath(), dbgText);
                        File.WriteAllText(failureBase.makeOutputObject(".stdout").getFilesystemPath(), getStdoutString());
                        File.WriteAllText(failureBase.makeOutputObject(".stderr").getFilesystemPath(), getStderr());
                    }
                }
            }

            if (passed() && _tmpStdoutPath != null)
            {
                File.Delete(finalStdoutPath);
                File.Move(_tmpStdoutPath, finalStdoutPath);
                _tmpStdoutPath = null;
            }
        }
Example #15
0
        /// <summary>
        /// Record how each output of a task appeared.
        /// </summary>
        /// <param name="completion">The task completion notification.</param>
        /// <returns>Overall result of the verb execution.</returns>
        private Disposition recordResult(VerbRunner.TaskCompletion completion)
        {
            WorkingDirectory workingDirectory = completion.workingDirectory;
            IVerb verb = completion.verb;
            Disposition executionDisposition = completion.disposition;

            List<BuildObjectValuePointer> outputs = new List<BuildObjectValuePointer>();
            List<string> missingOutputs = new List<string>();
            IEnumerable<BuildObject> expectedOutputs = verb.getOutputs();

            if (executionDisposition is Failed)
            {
                expectedOutputs = expectedOutputs.Concat(verb.getFailureOutputs());
            }

            bool hasVirtualOutputs = false;
            foreach (BuildObject outobj in expectedOutputs)
            {
                if (!(outobj is VirtualBuildObject))
                {
                    // For expected file outputs, check for existence in working directory.
                    // REVIEW: Add method to WorkingDirectory which does this?
                    if (File.Exists(workingDirectory.PathTo(outobj)))
                    {
                        // Try to catch accidental case mismatches that would burn us when we
                        // try to fetch the file back in.
                        ////string fsname = PathNormalizer.dbg_normalizePath_nocache(outobj.deprecatedGetFilesystemPath(), false);
                        ////Util.Assert(Path.GetFileName(fsname).Equals(outobj.getFileName()));
                        // REVIEW: Do we need to worry about case mismatches anymore?  See comments above.
                        outputs.Add(this.repository.Store(workingDirectory, outobj, executionDisposition));

                        // Store a copy of this verb output as a file in the real nuobj directory.
                        Util.Assert(outobj.getRelativePath().StartsWith(BuildEngine.theEngine.getObjRoot(), StringComparison.Ordinal));
                        string nuobjPath = IronRootDirectory.PathTo(outobj);
                        Directory.CreateDirectory(Path.GetDirectoryName(nuobjPath));
                        File.Copy(workingDirectory.PathTo(outobj), nuobjPath, true);
                    }
                    else
                    {
                        missingOutputs.Add(string.Format("Missing expected output {0}", outobj.getRelativePath()));
                    }
                }
                else
                {
                    hasVirtualOutputs = true;
                    if (this.repository.GetDisposition(outobj) is Fresh)
                    {
                        // Nothing to cache; virtual objects only survive in the Repository, the in-process store.
                    }
                    else
                    {
                        missingOutputs.Add(string.Format("Missing expected virtual {0}", outobj.getRelativePath()));
                    }
                }
            }

            if (!(executionDisposition is Failed) && missingOutputs.Count() > 0)
            {
                executionDisposition = new Failed(missingOutputs);
            }

            ResultSummaryRecord summary = new ResultSummaryRecord(verb, executionDisposition, outputs);
            string inputHash = this.computeInputHash(verb, true);
            Util.Assert(inputHash != null);
            if (!hasVirtualOutputs)
            {
                this.repository.StoreResult(inputHash, summary);
            }
            else
            {
                this.Say("Not caching verb persistently: " + verb);
            }

            this.verbIsComplete(verb, executionDisposition);
            return executionDisposition;
        }
 public ObjectFailedException(BuildObject obj, Failed failed)
     : base(obj.ToString() + ": " + failed.ToString())
 {
 }
Example #17
0
        public Disposition Complete(WorkingDirectory workingDirectory, double cpuTimeSeconds, string stdout, string stderr, Disposition disposition)
        {
            var dis = disposition;

            string cSharpPath = Path.ChangeExtension(workingDirectory.PathTo(this.expandedSource), CSharpExt);
            if (!File.Exists(cSharpPath))
            {
                // Dafny has a bug where compilation fails but result code is still 0.
                dis = new Failed();
            }

            if (dis is Fresh)
            {
                this.RewriteCSharpFile(cSharpPath, workingDirectory.PathTo(this.output));
            }

            return dis;
        }