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); }
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); } } } }
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)); }
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); }
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); }
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); } }
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); }