private void SerializeComputedGraph(GenericJavaScriptGraph <DeserializedJavaScriptProject, YarnConfiguration> graph) { AbsolutePath outputDirectory = m_host.GetFolderForFrontEnd(Name); AbsolutePath outputFile = outputDirectory.Combine(m_context.PathTable, Guid.NewGuid().ToString()); // Make sure the directories are there FileUtilities.CreateDirectory(outputDirectory.ToString(m_context.PathTable)); try { File.WriteAllText(outputFile.ToString(m_context.PathTable), JObject.FromObject(graph, ConstructProjectGraphSerializer(JsonSerializerSettings)).ToString()); // Graph-related files are requested to be left on disk. Let's print a message with their location. JavaScript.Tracing.Logger.Log.GraphBuilderFilesAreNotRemoved(m_context.LoggingContext, outputFile.ToString(m_context.PathTable)); } catch (Exception ex) { // Serializing the graph is done on a best-effort basis. If there is any issues with it, just log it and move on. Tracing.Logger.Log.CannotSerializeGraphFile(m_context.LoggingContext, m_resolverSettings.Location(m_context.PathTable), outputFile.ToString(m_context.PathTable), ex.ToString()); } }
private async Task <Possible <(JavaScriptGraph <TGraphConfiguration>, GenericJavaScriptGraph <DeserializedJavaScriptProject, TGraphConfiguration>)> > ComputeBuildGraphAsync( AbsolutePath outputFile, BuildParameters.IBuildParameters buildParameters) { // Determine the base location to use for finding the graph construction tool if (!TryFindGraphBuilderToolLocation( m_resolverSettings, buildParameters, out AbsolutePath foundLocation, out string failure)) { Tracing.Logger.Log.CannotFindGraphBuilderTool( m_context.LoggingContext, m_resolverSettings.Location(m_context.PathTable), failure); return(new JavaScriptGraphConstructionFailure(m_resolverSettings, m_context.PathTable)); } string nodeExeLocation; if (m_resolverSettings.NodeExeLocation != null) { var specifiedNodeExe = m_resolverSettings.NodeExeLocation.GetValue(); AbsolutePath nodeExeLocationPath; if (specifiedNodeExe is FileArtifact fileArtifact) { nodeExeLocationPath = fileArtifact.Path; } else { var pathCollection = ((IReadOnlyList <DirectoryArtifact>)specifiedNodeExe).Select(dir => dir.Path); if (!FrontEndUtilities.TryFindToolInPath(m_context, m_host, pathCollection, new[] { "node", "node.exe" }, out nodeExeLocationPath)) { failure = $"'node' cannot be found under any of the provided paths '{string.Join(";", pathCollection.Select(path => path.ToString(m_context.PathTable)))}'."; Tracing.Logger.Log.CannotFindGraphBuilderTool( m_context.LoggingContext, m_resolverSettings.Location(m_context.PathTable), failure); return(new JavaScriptGraphConstructionFailure(m_resolverSettings, m_context.PathTable)); } } nodeExeLocation = nodeExeLocationPath.ToString(m_context.PathTable); // Most graph construction tools (yarn, rush, etc.) rely on node.exe being on the PATH. Make sure // that's the case by appending the PATH exposed to the graph construction process with the location of the // specified node.exe. By prepending PATH with it, we also make sure yarn/rush will be using the same version // of node the user specified. string pathWithNode = buildParameters.ContainsKey("PATH") ? buildParameters["PATH"] : string.Empty; var nodeDirectory = nodeExeLocationPath.GetParent(m_context.PathTable); if (nodeDirectory.IsValid) { pathWithNode = nodeDirectory.ToString(m_context.PathTable) + Path.PathSeparator + pathWithNode; } buildParameters = buildParameters.Override(new[] { new KeyValuePair <string, string>("PATH", pathWithNode) }); } else { // We always use cmd.exe as the tool so if the node.exe location is not provided we can just pass 'node.exe' and let PATH do the work. nodeExeLocation = "node.exe"; } SandboxedProcessResult result = await RunJavaScriptGraphBuilderAsync(nodeExeLocation, outputFile, buildParameters, foundLocation); string standardError = result.StandardError.CreateReader().ReadToEndAsync().GetAwaiter().GetResult(); if (result.ExitCode != 0) { Tracing.Logger.Log.ProjectGraphConstructionError( m_context.LoggingContext, m_resolverSettings.Location(m_context.PathTable), standardError); return(new JavaScriptGraphConstructionFailure(m_resolverSettings, m_context.PathTable)); } // If the tool exited gracefully, but standard error is not empty, that // is interpreted as a warning. We propagate that to the BuildXL log if (!string.IsNullOrEmpty(standardError)) { Tracing.Logger.Log.GraphConstructionFinishedSuccessfullyButWithWarnings( m_context.LoggingContext, m_resolverSettings.Location(m_context.PathTable), standardError); } TrackFilesAndEnvironment(result.AllUnexpectedFileAccesses, outputFile.GetParent(m_context.PathTable)); JsonSerializer serializer = ConstructProjectGraphSerializer(JsonSerializerSettings); using (var sr = new StreamReader(outputFile.ToString(m_context.PathTable))) using (var reader = new JsonTextReader(sr)) { var flattenedJavaScriptGraph = serializer.Deserialize <GenericJavaScriptGraph <DeserializedJavaScriptProject, TGraphConfiguration> >(reader); // If a custom script command callback is specified, give it a chance to alter the script commands of // each package if (m_resolverSettings.CustomScripts != null) { var projectsWithCustomScripts = new List <DeserializedJavaScriptProject>(flattenedJavaScriptGraph.Projects.Count); foreach (var project in flattenedJavaScriptGraph.Projects) { m_resolverSettings.Root.TryGetRelative(m_context.PathTable, project.ProjectFolder, out var relativeFolder); var maybeCustomScripts = ResolveCustomScripts(project.Name, relativeFolder); if (!maybeCustomScripts.Succeeded) { return(maybeCustomScripts.Failure); } var customScripts = maybeCustomScripts.Result; // A null customScript means the callback did not provide any customization projectsWithCustomScripts.Add(customScripts == null ? project : project.WithCustomScripts(customScripts)); } flattenedJavaScriptGraph = new GenericJavaScriptGraph <DeserializedJavaScriptProject, TGraphConfiguration>( projectsWithCustomScripts, flattenedJavaScriptGraph.Configuration); } Possible <JavaScriptGraph <TGraphConfiguration> > graph = ApplyBxlExecutionSemantics() ? ResolveGraphWithExecutionSemantics(flattenedJavaScriptGraph) : ResolveGraphWithoutExecutionSemantics(flattenedJavaScriptGraph); return(graph.Then(graph => new Possible <(JavaScriptGraph <TGraphConfiguration>, GenericJavaScriptGraph <DeserializedJavaScriptProject, TGraphConfiguration>)>((graph, flattenedJavaScriptGraph)))); } }
private async Task <Possible <(JavaScriptGraph <TGraphConfiguration>, GenericJavaScriptGraph <DeserializedJavaScriptProject, TGraphConfiguration>)> > ComputeBuildGraphAsync( AbsolutePath outputFile, BuildParameters.IBuildParameters buildParameters) { // Determine the base location to use for finding the graph construction tool if (!TryFindGraphBuilderToolLocation( m_resolverSettings, buildParameters, out AbsolutePath foundLocation, out string failure)) { Tracing.Logger.Log.CannotFindGraphBuilderTool( m_context.LoggingContext, m_resolverSettings.Location(m_context.PathTable), failure); return(new JavaScriptGraphConstructionFailure(m_resolverSettings, m_context.PathTable)); } SandboxedProcessResult result = await RunJavaScriptGraphBuilderAsync(outputFile, buildParameters, foundLocation); string standardError = result.StandardError.CreateReader().ReadToEndAsync().GetAwaiter().GetResult(); if (result.ExitCode != 0) { Tracing.Logger.Log.ProjectGraphConstructionError( m_context.LoggingContext, m_resolverSettings.Location(m_context.PathTable), standardError); return(new JavaScriptGraphConstructionFailure(m_resolverSettings, m_context.PathTable)); } // If the tool exited gracefully, but standard error is not empty, that // is interpreted as a warning. We propagate that to the BuildXL log if (!string.IsNullOrEmpty(standardError)) { Tracing.Logger.Log.GraphConstructionFinishedSuccessfullyButWithWarnings( m_context.LoggingContext, m_resolverSettings.Location(m_context.PathTable), standardError); } TrackFilesAndEnvironment(result.AllUnexpectedFileAccesses, outputFile.GetParent(m_context.PathTable)); JsonSerializer serializer = ConstructProjectGraphSerializer(JsonSerializerSettings); using (var sr = new StreamReader(outputFile.ToString(m_context.PathTable))) using (var reader = new JsonTextReader(sr)) { var flattenedJavaScriptGraph = serializer.Deserialize <GenericJavaScriptGraph <DeserializedJavaScriptProject, TGraphConfiguration> >(reader); // If a custom script command callback is specified, give it a chance to alter the script commands of // each package if (m_resolverSettings.CustomScripts != null) { var projectsWithCustomScripts = new List <DeserializedJavaScriptProject>(flattenedJavaScriptGraph.Projects.Count); foreach (var project in flattenedJavaScriptGraph.Projects) { m_resolverSettings.Root.TryGetRelative(m_context.PathTable, project.ProjectFolder, out var relativeFolder); var maybeCustomScripts = ResolveCustomScripts(project.Name, relativeFolder); if (!maybeCustomScripts.Succeeded) { return(maybeCustomScripts.Failure); } var customScripts = maybeCustomScripts.Result; // A null customScript means the callback did not provide any customization projectsWithCustomScripts.Add(customScripts == null ? project : project.WithCustomScripts(customScripts)); } flattenedJavaScriptGraph = new GenericJavaScriptGraph <DeserializedJavaScriptProject, TGraphConfiguration>( projectsWithCustomScripts, flattenedJavaScriptGraph.Configuration); } Possible <JavaScriptGraph <TGraphConfiguration> > graph = ApplyBxlExecutionSemantics() ? ResolveGraphWithExecutionSemantics(flattenedJavaScriptGraph) : ResolveGraphWithoutExecutionSemantics(flattenedJavaScriptGraph); return(graph.Then(graph => new Possible <(JavaScriptGraph <TGraphConfiguration>, GenericJavaScriptGraph <DeserializedJavaScriptProject, TGraphConfiguration>)>((graph, flattenedJavaScriptGraph)))); } }