| File: MacroProcessor.cs | Web Access |
| Project: src\sdk\src\TemplateEngine\Microsoft.TemplateEngine.Orchestrator.RunnableProjects\Microsoft.TemplateEngine.Orchestrator.RunnableProjects.csproj (Microsoft.TemplateEngine.Orchestrator.RunnableProjects) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Extensions.Logging; using Microsoft.TemplateEngine.Abstractions; using Microsoft.TemplateEngine.Orchestrator.RunnableProjects.Abstractions; using Microsoft.TemplateEngine.Orchestrator.RunnableProjects.Macros; using Microsoft.TemplateEngine.Utils; namespace Microsoft.TemplateEngine.Orchestrator.RunnableProjects { internal static class MacroProcessor { /// <summary> /// Processes the macros defined in <paramref name="macroConfigs"/>. /// </summary> /// <exception cref="TemplateAuthoringException">when <see cref="IGeneratedSymbolMacro"/> config is invalid.</exception> /// <exception cref="MacroProcessingException">when the error occurs when macro is processed.</exception> internal static void ProcessMacros( IEngineEnvironmentSettings environmentSettings, IReadOnlyList<IMacroConfig> macroConfigs, IVariableCollection variables) { bool deterministicMode = IsDeterministicModeEnabled(environmentSettings); Dictionary<string, IMacro> knownMacros = environmentSettings.Components.OfType<IMacro>().ToDictionary(m => m.Type, m => m); foreach (IMacroConfig config in macroConfigs) { // Errors in macro dependencies are not supposed to interrupt template generation. // Skip this macro and add info in the output. if (config is IMacroConfigDependency macroWithDep && macroWithDep.Dependencies.Any()) { HashSet<IMacroConfig> dependentMacros = GetDependentMacros(config, macroConfigs); if (dependentMacros.OfType<BaseMacroConfig>().Any(bmc => bmc.MacroErrors.Any())) { environmentSettings.Host.Logger.LogWarning( string.Format( LocalizableStrings.MacroProcessing_Warning_DependencyErrors, config.VariableName, string.Join(",", dependentMacros.OfType<BaseMacroConfig>().SelectMany(d => d.MacroErrors)))); continue; } } try { if (knownMacros.TryGetValue(config.Type, out IMacro executingMacro)) { if (deterministicMode && executingMacro is IDeterministicModeMacro detMacro) { detMacro.EvaluateConfigDeterministically(environmentSettings, variables, config); } else { executingMacro.EvaluateConfig(environmentSettings, variables, config); } } else { environmentSettings.Host.Logger.LogWarning(LocalizableStrings.MacroProcessor_Warning_UnknownMacro, config.VariableName, config.Type); } } //TemplateAuthoringException means that config was invalid, just pass it. catch (Exception ex) when (ex is not TemplateAuthoringException) { throw new MacroProcessingException(config, ex); } } } internal static IReadOnlyList<IMacroConfig> SortMacroConfigsByDependencies(IReadOnlyList<string> symbols, IReadOnlyList<IMacroConfig> macroConfigs) { IEnumerable<(IMacroConfig, HashSet<IMacroConfig>)> preparedMacroConfigs = macroConfigs.Select(mc => { if (mc is IMacroConfigDependency macroWithDeps) { macroWithDeps.ResolveSymbolDependencies(symbols); return (mc, GetDependentMacros(mc, macroConfigs)); } return (mc, new HashSet<IMacroConfig>()); }); DirectedGraph<IMacroConfig> parametersDependenciesGraph = new(preparedMacroConfigs.ToDictionary(mc => mc.Item1, mc => mc.Item2)); if (!parametersDependenciesGraph.TryGetTopologicalSort(out IReadOnlyList<IMacroConfig> sortedConfigs) && parametersDependenciesGraph.HasCycle(out IReadOnlyList<IMacroConfig> cycle)) { throw new TemplateAuthoringException( string.Format( LocalizableStrings.Authoring_CyclicDependencyInSymbols, cycle.Select(p => p.VariableName).ToCsvString()), "Symbol circle"); } return sortedConfigs; } private static HashSet<IMacroConfig> GetDependentMacros(IMacroConfig config, IReadOnlyList<IMacroConfig> allMacroConfigs) { HashSet<IMacroConfig> dependents = new(); if (config is not IMacroConfigDependency macroWithDep) { return dependents; } foreach (string dependent in macroWithDep.Dependencies) { IMacroConfig? macro = allMacroConfigs.FirstOrDefault(mc => mc.VariableName == dependent); if (macro != null) { dependents.Add(macro); } } return dependents; } private static bool IsDeterministicModeEnabled(IEngineEnvironmentSettings environmentSettings) { string? unparsedValue = environmentSettings.Environment.GetEnvironmentVariable("TEMPLATE_ENGINE_ENABLE_DETERMINISTIC_MODE"); return bool.TryParse(unparsedValue, out bool result) && result; } } }