diff --git a/Antlr4BuildTasks/Antlr4BuildTasks.targets b/Antlr4BuildTasks/Antlr4BuildTasks.targets index f5690e2e..38f4448b 100755 --- a/Antlr4BuildTasks/Antlr4BuildTasks.targets +++ b/Antlr4BuildTasks/Antlr4BuildTasks.targets @@ -123,6 +123,7 @@ USERPROFILE/.node/ true + @@ -169,6 +170,7 @@ NodeDownloadDirectory="%(Antlr4.NodeDownloadDirectory)" AntlrNgPath="%(Antlr4.AntlrNgPath)" Visitor="%(Antlr4.Visitor)" + WorkingDirectory="%(Antlr4.WorkingDirectory)" > diff --git a/Antlr4BuildTasks/Tasks/RunAntlrTool.cs b/Antlr4BuildTasks/Tasks/RunAntlrTool.cs index f6ccb891..46437856 100755 --- a/Antlr4BuildTasks/Tasks/RunAntlrTool.cs +++ b/Antlr4BuildTasks/Tasks/RunAntlrTool.cs @@ -120,6 +120,7 @@ [Output] public ITaskItem[] GeneratedDirectories public string NodeExec { get; set; } public string NodeDownloadDirectory { get; set; } public string AntlrNgPath { get; set; } + public string WorkingDirectory { get; set; } public async System.Threading.Tasks.Task DownloadFileAsync(string uri, string outputPath) { @@ -219,7 +220,8 @@ public override bool Execute() Package = Package, DOptions = DOptions, TreatWarningsAsErrors = Error, - ForceATN = ForceAtn + ForceATN = ForceAtn, + WorkingDirectory = WorkingDirectory }; tool = ngTool; @@ -253,7 +255,8 @@ public override bool Execute() Package = Package, DOptions = DOptions, TreatWarningsAsErrors = Error, - ForceATN = ForceAtn + ForceATN = ForceAtn, + WorkingDirectory = WorkingDirectory }; tool = javaTool; diff --git a/Antlr4BuildTasks/Tools/AntlrNgTool.cs b/Antlr4BuildTasks/Tools/AntlrNgTool.cs index 8ead1400..db5656b6 100644 --- a/Antlr4BuildTasks/Tools/AntlrNgTool.cs +++ b/Antlr4BuildTasks/Tools/AntlrNgTool.cs @@ -189,8 +189,8 @@ private List BuildGenerationArguments(IEnumerable grammarFiles) // Add separator to prevent grammar files from being interpreted as option values arguments.Add("--"); - // Grammar files - arguments.AddRange(grammarFiles.Select(NormalizePath)); + // Grammar files - use relative path so "Generated from" comment is stable + arguments.AddRange(grammarFiles.Select(MakeGrammarRelativePath)); return arguments; } diff --git a/Antlr4BuildTasks/Tools/AntlrToolBase.cs b/Antlr4BuildTasks/Tools/AntlrToolBase.cs index 2f993e9c..a13e6c58 100644 --- a/Antlr4BuildTasks/Tools/AntlrToolBase.cs +++ b/Antlr4BuildTasks/Tools/AntlrToolBase.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -31,6 +32,7 @@ protected AntlrToolBase() public string DOptions { get; set; } public bool TreatWarningsAsErrors { get; set; } public bool ForceATN { get; set; } + public string WorkingDirectory { get; set; } public abstract bool Setup(); @@ -89,6 +91,11 @@ protected bool ExecuteProcess( RedirectStandardError = true, }; + if (ShouldUseWorkingDirectory(WorkingDirectory)) + { + startInfo.WorkingDirectory = WorkingDirectory; + } + MessageQueue.EnqueueMessage(Message.BuildInfoMessage( $"Executing command: \"{startInfo.FileName}\" {startInfo.Arguments}")); @@ -133,6 +140,28 @@ protected string NormalizePath(string path) return path?.Replace("\\", "/"); } + /// + /// Converts an absolute grammar file path to be relative to WorkingDirectory. + /// If WorkingDirectory is not set or does not exist, returns the path as-is (normalized). + /// + protected string MakeGrammarRelativePath(string grammarFile) + { + if (string.IsNullOrEmpty(grammarFile) || !ShouldUseWorkingDirectory(WorkingDirectory)) + return NormalizePath(grammarFile); + + var fullPath = Path.GetFullPath(grammarFile); + var workDir = Path.GetFullPath(WorkingDirectory); + if (!workDir.EndsWith(Path.DirectorySeparatorChar.ToString())) + workDir += Path.DirectorySeparatorChar; + + if (fullPath.StartsWith(workDir, StringComparison.OrdinalIgnoreCase)) + { + return NormalizePath(fullPath.Substring(workDir.Length)); + } + + return NormalizePath(grammarFile); + } + /// /// Splits a semicolon-separated list and filters empty entries /// @@ -145,5 +174,13 @@ protected IEnumerable SplitAndFilterList(string list) .Select(s => s.Trim()) .Where(s => !string.IsNullOrWhiteSpace(s)); } + + /// + /// Returns true if WorkingDirectory is set and the directory exists on disk. + /// + private static bool ShouldUseWorkingDirectory(string workingDirectory) + { + return !string.IsNullOrEmpty(workingDirectory) && Directory.Exists(workingDirectory); + } } } diff --git a/Antlr4BuildTasks/Tools/IAntlrTool.cs b/Antlr4BuildTasks/Tools/IAntlrTool.cs index 8b6b6670..9657ba14 100644 --- a/Antlr4BuildTasks/Tools/IAntlrTool.cs +++ b/Antlr4BuildTasks/Tools/IAntlrTool.cs @@ -98,5 +98,12 @@ bool DiscoverGeneratedFiles( /// Gets or sets whether to force ATN for all decisions /// bool ForceATN { get; set; } + + /// + /// Gets or sets the working directory for the tool process. + /// When set, grammar file paths are converted to be relative to this directory + /// so that the "Generated from" comment in output files uses relative paths. + /// + string WorkingDirectory { get; set; } } } diff --git a/Antlr4BuildTasks/Tools/JavaAntlrTool.cs b/Antlr4BuildTasks/Tools/JavaAntlrTool.cs index 407a8cd8..36313664 100644 --- a/Antlr4BuildTasks/Tools/JavaAntlrTool.cs +++ b/Antlr4BuildTasks/Tools/JavaAntlrTool.cs @@ -145,8 +145,8 @@ private List BuildDependencyArguments(string grammarFile) // Common arguments AddCommonArguments(grammarFile.EndsWith("Parser.g4"), arguments); - // Grammar files - arguments.Add(grammarFile); + // Grammar files - use relative path so "Generated from" comment is stable + arguments.Add(MakeGrammarRelativePath(grammarFile)); return arguments; } @@ -167,8 +167,8 @@ private List BuildGenerationArguments(string grammarFile) if (EnableLogging) arguments.Add("-Xlog"); - // Grammar files - arguments.Add(grammarFile); + // Grammar files - use relative path so "Generated from" comment is stable + arguments.Add(MakeGrammarRelativePath(grammarFile)); return arguments; }