Esempio n. 1
0
        /// <summary>
        /// Prepares the working directory tree for a verb's execution.
        /// </summary>
        /// <param name="verb">The verb whose execution we're preparing for.</param>
        private void PrepareForVerb(WorkingDirectory workingDirectory, IVerb verb)
        {
            // Debugging aide: write out the abstract id for this verb.
            File.WriteAllText(workingDirectory.PathTo("Debug.txt"), verb.getAbstractIdentifier().ToString());

            Repository repository = BuildEngine.theEngine.Repository;

            // Copy all verb inputs from the item cache to here.
            DependencyDisposition ddisp;

            foreach (BuildObject input in verb.getDependencies(out ddisp))
            {
                if (!(input is VirtualBuildObject))
                {
                    workingDirectory.CreateDirectoryFor(input);  // REVIEW: No longer needed?
                    repository.Fetch(workingDirectory, input);
                }
            }

            // Ensures that the directory tree for each of the verb's outputs exists.
            foreach (BuildObject output in verb.getOutputs())
            {
                workingDirectory.CreateDirectoryFor(output);
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Run the requested executable and produce a report of the results.
        /// </summary>
        /// <param name="executionRequest">The execution request.</param>
        /// <returns>A report of the results.</returns>
        private CloudExecutionReport RunAnExecutable(CloudExecutionRequest executionRequest)
        {
            // REVIEW: How/whether to use this.
            BuildObject diagnosticsBase = new BuildObject(Path.Combine("nuobj", "diagnostics", "process"));

                // Prep working directory with input files and output dirs.
                // TODO: The below will throw cache exceptions if something
                // isn't there (they should all be though).  Need to catch
                // these and fail the execution request when this happens.
                WorkingDirectory workingDirectory = new WorkingDirectory(this.virtualIronRoot);
                foreach (BuildObjectValuePointer inputFile in executionRequest.InputFileMappings)
                {
                    // REVIEW: How to determine cache container here.
                    ItemCacheContainer container = ItemCacheContainer.Sources;
                    if (this.multiplexedItemCache.GetItemSize(container, inputFile.ObjectHash) == -1)
                    {
                        container = ItemCacheContainer.Objects;
                    }

                    // TODO: Move path/directory manipulation code into
                    // WorkingDirectory and/or ItemCache.
                    string inputFilePath = workingDirectory.PathTo(inputFile.RelativePath);
                    Directory.CreateDirectory(Path.GetDirectoryName(inputFilePath));  // REVIEW: Still neeeded?
                    this.multiplexedItemCache.FetchItemToFile(
                        container,
                        inputFile.ObjectHash,
                        inputFilePath);
                }

                foreach (BuildObject outputFile in executionRequest.OutputFiles)
                {
                    workingDirectory.CreateDirectoryFor(outputFile);
                }

                // Run executable.
                ProcessInvoker pinv = new ProcessInvoker(
                    workingDirectory,
                    executionRequest.Executable,
                    new string[] { executionRequest.Arguments },
                    diagnosticsBase,
                    null, // This is captureStdout.  TODO: Should cleanup how this is used in ProcessInvoker.
                    null); // This is dbgText.  REVIEW: How/whether to use this.

                // When ProcessInvoker's constructor returns, the process has
                // finished running.
                Console.WriteLine("Request {0} completed in {1} seconds.", executionRequest.Identifier, pinv.CpuTime);

                // Store output files in the (cloud) item cache, and create a
                // list of the mappings.
                List<BuildObjectValuePointer> outputFileMappings = new List<BuildObjectValuePointer>();
                foreach (BuildObject outFile in executionRequest.OutputFiles)
                {
                    if (File.Exists(workingDirectory.PathTo(outFile)))
                    {
                        string fileHash = Util.hashFilesystemPath(workingDirectory.PathTo(outFile));
                        Util.Assert(!string.IsNullOrEmpty(fileHash));

                        // Note we explicitly write to the cloud cache here.
                        this.cloudCache.StoreItemFromFile(ItemCacheContainer.Objects, fileHash, workingDirectory.PathTo(outFile));
                        outputFileMappings.Add(new BuildObjectValuePointer(fileHash, outFile.getRelativePath()));
                    }
                }

                // Collect the results into a report.
                CloudExecutionReport report = new CloudExecutionReport(
                    executionRequest.Identifier,
                CloudExecutionReport.StatusCode.Completed,
                    pinv.ExitCode,
                    pinv.GetStdout(),
                    pinv.GetStderr(),
                    pinv.CpuTime,
                    outputFileMappings);

            return report;
        }
Esempio n. 3
0
        /// <summary>
        /// Prepares the working directory tree for a verb's execution.
        /// </summary>
        /// <param name="verb">The verb whose execution we're preparing for.</param>
        private void PrepareForVerb(WorkingDirectory workingDirectory, IVerb verb)
        {
            // Debugging aide: write out the abstract id for this verb.
            File.WriteAllText(workingDirectory.PathTo("Debug.txt"), verb.getAbstractIdentifier().ToString());

            Repository repository = BuildEngine.theEngine.Repository;

            // Copy all verb inputs from the item cache to here.
            DependencyDisposition ddisp;
            foreach (BuildObject input in verb.getDependencies(out ddisp))
            {
                if (!(input is VirtualBuildObject))
                {
                    workingDirectory.CreateDirectoryFor(input);  // REVIEW: No longer needed?
                    repository.Fetch(workingDirectory, input);
                }
            }

            // Ensures that the directory tree for each of the verb's outputs exists.
            foreach (BuildObject output in verb.getOutputs())
            {
                workingDirectory.CreateDirectoryFor(output);
            }
        }
        /// <summary>
        /// Initializes a new instance of the ProcessInvoker class.
        /// </summary>
        /// <param name="workingDirectory">The working directory the process is started in.</param>
        /// <param name="executable">The executable to run.</param>
        /// <param name="args">The command line arguments to provide to the executable.</param>
        /// <param name="failureBase">Not sure what this is -- some debugging/diagnostic thing.</param>
        /// <param name="captureStdout">Where to (optionally) capture standard out.</param>
        /// <param name="dbgText">Debugging text for something or another.</param>
        /// <param name="allowAbsoluteExe">Whether to allow an absolute (rather than relative) file path to the executable.</param>
        /// <param name="allowAbsoluteArgs">Whether to allow absolute (rather than relative) file paths as arguments.</param>
        /// <param name="workingDirOverride">The working directory to use.</param>
        public ProcessInvoker(
            WorkingDirectory workingDirectory,
            string executable,
            string[] args,
            BuildObject failureBase,
            BuildObject captureStdout = null,
            string dbgText            = null,
            bool allowAbsoluteExe     = false,
            bool allowAbsoluteArgs    = false,
            string workingDirOverride = null)
        {
            // Catch bad verb authors before they hurt themselves.
            Util.Assert(allowAbsoluteExe || !executable.Contains(":"));  // Hey, this looks like an absolute path! Use .getRelativePath(); it makes your output more stable.
            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.workingDirectory = workingDirectory;
            this.stdout           = new StringBuilder();
            this.stderr           = new StringBuilder();

            using (Job job = new Job())
            {
                using (Process proc = new Process())
                {
                    if (allowAbsoluteExe)
                    {
                        proc.StartInfo.FileName = executable;
                    }
                    else
                    {
                        // TODO: *All* async verbs need to list their executable (and all the libs it depends upon) as dependencies.
                        proc.StartInfo.FileName = workingDirectory.PathTo(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       = workingDirOverride == null ? workingDirectory.Root : workingDirOverride;
                    proc.StartInfo.RedirectStandardOutput = true;

                    // REVIEW: Maybe we should always capture stdout in a StringBuilder and just write it out to a file afterwards if requested?
                    if (captureStdout != null)
                    {
                        this.tmpStdout           = new BuildObject(captureStdout.getRelativePath() + ".tmp");
                        this.stdoutFile          = new StreamWriter(workingDirectory.PathTo(this.tmpStdout));
                        proc.OutputDataReceived += new DataReceivedEventHandler(this.StdoutRedirectHandler);
                    }
                    else
                    {
                        // Collect stdout here for diagnostics.
                        proc.OutputDataReceived += new DataReceivedEventHandler(this.StdoutHandler);
                    }

                    proc.StartInfo.RedirectStandardError = true;
                    proc.ErrorDataReceived        += new DataReceivedEventHandler(this.StderrHandler);
                    proc.StartInfo.UseShellExecute = false;

                    string commandLine = proc.StartInfo.FileName + " " + proc.StartInfo.Arguments;
                    if (failureBase != null && 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");
                        workingDirectory.CreateDirectoryFor(failureBatObj);
                        File.WriteAllText(workingDirectory.PathTo(failureBatObj), commandLine);
                    }

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

                    this.cpuTime = job.GetCpuTime().TotalSeconds;

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

                    if (failureBase != null && AlwaysEmitDiagnostics)
                    {
                        workingDirectory.CreateDirectoryFor(failureBase);
                        File.WriteAllText(workingDirectory.PathTo(failureBase.makeOutputObject(".bat")), commandLine);
                        File.WriteAllText(workingDirectory.PathTo(failureBase.makeOutputObject(".txt")), dbgText);
                        File.WriteAllText(workingDirectory.PathTo(failureBase.makeOutputObject(".stdout")), this.GetStdoutString());
                        File.WriteAllText(workingDirectory.PathTo(failureBase.makeOutputObject(".stderr")), this.GetStderr());
                    }
                }
            }

            // REVIEW: Add Delete, Exists and Move methods to WorkingDirectory class?
            if (this.tmpStdout != null && File.Exists(workingDirectory.PathTo(this.tmpStdout)))
            {
                // REVIEW: Nothing should be here.  Bother with the delete?
                File.Delete(workingDirectory.PathTo(captureStdout));
                File.Move(workingDirectory.PathTo(this.tmpStdout), workingDirectory.PathTo(captureStdout));
                this.tmpStdout = null;
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Initializes a new instance of the ProcessInvoker class.
        /// </summary>
        /// <param name="workingDirectory">The working directory the process is started in.</param>
        /// <param name="executable">The executable to run.</param>
        /// <param name="args">The command line arguments to provide to the executable.</param>
        /// <param name="failureBase">Not sure what this is -- some debugging/diagnostic thing.</param>
        /// <param name="captureStdout">Where to (optionally) capture standard out.</param>
        /// <param name="dbgText">Debugging text for something or another.</param>
        /// <param name="allowAbsoluteExe">Whether to allow an absolute (rather than relative) file path to the executable.</param>
        /// <param name="allowAbsoluteArgs">Whether to allow absolute (rather than relative) file paths as arguments.</param>
        /// <param name="workingDirOverride">The working directory to use.</param>
        public ProcessInvoker(
            WorkingDirectory workingDirectory,
            string executable,
            string[] args,
            BuildObject failureBase,
            BuildObject captureStdout = null,
            string dbgText = null,
            bool allowAbsoluteExe = false,
            bool allowAbsoluteArgs = false,
            string workingDirOverride = null)
        {
            // Catch bad verb authors before they hurt themselves.
            Util.Assert(allowAbsoluteExe || !executable.Contains(":"));  // Hey, this looks like an absolute path! Use .getRelativePath(); it makes your output more stable.
            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.workingDirectory = workingDirectory;
            this.stdout = new StringBuilder();
            this.stderr = new StringBuilder();

            using (Job job = new Job())
            {
                using (Process proc = new Process())
                {
                    if (allowAbsoluteExe)
                    {
                        proc.StartInfo.FileName = executable;
                    }
                    else
                    {
                        // TODO: *All* async verbs need to list their executable (and all the libs it depends upon) as dependencies.
                        proc.StartInfo.FileName = workingDirectory.PathTo(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 = workingDirOverride == null ? workingDirectory.Root : workingDirOverride;
                    proc.StartInfo.RedirectStandardOutput = true;

                    // REVIEW: Maybe we should always capture stdout in a StringBuilder and just write it out to a file afterwards if requested?
                    if (captureStdout != null)
                    {
                        this.tmpStdout = new BuildObject(captureStdout.getRelativePath() + ".tmp");
                        this.stdoutFile = new StreamWriter(workingDirectory.PathTo(this.tmpStdout));
                        proc.OutputDataReceived += new DataReceivedEventHandler(this.StdoutRedirectHandler);
                    }
                    else
                    {
                        // Collect stdout here for diagnostics.
                        proc.OutputDataReceived += new DataReceivedEventHandler(this.StdoutHandler);
                    }

                    proc.StartInfo.RedirectStandardError = true;
                    proc.ErrorDataReceived += new DataReceivedEventHandler(this.StderrHandler);
                    proc.StartInfo.UseShellExecute = false;

                    string commandLine = proc.StartInfo.FileName + " " + proc.StartInfo.Arguments;
                    if (failureBase != null && 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");
                        workingDirectory.CreateDirectoryFor(failureBatObj);
                        File.WriteAllText(workingDirectory.PathTo(failureBatObj), commandLine);
                    }

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

                    this.cpuTime = job.GetCpuTime().TotalSeconds;

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

                    if (failureBase != null && AlwaysEmitDiagnostics)
                    {
                        workingDirectory.CreateDirectoryFor(failureBase);
                        File.WriteAllText(workingDirectory.PathTo(failureBase.makeOutputObject(".bat")), commandLine);
                        File.WriteAllText(workingDirectory.PathTo(failureBase.makeOutputObject(".txt")), dbgText);
                        File.WriteAllText(workingDirectory.PathTo(failureBase.makeOutputObject(".stdout")), this.GetStdoutString());
                        File.WriteAllText(workingDirectory.PathTo(failureBase.makeOutputObject(".stderr")), this.GetStderr());
                    }
                }
            }

            // REVIEW: Add Delete, Exists and Move methods to WorkingDirectory class?
            if (this.tmpStdout != null && File.Exists(workingDirectory.PathTo(this.tmpStdout)))
            {
                // REVIEW: Nothing should be here.  Bother with the delete?
                File.Delete(workingDirectory.PathTo(captureStdout));
                File.Move(workingDirectory.PathTo(this.tmpStdout), workingDirectory.PathTo(captureStdout));
                this.tmpStdout = null;
            }
        }