forked from jasder/antlr
361 lines
11 KiB
C#
361 lines
11 KiB
C#
/*
|
|
* [The "BSD licence"]
|
|
* Copyright (c) 2013 Sam Harwell
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
namespace Antlr4.Build.Tasks
|
|
{
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Security;
|
|
using System.Security.Policy;
|
|
using System.Threading;
|
|
using Microsoft.Build.Framework;
|
|
using Microsoft.Build.Utilities;
|
|
using Directory = System.IO.Directory;
|
|
using File = System.IO.File;
|
|
using FileAttributes = System.IO.FileAttributes;
|
|
using Path = System.IO.Path;
|
|
|
|
public class Antlr4ClassGenerationTask
|
|
: Task
|
|
{
|
|
private static AppDomain _sharedAppDomain;
|
|
|
|
private const string DefaultGeneratedSourceExtension = "g4";
|
|
private List<ITaskItem> _generatedCodeFiles = new List<ITaskItem>();
|
|
|
|
public Antlr4ClassGenerationTask()
|
|
{
|
|
this.GeneratedSourceExtension = DefaultGeneratedSourceExtension;
|
|
}
|
|
|
|
[Required]
|
|
public string ToolPath
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
[Required]
|
|
public string OutputPath
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public string TargetLanguage
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public string TargetFrameworkVersion
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public string BuildTaskPath
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public ITaskItem[] SourceCodeFiles
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public ITaskItem[] TokensFiles
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public ITaskItem[] AbstractGrammarFiles
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public string GeneratedSourceExtension
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public string TargetNamespace
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public string[] LanguageSourceExtensions
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public bool GenerateListener
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public bool GenerateVisitor
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public bool ForceAtn
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public bool AbstractGrammar
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
[Required]
|
|
public string JavaVendor
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
[Required]
|
|
public string JavaInstallation
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
[Output]
|
|
public ITaskItem[] GeneratedCodeFiles
|
|
{
|
|
get
|
|
{
|
|
return this._generatedCodeFiles.ToArray();
|
|
}
|
|
set
|
|
{
|
|
this._generatedCodeFiles = new List<ITaskItem>(value);
|
|
}
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
|
|
public AppDomain GetAntlrTaskAppDomain()
|
|
{
|
|
if (_sharedAppDomain != null)
|
|
return _sharedAppDomain;
|
|
|
|
AppDomainSetup info = new AppDomainSetup
|
|
{
|
|
ApplicationBase = BuildTaskPath,
|
|
LoaderOptimization = LoaderOptimization.MultiDomainHost,
|
|
ShadowCopyFiles = "true"
|
|
};
|
|
|
|
string friendlyName = "AntlrClassGenerationDomain_" + Guid.NewGuid();
|
|
_sharedAppDomain = AppDomain.CreateDomain(friendlyName, AppDomain.CurrentDomain.Evidence, info, new NamedPermissionSet("FullTrust"), new StrongName[0]);
|
|
return _sharedAppDomain;
|
|
}
|
|
|
|
public override bool Execute()
|
|
{
|
|
AppDomain domain = null;
|
|
bool success;
|
|
|
|
if (!Path.IsPathRooted(ToolPath))
|
|
ToolPath = Path.Combine(Path.GetDirectoryName(BuildEngine.ProjectFileOfTaskNode), ToolPath);
|
|
|
|
if (!Path.IsPathRooted(BuildTaskPath))
|
|
BuildTaskPath = Path.Combine(Path.GetDirectoryName(BuildEngine.ProjectFileOfTaskNode), BuildTaskPath);
|
|
|
|
try
|
|
{
|
|
domain = GetAntlrTaskAppDomain();
|
|
AntlrClassGenerationTaskInternal wrapper = CreateBuildTaskWrapper(domain);
|
|
success = wrapper.Execute();
|
|
|
|
if (success)
|
|
{
|
|
_generatedCodeFiles.AddRange(wrapper.GeneratedCodeFiles.Select(file => (ITaskItem)new TaskItem(file)));
|
|
}
|
|
|
|
foreach (BuildMessage message in wrapper.BuildMessages)
|
|
{
|
|
ProcessBuildMessage(message);
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
if (IsFatalException(exception))
|
|
throw;
|
|
|
|
ProcessExceptionAsBuildMessage(exception);
|
|
success = false;
|
|
}
|
|
finally
|
|
{
|
|
if (domain != null && domain != _sharedAppDomain)
|
|
AppDomain.Unload(domain);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
private void ProcessExceptionAsBuildMessage(Exception exception)
|
|
{
|
|
ProcessBuildMessage(new BuildMessage(exception.Message));
|
|
}
|
|
|
|
private void ProcessBuildMessage(BuildMessage message)
|
|
{
|
|
string logMessage;
|
|
string errorCode;
|
|
errorCode = Log.ExtractMessageCode(message.Message, out logMessage);
|
|
if (string.IsNullOrEmpty(errorCode))
|
|
{
|
|
errorCode = "AC1000";
|
|
logMessage = "Unknown build error: " + message.Message;
|
|
}
|
|
|
|
string subcategory = null;
|
|
string helpKeyword = null;
|
|
|
|
switch (message.Severity)
|
|
{
|
|
case TraceLevel.Error:
|
|
this.Log.LogError(subcategory, errorCode, helpKeyword, message.FileName, message.LineNumber, message.ColumnNumber, 0, 0, logMessage);
|
|
break;
|
|
case TraceLevel.Warning:
|
|
this.Log.LogWarning(subcategory, errorCode, helpKeyword, message.FileName, message.LineNumber, message.ColumnNumber, 0, 0, logMessage);
|
|
break;
|
|
case TraceLevel.Info:
|
|
this.Log.LogMessage(MessageImportance.Normal, logMessage);
|
|
break;
|
|
case TraceLevel.Verbose:
|
|
this.Log.LogMessage(MessageImportance.Low, logMessage);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private AntlrClassGenerationTaskInternal CreateBuildTaskWrapper(AppDomain domain)
|
|
{
|
|
AntlrClassGenerationTaskInternal wrapper = (AntlrClassGenerationTaskInternal)domain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(AntlrClassGenerationTaskInternal).FullName);
|
|
|
|
IList<string> sourceCodeFiles = null;
|
|
if (this.SourceCodeFiles != null)
|
|
{
|
|
sourceCodeFiles = new List<string>(SourceCodeFiles.Length);
|
|
foreach (ITaskItem taskItem in SourceCodeFiles)
|
|
sourceCodeFiles.Add(taskItem.ItemSpec);
|
|
}
|
|
|
|
if (this.TokensFiles != null && this.TokensFiles.Length > 0)
|
|
{
|
|
Directory.CreateDirectory(OutputPath);
|
|
|
|
HashSet<string> copied = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (ITaskItem taskItem in TokensFiles)
|
|
{
|
|
string fileName = taskItem.ItemSpec;
|
|
if (!File.Exists(fileName))
|
|
{
|
|
Log.LogError("The tokens file '{0}' does not exist.", fileName);
|
|
continue;
|
|
}
|
|
|
|
string vocabName = Path.GetFileNameWithoutExtension(fileName);
|
|
if (!copied.Add(vocabName))
|
|
{
|
|
Log.LogWarning("The tokens file '{0}' conflicts with another tokens file in the same project.", fileName);
|
|
continue;
|
|
}
|
|
|
|
string target = Path.Combine(OutputPath, Path.GetFileName(fileName));
|
|
if (!Path.GetExtension(target).Equals(".tokens", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
Log.LogError("The destination for the tokens file '{0}' did not have the correct extension '.tokens'.", target);
|
|
continue;
|
|
}
|
|
|
|
File.Copy(fileName, target, true);
|
|
File.SetAttributes(target, File.GetAttributes(target) & ~FileAttributes.ReadOnly);
|
|
}
|
|
}
|
|
|
|
wrapper.ToolPath = ToolPath;
|
|
wrapper.SourceCodeFiles = sourceCodeFiles;
|
|
wrapper.TargetLanguage = TargetLanguage;
|
|
wrapper.TargetFrameworkVersion = TargetFrameworkVersion;
|
|
wrapper.OutputPath = OutputPath;
|
|
wrapper.LanguageSourceExtensions = LanguageSourceExtensions;
|
|
wrapper.TargetNamespace = TargetNamespace;
|
|
wrapper.GenerateListener = GenerateListener;
|
|
wrapper.GenerateVisitor = GenerateVisitor;
|
|
wrapper.ForceAtn = ForceAtn;
|
|
wrapper.AbstractGrammar = AbstractGrammar;
|
|
wrapper.JavaVendor = JavaVendor;
|
|
wrapper.JavaInstallation = JavaInstallation;
|
|
return wrapper;
|
|
}
|
|
|
|
internal static bool IsFatalException(Exception exception)
|
|
{
|
|
while (exception != null)
|
|
{
|
|
if ((exception is OutOfMemoryException)
|
|
|| (exception is InsufficientMemoryException)
|
|
|| (exception is ThreadAbortException))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (!(exception is TypeInitializationException) && !(exception is TargetInvocationException))
|
|
{
|
|
break;
|
|
}
|
|
|
|
exception = exception.InnerException;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|