Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Propogate additional compilation data from RoslynCompilationService
Browse files Browse the repository at this point in the history
* Update CompilationFailedException to include path of file being compiled
* Pass in path being compiled to Rolsyn.
* Adding doc comments for compilation pieces

Fixes #869
  • Loading branch information
pranavkm committed Jul 28, 2014
1 parent b0d52f7 commit b54ea6b
Show file tree
Hide file tree
Showing 13 changed files with 213 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,57 @@

namespace Microsoft.AspNet.Mvc.Razor
{
/// <summary>
/// An exception that is thrown when acessing the result of a failed compilation.
/// </summary>
public class CompilationFailedException : Exception
{
public CompilationFailedException(IEnumerable<CompilationMessage> messages, string generatedCode)
/// <summary>
/// Instantiates a new instance of <see cref="CompilationFailedException"/>.
/// </summary>
/// <param name="filePath">The path to the file that was compiled.</param>
/// <param name="compiledCode">The contents that were compiled.</param>
/// <param name="messages">A sequence of <see cref="CompilationMessage"/> encountered during compilation.</param>
public CompilationFailedException(
[NotNull] string filePath,
[NotNull] string compiledCode,
[NotNull] IEnumerable<CompilationMessage> messages)
: base(FormatMessage(messages))
{
FilePath = filePath;
Messages = messages.ToList();
GeneratedCode = generatedCode;
CompiledContent = compiledCode;
}

public string GeneratedCode { get; private set; }
/// <summary>
/// Gets the path to the file that produced the compilation failure..
/// </summary>
public string FilePath { get; private set; }

/// <summary>
/// Gets a sequence of <see cref="CompilationMessage"/> encountered during compilation.
/// </summary>
public IEnumerable<CompilationMessage> Messages { get; private set; }

public string CompilationSource
{
get { return GeneratedCode; }
}
/// <summary>
/// Gets the content that was compiled.
/// </summary>
public string CompiledContent { get; private set; }

/// <inheritdoc />
public override string Message
{
get
{
return "Compilation Failed:" + FormatMessage(Messages);
return Resources.FormatCompilationFailed(FilePath) +
Environment.NewLine +
FormatMessage(Messages);
}
}

private static string FormatMessage(IEnumerable<CompilationMessage> messages)
{
return String.Join(Environment.NewLine, messages);
return string.Join(Environment.NewLine, messages);
}
}
}
11 changes: 11 additions & 0 deletions src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilationMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,26 @@

namespace Microsoft.AspNet.Mvc.Razor
{
/// <summary>
/// Represents a message encountered during compilation.
/// </summary>
public class CompilationMessage
{
/// <summary>
/// Initializes a <see cref="CompilationMessage"/> with the specified message.
/// </summary>
/// <param name="message">A message produced from compilation.</param>
public CompilationMessage(string message)
{
Message = message;
}

/// <summary>
/// Gets a message produced from compilation.
/// </summary>
public string Message { get; private set; }

/// <inheritdoc />
public override string ToString()
{
return Message;
Expand Down
91 changes: 78 additions & 13 deletions src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilationResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,111 @@

using System;
using System.Collections.Generic;
using System.Linq;

namespace Microsoft.AspNet.Mvc.Razor
{
/// <summary>
/// Represents the result of compilation.
/// </summary>
public class CompilationResult
{
private readonly Type _type;
private Type _type;

private CompilationResult(string generatedCode, Type type, IEnumerable<CompilationMessage> messages)
private CompilationResult()
{
_type = type;
GeneratedCode = generatedCode;
Messages = messages.ToList();
}

/// <summary>
/// Gets the path of the file that was compiled.
/// </summary>
public string FilePath { get; private set; }

/// <summary>
/// Gets a sequence of <see cref="CompilationMessage"/> encountered during compilation.
/// </summary>
public IEnumerable<CompilationMessage> Messages { get; private set; }

public string GeneratedCode { get; private set; }

/// <summary>
/// Gets additional information from compilation.
/// </summary>
/// <remarks>
/// In the event of a compilation failure, values from additional info
/// are copied to the <see cref="Exception.Data"/> property of the exception thrown.
/// </remarks>
public IDictionary<string, object> AdditionalInfo
{
get; private set;
}

/// <summary>
/// Gets the content that was compiled.
/// </summary>
public string CompiledContent { get; private set; }

/// <summary>
/// Gets the type produced as a result of compilation.
/// </summary>
/// <exception cref="CompilationFailedException">An error occured during compilation.</exception>
public Type CompiledType
{
get
{
if (_type == null)
{
throw new CompilationFailedException(Messages, GeneratedCode);
throw CreateCompilationFailedException();
}

return _type;
}
}

public static CompilationResult Failed(string generatedCode, IEnumerable<CompilationMessage> messages)
/// <summary>
/// Creates a <see cref="CompilationResult"/> that represents a failure in compilation.
/// </summary>
/// <param name="filePath">The path of the file that was compiled.</param>
/// <param name="compiledContent">The content that was compiled.</param>
/// <param name="messages">The sequence of failure messages encountered during compilation.</param>
/// <param name="additionalInfo">Additional info about the compilation.</param>
/// <returns>A CompilationResult instance representing a failure.</returns>
public static CompilationResult Failed([NotNull] string filePath,
[NotNull] string compiledContent,
[NotNull] IEnumerable<CompilationMessage> messages,
IDictionary<string, object> additionalInfo)
{
return new CompilationResult(generatedCode, type: null, messages: messages);
return new CompilationResult
{
CompiledContent = compiledContent,
Messages = messages,
FilePath = filePath,
AdditionalInfo = additionalInfo
};
}

public static CompilationResult Successful(string generatedCode, Type type)
/// <summary>
/// Creates a <see cref="CompilationResult"/> that represents a success in compilation.
/// </summary>
/// <param name="type">The compiled type.</param>
/// <returns>A CompilationResult instance representing a success.</returns>
public static CompilationResult Successful([NotNull] Type type)
{
return new CompilationResult(generatedCode, type, Enumerable.Empty<CompilationMessage>());
return new CompilationResult
{
_type = type
};
}

private CompilationFailedException CreateCompilationFailedException()
{
var exception = new CompilationFailedException(FilePath, CompiledContent, Messages);
if (AdditionalInfo != null)
{
foreach (var item in AdditionalInfo)
{
exception.Data.Add(item.Key, item.Value);
}
}

return exception;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public CompilationResult GetOrAdd(IFileInfo file, Func<CompilationResult> compil
return result;
}

return CompilationResult.Successful(generatedCode: null, type: compiledType);
return CompilationResult.Successful(compiledType);
}
}
}
15 changes: 12 additions & 3 deletions src/Microsoft.AspNet.Mvc.Razor/Compilation/ICompilationService.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Threading.Tasks;

namespace Microsoft.AspNet.Mvc.Razor
{
/// <summary>
/// Provides methods for compilation.
/// </summary>
public interface ICompilationService
{
CompilationResult Compile(string content);
/// <summary>
/// Compiles content and returns the result of compilation.
/// </summary>
/// <param name="path">The path where the source file is located.</param>
/// <param name="content">The contents to be compiled.</param>
/// <returns>
/// A CompilationResult representing the result of compilation.
/// </returns>
CompilationResult Compile(string path, string content);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,34 @@
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Framework.Runtime;

namespace Microsoft.AspNet.Mvc.Razor.Compilation
{
/// <summary>
/// A type that provides compilation of C# content using Roslyn.
/// </summary>
public class RoslynCompilationService : ICompilationService
{
private static readonly ConcurrentDictionary<string, MetadataReference> _metadataFileCache =
public static readonly string CompilationResultDiagnosticsKey = "Diagnostics";
private readonly ConcurrentDictionary<string, MetadataReference> _metadataFileCache =
new ConcurrentDictionary<string, MetadataReference>(StringComparer.OrdinalIgnoreCase);

private readonly ILibraryManager _libraryManager;
private readonly IApplicationEnvironment _environment;
private readonly IAssemblyLoaderEngine _loader;

/// <summary>
/// Initalizes Roslyn based <see cref="ICompilationService"/>.
/// </summary>
/// <param name="environment">The environment for the executing application.</param>
/// <param name="loaderEngine">The loader used to load compiled assemblies.</param>
/// <param name="libraryManager">The library manager that provides export and reference information.</param>
public RoslynCompilationService(IApplicationEnvironment environment,
IAssemblyLoaderEngine loaderEngine,
ILibraryManager libraryManager)
Expand All @@ -32,9 +44,11 @@ public RoslynCompilationService(IApplicationEnvironment environment,
_libraryManager = libraryManager;
}

public CompilationResult Compile(string content)
/// <inheritdoc />
public CompilationResult Compile(string path, string content)
{
var syntaxTrees = new[] { CSharpSyntaxTree.ParseText(content) };
var sourceText = SourceText.From(content, Encoding.UTF8);
var syntaxTrees = new[] { CSharpSyntaxTree.ParseText(sourceText, path: path) };
var targetFramework = _environment.TargetFramework;

var references = GetApplicationReferences();
Expand Down Expand Up @@ -70,7 +84,11 @@ public CompilationResult Compile(string content)
.Select(d => GetCompilationMessage(formatter, d))
.ToList();

return CompilationResult.Failed(content, messages);
var additionalInfo = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
{
{ CompilationResultDiagnosticsKey, result.Diagnostics }
};
return CompilationResult.Failed(path, content, messages, additionalInfo);
}

Assembly assembly;
Expand All @@ -89,7 +107,7 @@ public CompilationResult Compile(string content)
var type = assembly.GetExportedTypes()
.First();

return CompilationResult.Successful(string.Empty, type);
return CompilationResult.Successful(type);
}
}
}
Expand Down Expand Up @@ -135,12 +153,12 @@ private MetadataReference CreateMetadataFileReference(string path)
});
}

private CompilationMessage GetCompilationMessage(DiagnosticFormatter formatter, Diagnostic diagnostic)
private static CompilationMessage GetCompilationMessage(DiagnosticFormatter formatter, Diagnostic diagnostic)
{
return new CompilationMessage(formatter.Format(diagnostic));
}

private bool IsError(Diagnostic diagnostic)
private static bool IsError(Diagnostic diagnostic)
{
return diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error;
}
Expand Down
16 changes: 16 additions & 0 deletions src/Microsoft.AspNet.Mvc.Razor/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ public CompilationResult Compile([NotNull] IFileInfo file)
return _cache.GetOrAdd(file, () => CompileCore(file));
}

// TODO: Make this internal
public CompilationResult CompileCore(IFileInfo file)
internal CompilationResult CompileCore(IFileInfo file)
{
GeneratorResults results;
using (var inputStream = file.CreateReadStream())
Expand All @@ -49,10 +48,10 @@ public CompilationResult CompileCore(IFileInfo file)
if (!results.Success)
{
var messages = results.ParserErrors.Select(e => new CompilationMessage(e.Message));
throw new CompilationFailedException(messages, results.GeneratedCode);
throw new CompilationFailedException(file.PhysicalPath, results.GeneratedCode, messages);
}

return _baseCompilationService.Compile(results.GeneratedCode);
return _baseCompilationService.Compile(file.PhysicalPath, results.GeneratedCode);
}

private static string EnsureTrailingSlash([NotNull]string path)
Expand Down
3 changes: 3 additions & 0 deletions src/Microsoft.AspNet.Mvc.Razor/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@
<data name="ArgumentCannotBeNullOrEmpty" xml:space="preserve">
<value>The value cannot be null or empty.</value>
</data>
<data name="CompilationFailed" xml:space="preserve">
<value>Compilation for '{0}' failed:</value>
</data>
<data name="LayoutCannotBeLocated" xml:space="preserve">
<value>The layout view '{0}' could not be located.</value>
</data>
Expand Down
Loading

0 comments on commit b54ea6b

Please sign in to comment.