Example #1
0
        private void ProcessOutputs(RushProject project, ProcessBuilder processBuilder)
        {
            // HACK HACK. We are missing output dirs with exclusions, so we don't include node_modules here, which is a pain for scrubbing.
            // So let's add known common directories one by one
            // Ideally we'd like to say 'the root of the project without node_modules'. No projects are supposed to write there.
            processBuilder.AddOutputDirectory(DirectoryArtifact.CreateWithZeroPartialSealId(project.ProjectFolder.Combine(PathTable, "lib")), SealDirectoryKind.SharedOpaque);
            processBuilder.AddOutputDirectory(DirectoryArtifact.CreateWithZeroPartialSealId(project.ProjectFolder.Combine(PathTable, "temp")), SealDirectoryKind.SharedOpaque);
            processBuilder.AddOutputDirectory(DirectoryArtifact.CreateWithZeroPartialSealId(project.ProjectFolder.Combine(PathTable, "dist")), SealDirectoryKind.SharedOpaque);
            processBuilder.AddOutputDirectory(DirectoryArtifact.CreateWithZeroPartialSealId(project.ProjectFolder.Combine(PathTable, "release")), SealDirectoryKind.SharedOpaque);
            processBuilder.AddOutputDirectory(DirectoryArtifact.CreateWithZeroPartialSealId(project.ProjectFolder.Combine(PathTable, "src")), SealDirectoryKind.SharedOpaque);

            // HACK HACK. Only non-test projects generate these files at the root of the project. So don't add them twice otherwise graph construction complains
            if (!project.Name.EndsWith("_test"))
            {
                processBuilder.AddOutputFile(new FileArtifact(project.ProjectFolder.Combine(PathTable, "test-api.js"), 0), FileExistence.Optional);
                processBuilder.AddOutputFile(new FileArtifact(project.ProjectFolder.Combine(PathTable, "test-api.d.ts"), 0), FileExistence.Optional);
            }

            // Some projects share their temp folder and some files might get consumed across. So treat it as a regular output directory
            processBuilder.AddOutputDirectory(DirectoryArtifact.CreateWithZeroPartialSealId(project.TempFolder), SealDirectoryKind.SharedOpaque);

            // Add all the additional output directories that the rush graph knows about
            foreach (var additionalOutput in project.AdditionalOutputDirectories)
            {
                processBuilder.AddOutputDirectory(DirectoryArtifact.CreateWithZeroPartialSealId(additionalOutput), SealDirectoryKind.SharedOpaque);
            }

            // Add additional output directories configured in the main config file
            AddAdditionalOutputDirectories(processBuilder, project.ProjectFolder);
        }
Example #2
0
        private void ConfigureProcessBuilder(
            ProcessBuilder processBuilder,
            RushProject project)
        {
            SetCmdTool(processBuilder, project);

            // Working directory - the directory where the project file lives.
            processBuilder.WorkingDirectory = DirectoryArtifact.CreateWithZeroPartialSealId(project.ProjectFolder);

            // We allow undeclared inputs to be read
            processBuilder.Options |= Process.Options.AllowUndeclaredSourceReads;

            // We want to enforce the use of weak fingerprint augmentation since input predictions could be not complete/sufficient
            // to avoid a large number of path sets
            processBuilder.Options |= Process.Options.EnforceWeakFingerprintAugmentation;

            // By default the double write policy is to allow same content double writes.
            processBuilder.DoubleWritePolicy |= DoubleWritePolicy.AllowSameContentDoubleWrites;

            PipConstructionUtilities.UntrackUserConfigurableArtifacts(processBuilder, m_resolverSettings);

            var logDirectory = GetLogDirectory(project);
            var logFile      = logDirectory.Combine(PathTable, "build.log");

            // Execute the build command and redirect the output to a designated log file
            processBuilder.ArgumentsBuilder.Add(PipDataAtom.FromString("/C"));
            processBuilder.ArgumentsBuilder.Add(PipDataAtom.FromString(project.BuildCommand));
            processBuilder.ArgumentsBuilder.Add(PipDataAtom.FromString(">"));
            processBuilder.ArgumentsBuilder.Add(PipDataAtom.FromAbsolutePath(logFile));
            processBuilder.AddOutputFile(logFile, FileExistence.Required);

            FrontEndUtilities.SetProcessEnvironmentVariables(CreateEnvironment(project), m_userDefinedPassthroughVariables, processBuilder, m_context.PathTable);
        }
Example #3
0
 /// <summary>
 /// Adds output file option.
 /// </summary>
 public ArgumentsBuilder AddOutputOption(string optionName, AbsolutePath outputPath)
 {
     Contract.Requires(outputPath.IsValid);
     AddOption(optionName, outputPath, (b, v) => b.Add(outputPath));
     m_processBuilder.AddOutputFile(outputPath);
     return(this);
 }
        private void AddOutputs(NinjaNode node, ProcessBuilder processBuilder)
        {
            foreach (AbsolutePath output in node.Outputs)
            {
                FileArtifact file;
                if (m_outputFileArtifacts.TryGetValue(output, out file))
                {
                    processBuilder.AddOutputFile(file, FileExistence.Required);
                }
                else
                {
                    processBuilder.AddOutputFile(output, FileExistence.Required);
                }
            }

            processBuilder.AddOutputDirectory(DirectoryArtifact.CreateWithZeroPartialSealId(m_projectRoot), SealDirectoryKind.SharedOpaque);
        }
Example #5
0
        private void AddOutputs(NinjaNode node, ProcessBuilder processBuilder)
        {
            foreach (AbsolutePath output in node.Outputs)
            {
                // TODO: outputs should be optional/required depending on the Ninja graph semantics instead of always optional
                FileArtifact file;
                if (m_outputFileArtifacts.TryGetValue(output, out file))
                {
                    processBuilder.AddOutputFile(file, FileExistence.Optional);
                }
                else
                {
                    processBuilder.AddOutputFile(output, FileExistence.Optional);
                }
            }

            processBuilder.AddOutputDirectory(DirectoryArtifact.CreateWithZeroPartialSealId(m_projectRoot), SealDirectoryKind.SharedOpaque);
        }
Example #6
0
        /// <summary>
        /// Adds output file option.
        /// </summary>
        public ArgumentsBuilder AddOutputFileOption(string optionName, AbsolutePath outputPath)
        {
            Contract.Requires(!string.IsNullOrEmpty(optionName));
            Contract.Requires(outputPath.IsValid);
            Contract.Assert(!m_finished);

            m_dataBuilder.AddPathOption(optionName, outputPath);
            m_processBuilder.AddOutputFile(outputPath);
            return(this);
        }
Example #7
0
        private bool TryConfigureProcessBuilder(
            ProcessBuilder processBuilder,
            PipConstructionHelper pipConstructionHelper,
            ProjectWithPredictions project,
            QualifierId qualifierId,
            out AbsolutePath outputResultCacheFile,
            out string failureDetail)
        {
            outputResultCacheFile = AbsolutePath.Invalid;
            if (!TrySetBuildToolExecutor(pipConstructionHelper, processBuilder, project))
            {
                failureDetail = "Failed to construct tooldefinition";
                return(false);
            }

            // Working directory - the directory where the project file lives.
            processBuilder.WorkingDirectory = DirectoryArtifact.CreateWithZeroPartialSealId(project.FullPath.GetParent(PathTable));

            // We allow undeclared inputs to be read
            processBuilder.Options |= Process.Options.AllowUndeclaredSourceReads;

            // Run in a container if specified
            if (m_resolverSettings.RunInContainer)
            {
                processBuilder.Options |= Process.Options.NeedsToRunInContainer;
                processBuilder.ContainerIsolationLevel = ContainerIsolationLevel.IsolateAllOutputs;
            }

            // By default the double write policy is to allow same content double writes.
            processBuilder.DoubleWritePolicy |= m_resolverSettings.DoubleWritePolicy ?? DoubleWritePolicy.AllowSameContentDoubleWrites;

            SetUntrackedFilesAndDirectories(processBuilder);

            // Add the log directory and its corresponding files
            var          qualifier    = m_context.QualifierTable.GetQualifier(qualifierId);
            AbsolutePath logDirectory = GetLogDirectory(project, qualifier);

            processBuilder.AddOutputFile(logDirectory.Combine(PathTable, "msbuild.log"), FileExistence.Optional);
            processBuilder.AddOutputFile(logDirectory.Combine(PathTable, "msbuild.wrn"), FileExistence.Optional);
            processBuilder.AddOutputFile(logDirectory.Combine(PathTable, "msbuild.err"), FileExistence.Optional);
            processBuilder.AddOutputFile(logDirectory.Combine(PathTable, "msbuild.prf"), FileExistence.Optional);

            if (m_resolverSettings.EnableBinLogTracing == true)
            {
                processBuilder.AddOutputFile(logDirectory.Combine(PathTable, "msbuild.binlog"), FileExistence.Optional);
            }

            // Unless the legacy non-isolated mode is explicitly specified, the project builds in isolation, and therefore
            // it produces an output cache file. This file is placed on the (unique) object directory for this project
            if (m_resolverSettings.UseLegacyProjectIsolation != true)
            {
                var objectDirectory = pipConstructionHelper.GetUniqueObjectDirectory(project.FullPath.GetName(PathTable));
                outputResultCacheFile = objectDirectory.Path.Combine(PathTable, PathAtom.Create(PathTable.StringTable, OutputCacheFileName));
                processBuilder.AddOutputFile(outputResultCacheFile, FileExistence.Required);
            }

            // Path to the project
            processBuilder.ArgumentsBuilder.Add(PipDataAtom.FromAbsolutePath(project.FullPath));
            // Response file with the rest of the arguments
            var rspFileSpec = ResponseFileSpecification.Builder()
                              .AllowForRemainingArguments(processBuilder.ArgumentsBuilder.CreateCursor())
                              .ForceCreation(true)
                              .Prefix("@")
                              .Build();

            processBuilder.SetResponseFileSpecification(rspFileSpec);

            if (!TryAddMsBuildArguments(project, processBuilder.ArgumentsBuilder, logDirectory, outputResultCacheFile, out failureDetail))
            {
                return(false);
            }

            // Q_SESSION_GUID is used to provide a unique build GUID to build tools and scripts.
            // It'll cause full cache misses if we try to hash it as an input, however, so exclude.
            processBuilder.SetPassthroughEnvironmentVariable(StringId.Create(m_context.StringTable, BuildEnvironmentConstants.QSessionGuidEnvVar));

            // GlobalUnsafePassthroughEnvironmentVariables
            processBuilder.SetGlobalPassthroughEnvironmentVariable(m_frontEndHost.Configuration.FrontEnd.GlobalUnsafePassthroughEnvironmentVariables, m_context.StringTable);

            // mspdbsrv: _MSPDBSRV_ENDPOINT_ sets up one mspdbsrv.exe instance per build target execution.
            // However this process will live beyond the build.cmd or msbuild.exe call.
            // Allow the pip job object to clean the process without complaint.
            //
            // vctip.exe: On any compile error this telemetry upload exe will be run as a detached process.
            // Just let it be killed.
            // TODO: Can we stop it running? https://stackoverflow.microsoft.com/questions/74425/how-to-disable-vctip-exe-in-vc14
            //
            // conhost.exe: This process needs a little bit more time to finish after the main process, but killing it right away
            // is inconsequential.
            //
            // All child processes: Don't wait to kill the processes.
            // CODESYNC: CloudBuild repo TrackerExecutor.cs "info.NestedProcessTerminationTimeout = TimeSpan.Zero"
            processBuilder.AllowedSurvivingChildProcessNames = ReadOnlyArray <PathAtom> .FromWithoutCopy(
                PathAtom.Create(m_context.StringTable, "mspdbsrv.exe"),
                PathAtom.Create(m_context.StringTable, "vctip.exe"),
                PathAtom.Create(m_context.StringTable, "conhost.exe"),
                PathAtom.Create(m_context.StringTable, "VBCSCompiler.exe"));

            // There are some cases (e.g. a 64-bit MSBuild launched as a child process from a 32-bit MSBuild instance) where
            // processes need a little bit more time to finish. Increasing the timeout does not affect job objects where no child
            // processes survive, or job object where the only surviving processes are the ones explicitly allowed to survive (which
            // are killed immediately). So overall, this non-zero timeout will only make some pips that would have failed to take a little
            // bit longer (and hopefully succeed)
            processBuilder.NestedProcessTerminationTimeout = TimeSpan.FromMilliseconds(500);

            SetProcessEnvironmentVariables(CreateEnvironment(logDirectory, project), processBuilder);

            failureDetail = string.Empty;
            return(true);
        }
Example #8
0
        private bool TryConfigureProcessBuilder(
            ProcessBuilder processBuilder,
            PipConstructionHelper pipConstructionHelper,
            ProjectWithPredictions project,
            QualifierId qualifierId,
            out AbsolutePath outputResultCacheFile,
            out string failureDetail)
        {
            outputResultCacheFile = AbsolutePath.Invalid;
            if (!TrySetBuildToolExecutor(pipConstructionHelper, processBuilder, project))
            {
                failureDetail = "Failed to construct tooldefinition";
                return(false);
            }

            // Working directory - the directory where the project file lives.
            processBuilder.WorkingDirectory = DirectoryArtifact.CreateWithZeroPartialSealId(project.FullPath.GetParent(PathTable));

            // We allow undeclared inputs to be read
            processBuilder.Options |= Process.Options.AllowUndeclaredSourceReads;

            // Run in a container if specified
            if (m_resolverSettings.RunInContainer)
            {
                processBuilder.Options |= Process.Options.NeedsToRunInContainer;
                processBuilder.ContainerIsolationLevel = ContainerIsolationLevel.IsolateAllOutputs;
            }

            // Until we can deal with double writes in a better way, this unsafe option allows the build to progress and
            // prints warnings
            processBuilder.DoubleWritePolicy |= DoubleWritePolicy.UnsafeFirstDoubleWriteWins;

            SetUntrackedFilesAndDirectories(processBuilder);

            // Add the log directory and its corresponding files
            var          qualifier    = m_context.QualifierTable.GetQualifier(qualifierId);
            AbsolutePath logDirectory = GetLogDirectory(project, qualifier);

            processBuilder.AddOutputFile(logDirectory.Combine(PathTable, "msbuild.log"), FileExistence.Optional);
            processBuilder.AddOutputFile(logDirectory.Combine(PathTable, "msbuild.wrn"), FileExistence.Optional);
            processBuilder.AddOutputFile(logDirectory.Combine(PathTable, "msbuild.err"), FileExistence.Optional);
            processBuilder.AddOutputFile(logDirectory.Combine(PathTable, "msbuild.prf"), FileExistence.Optional);

            if (m_resolverSettings.EnableBinLogTracing == true)
            {
                processBuilder.AddOutputFile(logDirectory.Combine(PathTable, "msbuild.binlog"), FileExistence.Optional);
            }

            // Unless the legacy non-isolated mode is explicitly specified, the project builds in isolation, and therefore
            // it produces an output cache file. This file is placed on the (unique) object directory for this project
            if (m_resolverSettings.UseLegacyProjectIsolation != true)
            {
                var objectDirectory = pipConstructionHelper.GetUniqueObjectDirectory(project.FullPath.GetName(PathTable));
                outputResultCacheFile = objectDirectory.Path.Combine(PathTable, PathAtom.Create(PathTable.StringTable, OutputCacheFileName));
                processBuilder.AddOutputFile(outputResultCacheFile, FileExistence.Required);
            }

            // Path to the project
            processBuilder.ArgumentsBuilder.Add(PipDataAtom.FromAbsolutePath(project.FullPath));
            // Response file with the rest of the arguments
            var rspFileSpec = ResponseFileSpecification.Builder()
                              .AllowForRemainingArguments(processBuilder.ArgumentsBuilder.CreateCursor())
                              .ForceCreation(true)
                              .Prefix("@")
                              .Build();

            processBuilder.SetResponseFileSpecification(rspFileSpec);

            if (!TryAddMsBuildArguments(project, qualifier, processBuilder.ArgumentsBuilder, logDirectory, outputResultCacheFile, out failureDetail))
            {
                return(false);
            }

            // Q_SESSION_GUID is used to provide a unique build GUID to build tools and scripts.
            // It'll cause full cache misses if we try to hash it as an input, however, so exclude.
            processBuilder.SetPassthroughEnvironmentVariable(StringId.Create(m_context.StringTable, BuildEnvironmentConstants.QSessionGuidEnvVar));

            // mspdbsrv: _MSPDBSRV_ENDPOINT_ sets up one mspdbsrv.exe instance per build target execution.
            // However this process will live beyond the build.cmd or msbuild.exe call.
            // Allow the pip job object to clean the process without complaint.
            //
            // vctip.exe: On any compile error this telemetry upload exe will be run as a detached process.
            // Just let it be killed.
            // TODO: Can we stop it running? https://stackoverflow.microsoft.com/questions/74425/how-to-disable-vctip-exe-in-vc14
            //
            // conhost.exe: This process needs a little bit more time to finish after the main process. We shouldn't be allowing
            // this one to survive, we just need the timeout to be slightly more than zero. This will also be beneficial to other
            // arbitrary processeses that need a little bit more time. But, apparently, setting a timeout has a perf impact that is
            // being investigated. TODO: revisit this once this is fixed.
            //
            // All child processes: Don't wait to kill the processes.
            // CODESYNC: CloudBuild repo TrackerExecutor.cs "info.NestedProcessTerminationTimeout = TimeSpan.Zero"
            processBuilder.AllowedSurvivingChildProcessNames = ReadOnlyArray <PathAtom> .FromWithoutCopy(
                PathAtom.Create(m_context.StringTable, "mspdbsrv.exe"),
                PathAtom.Create(m_context.StringTable, "vctip.exe"),
                PathAtom.Create(m_context.StringTable, "conhost.exe"));

            processBuilder.NestedProcessTerminationTimeout = TimeSpan.Zero;

            SetProcessEnvironmentVariables(CreateEnvironment(logDirectory, project), processBuilder);

            failureDetail = string.Empty;
            return(true);
        }