/// <summary> /// Creates the PipData from the given object /// </summary> public static PipData CreatePipData(StringTable stringTable, ObjectLiteral obj, PipDataBuilder pipDataBuilder) { var settings = new XmlWriterSettings { Encoding = Encoding.UTF8, Indent = true, }; using (var stringBuilderWrapper = Pools.StringBuilderPool.GetInstance()) { var stringBuilder = stringBuilderWrapper.Instance; using (var writer = new UTF8StringWriter(stringBuilder)) using (var xmlWriter = XmlWriter.Create(writer, settings)) { var xmlContext = new XmlWritingContext(stringTable, pipDataBuilder, stringBuilderWrapper.Instance, xmlWriter); if (!WriteNode(obj, in xmlContext)) { return(PipData.Invalid); } FlushXmlToPipBuilder(in xmlContext); return(pipDataBuilder.ToPipData(string.Empty, PipDataFragmentEscaping.NoEscaping)); } } }
private void SetProcessEnvironmentVariables(IReadOnlyDictionary <string, string> environment, ProcessBuilder processBuilder) { foreach (KeyValuePair <string, string> kvp in environment) { if (kvp.Value != null) { var envPipData = new PipDataBuilder(m_context.StringTable); // Casing for paths is not stable as reported by BuildPrediction. So here we try to guess if the value // represents a path, and normalize it string value = kvp.Value; if (!string.IsNullOrEmpty(value) && AbsolutePath.TryCreate(PathTable, value, out var absolutePath)) { envPipData.Add(absolutePath); } else { envPipData.Add(value); } processBuilder.SetEnvironmentVariable( StringId.Create(m_context.StringTable, kvp.Key), envPipData.ToPipData(string.Empty, PipDataFragmentEscaping.NoEscaping)); } } if (m_userDefinedPassthroughVariables != null) { foreach (string passThroughVariable in m_userDefinedPassthroughVariables) { processBuilder.SetPassthroughEnvironmentVariable(StringId.Create(m_context.StringTable, passThroughVariable)); } } }
/// <summary> /// Creates arguments. /// </summary> protected PipData CreateArguments( IEnumerable <Operation> processOperations, StringTable stringTable) { var pipDataBuilder = new PipDataBuilder(stringTable); CreateArguments(pipDataBuilder, processOperations, stringTable); return(pipDataBuilder.ToPipData(" ", PipDataFragmentEscaping.CRuntimeArgumentRules)); }
private void SetEnvironmentVariables(ProcessBuilder processBuilder, NinjaNode node) { foreach (KeyValuePair <string, string> kvp in m_environmentVariables.Value) { if (kvp.Value != null) { var envPipData = new PipDataBuilder(m_context.StringTable); if (SpecialEnvironmentVariables.PassThroughPrefixes.All(prefix => !kvp.Key.StartsWith(prefix))) { envPipData.Add(kvp.Value); processBuilder.SetEnvironmentVariable(StringId.Create(m_context.StringTable, kvp.Key), envPipData.ToPipData(string.Empty, PipDataFragmentEscaping.NoEscaping)); } } } foreach (var kvp in m_passThroughEnvironmentVariables.Value) { processBuilder.SetPassthroughEnvironmentVariable(StringId.Create(m_context.StringTable, kvp.Key)); } foreach (var envVar in SpecialEnvironmentVariables.CloudBuildEnvironment) { processBuilder.SetPassthroughEnvironmentVariable(StringId.Create(m_context.StringTable, envVar)); } // GlobalUnsafePassthroughEnvironmentVariables processBuilder.SetGlobalPassthroughEnvironmentVariable(m_frontEndHost.Configuration.FrontEnd.GlobalUnsafePassthroughEnvironmentVariables, m_context.StringTable); // We will specify a different MSPDBSRV endpoint for every pip. // This means every pip that needs to communicate to MSPDBSRV will // spawn a different child process. // This is because if two pips use the same endpoint at the same time // then second one will fail after the first one finishes, because the // first one was the one that spawned MSPDBSRV.EXE as a child // (which gets killed). // // IMPORTANT: This will cause the build to fail if two pips target the same PDB file. // Both LINK.EXE and CL.EXE can use MSPDBSRV.EXE: // - If all linkers specify a different /pdb argument (or don't specify this argument and // are linking programs with different names // [https://docs.microsoft.com/en-us/cpp/build/reference/debug-generate-debug-info]) // then this won't happen // // - We're forcing the compiler to not output to PDBs (/Z7) // // so this should work in the normal cases. var mspdbsrvPipDataBuilder = new PipDataBuilder(m_context.StringTable); mspdbsrvPipDataBuilder.Add(PipConstructionUtilities.ComputeSha256(node.Command)); // Unique value for each pip processBuilder.SetEnvironmentVariable( StringId.Create(m_context.StringTable, SpecialEnvironmentVariables.MsPdvSrvEndpoint), mspdbsrvPipDataBuilder.ToPipData(string.Empty, PipDataFragmentEscaping.NoEscaping)); }
public FileArtifact WriteFile(AbsolutePath destination, PipDataAtom content, WriteFileEncoding?encoding = null) { PipDataBuilder pipDataBuilder = new PipDataBuilder(Context.StringTable); pipDataBuilder.Add(content); if (!PipConstructionHelper.TryWriteFile( destination, pipDataBuilder.ToPipData(Context.StringTable.Empty, PipDataFragmentEscaping.NoEscaping), encoding ?? WriteFileEncoding.Utf8, null, null, out var result)) { throw new BuildXLTestException("Failed to add writefile pip"); } return(result); }
/// <summary> /// Configure the environment for a process /// </summary> public static void SetProcessEnvironmentVariables( IReadOnlyDictionary <string, string> userDefinedEnvironment, [CanBeNull] IEnumerable <string> userDefinedPassthroughVariables, ProcessBuilder processBuilder, PathTable pathTable) { Contract.RequiresNotNull(userDefinedEnvironment); Contract.RequiresNotNull(processBuilder); foreach (KeyValuePair <string, string> kvp in userDefinedEnvironment) { if (kvp.Value != null) { var envPipData = new PipDataBuilder(pathTable.StringTable); // Casing for paths is not stable as reported by BuildPrediction. So here we try to guess if the value // represents a path, and normalize it string value = kvp.Value; if (!string.IsNullOrEmpty(value) && AbsolutePath.TryCreate(pathTable, value, out var absolutePath)) { envPipData.Add(absolutePath); } else { envPipData.Add(value); } processBuilder.SetEnvironmentVariable( StringId.Create(pathTable.StringTable, kvp.Key), envPipData.ToPipData(string.Empty, PipDataFragmentEscaping.NoEscaping)); } } if (userDefinedPassthroughVariables != null) { foreach (string passThroughVariable in userDefinedPassthroughVariables) { processBuilder.SetPassthroughEnvironmentVariable(StringId.Create(pathTable.StringTable, passThroughVariable)); } } }
private PerProcessPipPerformanceInformation CreateSamplePip(int index) { Func <RunnablePip, Task <PipResult> > taskFactory = async(runnablePip) => { PipResult result; var operationTracker = new OperationTracker(runnablePip.LoggingContext); var pip = runnablePip.Pip; using (var operationContext = operationTracker.StartOperation(PipExecutorCounter.PipRunningStateDuration, pip.PipId, pip.PipType, runnablePip.LoggingContext)) { result = await TestPipExecutor.ExecuteAsync(operationContext, m_executionEnvironment, pip); } return(result); }; var pathTable = m_context.PathTable; var executable = FileArtifact.CreateSourceFile(AbsolutePath.Create(pathTable, X("/x/pkgs/tool.exe"))); var dependencies = new HashSet <FileArtifact> { executable }; var processBuilder = new ProcessBuilder() .WithExecutable(executable) .WithWorkingDirectory(AbsolutePath.Create(pathTable, X("/x/obj/working"))) .WithArguments(PipDataBuilder.CreatePipData(pathTable.StringTable, " ", PipDataFragmentEscaping.CRuntimeArgumentRules, "-loadargs")) .WithStandardDirectory(AbsolutePath.Create(pathTable, X("/x/obj/working.std"))) .WithDependencies(dependencies) .WithContext(m_context); var dataBuilder = new PipDataBuilder(m_context.PathTable.StringTable); var pipData = dataBuilder.ToPipData(" ", PipDataFragmentEscaping.NoEscaping); var pip = processBuilder.WithArguments(pipData).Build(); var pipId = m_executionEnvironment.PipTable.Add((uint)(index + 1), pip); var runnableProcessPip = (ProcessRunnablePip)(RunnablePip.Create(m_loggingContext, m_executionEnvironment, pipId, PipType.Process, 0, taskFactory, 0)); m_runnablePips.Add(index, runnableProcessPip); // For verification return(GeneratePipInfoWithRunnablePipAndIndex(ref runnableProcessPip, index)); }
/// <summary> /// Creates the PipData from the given object /// </summary> public static PipData CreatePipData(StringTable stringTable, ObjectLiteral obj, string quoteChar, PipDataBuilder pipDataBuilder) { using (var stringBuilderWrapper = Pools.StringBuilderPool.GetInstance()) { var stringBuilder = stringBuilderWrapper.Instance; using (var textWriter = new StringWriter(stringBuilder)) using (var jsonWriter = new JsonTextWriter(textWriter)) { // textWriter.NewLine = "\n"; // Note, we do NOT Force consisten newline endings across platforms. // After talking with the Mac team, it seems there is no hope we'll ever have shared cache entries between them. // As a feature we might at some point allow the pip author to parametrise this (as well as the path separators used) but keeping it simple for now. jsonWriter.Culture = CultureInfo.InvariantCulture; jsonWriter.Formatting = Formatting.Indented; jsonWriter.Indentation = 2; // The JSON standard mandates the double quote as the conform quotation symbol with single quotes // only being used in custom implementations by consumers, allow both if (quoteChar != "\'" && quoteChar != "\"") { return(PipData.Invalid); } jsonWriter.IndentChar = ' '; jsonWriter.QuoteChar = Convert.ToChar(quoteChar); jsonWriter.AutoCompleteOnClose = false; var jsonContext = new JsonWritingContext(stringTable, pipDataBuilder, stringBuilderWrapper.Instance, jsonWriter); if (!WriteObject(obj, in jsonContext)) { return(PipData.Invalid); } FlushJsonToPipBuilder(in jsonContext); return(pipDataBuilder.ToPipData(string.Empty, PipDataFragmentEscaping.NoEscaping)); } } }
/// <summary> /// Gets the command line arguments for the process. /// </summary> public PipData GetArgumentsDataFromProcess(Process process) { PipData arguments = process.Arguments; if (process.ResponseFile.IsValid) { var responseFileData = process.ResponseFileData; PipDataBuilder pipDataBuilder = new PipDataBuilder(StringTable); // Add all the arguments from the command line excluding the response file (the last fragment) foreach (var fragment in process.Arguments.Take(process.Arguments.FragmentCount - 1).Concat(responseFileData)) { Contract.Assume(fragment.FragmentType != PipFragmentType.Invalid); pipDataBuilder.Add(fragment); } arguments = pipDataBuilder.ToPipData(arguments.FragmentSeparator, arguments.FragmentEscaping); } return(arguments); }
public static Process CreateDummyProcessWithInputs(IEnumerable <FileArtifact> inputs, BuildXLContext context) { FileArtifact output = FileArtifact.CreateSourceFile(AbsolutePath.Create(context.PathTable, @"\\FAKEPATH\output" + s_procCounter + ".txt")).CreateNextWrittenVersion(); s_procCounter++; FileArtifact exe = FileArtifact.CreateSourceFile(AbsolutePath.Create(context.PathTable, @"\\FAKEPATH\tool.exe")); var pipDataBuilder = new PipDataBuilder(context.StringTable); return(new Process( executable: exe, workingDirectory: AbsolutePath.Create(context.PathTable, @"\\FAKEPATH\"), arguments: pipDataBuilder.ToPipData(" ", PipDataFragmentEscaping.NoEscaping), responseFile: FileArtifact.Invalid, responseFileData: PipData.Invalid, environmentVariables: ReadOnlyArray <EnvironmentVariable> .Empty, standardInput: FileArtifact.Invalid, standardOutput: FileArtifact.Invalid, standardError: FileArtifact.Invalid, standardDirectory: output.Path.GetParent(context.PathTable), warningTimeout: null, timeout: null, dependencies: ReadOnlyArray <FileArtifact> .From(inputs.Union(new[] { exe })), outputs: ReadOnlyArray <FileArtifactWithAttributes> .FromWithoutCopy(output.WithAttributes()), directoryDependencies: ReadOnlyArray <DirectoryArtifact> .Empty, directoryOutputs: ReadOnlyArray <DirectoryArtifact> .Empty, orderDependencies: ReadOnlyArray <PipId> .Empty, untrackedPaths: ReadOnlyArray <AbsolutePath> .Empty, untrackedScopes: ReadOnlyArray <AbsolutePath> .Empty, tags: ReadOnlyArray <StringId> .Empty, successExitCodes: ReadOnlyArray <int> .Empty, semaphores: ReadOnlyArray <ProcessSemaphoreInfo> .Empty, provenance: PipProvenance.CreateDummy(context), toolDescription: StringId.Invalid, additionalTempDirectories: ReadOnlyArray <AbsolutePath> .Empty)); }
public void PipDataReadWrite() { var pipData = m_pipDataBuilder.ToPipData(EnclosingSeparator, EnclosingEscaping); byte[] bytes = new byte[10000]; int startIndex = 23; int index = startIndex; foreach (var entry in pipData.Entries) { var expectedIndex = index + PipDataEntry.BinarySize; entry.Write(bytes, ref index); // Ensure the correct number of bytes are written Assert.Equal(expectedIndex, index); } var endIndex = index; index = startIndex; List <PipDataEntry> readEntries = new List <PipDataEntry>(); while (index < endIndex) { var expectedIndex = index + PipDataEntry.BinarySize; readEntries.Add(PipDataEntry.Read(bytes, ref index)); // Ensure the correct number of bytes are read Assert.Equal(expectedIndex, index); } var readPipData = PipData.CreateInternal(pipData.HeaderEntry, PipDataEntryList.FromEntries(readEntries), StringId.Invalid); VerifyFullPipData(readPipData); }
private static Process CreateConsoleProcessInContainer( BuildXLContext context, TempFileStorage tempFiles, PathTable pt, string arguments, ReadOnlyArray <FileArtifactWithAttributes> outputFiles, ReadOnlyArray <DirectoryArtifact> directoryOutputs, ContainerIsolationLevel containerIsolationLevel = ContainerIsolationLevel.IsolateAllOutputs) { var executableFileArtifact = FileArtifact.CreateSourceFile(AbsolutePath.Create(context.PathTable, CmdHelper.CmdX64)); var argumentBuilder = new PipDataBuilder(context.PathTable.StringTable); argumentBuilder.Add("/d"); argumentBuilder.Add("/c"); using (argumentBuilder.StartFragment(PipDataFragmentEscaping.CRuntimeArgumentRules, " ")) { foreach (var arg in arguments.Split(new[] { ' ' }, System.StringSplitOptions.RemoveEmptyEntries)) { argumentBuilder.Add(arg); } } string workingDirectory = tempFiles.GetUniqueDirectory(); var workingDirectoryAbsolutePath = AbsolutePath.Create(context.PathTable, workingDirectory); string uniqueOutputDirectory = tempFiles.GetUniqueDirectory(); var uniqueOutputDirectoryPath = AbsolutePath.Create(context.PathTable, uniqueOutputDirectory); string uniqueRedirectedOutputDirectory = tempFiles.GetUniqueDirectory("redirected"); var uniqueRedirectedOutputDirectoryPath = AbsolutePath.Create(context.PathTable, uniqueRedirectedOutputDirectory); var pip = new Process( executableFileArtifact, workingDirectoryAbsolutePath, argumentBuilder.ToPipData(" ", PipDataFragmentEscaping.NoEscaping), FileArtifact.Invalid, PipData.Invalid, ReadOnlyArray <EnvironmentVariable> .FromWithoutCopy(), FileArtifact.Invalid, FileArtifact.Invalid, FileArtifact.Invalid, tempFiles.GetUniqueDirectory(pt), null, null, dependencies: ReadOnlyArray <FileArtifact> .FromWithoutCopy(new[] { executableFileArtifact }), outputs: outputFiles, directoryDependencies: ReadOnlyArray <DirectoryArtifact> .Empty, directoryOutputs: directoryOutputs, orderDependencies: ReadOnlyArray <PipId> .Empty, untrackedPaths: ReadOnlyArray <AbsolutePath> .Empty, untrackedScopes: ReadOnlyArray <AbsolutePath> .Empty, tags: ReadOnlyArray <StringId> .Empty, successExitCodes: ReadOnlyArray <int> .Empty, semaphores: ReadOnlyArray <ProcessSemaphoreInfo> .Empty, provenance: PipProvenance.CreateDummy(context), toolDescription: StringId.Invalid, additionalTempDirectories: ReadOnlyArray <AbsolutePath> .Empty, options: Process.Options.NeedsToRunInContainer, uniqueOutputDirectory: uniqueOutputDirectoryPath, uniqueRedirectedDirectoryRoot: uniqueRedirectedOutputDirectoryPath, containerIsolationLevel: containerIsolationLevel); return(pip); }
public void ExecutionProcessReadingStdIn() { FileArtifact stdOut = CreateOutputFileArtifact(); ProcessBuilder builder = CreatePipBuilder(new[] { Operation.ReadStdIn() }); PipDataBuilder dataBuilder = new PipDataBuilder(Context.PathTable.StringTable); dataBuilder.Add("Data0"); dataBuilder.Add("Data1"); dataBuilder.Add("Data2"); builder.StandardInput = global::BuildXL.Pips.StandardInput.CreateFromData(dataBuilder.ToPipData(Environment.NewLine, PipDataFragmentEscaping.NoEscaping)); builder.SetStandardOutputFile(stdOut.Path); builder.Options |= Process.Options.RequiresAdmin; ProcessWithOutputs process = SchedulePipBuilder(builder); RunScheduler().AssertSuccess(); string[] output = File.ReadAllLines(ArtifactToString(stdOut)); string actualContent = string.Join(Environment.NewLine, output); XAssert.AreEqual(3, output.Length, "Actual content: {0}{1}", Environment.NewLine, string.Join(Environment.NewLine, output)); for (int i = 0; i < 3; ++i) { XAssert.AreEqual("Data" + i, output[i], "Actual content: {0}", output[i]); } }
private async Task ProcessWindowsCallHelper( string functionName, SandboxConfiguration config = null, IEnumerable <string> extraDependencies = null, IEnumerable <string> extraOutputs = null, int callCount = 1, string commandPrefix = "", bool readsAndWritesDirectories = false, bool untrackedOutputs = false, string[] expectedWarningStrings = null, string[] expectedErrorStrings = null) { if (config == null) { config = new SandboxConfiguration { FileAccessIgnoreCodeCoverage = true, FailUnexpectedFileAccesses = true }; } var context = BuildXLContext.CreateInstanceForTesting(); var pathTable = context.PathTable; var fileContentTable = FileContentTable.CreateNew(LoggingContext); // have to force the config for truncation config.OutputReportingMode = OutputReportingMode.FullOutputOnWarningOrError; bool expectSuccess = expectedErrorStrings == null && expectedWarningStrings == null; using (var tempFiles = new TempFileStorage(canGetFileNames: true, rootPath: TemporaryDirectory)) { string currentCodeFolder = Path.GetDirectoryName(AssemblyHelper.GetAssemblyLocation(Assembly.GetExecutingAssembly())); Contract.Assume(currentCodeFolder != null); string executable = Path.Combine(currentCodeFolder, DetourTestFolder, "DetoursTests.exe"); string workingDirectory = tempFiles.GetUniqueDirectory(); AbsolutePath workingDirectoryAbsolutePath = AbsolutePath.Create(pathTable, workingDirectory); XAssert.IsTrue(File.Exists(executable), "Could not find the test file: " + executable); FileArtifact executableFileArtifact = FileArtifact.CreateSourceFile(AbsolutePath.Create(pathTable, executable)); var extraUntrackedScopes = new List <AbsolutePath>(); var dependencies = new List <FileArtifact> { executableFileArtifact }; if (extraDependencies != null) { foreach (string file in extraDependencies) { string filePath = Path.Combine(workingDirectory, file); AbsolutePath path = AbsolutePath.Create(pathTable, filePath); if (readsAndWritesDirectories) { Directory.CreateDirectory(filePath); // We don't support directories as inputs in BuildXL yet. extraUntrackedScopes.Add(path); } else { File.WriteAllText(filePath, "Definitely a file"); FileArtifact fileArtifact = FileArtifact.CreateSourceFile(path); dependencies.Add(fileArtifact); } } } var outputs = new List <FileArtifactWithAttributes>(); if (extraOutputs != null) { foreach (string file in extraOutputs) { string filePath = Path.Combine(workingDirectory, file); AbsolutePath path = AbsolutePath.Create(pathTable, filePath); if (readsAndWritesDirectories) { // We don't support directory outputs in BuildXL at the moment, so e.g. deleting a directory needs to be untracked. extraUntrackedScopes.Add(path); } else if (untrackedOutputs) { extraUntrackedScopes.Add(path); } else { FileArtifact fileArtifact = FileArtifact.CreateSourceFile(path).CreateNextWrittenVersion(); outputs.Add(fileArtifact.WithAttributes()); } } } var tempDirectory = tempFiles.GetUniqueDirectory(); var environmentVariables = new List <EnvironmentVariable>(); var environmentValue = new PipDataBuilder(pathTable.StringTable); var tempPath = AbsolutePath.Create(pathTable, tempDirectory); environmentValue.Add(tempPath); environmentVariables.Add(new EnvironmentVariable(StringId.Create(pathTable.StringTable, "TMP"), environmentValue.ToPipData(" ", PipDataFragmentEscaping.NoEscaping))); environmentVariables.Add(new EnvironmentVariable(StringId.Create(pathTable.StringTable, "TEMP"), environmentValue.ToPipData(" ", PipDataFragmentEscaping.NoEscaping))); var untrackedPaths = CmdHelper.GetCmdDependencies(pathTable); var untrackedScopes = extraUntrackedScopes.Concat(CmdHelper.GetCmdDependencyScopes(pathTable).Concat(new[] { tempPath })).Distinct(); var pip = new Process( executableFileArtifact, workingDirectoryAbsolutePath, PipDataBuilder.CreatePipData(pathTable.StringTable, " ", PipDataFragmentEscaping.NoEscaping, commandPrefix + functionName + "Logging"), FileArtifact.Invalid, PipData.Invalid, ReadOnlyArray <EnvironmentVariable> .From(environmentVariables), FileArtifact.Invalid, FileArtifact.Invalid, FileArtifact.Invalid, tempFiles.GetUniqueDirectory(pathTable), null, null, ReadOnlyArray <FileArtifact> .From(dependencies), ReadOnlyArray <FileArtifactWithAttributes> .From(outputs), ReadOnlyArray <DirectoryArtifact> .Empty, ReadOnlyArray <DirectoryArtifact> .Empty, ReadOnlyArray <PipId> .Empty, ReadOnlyArray <AbsolutePath> .From(untrackedPaths), ReadOnlyArray <AbsolutePath> .From(untrackedScopes), ReadOnlyArray <StringId> .Empty, ReadOnlyArray <int> .Empty, ReadOnlyArray <ProcessSemaphoreInfo> .Empty, provenance: PipProvenance.CreateDummy(context), toolDescription: StringId.Invalid, additionalTempDirectories: ReadOnlyArray <AbsolutePath> .Empty); if (expectSuccess) { await AssertProcessSucceedsAsync( context, config, pip); } else { await AssertProcessCompletesWithStatusAsync( SandboxedProcessPipExecutionStatus.ExecutionFailed, context, config, pip, null); } } int expectedErrorCount = 0; int expectedWarningCount = 0; IEnumerable <string> requiredLogMessageSubstrings = new string[] { }; if (expectedErrorStrings != null) { expectedErrorCount = expectedErrorStrings.Count(); requiredLogMessageSubstrings = requiredLogMessageSubstrings.Concat(expectedErrorStrings); } if (expectedWarningStrings != null) { expectedWarningCount = expectedWarningStrings.Count(); requiredLogMessageSubstrings = requiredLogMessageSubstrings.Concat(expectedWarningStrings); } SetExpectedFailures(expectedErrorCount, expectedWarningCount, requiredLogMessageSubstrings.ToArray()); }
/// <summary> /// Completes this instance of <see cref="ArgumentsDataBuilder"/>. /// </summary> public PipData Finish() { m_finished = true; return(m_builder.ToPipData(" ", PipDataFragmentEscaping.CRuntimeArgumentRules)); }
/// <summary> /// Creates arguments. /// </summary> internal static PipData CreateCmdArguments( StringTable stringTable, IEnumerable <FileArtifact> dependencies, IEnumerable <FileArtifact> outputs, bool includeWarning = false) { Contract.Requires(dependencies != null, "Argument dependencies cannot be null"); Contract.Requires(outputs != null, "Argument outputs cannot be null"); var pipDataBuilder = new PipDataBuilder(stringTable); int i = 0; pipDataBuilder.Add("/d"); pipDataBuilder.Add("/c"); foreach (FileArtifact output in outputs) { if (i > 0) { pipDataBuilder.Add("&"); } using (pipDataBuilder.StartFragment(PipDataFragmentEscaping.CRuntimeArgumentRules, " ")) { var allDependencies = dependencies.ToList(); pipDataBuilder.Add("type"); foreach (FileArtifact dependency in allDependencies) { pipDataBuilder.Add(dependency); } pipDataBuilder.Add(">"); pipDataBuilder.Add(output); } pipDataBuilder.Add("&"); using (pipDataBuilder.StartFragment(PipDataFragmentEscaping.CRuntimeArgumentRules, " ")) { pipDataBuilder.Add("echo"); pipDataBuilder.Add("buildxl"); pipDataBuilder.Add(">>"); pipDataBuilder.Add(output); } i++; } if (includeWarning) { if (outputs.Any()) { pipDataBuilder.Add("&&"); } using (pipDataBuilder.StartFragment(PipDataFragmentEscaping.CRuntimeArgumentRules, " ")) { pipDataBuilder.Add("echo"); pipDataBuilder.Add(WarningRegexDescription); } } return(pipDataBuilder.ToPipData(" ", PipDataFragmentEscaping.CRuntimeArgumentRules)); }