Exemplo n.º 1
0
        public override void Dispose()
        {
            base.Dispose();

            // Make sure the timer and any outstanding callbacks are finished before disposing the writer
            m_flushTimer.Dispose();
            m_writer.Dispose();
        }
Exemplo n.º 2
0
        private bool PersistFullBuild(IConfiguration configuration, PathTable pathTable, CancellationToken cancellationToken)
        {
            var snapshotFile = m_snapshotFile.ToString(pathTable);
            var mountFolder  = snapshotFile + ".tmpdir";

            try
            {
                // Find a drive to mount the VHD as for this process. The VHD will be mapped to a particular NTFS folder
                // and then mounted as a drive using the drive mapping functionality. This is done to avoid issues with MAX_PATH
                // issues since many codebases have paths nearing MAX_PATH which would go over if rooted in a subdirectory
                // of even a reasonably short path length.
                char unusedDrive       = char.MinValue;
                var  systemDriveLetter = SpecialFolderUtilities.SystemDirectory[0];
                for (char drive = 'A'; drive <= 'Z'; drive++)
                {
                    if (drive == systemDriveLetter || drive == 'C')
                    {
                        continue;
                    }

                    if (!AbsolutePath.TryGet(pathTable, new StringSegment(drive + @":\"), out _))
                    {
                        unusedDrive = drive;
                    }
                }

                if (unusedDrive == char.MinValue)
                {
                    Tracing.Logger.Log.GenericSnapshotError(m_loggingContext, "Snapshot error: Could not find unused drive to use for snapshot destination");
                    return(false);
                }

                bool mounted = VhdUtilities.Mount(snapshotFile, sizeMb: 1 << 20 /* 1 TB */, mountFolder: mountFolder);
                if (!mounted)
                {
                    Tracing.Logger.Log.GenericSnapshotError(
                        m_loggingContext,
                        I($"Snapshot error: Could not create VHD '{unusedDrive}' and mount at '{mountFolder}'"));
                    return(false);
                }

                // Map drive for mounted vhd in order to minimize chance of MAX_PATH issues copying files to VHD.
                bool mappingApplied =
                    ProcessNativeMethods.ApplyDriveMappings(new[] { new PathMapping(unusedDrive, Path.GetFullPath(mountFolder)) });
                if (!mappingApplied)
                {
                    Tracing.Logger.Log.GenericSnapshotError(
                        m_loggingContext,
                        I($"Snapshot error: Drive mapping could not be applied from '{unusedDrive}' to '{mountFolder}'"));
                    return(false);
                }

                var mountDriveRoot       = unusedDrive + @":\";
                var buildEvaluationFiles = new HashSet <AbsolutePath>(m_files);

                if (configuration.Cache.CacheConfigFile.IsValid)
                {
                    buildEvaluationFiles.Add(configuration.Cache.CacheConfigFile);
                }

                // Track used roots so mappings can be added for inputs and outputs when running BuildXL on snap
                ConcurrentDictionary <AbsolutePath, Unit> usedRoots = new ConcurrentDictionary <AbsolutePath, Unit>();

                int capturedFileCount = 0;
                int totalFileCount    = buildEvaluationFiles.Count + m_pipGraph.FileCount;

                Action updateStatus = () => { Console.WriteLine("Snapshot: {0} of {1} files captured", capturedFileCount, totalFileCount); };

                updateStatus();
                var t = new StoppableTimer(updateStatus, 5000, 5000);

                // Copy the build evaluation files
                Parallel.ForEach(
                    buildEvaluationFiles.ToArray(),
                    new ParallelOptions()
                {
                    MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = cancellationToken
                },
                    buildEvaluationFile =>
                {
                    usedRoots[buildEvaluationFile.GetRoot(pathTable)] = Unit.Void;
                    CopyPath(pathTable, buildEvaluationFile, mountDriveRoot);
                    Interlocked.Increment(ref capturedFileCount);
                });

                if (m_pipGraph != null)
                {
                    // Copy all source files
                    Parallel.ForEach(
                        m_pipGraph.AllFiles.ToArray(),
                        new ParallelOptions {
                        MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = cancellationToken
                    },
                        pipFile =>
                    {
                        try
                        {
                            usedRoots[pipFile.Path.GetRoot(pathTable)] = Unit.Void;
                            if (pipFile.IsOutputFile)
                            {
                                return;
                            }

                            if (!buildEvaluationFiles.Contains(pipFile.Path))
                            {
                                CopyPath(pathTable, pipFile.Path, mountDriveRoot);
                            }
                        }
                        finally
                        {
                            Interlocked.Increment(ref capturedFileCount);
                        }
                    });
                }

                // kill and wait for the status timer to die...
                t.Dispose();

                using (var writer = new StreamWriter(File.OpenWrite(Path.Combine(mountDriveRoot, "notes.txt"))))
                {
                    writer.WriteLine("Full Build Snapshot Info");
                    writer.WriteLine();
                    WriteNotes(configuration, pathTable, writer, path => path.ToString(pathTable));
                }

                using (var writer = new StreamWriter(File.OpenWrite(Path.Combine(mountDriveRoot, "runbxl.cmd"))))
                {
                    writer.Write("%BUILDXL_EXE_PATH% \"@%~dp0\\buildxl.rsp\" %*");
                    foreach (var usedRoot in usedRoots.Keys)
                    {
                        var usedRootLetter = usedRoot.ToString(pathTable)[0];
                        if (usedRootLetter.ToUpperInvariantFast() == 'C')
                        {
                            continue;
                        }

                        Directory.CreateDirectory(Path.Combine(mountDriveRoot, usedRootLetter.ToString()));
                        writer.Write(" /rootMap:{0}=\"%~dp0\\{0}\"", usedRootLetter);
                    }
                }

                using (var writer = new StreamWriter(File.OpenWrite(Path.Combine(mountDriveRoot, "buildxl.rsp"))))
                {
                    foreach (var argument in m_commandLineArguments)
                    {
                        writer.WriteLine(argument);
                    }

                    // Disable snapshot when running from snapshot
                    writer.WriteLine("/snapshotMode:None");

                    foreach (var envVar in m_environmentVariables)
                    {
                        writer.WriteLine("/envVar:{0}={1}", envVar.Key, envVar.Value ?? string.Empty);
                    }
                }
            }
            finally
            {
                Analysis.IgnoreResult(VhdUtilities.Dismount(snapshotFile, mountFolder));

                // TODO: Report error.
            }

            return(true);
        }