예제 #1
0
        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);
        }
예제 #2
0
        /*** 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);
        }
예제 #3
0
 private MSBuildProjectOutputs(IEnumerable <StaticDirectory> outputDirectories, FileArtifact outputCacheFile)
 {
     OutputDirectories = outputDirectories;
     OutputCacheFile   = outputCacheFile;
 }
예제 #4
0
 /// <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));
 }
예제 #5
0
 /// <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));
 }
예제 #6
0
 /// <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));
 }
예제 #7
0
 /// <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));
 }
예제 #8
0
        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);
        }
예제 #9
0
        /// <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);
        }
예제 #10
0
        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);
        }
예제 #11
0
            /// <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();
            }
예제 #12
0
        /// <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));
                    }
                }
            }
        }
예제 #13
0
        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);
                            }
                        }));
                    }
                }
            }
        }
예제 #14
0
 /// <nodoc />
 public FileArtifact Remap(FileArtifact file)
 {
     return(m_oldPathTable == null || !file.IsValid ? file : FileArtifact.CreateSourceFile(Remap(file.Path)));
 }
예제 #15
0
 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);
        }
예제 #17
0
 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));
        }
예제 #19
0
 /// <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)));
 }
예제 #20
0
 private FileArtifact SourceFile(string path) => FileArtifact.CreateSourceFile(AbsolutePath.Create(PathTable, path));
예제 #21
0
 /// <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));
 }
예제 #22
0
 private FileArtifact OutputFile(string path) => FileArtifact.CreateOutputFile(AbsolutePath.Create(PathTable, path));
예제 #23
0
 /// <summary>
 /// Creates a read file operation
 /// </summary>
 public static Operation ReadFile(FileArtifact path, bool doNotInfer = false)
 {
     return(new Operation(Type.ReadFile, path, doNotInfer: doNotInfer));
 }
예제 #24
0
        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);
        }
예제 #25
0
 /// <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));
        }
예제 #27
0
            /// <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);
        }
예제 #29
0
        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));
            }
        }
예제 #30
0
 /// <summary>
 /// Serializes a file artifact into a string identifier.
 /// </summary>
 public static string ToString(FileArtifact file)
 {
     return(I($"{file.Path.RawValue}{Separator}{file.RewriteCount}"));
 }