示例#1
0
        internal bool TrySchedulePip(NinjaNode node, QualifierId qualifierId, out Process process)
        {
            try
            {
                // Create command line and inputs and outputs for pipBuilder.
                if (!TryBuildProcessAndSchedulePip(
                        node,
                        qualifierId,
                        out process))
                {
                    Tracing.Logger.Log.PipSchedulingFailed(m_context.LoggingContext, Location.FromFile(m_specPath.ToString(m_context.PathTable)));
                    return(false);
                }
            }
            catch (Exception e)
            {
                Tracing.Logger.Log.UnexpectedPipConstructorException(
                    m_context.LoggingContext,
                    Location.FromFile(m_specPath.ToString(m_context.PathTable)),
                    e.GetLogEventMessage(),
                    e.StackTrace);

                process = null;
                return(false);
            }

            return(true);
        }
示例#2
0
        private void AddInputs(NinjaNode node, ProcessBuilder processBuilder)
        {
            foreach (AbsolutePath input in node.Inputs.Where(i => !m_outputFileArtifacts.ContainsKey(i))) // We only want source files. If the file is in m_outputFileArtifacts then it was output by someone else
            {                                                                                             // and we have that dependency expressed in node.
                processBuilder.AddInputFile(FileArtifact.CreateSourceFile(input));
            }

            foreach (NinjaNode dependency in node.Dependencies)
            {
                bool outputsPresent = m_processOutputs.TryGetValue(dependency, out ProcessOutputs processOutputs);

                string ListOutputs(NinjaNode n) => string.Join(" ", n.Outputs.Select(x => x.GetName(m_context.PathTable).ToString(m_context.StringTable)).ToList());

                if (!outputsPresent)
                {
                    Contract.Assert(false, $"Pips must have been presented in dependency order: [build { ListOutputs(dependency) }] missing, dependency of [build { ListOutputs(node)} ]");
                }

                foreach (FileArtifact output in processOutputs.GetOutputFiles())
                {
                    if (node.Inputs.Contains(output.Path))
                    {
                        processBuilder.AddInputFile(output);
                    }
                }
            }
        }
示例#3
0
        private void SetEnvironmentVariables(ProcessBuilder processBuilder, NinjaNode node)
        {
            foreach (KeyValuePair <string, string> kvp in m_environmentVariables.Value)
            {
                if (kvp.Value != null)
                {
                    var envPipData = new PipDataBuilder(m_context.StringTable);
                    if (SpecialEnvironmentVariables.PassThroughPrefixes.All(prefix => !kvp.Key.StartsWith(prefix)))
                    {
                        envPipData.Add(kvp.Value);
                        processBuilder.SetEnvironmentVariable(StringId.Create(m_context.StringTable, kvp.Key), envPipData.ToPipData(string.Empty, PipDataFragmentEscaping.NoEscaping));
                    }
                }
            }

            foreach (var kvp in m_passThroughEnvironmentVariables.Value)
            {
                processBuilder.SetPassthroughEnvironmentVariable(StringId.Create(m_context.StringTable, kvp.Key));
            }

            foreach (var envVar in SpecialEnvironmentVariables.CloudBuildEnvironment)
            {
                processBuilder.SetPassthroughEnvironmentVariable(StringId.Create(m_context.StringTable, envVar));
            }

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

            // We will specify a different MSPDBSRV endpoint for every pip.
            // This means every pip that needs to communicate to MSPDBSRV will
            // spawn a different child process.
            // This is because if two pips use the same endpoint at the same time
            // then second one will fail after the first one finishes, because the
            // first one was the one that spawned MSPDBSRV.EXE as a child
            // (which gets killed).
            //
            // IMPORTANT: This will cause the build to fail if two pips target the same PDB file.
            // Both LINK.EXE and CL.EXE can use MSPDBSRV.EXE:
            //   - If all linkers specify a different /pdb argument (or don't specify this argument and
            //   are linking programs with different names
            //   [https://docs.microsoft.com/en-us/cpp/build/reference/debug-generate-debug-info])
            //   then this won't happen
            //
            //   - We're forcing the compiler to not output to PDBs (/Z7)
            //
            // so this should work in the normal cases.
            var mspdbsrvPipDataBuilder = new PipDataBuilder(m_context.StringTable);

            mspdbsrvPipDataBuilder.Add(PipConstructionUtilities.ComputeSha256(node.Command));   // Unique value for each pip
            processBuilder.SetEnvironmentVariable(
                StringId.Create(m_context.StringTable, SpecialEnvironmentVariables.MsPdvSrvEndpoint),
                mspdbsrvPipDataBuilder.ToPipData(string.Empty, PipDataFragmentEscaping.NoEscaping));
        }
示例#4
0
        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);
        }
示例#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);
        }
示例#6
0
        private bool TryBuildProcessAndSchedulePip(NinjaNode node, QualifierId qualifierId, out Process process)
        {
            process = null;
            if (node.Rule.Equals("phony"))
            {
                // For now, we are including phony rules ONLY as the final 'node', this is, the one that
                // corresponds to the target. TODO: Get rid of all phonies.
                // We can safely skip these, they only represent a rename in the graph.
                return(true);
            }
            using (var processBuilder = ProcessBuilder.Create(m_context.PathTable, m_context.GetPipDataBuilder()))
            {
                if (!TryConfigureProcessBuilder(processBuilder, node, qualifierId))
                {
                    // Error has been logged
                    return(false);
                }

                // Process all outputs and inputs
                AddOutputs(node, processBuilder);
                AddInputs(node, processBuilder);

                // Try to schedule the process pip
                if (!m_pipConstructionHelper.TryAddProcess(processBuilder, out ProcessOutputs outputs, out process))
                {
                    // Error has been logged
                    return(false);
                }

                // Add the computed outputs for this project, so dependencies can consume it
                m_processOutputs[node] = outputs;
                foreach (var output in outputs.GetOutputFiles())
                {
                    m_outputFileArtifacts[output.Path] = output;
                }

                return(true);
            }
        }
示例#7
0
        private bool TryConfigureProcessBuilder(ProcessBuilder processBuilder, NinjaNode node, QualifierId qualifierId)
        {
            SeparateExecutableFromCommands(node.Command, out string executable, out string args);

            if (!TryFindExecutablePath(executable, out AbsolutePath exePath))
            {
                Tracing.Logger.Log.InvalidExecutablePath(m_context.LoggingContext, Location.FromFile(m_specPath.ToString(m_context.PathTable)), node.Command);
                return(false);
            }

            FileArtifact prExeArtifact = FileArtifact.CreateSourceFile(exePath);

            processBuilder.Executable = prExeArtifact;
            processBuilder.AddInputFile(prExeArtifact);

            using (var pipDataBuilderWrapper = m_context.GetPipDataBuilder())
            {
                var pipDataBuilder = pipDataBuilderWrapper.Instance;
                pipDataBuilder.Add(args);
                processBuilder.ArgumentsBuilder.Add(pipDataBuilder.ToPipData(string.Empty, PipDataFragmentEscaping.NoEscaping));
            }


            if (node.ResponseFile.HasValue)
            {
                using (var pipDataBuilderWrapper = m_context.GetPipDataBuilder())
                {
                    var pipDataBuilder = pipDataBuilderWrapper.Instance;
                    pipDataBuilder.Add(node.ResponseFile?.Content);
                    PipData responseFileData = pipDataBuilder.ToPipData(string.Empty, PipDataFragmentEscaping.NoEscaping);

                    // We tell the process builder to write the response file but to not add any arguments to the process (requiresExplicitArgument = false)
                    // because that information is already in node.Command
                    var rspFileSpec = ResponseFileSpecification.Builder()
                                      .ExplicitData(responseFileData)
                                      .RequiresArgument(false)
                                      .ExplicitPath((AbsolutePath)node.ResponseFile?.Path)
                                      .Build();

                    processBuilder.SetResponseFileSpecification(rspFileSpec);
                }
            }

            // TODO: Maybe a better description. Add ninja description or change command for input/outputs
            processBuilder.ToolDescription = StringId.Create(m_context.StringTable,
                                                             I($"{m_moduleDefinition.Descriptor.Name} - {node.Rule} - {executable} :: [{node.Command}]"));


            processBuilder.Options |= Process.Options.AllowUndeclaredSourceReads | Process.Options.OutputsMustRemainWritable | Process.Options.OutputsMustRemainWritable;
            processBuilder.EnableTempDirectory();

            // Working directory - the directory containing the ninja spec file
            // Ninja generators may express paths relative to this
            processBuilder.WorkingDirectory = DirectoryArtifact.CreateWithZeroPartialSealId(m_specPath.GetParent(m_context.PathTable));

            // Untrack directories
            UntrackFilesAndDirectories(processBuilder);

            // Allow some surviving child process
            AddRequiredSurvivingChildren(processBuilder);

            // Environment variables
            SetEnvironmentVariables(processBuilder, node);
            return(true);
        }
 /// <summary>
 /// Projects should be added in topological order
 /// </summary>
 public NinjaSchedulingProjectBuilder Add(NinjaNode node)
 {
     Contract.Requires(node != null);
     m_projects.Add(node);
     return(this);
 }