private Possible <Unit> TryReportInputs(List <FileArtifactKeyedHash> hashes) { var dynamicDirectoryMap = new Dictionary <DirectoryArtifact, List <FileArtifact> >(); var failedFiles = new List <(FileArtifact file, ContentHash hash)>(); var fileContentManager = m_environment.State.FileContentManager; foreach (FileArtifactKeyedHash fileArtifactKeyedHash in hashes) { FileArtifact file; if (fileArtifactKeyedHash.AssociatedDirectories != null && fileArtifactKeyedHash.AssociatedDirectories.Count != 0) { // All workers have the same entries in the path table up to the schedule phase. Dynamic outputs add entries to the path table of the worker that generates them. // Dynamic outputs can be generated by one worker, but consumed by another, which potentially is not the master. Thus, the two workers do not have the same entries // in the path table. // The approach we have here may be sub-optimal(the worker may have had the paths already). But it ensures correctness. // Need to create absolute path because the path is potentially not in the path table. file = new FileArtifact( AbsolutePath.Create(m_environment.Context.PathTable, fileArtifactKeyedHash.PathString), fileArtifactKeyedHash.RewriteCount); foreach (var bondDirectoryArtifact in fileArtifactKeyedHash.AssociatedDirectories) { var directory = new DirectoryArtifact( new AbsolutePath(bondDirectoryArtifact.DirectoryPathValue), bondDirectoryArtifact.DirectorySealId, bondDirectoryArtifact.IsDirectorySharedOpaque); if (!dynamicDirectoryMap.TryGetValue(directory, out var files)) { files = new List <FileArtifact>(); dynamicDirectoryMap.Add(directory, files); } files.Add(file); } } else { file = fileArtifactKeyedHash.File; } if (fileArtifactKeyedHash.IsSourceAffected) { fileContentManager.SourceChangeAffectedInputs.ReportSourceChangedAffectedFile(file.Path); } var materializationInfo = fileArtifactKeyedHash.GetFileMaterializationInfo(m_environment.Context.PathTable); if (!fileContentManager.ReportWorkerPipInputContent( m_appLoggingContext, file, materializationInfo)) { failedFiles.Add((file, materializationInfo.Hash)); } } foreach (var directoryAndContents in dynamicDirectoryMap) { fileContentManager.ReportDynamicDirectoryContents(directoryAndContents.Key, directoryAndContents.Value, PipOutputOrigin.NotMaterialized); } if (failedFiles.Count != 0) { return(new ArtifactMaterializationFailure(failedFiles.ToReadOnlyArray(), m_environment.Context.PathTable)); } return(Unit.Void); }
/*** COMMAND LINE PARSING FUNCTION ***/ /// <summary> /// Converts command line arg input to process ops /// Input format: "OpType?Path?Content?LinkPath?SymLinkFlag" /// </summary> public static Operation CreateFromCommandLine(PathTable pathTable, string commandLineArg) { var opArgs = commandLineArg.Split(OperationArgsDelimiter); if (opArgs.Length != NumArgsExpected) { throw new ArgumentException( string.Format(System.Globalization.CultureInfo.CurrentCulture, "An input argument (or delimiter) is missing. Expected {0} arguments, but received {1} arguments. Valid format is: OpType?Path?Content?LinkPath?SymLinkFlag. Raw command line is {2}", NumArgsExpected, opArgs.Length, commandLineArg)); } Type opType; AbsolutePath absolutePath = AbsolutePath.Invalid; SymbolicLinkFlag symLinkFlag; AbsolutePath absoluteLinkPath = AbsolutePath.Invalid; if (!Enum.TryParse <Type>(opArgs[0], out opType)) { throw new InvalidCastException("A malformed or invalid input argument was passed for the Operation type enum."); } if (opArgs[1].Length != 0 && !AbsolutePath.TryCreate(pathTable, opArgs[1], out absolutePath)) { throw new InvalidCastException("A malformed or invalid input argument was passed for the Operation path. Could not convert to absolute path."); } // This might be a directory, but since the running executable only cares about the string path, // arbitrarily choose to store it in a FileArtifact FileArtifact?path = null; if (absolutePath.IsValid) { path = new FileArtifact(absolutePath); } string content = opArgs[2].Length == 0 ? null : opArgs[2]; if (opArgs[3].Length != 0 && !AbsolutePath.TryCreate(pathTable, opArgs[3], out absoluteLinkPath)) { throw new InvalidCastException("A malformed or invalid input argument was passed for the Operation link path. Could not convert to absolute path."); } // This might be a directory, but since the running executable only cares about the string path, // arbitrarily choose to store it in a FileArtifact FileArtifact?linkPath = null; if (absoluteLinkPath.IsValid) { linkPath = new FileArtifact(absoluteLinkPath); } if (!Enum.TryParse <SymbolicLinkFlag>(opArgs[4], out symLinkFlag)) { throw new InvalidCastException("A malformed or invalid input argument was passed for the Operation symbolic link flag enum."); } string additionalArgs = opArgs[5].Length == 0 ? null : opArgs[5]; var op = new Operation(opType, path, content, linkPath, symLinkFlag) { PathTable = pathTable, AdditionalArgs = additionalArgs }; return(op); }
private MSBuildProjectOutputs(IEnumerable <StaticDirectory> outputDirectories, FileArtifact outputCacheFile) { OutputDirectories = outputDirectories; OutputCacheFile = outputCacheFile; }
/// <summary> /// <see cref="WriteFile"/>, with the option to retry writing to the file a specified number of times /// </summary> public static Operation WriteFileWithRetries(FileArtifact path, string content = null, bool doNotInfer = false, int retries = 5) { Contract.Requires(retries >= 0); return(new Operation(Type.WriteFileWithRetries, path, content, doNotInfer: doNotInfer, retriesOnWrite: retries)); }
/// <summary> /// Creates a copy file operation /// </summary> public static Operation CopyFile(FileArtifact srcPath, FileArtifact destPath, bool doNotInfer = false) { return(new Operation(Type.CopyFile, path: srcPath, linkPath: destPath, doNotInfer: doNotInfer)); }
/// <summary> /// Creates a write file operation that appends. The file is created if it does not exist. /// Writes random content to file at path if no content is specified. /// </summary> public static Operation WriteFile(FileArtifact path, string content = null, bool doNotInfer = false) { return(content == Environment.NewLine ? new Operation(Type.AppendNewLine, path, doNotInfer: doNotInfer) : new Operation(Type.WriteFile, path, content, doNotInfer: doNotInfer)); }
/// <summary> /// Creates a delete file operation /// </summary> public static Operation DeleteFile(FileArtifact path, bool doNotInfer = false) { return(new Operation(Type.DeleteFile, path, content: null, doNotInfer: doNotInfer)); }
public void TestAddingAndUnifyingIpcPip() { var fragment = CreatePipGraphFragmentTest(nameof(TestAddingAndUnifyingIpcPip)); (IIpcMoniker moniker, PipId servicePipId) = TestPipGraphFragmentUtils.CreateService(fragment); var processBuilder = fragment.GetProcessBuilder(); var argumentsBuilder = new ArgumentsBuilder(processBuilder); FileArtifact outputFileToVerify = fragment.CreateOutputFile("g"); AbsolutePath outputDirectoryToVerify = fragment.CreateOutputDirectory("d").Path; argumentsBuilder .AddInputFileOption("/input:", fragment.CreateSourceFile("f")) .AddOutputFileOption("/output:", outputFileToVerify.Path) .AddOutputDirectoryOption("/outputDir:", outputDirectoryToVerify) .Finish(); (Process process, ProcessOutputs processOutputs) = fragment.ScheduleProcessBuilder(processBuilder); XAssert.IsTrue(processOutputs.TryGetOutputDirectory(outputDirectoryToVerify, out var outputDirectory)); var addFileProcessBuilder = fragment.GetIpcProcessBuilder(); new ArgumentsBuilder(addFileProcessBuilder) .AddStringOption("--command ", "addFile") .AddIpcMonikerOption("--ipcMoniker ", moniker) .AddInputFileOption("--file ", outputFileToVerify) .AddInputDirectoryOption("--directory ", outputDirectory.Root) .AddFileIdOption("--fileId ", outputFileToVerify) .AddDirectoryIdOption("--directoryId ", outputDirectory.Root) .AddVsoHashOption("--vsoHash ", outputFileToVerify) .Finish(); FileArtifact ipcOutputFileToVerify; IpcPip ipcPip = fragment.ScheduleIpcPip( moniker, servicePipId, addFileProcessBuilder, ipcOutputFileToVerify = fragment.CreateOutputFile("add"), false); var graph = SerializeAndDeserializeIndependentFragments(fragment, fragment); VerifyGraphSuccessfullyConstructed(graph); VerifyProducerExists(graph, fragment, outputFileToVerify.Path); VerifyProducerExists(graph, fragment, outputDirectoryToVerify); VerifyProducerExists(graph, fragment, ipcOutputFileToVerify); var remappedOutputFile = FileArtifact.CreateOutputFile(RemapFragmentPath(fragment, outputFileToVerify.Path)); var remappedOutputDirectory = DirectoryArtifact.CreateWithZeroPartialSealId(RemapFragmentPath(fragment, outputDirectory.Root)); PipData expectedArguments = new ArgumentsDataBuilder(Context.StringTable) .AddStringOption("--command ", "addFile") .AddIpcMonikerOption("--ipcMoniker ", moniker) .AddPathOption("--file ", remappedOutputFile.Path) .AddPathOption("--directory ", remappedOutputDirectory.Path) .AddFileIdOption("--fileId ", remappedOutputFile) .AddDirectoryIdOption("--directoryId ", remappedOutputDirectory) .AddVsoHashOption("--vsoHash ", remappedOutputFile) .Finish(); VerifyResultingArguments(graph, fragment, ipcOutputFileToVerify, expectedArguments); }
/// <summary> /// Verifies the resulting arguments after full graph construction. /// </summary> /// <param name="graph">Resulting graph.</param> /// <param name="fragmentOrigin">Graph fragment where the arguments are constructed.</param> /// <param name="outputInFragmentOrigin">Output file to identify pip.</param> /// <param name="expectedArguments">Expected arguments.</param> private void VerifyResultingArguments(PipGraph graph, TestPipGraphFragment fragmentOrigin, FileArtifact outputInFragmentOrigin, PipData expectedArguments) { var pipId = graph.TryGetProducer(FileArtifact.CreateOutputFile(RemapFragmentPath(fragmentOrigin, outputInFragmentOrigin))); XAssert.IsTrue(pipId.IsValid); Pip pip = graph.PipTable.HydratePip(pipId, PipQueryContext.PipGraphGetProducingPip); XAssert.IsNotNull(pip); PipData actualArguments = PipData.Invalid; if (pip is Process process) { actualArguments = process.Arguments; } else if (pip is IpcPip ipcPip) { actualArguments = ipcPip.MessageBody; } else { XAssert.Fail("No arguments associated with pip"); } string expected = expectedArguments.ToString(Context.PathTable).ToUpperInvariant(); string actual = actualArguments.ToString(Context.PathTable).ToUpperInvariant(); XAssert.AreEqual(expected, actual); }
private bool TryScheduleIpcPip(Context context, ObjectLiteral obj, bool allowUndefinedTargetService, bool isServiceFinalization, out FileArtifact outputFile, out PipId pipId) { // IpcClientInfo IIpcMoniker moniker = Converter.ExtractRef <IIpcMoniker>(obj, m_ipcSendMoniker, allowUndefined: false); int? numRetries = Converter.ExtractNumber(obj, m_ipcSendMaxConnectRetries, allowUndefined: true); int? retryDelayMillis = Converter.ExtractNumber(obj, m_ipcSendConnectRetryDelayMillis, allowUndefined: true); var clientConfig = new ClientConfig(numRetries, retryDelayMillis); var ipcClientInfo = new IpcClientInfo(moniker.ToStringId(context.StringTable), clientConfig); // target service pip PipId?servicePipId = Converter.ExtractValue <PipId>(obj, m_ipcSendTargetServicePip, allowUndefined: allowUndefinedTargetService); // arguments PipData arguments; ReadOnlyArray <FileArtifact> fileDependencies; ReadOnlyArray <DirectoryArtifact> directoryDependencies; using (var ipcProcessBuilder = ProcessBuilder.Create(context.PathTable, context.FrontEndContext.GetPipDataBuilder())) { // process arguments ArrayLiteral argumentsArrayLiteral = Converter.ExtractArrayLiteral(obj, m_ipcSendMessageBody); TransformerExecuteArgumentsProcessor.ProcessArguments(context, ipcProcessBuilder, argumentsArrayLiteral); // input file dependencies var dependenciesArray = Converter.ExtractArrayLiteral(obj, m_ipcSendDependencies, allowUndefined: true); if (dependenciesArray != null) { for (int i = 0; i < dependenciesArray.Length; i++) { ProcessImplicitDependency(ipcProcessBuilder, dependenciesArray[i], convContext: new ConversionContext(pos: i, objectCtx: dependenciesArray)); } } arguments = ipcProcessBuilder.ArgumentsBuilder.ToPipData(" ", PipDataFragmentEscaping.CRuntimeArgumentRules); fileDependencies = ipcProcessBuilder.GetInputFilesSoFar(); directoryDependencies = ipcProcessBuilder.GetInputDirectoriesSoFar(); } // output AbsolutePath output = Converter.ExtractPath(obj, m_ipcSendOutputFile, allowUndefined: true); if (!output.IsValid) { output = context.GetPipConstructionHelper().GetUniqueObjectDirectory(m_ipcObjectFolderName).Path.Combine(context.PathTable, m_ipcOutputFileName); } // tags string[] tags = null; var tagsArray = Converter.ExtractArrayLiteral(obj, m_executeTags, allowUndefined: true); if (tagsArray != null && tagsArray.Count > 0) { tags = new string[tagsArray.Count]; for (int i = 0; i < tagsArray.Count; i++) { tags[i] = Converter.ExpectString(tagsArray[i], context: new ConversionContext(pos: i, objectCtx: tagsArray)); } } // skip materialization for files FileOrDirectoryArtifact[] skipMaterializationArtifacts = CollectionUtilities.EmptyArray <FileOrDirectoryArtifact>(); ArrayLiteral skipMaterializationLiteral = Converter.ExtractArrayLiteral(obj, m_ipcSendLazilyMaterializedDependencies, allowUndefined: true); if (skipMaterializationLiteral != null) { skipMaterializationArtifacts = new FileOrDirectoryArtifact[skipMaterializationLiteral.Length]; for (int i = 0; i < skipMaterializationLiteral.Length; i++) { Converter.ExpectFileOrStaticDirectory( skipMaterializationLiteral[i], out var fileArtifact, out var staticDirectory, context: new ConversionContext(pos: i, objectCtx: skipMaterializationLiteral)); Contract.Assert(fileArtifact.IsValid ^ staticDirectory != null); skipMaterializationArtifacts[i] = fileArtifact.IsValid ? FileOrDirectoryArtifact.Create(fileArtifact) : FileOrDirectoryArtifact.Create(staticDirectory.Root); } } // must run on master var mustRunOnMaster = Converter.ExtractOptionalBoolean(obj, m_ipcSendMustRunOnMaster) == true; outputFile = FileArtifact.CreateOutputFile(output); // create IPC pip and add it to the graph bool result = context.GetPipConstructionHelper().TryAddIpc( ipcClientInfo, arguments, outputFile, servicePipDependencies: servicePipId != null ? ReadOnlyArray <PipId> .From(new[] { servicePipId.Value }) : ReadOnlyArray <PipId> .Empty, fileDependencies: fileDependencies, directoryDependencies: directoryDependencies, skipMaterializationFor: ReadOnlyArray <FileOrDirectoryArtifact> .FromWithoutCopy(skipMaterializationArtifacts), isServiceFinalization: isServiceFinalization, mustRunOnMaster: mustRunOnMaster, tags: tags, out var ipcPip); pipId = ipcPip.PipId; return(result); }
/// <nodoc /> public MacOsDefaults(PathTable pathTable, IMutablePipGraph pipGraph) { m_provenance = new PipProvenance( 0, ModuleId.Invalid, StringId.Invalid, FullSymbol.Invalid, LocationData.Invalid, QualifierId.Unqualified, PipData.Invalid); m_sourceSealDirectoryPaths = new[] { MacPaths.Applications, MacPaths.Library, MacPaths.UserProvisioning } .Select(p => AbsolutePath.Create(pathTable, p)) .ToArray(); // Sealed Source inputs // (using Lazy so that these directories are sealed and added to the graph only if explicitly requested by a process) m_lazySourceSealDirectories = Lazy.Create(() => new DefaultSourceSealDirectories(m_sourceSealDirectoryPaths.Select(p => GetSourceSeal(pipGraph, p)).ToArray())); m_untrackedFiles = new[] { // login.keychain is created by the OS the first time any process invokes an OS API that references the keychain. // Untracked because build state will not be stored there and code signing will fail if required certs are in the keychain MacPaths.Etc, MacPaths.UserKeyChainsDb, MacPaths.UserKeyChains, MacPaths.UserCFTextEncoding, MacPaths.TmpDir } .Select(p => FileArtifact.CreateSourceFile(AbsolutePath.Create(pathTable, p))) .ToArray(); m_untrackedDirectories = new[] { MacPaths.Bin, MacPaths.Dev, MacPaths.Private, MacPaths.Sbin, MacPaths.SystemLibrary, MacPaths.UsrBin, MacPaths.UsrInclude, MacPaths.UsrLibexec, MacPaths.UsrShare, MacPaths.UsrStandalone, MacPaths.UsrSbin, MacPaths.Var, MacPaths.UserPreferences, // it's important to untrack /usr/lib instead of creating a sealed source directory // - the set of dynamically loaded libraries during an execution of a process is // not necessarily deterministic, i.e., when the same process---which itself is // deterministic---is executed multiple times on same inputs, the set of // dynamically loaded libraries is not necessarily going to stay the same. MacPaths.UsrLib } .Select(p => DirectoryArtifact.CreateWithZeroPartialSealId(pathTable, p)) .ToArray(); }
/// <summary> /// Adds all predicted dependencies as inputs, plus all individual inputs predicted for the project /// </summary> /// <remarks> /// Adding all predicted dependencies is key to get the right scheduling. On the other hand, all predicted inputs /// are not really needed since we are running in undeclared read mode. However, they contribute to make the weak fingerprint stronger (that /// otherwise will likely be just a shared opaque output at the root). /// </remarks> private void ProcessInputs( ProjectWithPredictions project, ProcessBuilder processBuilder) { // Predicted output directories for all direct dependencies, plus the output directories for the given project itself var knownOutputDirectories = project.ProjectReferences.SelectMany(reference => reference.PredictedOutputFolders).Union(project.PredictedOutputFolders); // Add all predicted inputs that are recognized as true source files // This is done to make the weak fingerprint stronger. Pips are scheduled so undeclared source reads are allowed. This means // we don't actually need accurate (or in fact any) input predictions to run successfully. But we are trying to avoid the degenerate case // of a very small weak fingerprint with too many candidates, that can slow down two-phase cache look-up. foreach (AbsolutePath buildInput in project.PredictedInputFiles) { // If any of the predicted inputs is under the predicted output folder of a dependency, then there is a very good chance the predicted input is actually an intermediate file // In that case, don't add the input as a source file to stay on the safe side. Otherwise we will have a file that is both declared as a source file and contained in a directory // dependency. if (knownOutputDirectories.Any(outputFolder => buildInput.IsWithin(PathTable, outputFolder))) { continue; } // If any of the predicted inputs is under an untracked directory scope, don't add it as an input if (processBuilder.GetUntrackedDirectoryScopesSoFar().Any(untrackedDirectory => buildInput.IsWithin(PathTable, untrackedDirectory))) { continue; } processBuilder.AddInputFile(FileArtifact.CreateSourceFile(buildInput)); } IEnumerable <ProjectWithPredictions> references; // The default for EnableTransitiveProjectReferences is false, so it has to be true explicitly to kick in if (m_resolverSettings.EnableTransitiveProjectReferences == true) { // In this case all the transitive closure is automatically exposed to the project as direct references var transitiveReferences = new HashSet <ProjectWithPredictions>(); ComputeTransitiveDependenciesFor(project, transitiveReferences); references = transitiveReferences; } else { // Only direct dependencies are declared. // Add all known explicit inputs from project references. But rule out // projects that have a known empty list of targets: those projects are not scheduled, so // there is nothing to consume from them. references = project.ProjectReferences.Where(projectReference => projectReference.PredictedTargetsToExecute.Targets.Count != 0); } var argumentsBuilder = processBuilder.ArgumentsBuilder; foreach (ProjectWithPredictions projectReference in references) { bool outputsPresent = m_processOutputsPerProject.TryGetValue(projectReference, out MSBuildProjectOutputs projectOutputs); if (!outputsPresent) { Contract.Assert(false, $"Pips must have been presented in dependency order: {projectReference.FullPath.ToString(PathTable)} missing, dependency of {project.FullPath.ToString(PathTable)}"); } // Add all known output directories foreach (StaticDirectory output in projectOutputs.OutputDirectories) { processBuilder.AddInputDirectory(output.Root); } // If the dependency was built in isolation, this project needs to access the generated cache files if (projectOutputs.BuildsInIsolation) { var outputCache = projectOutputs.OutputCacheFile; processBuilder.AddInputFile(outputCache); // Instruct MSBuild to use the cache file from the associated dependency as an input. // Flag /irc is the short form of /inputResultsCaches, and part of MSBuild 'build in isolation' mode. using (argumentsBuilder.StartFragment(PipDataFragmentEscaping.NoEscaping, string.Empty)) { argumentsBuilder.Add(PipDataAtom.FromString("/irc:")); argumentsBuilder.Add(PipDataAtom.FromAbsolutePath(outputCache)); } } } }
public async Task Stress() { const int N = 5; const int M = N * N; var context = BuildXLContext.CreateInstanceForTesting(); var loggingContext = CreateLoggingContextForTest(); var pathTable = context.PathTable; using (var tempFiles = new TempFileStorage(canGetFileNames: true)) { var config = ConfigHelpers.CreateDefault(pathTable, tempFiles.GetUniqueFileName(), tempFiles); using (var pipTable = new PipTable( context.PathTable, context.SymbolTable, initialBufferSize: 1024, maxDegreeOfParallelism: (Environment.ProcessorCount + 2) / 3, debug: false)) { var executionEnvironment = new PipQueueTestExecutionEnvironment( context, config, pipTable, Path.Combine(TestOutputDirectory, "temp"), TryGetSubstSourceAndTarget(out string substSource, out string substTarget) ? (substSource, substTarget) : default((string, string)?), GetSandboxConnection()); 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, executionEnvironment, pip); } executionEnvironment.MarkExecuted(pip); return(result); }; string executable = CmdHelper.OsShellExe; FileArtifact executableArtifact = FileArtifact.CreateSourceFile(AbsolutePath.Create(pathTable, executable)); // This is the only file artifact we reference without a producer. Rather than scheduling a hashing pip, let's just invent one (so fingerprinting can succeed). executionEnvironment.AddWellKnownFile(executableArtifact, WellKnownContentHashes.UntrackedFile); using (var phase1PipQueue = new PipQueue(LoggingContext, executionEnvironment.Configuration.Schedule)) { // phase 1: create some files var baseFileArtifacts = new List <FileArtifact>(); for (int i = 0; i < N; i++) { string destination = tempFiles.GetUniqueFileName(); AbsolutePath destinationAbsolutePath = AbsolutePath.Create(pathTable, destination); FileArtifact destinationArtifact = FileArtifact.CreateSourceFile(destinationAbsolutePath).CreateNextWrittenVersion(); baseFileArtifacts.Add(destinationArtifact); PipData contents = PipDataBuilder.CreatePipData( context.StringTable, " ", PipDataFragmentEscaping.CRuntimeArgumentRules, i.ToString(CultureInfo.InvariantCulture)); var writeFile = new WriteFile(destinationArtifact, contents, WriteFileEncoding.Utf8, ReadOnlyArray <StringId> .Empty, PipProvenance.CreateDummy(context)); var pipId = pipTable.Add((uint)(i + 1), writeFile); var contentHash = ContentHashingUtilities.HashString(contents.ToString(pathTable)); executionEnvironment.AddExpectedWrite(writeFile, destinationArtifact, contentHash); var runnable = RunnablePip.Create(loggingContext, executionEnvironment, pipId, pipTable.GetPipType(pipId), 0, taskFactory, 0); runnable.Start(new OperationTracker(loggingContext), loggingContext); runnable.SetDispatcherKind(DispatcherKind.IO); phase1PipQueue.Enqueue(runnable); } phase1PipQueue.SetAsFinalized(); phase1PipQueue.DrainQueues(); await Task.WhenAll( Enumerable.Range(0, 2).Select( async range => { using (var phase2PipQueue = new PipQueue(LoggingContext, executionEnvironment.Configuration.Schedule)) { // phase 2: do some more with those files var pips = new ConcurrentDictionary <PipId, Tuple <string, int> >(); var checkerTasks = new ConcurrentQueue <Task>(); Action <PipId, Task <PipResult> > callback = (id, task) => { XAssert.IsTrue(task.Status == TaskStatus.RanToCompletion); XAssert.IsFalse(task.Result.Status.IndicatesFailure()); Tuple <string, int> t; if (!pips.TryRemove(id, out t)) { XAssert.Fail(); } checkerTasks.Enqueue( Task.Run( () => { string actual = File.ReadAllText(t.Item1).Trim(); // TODO: Make this async XAssert.AreEqual(actual, t.Item2.ToString()); })); }; var r = new Random(0); for (int i = 0; i < M; i++) { int sourceIndex = r.Next(baseFileArtifacts.Count); FileArtifact sourceArtifact = baseFileArtifacts[sourceIndex]; string destination = tempFiles.GetUniqueFileName(); AbsolutePath destinationAbsolutePath = AbsolutePath.Create(pathTable, destination); FileArtifact destinationArtifact = FileArtifact.CreateSourceFile(destinationAbsolutePath).CreateNextWrittenVersion(); Pip pip; DispatcherKind queueKind; switch (r.Next(2)) { case 0: pip = new CopyFile(sourceArtifact, destinationArtifact, ReadOnlyArray <StringId> .Empty, PipProvenance.CreateDummy(context)); queueKind = DispatcherKind.IO; executionEnvironment.AddExpectedWrite(pip, destinationArtifact, executionEnvironment.GetExpectedContent(sourceArtifact)); break; case 1: string workingDirectory = OperatingSystemHelper.IsUnixOS ? "/tmp" : Environment.GetFolderPath(Environment.SpecialFolder.Windows); AbsolutePath workingDirectoryAbsolutePath = AbsolutePath.Create(pathTable, workingDirectory); var pipData = OperatingSystemHelper.IsUnixOS ? PipDataBuilder.CreatePipData(pathTable.StringTable, " ", PipDataFragmentEscaping.CRuntimeArgumentRules, "-c", "'", "cp", sourceArtifact, destinationArtifact, "'") : PipDataBuilder.CreatePipData(pathTable.StringTable, " ", PipDataFragmentEscaping.CRuntimeArgumentRules, "/d", "/c", "copy", "/B", sourceArtifact, destinationArtifact); queueKind = DispatcherKind.CPU; pip = new Process( executableArtifact, workingDirectoryAbsolutePath, pipData, FileArtifact.Invalid, PipData.Invalid, ReadOnlyArray <EnvironmentVariable> .Empty, FileArtifact.Invalid, FileArtifact.Invalid, FileArtifact.Invalid, tempFiles.GetUniqueDirectory(pathTable), null, null, ReadOnlyArray <FileArtifact> .FromWithoutCopy(executableArtifact, sourceArtifact), ReadOnlyArray <FileArtifactWithAttributes> .FromWithoutCopy(destinationArtifact.WithAttributes()), ReadOnlyArray <DirectoryArtifact> .Empty, ReadOnlyArray <DirectoryArtifact> .Empty, ReadOnlyArray <PipId> .Empty, ReadOnlyArray <AbsolutePath> .From(CmdHelper.GetCmdDependencies(pathTable)), ReadOnlyArray <AbsolutePath> .From(CmdHelper.GetCmdDependencyScopes(pathTable)), ReadOnlyArray <StringId> .Empty, ReadOnlyArray <int> .Empty, ReadOnlyArray <ProcessSemaphoreInfo> .Empty, provenance: PipProvenance.CreateDummy(context), toolDescription: StringId.Invalid, additionalTempDirectories: ReadOnlyArray <AbsolutePath> .Empty); executionEnvironment.AddExpectedWrite(pip, destinationArtifact, executionEnvironment.GetExpectedContent(sourceArtifact)); break; default: Contract.Assert(false); continue; } var pipId = pipTable.Add((uint)((range *M) + N + i + 1), pip); Func <RunnablePip, Task> taskFactoryWithCallback = async(runnablePip) => { var task = taskFactory(runnablePip); var pipResult = await task; callback(pipId, task); }; var runnable = RunnablePip.Create(loggingContext, executionEnvironment, pipId, pipTable.GetPipType(pipId), 0, taskFactoryWithCallback, 0); runnable.Start(new OperationTracker(loggingContext), loggingContext); runnable.SetDispatcherKind(queueKind); phase2PipQueue.Enqueue(runnable); if (!pips.TryAdd(pipId, Tuple.Create(destination, sourceIndex))) { Contract.Assert(false); } } phase2PipQueue.SetAsFinalized(); phase2PipQueue.DrainQueues(); XAssert.AreEqual(0, pips.Count); await Task.WhenAll(checkerTasks); } })); } } } }
/// <nodoc /> public FileArtifact Remap(FileArtifact file) { return(m_oldPathTable == null || !file.IsValid ? file : FileArtifact.CreateSourceFile(Remap(file.Path))); }
private EvaluationResult CreateSourceFile(string path) { return(EvaluationResult.Create(FileArtifact.CreateSourceFile(CreateAbsolutePath(path)))); }
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); }
private FileArtifact CreateOutputFile(string path) { return(FileArtifact.CreateOutputFile(CreateAbsolutePath(path))); }
private EvaluationResult SealDirectoryHelper(Context context, ModuleLiteral env, EvaluationStackFrame args, SealDirectoryKind sealDirectoryKind) { AbsolutePath path; ArrayLiteral contents; ArrayLiteral outputDirectoryContents; string[] tags; string description; bool scrub; if (args.Length > 0 && args[0].Value is ObjectLiteral) { var obj = Args.AsObjectLiteral(args, 0); var directory = Converter.ExtractDirectory(obj, m_sealRoot, allowUndefined: false); path = directory.Path; contents = Converter.ExtractArrayLiteral(obj, m_sealFiles, allowUndefined: false); tags = Converter.ExtractStringArray(obj, m_sealTags, allowUndefined: true); description = Converter.ExtractString(obj, m_sealDescription, allowUndefined: true); scrub = sealDirectoryKind.IsFull() ? Converter.ExtractOptionalBoolean(obj, m_sealScrub) ?? false : false; outputDirectoryContents = sealDirectoryKind.IsFull() ? Converter.ExtractOptionalArrayLiteral(obj, m_sealOutputDirectories, allowUndefined: false) : null; } else { path = Args.AsPath(args, 0, false); contents = Args.AsArrayLiteral(args, 1); tags = Args.AsStringArrayOptional(args, 2); description = Args.AsStringOptional(args, 3); // Only do scrub for fully seal directory scrub = sealDirectoryKind.IsFull() ? Args.AsBoolOptional(args, 4) : false; outputDirectoryContents = sealDirectoryKind.IsFull() ? Args.AsArrayLiteralOptional(args, 5) : null; } var fileContents = new FileArtifact[contents.Length]; for (int i = 0; i < contents.Length; ++i) { fileContents[i] = Converter.ExpectFile(contents[i], strict: false, context: new ConversionContext(pos: i, objectCtx: contents)); } var outputDirectoryArtifactContents = new DirectoryArtifact[outputDirectoryContents?.Count ?? 0]; for (int i = 0; i < outputDirectoryArtifactContents.Length; ++i) { outputDirectoryArtifactContents[i] = Converter.ExpectStaticDirectory(outputDirectoryContents[i], context: new ConversionContext(pos: i, objectCtx: contents)).Root; } var sortedFileContents = SortedReadOnlyArray <FileArtifact, OrdinalFileArtifactComparer> .CloneAndSort(fileContents, OrdinalFileArtifactComparer.Instance); var sortedDirectoryContents = SortedReadOnlyArray <DirectoryArtifact, OrdinalDirectoryArtifactComparer> .CloneAndSort( outputDirectoryArtifactContents, OrdinalDirectoryArtifactComparer.Instance); DirectoryArtifact sealedDirectoryArtifact; if (!context.GetPipConstructionHelper().TrySealDirectory( directoryRoot: path, contents: sortedFileContents, outputDirectorycontents: sortedDirectoryContents, kind: sealDirectoryKind, tags: tags, description: description, patterns: null, sealedDirectory: out sealedDirectoryArtifact, scrub: scrub)) { // Error has been logged return(EvaluationResult.Error); } var result = new StaticDirectory(sealedDirectoryArtifact, sealDirectoryKind, sortedFileContents.WithCompatibleComparer(OrdinalPathOnlyFileArtifactComparer.Instance)); return(new EvaluationResult(result)); }
/// <summary> /// Creates a write file operation that appends. The file is created if it does not exist. /// Writes random content to file at path if no content is specified. /// </summary> public static Operation WriteFileIfInputEqual(FileArtifact path, string input, string value, string content = null) { return(new Operation(Type.WriteFileIfInputEqual, path, EncodeList(input, value, content))); }
private FileArtifact SourceFile(string path) => FileArtifact.CreateSourceFile(AbsolutePath.Create(PathTable, path));
/// <summary> /// Creates a delete file operation with with the option to retry deleting to the file a specified number of times /// </summary> public static Operation DeleteFileWithRetries(FileArtifact path, bool doNotInfer = false, int retries = 5) { return(new Operation(Type.DeleteFileWithRetries, path, content: null, doNotInfer: doNotInfer, retriesOnWrite: retries)); }
private FileArtifact OutputFile(string path) => FileArtifact.CreateOutputFile(AbsolutePath.Create(PathTable, path));
/// <summary> /// Creates a read file operation /// </summary> public static Operation ReadFile(FileArtifact path, bool doNotInfer = false) { return(new Operation(Type.ReadFile, path, doNotInfer: doNotInfer)); }
public void ValidateCachingDirectoryEnumerationReadOnlyMountUntrackedScope(bool maskUntrackedAccesses) { // When true (default), directory enumerations in untracked scopes are ignored // When false, directory enumerations in untracked scopes are tracked as normal Configuration.Sandbox.MaskUntrackedAccesses = maskUntrackedAccesses; AbsolutePath readonlyRootPath; AbsolutePath.TryCreate(Context.PathTable, ReadonlyRoot, out readonlyRootPath); DirectoryArtifact dir = DirectoryArtifact.CreateWithZeroPartialSealId(CreateUniqueDirectory(ReadonlyRoot)); // Enumerate untracked scope /dir and its parent directory FileArtifact outFile = CreateOutputFileArtifact(); var ops = new Operation[] { Operation.EnumerateDir(DirectoryArtifact.CreateWithZeroPartialSealId(readonlyRootPath)), Operation.EnumerateDir(dir), Operation.WriteFile(outFile) }; var builder = CreatePipBuilder(ops); builder.AddUntrackedDirectoryScope(dir); Process pip = SchedulePipBuilder(builder).Process; RunScheduler().AssertCacheMiss(pip.PipId); RunScheduler().AssertCacheHit(pip.PipId); string checkpoint1 = File.ReadAllText(ArtifactToString(outFile)); // Create /dir/nestedFile in untracked scope FileArtifact nestedFile = CreateSourceFile(ArtifactToString(dir)); if (!maskUntrackedAccesses) { RunScheduler().AssertCacheMiss(pip.PipId); } RunScheduler().AssertCacheHit(pip.PipId); // Modify /dir/nestedFile in untracked scope File.WriteAllText(ArtifactToString(nestedFile), "nestedFile"); RunScheduler().AssertCacheHit(pip.PipId); // Delete /dir/nestedFile in untracked scope File.Delete(ArtifactToString(nestedFile)); RunScheduler().AssertCacheHit(pip.PipId); string checkpoint2 = File.ReadAllText(ArtifactToString(outFile)); // Filesystem should match state from original run, so cache replays output from that run XAssert.AreEqual(checkpoint1, checkpoint2); // Create /dir/nestedDir in untracked scope DirectoryArtifact nestedDir = DirectoryArtifact.CreateWithZeroPartialSealId(CreateUniqueDirectory(ArtifactToString(dir))); if (!maskUntrackedAccesses) { RunScheduler().AssertCacheMiss(pip.PipId); } RunScheduler().AssertCacheHit(pip.PipId); // Delete /dir/nestedDir in untracked scope Directory.Delete(ArtifactToString(nestedDir)); RunScheduler().AssertCacheHit(pip.PipId); string checkpoint3 = File.ReadAllText(ArtifactToString(outFile)); // Filesystem should match state from original run, so cache replays output from that run XAssert.AreEqual(checkpoint1, checkpoint3); // Delete untracked scope /dir Directory.Delete(ArtifactToString(dir)); RunScheduler().AssertCacheMiss(pip.PipId); RunScheduler().AssertCacheHit(pip.PipId); }
/// <summary> /// Creates a create hard link operation /// </summary> public static Operation CreateHardlink(FileArtifact linkPath, FileArtifact path, bool doNotInfer = false) { return(new Operation(Type.CreateHardlink, path, linkPath: linkPath, doNotInfer: doNotInfer)); }
/// <summary> /// Checks if file content exists in the artifact cache. /// </summary> public bool FileContentExistsInArtifactCache(FileArtifact file) { ContentHash hash = ContentHashingUtilities.HashFileAsync(file.Path.ToString(Context.PathTable)).Result; return(ExistsInArtifactCache(hash)); }
/// <summary> /// Creates a project that is built in isolation, and therefore it produces an output cache file /// </summary> public static MSBuildProjectOutputs CreateIsolated(IEnumerable <StaticDirectory> outputDirectories, FileArtifact outputCacheFile) { Contract.Requires(outputDirectories != null); Contract.Requires(outputCacheFile != FileArtifact.Invalid); return(new MSBuildProjectOutputs(outputDirectories, outputCacheFile)); }
/// <summary> /// Discards file content in artifact cache if exists. /// </summary> public void DiscardFileContentInArtifactCacheIfExists(FileArtifact file, CacheSites sites = CacheSites.LocalAndRemote) { ContentHash hash = ContentHashingUtilities.HashFileAsync(file.Path.ToString(Context.PathTable)).Result; DiscardContentInArtifactCacheIfExists(hash, sites); }
public void ScrubFileDirectoriesWithPipGraph() { string rootDirectory = Path.Combine(TemporaryDirectory, nameof(ScrubFileDirectoriesWithPipGraph)); string sourceRoot = Path.Combine(rootDirectory, "Src"); string outputRoot = Path.Combine(rootDirectory, "Out"); string targetRoot = Path.Combine(rootDirectory, "Target"); var pathTable = new PathTable(); using (TestEnv env = TestEnv.CreateTestEnvWithPausedScheduler( new List <IMount> { new Mount() { Name = PathAtom.Create(pathTable.StringTable, "testRoot"), Path = AbsolutePath.Create(pathTable, TemporaryDirectory), IsWritable = true, IsReadable = true, IsScrubbable = true, AllowCreateDirectory = true, } }, pathTable) ) { string inputFilePath = Path.Combine(sourceRoot, "input.txt"); WriteFile(inputFilePath); string outputFilePath = Path.Combine(outputRoot, "output.txt"); WriteFile(outputFilePath); string tempOutputDirectoryPath = Path.Combine(outputRoot, "TempOutDir"); string tempOutputPath = Path.Combine(tempOutputDirectoryPath, "tempOutputInDir.txt"); Directory.CreateDirectory(tempOutputDirectoryPath); string optionalOutputDirectoryPath = Path.Combine(outputRoot, "OptionalOutDir"); string optionalOutputPath = Path.Combine(optionalOutputDirectoryPath, "optionalOutputInDir.txt"); Directory.CreateDirectory(optionalOutputDirectoryPath); string targetFileInOutputDirectoryPath = Path.Combine(targetRoot, "targetInDir.txt"); WriteFile(targetFileInOutputDirectoryPath); string outputDirectoryPath = Path.Combine(outputRoot, "OutDir"); string outputFileInOutputDirectoryPath = Path.Combine(outputDirectoryPath, "outputInDir.txt"); WriteFile(outputFileInOutputDirectoryPath); string sharedOutputDirectoryPath = Path.Combine(outputRoot, "SharedOutDir"); string outputFileInOutputSharedDirectoryPath = Path.Combine(sharedOutputDirectoryPath, "outputInSharedDir.txt"); WriteFile(outputFileInOutputSharedDirectoryPath); string junkOutputPath = Path.Combine(outputRoot, "junk.txt"); WriteFile(junkOutputPath); string junkOutputInOutputDirectoryPath = Path.Combine(outputDirectoryPath, "junkInDir.txt"); WriteFile(junkOutputInOutputDirectoryPath); string junkTempOutputPath = Path.Combine(tempOutputDirectoryPath, "junkTempOutput.txt"); WriteFile(junkTempOutputPath); string junkOptionalOutputPath = Path.Combine(optionalOutputDirectoryPath, "junkOptionalOutput.txt"); WriteFile(junkOptionalOutputPath); string junkDirectoryPath = Path.Combine(outputRoot, "JunkDir"); string junkFileInJunkDirectoryPath = Path.Combine(junkDirectoryPath, "junkInJunkDir.txt"); WriteFile(junkFileInJunkDirectoryPath); var pipBuilder = CreatePipBuilderWithTag(env, nameof(ScrubFileDirectoriesWithPipGraph)); FileArtifact input = env.Paths.CreateSourceFile(env.Paths.CreateAbsolutePath(inputFilePath)); pipBuilder.AddInputFile(input); AbsolutePath output = env.Paths.CreateAbsolutePath(outputFilePath); pipBuilder.AddOutputFile(output); AbsolutePath tempOutput = env.Paths.CreateAbsolutePath(tempOutputPath); pipBuilder.AddOutputFile(tempOutput, FileExistence.Temporary); AbsolutePath optionalOutput = env.Paths.CreateAbsolutePath(optionalOutputPath); pipBuilder.AddOutputFile(optionalOutput, FileExistence.Optional); AbsolutePath outputDirectory = env.Paths.CreateAbsolutePath(outputDirectoryPath); pipBuilder.AddOutputDirectory(outputDirectory); AbsolutePath targetRootAbsolutePath = env.Paths.CreateAbsolutePath(targetRoot); pipBuilder.AddOutputDirectory(targetRootAbsolutePath); AbsolutePath sharedOutputDirectory = env.Paths.CreateAbsolutePath(sharedOutputDirectoryPath); pipBuilder.AddOutputDirectory(sharedOutputDirectory, SealDirectoryKind.SharedOpaque); env.PipConstructionHelper.AddProcess(pipBuilder); PipGraph pipGraph = AssertSuccessGraphBuilding(env); RunScrubberWithPipGraph(env, pipGraph, pathsToScrub: new[] { outputRoot, targetRoot }); // All non-junk files/directories should be preserved, except ... (see below) XAssert.IsTrue(File.Exists(inputFilePath)); XAssert.IsTrue(File.Exists(outputFilePath)); XAssert.IsTrue(Directory.Exists(tempOutputDirectoryPath)); XAssert.IsTrue(Directory.Exists(optionalOutputDirectoryPath)); XAssert.IsTrue(Directory.Exists(outputDirectoryPath)); XAssert.IsTrue(Directory.Exists(sharedOutputDirectoryPath)); // Shared output directory is always scrubbed, and thus its contents should be removed. XAssert.IsFalse(File.Exists(outputFileInOutputSharedDirectoryPath)); // All junk files/directories should be removed, except ... (see below). XAssert.IsFalse(File.Exists(junkOutputPath)); XAssert.IsFalse(File.Exists(junkTempOutputPath)); XAssert.IsFalse(File.Exists(junkOptionalOutputPath)); XAssert.IsFalse(Directory.Exists(junkDirectoryPath)); // Junk output in an output directory is not removed because // when we run again the pip (can be from cache), the whole output directory will be removed. XAssert.IsTrue(File.Exists(junkOutputInOutputDirectoryPath)); } }
/// <summary> /// Serializes a file artifact into a string identifier. /// </summary> public static string ToString(FileArtifact file) { return(I($"{file.Path.RawValue}{Separator}{file.RewriteCount}")); }