File: MS\Internal\Tasks\CompilerWrapper.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationBuildTasks\PresentationBuildTasks.csproj (PresentationBuildTasks)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
//----------------------------------------------------------------------------------------
//
// Description:
//       A wrapper class which can take inputs from build task and call the underneath
//       MarkupCompiler, CompilationUnit and other related types to do the real
//       compilation work.
//       This wrapper runs in the same Appdomain as MarkupCompiler, CompilationUnit,
//
//       It miminizes the communication between two domains among the build tasks and
//       the real compiler classes.
//
//---------------------------------------------------------------------------------------
 
using System;
using System.IO;
using System.Collections;
using System.Reflection;
using System.Runtime.InteropServices;
 
using MS.Internal.Markup;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using MS.Internal.Tasks;
using MS.Utility;
 
namespace MS.Internal
{
    // <summary>
    // CompilerWrapper
    // </summary>
    internal class CompilerWrapper : MarshalByRefObject
    {
        // <summary>
        // ctor
        // </summary>
        internal CompilerWrapper()
        {
            _mc = new MarkupCompiler();
            _sourceDir = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar;
            _nErrors = 0;
        }
 
        // <summary>
        // The valid source file extension for the passed language.
        // Normally a language supports more valid source file extensions.
        // User could choose one of them in project file.
        // If this property is not set, we will take use of the default one for the language.
        // </summary>
        internal string LanguageSourceExtension
        {
            set { _mc.LanguageSourceExtension = value; }
        }
 
        //<summary>
        // OutputPath : Generated code files, Baml fles will be put in this directory.
        //</summary>
        internal string OutputPath
        {
            set { _mc.TargetPath = value; }
        }
 
        // <summary>
        // The version of the assembly
        // </summary>
        internal string AssemblyVersion
        {
            set { _mc.AssemblyVersion = value; }
        }
 
        // <summary>
        // The key token of the assembly
        // </summary>
        internal string AssemblyPublicKeyToken
        {
            set { _mc.AssemblyPublicKeyToken = value; }
        }
 
        //<summary>
        // ApplicationMarkup
        //</summary>
        internal FileUnit ApplicationMarkup
        {
            set { _applicationMarkup = value; }
        }
 
        // <summary>
        // Loose file content list
        // </summary>
        internal string[] ContentFiles
        {
            set { _mc.ContentList = value; }
        }
 
        /// <summary>
        /// Splash screen image to be displayed before application init
        /// </summary>
        internal string SplashImage
        {
            set { _mc.SplashImage = value; }
        }
 
        // <summary>
        // Assembly References.
        // </summary>
        // <value></value>
        internal ArrayList References
        {
            set { _mc.ReferenceAssemblyList = value; }
        }
 
        //<summary>
        //If true code for supporting hosting in Browser is generated
        //</summary>
        internal bool HostInBrowser
        {
            set { _mc.HostInBrowser = value; }
        }
 
        //<summary>
        // Generated debugging information in the BAML file.
        //</summary>
        internal bool XamlDebuggingInformation
        {
            set { _mc.XamlDebuggingInformation = value; }
        }
 
        /// <summary>
        /// Sets the checksum algorithm used in code-behind.
        /// </summary>
        internal string ChecksumAlgorithm
        {
            set => _mc.ChecksumAlgorithm = value;
        }
 
        // <summary>
        // Controls how to generate localization information for each xaml file.
        // Valid values: None, CommentsOnly, All.
        //
        // </summary>
        internal int LocalizationDirectivesToLocFile
        {
            set { _localizationDirectivesToLocFile = value; }
        }
 
        // <summary>
        // Keep the application definition file if it contains local types which are implemented
        // in current target assembly.
        // </summary>
        internal string LocalXamlApplication
        {
            get { return _mc.LocalXamlApplication; }
        }
 
 
        // <summary>
        // Keep the page markup files if they contain local types which are implemented
        // in current target assembly.
        // </summary>
        internal string[] LocalXamlPages
        {
            get { return _mc.LocalXamlPages; }
        }
 
 
        /// <summary>
        /// Get/Sets the TaskFileService on MarkupCompiler
        /// </summary>
        internal ITaskFileService TaskFileService
        {
            get { return _mc.TaskFileService; }
            set { _mc.TaskFileService = value; }
        }
 
        //
        // A wrapper property which maps to MarkupCompiler class's corresponding property.
        // This property will be called by MarkupCompilePass1 and Pass2 build tasks.
        //
        // It indicates whether any xaml file in current xaml list references internal types
        // from friend assembly or local target assembly.
        //
        internal bool HasInternals
        {
            get { return MarkupCompiler.HasInternals; }
        }
 
        // <summary>
        // TaskLoggingHelper
        // </summary>
        internal TaskLoggingHelper TaskLogger
        {
            set
            {
                _taskLogger = value;
                _mc.TaskLogger = value;
            }
        }
 
        internal string UnknownErrorID
        {
            set { _unknownErrorID = value; }
        }
 
        internal int ErrorTimes
        {
            get { return _nErrors; }
        }
 
        internal bool SupportCustomOutputPaths 
        {
            set { _mc.SupportCustomOutputPaths = value; }
        }
 
        // <summary>
        // Start the compilation.
        // </summary>
        // <param name="assemblyName"></param>
        // <param name="language"></param>
        // <param name="rootNamespace"></param>
        // <param name="fileList"></param>
        // <param name="isSecondPass"></param>
        // <returns></returns>
        internal bool DoCompilation(string assemblyName, string language, string rootNamespace, FileUnit[] fileList, bool isSecondPass)
        {
            bool ret = true;
 
            CompilationUnit compUnit = new CompilationUnit(assemblyName, language, rootNamespace, fileList);
            compUnit.Pass2 = isSecondPass;
 
            // Set some properties required by the CompilationUnit
            compUnit.ApplicationFile = _applicationMarkup;
            compUnit.SourcePath = _sourceDir;
 
            //Set the properties required by MarkupCompiler
 
            _mc.SourceFileResolve += new SourceFileResolveEventHandler(OnSourceFileResolve);
            _mc.Error += new MarkupErrorEventHandler(OnCompilerError);
 
            LocalizationDirectivesToLocFile localizeFlag = (LocalizationDirectivesToLocFile)_localizationDirectivesToLocFile;
 
 
            //
            // Localization file should not be generated for Intellisense build. Thus
            // checking IsRealBuild.
            //
            if ((localizeFlag == MS.Internal.LocalizationDirectivesToLocFile.All
                 || localizeFlag == MS.Internal.LocalizationDirectivesToLocFile.CommentsOnly)
                && (TaskFileService.IsRealBuild))
            {
                _mc.ParserHooks = new LocalizationParserHooks(_mc, localizeFlag, isSecondPass);
            }
 
            if (isSecondPass)
            {
                for (int i = 0; i < _mc.ReferenceAssemblyList.Count; i++)
                {
                    ReferenceAssembly asmReference = _mc.ReferenceAssemblyList[i] as ReferenceAssembly;
 
                    if (asmReference != null)
                    {
                        if (string.Equals(asmReference.AssemblyName, assemblyName, StringComparison.OrdinalIgnoreCase))
                        {
                            // Set the local assembly file to markupCompiler
                            _mc.LocalAssemblyFile = asmReference;
                        }
                    }
                }
            }
 
            // finally compile the app
            _mc.Compile(compUnit);
 
            return ret;
        }
 
        #region private method
 
 
        // <summary>
        // Event handler for the Compiler Errors
        // </summary>
        // <param name="sender"></param>
        // <param name="e"></param>
        private void OnCompilerError(Object sender, MarkupErrorEventArgs e)
        {
            _nErrors++;
 
            //
            // Since Output from LogError() cannot be recognized by VS TaskList, so
            // we replaced it with LogErrorFromText( ) and pass all the required information
            // such as filename, line, offset, etc.
            //
            string strErrorCode;
 
            // Generate error message by going through the whole exception chain, including
            // its inner exceptions.
            string message = TaskHelper.GetWholeExceptionMessage(e.Exception);
            string errorText;
 
            strErrorCode = _taskLogger.ExtractMessageCode(message, out errorText);
 
            if (String.IsNullOrEmpty(strErrorCode))
            {
                // If the exception is a Xml exception, show a pre-asigned error id for it.
                if (IsXmlException(e.Exception))
                {
                    message = SR.Format(SR.InvalidXml, message);
                    strErrorCode = _taskLogger.ExtractMessageCode(message, out errorText);
                }
                else
                {
                    strErrorCode = _unknownErrorID;
                    errorText = SR.Format(SR.UnknownBuildError, errorText);
                }
            }
 
            _taskLogger.LogError(null, strErrorCode, null, e.FileName, e.LineNumber, e.LinePosition, 0, 0, errorText);
        }
 
 
        //
        // Detect if the exception is xmlException
        //
        private bool IsXmlException(Exception e)
        {
            bool isXmlException = false;
 
            while (e != null)
            {
                if (e is System.Xml.XmlException)
                {
                    isXmlException = true;
                    break;
                }
                else
                {
                    e = e.InnerException;
                }
            }
 
            return isXmlException;
 
        }
 
        //
        // Handle the SourceFileResolve Event from MarkupCompiler.
        // It tries to GetResolvedFilePath for the new SourceDir and new RelativePath.
        //
        private void OnSourceFileResolve(Object sender, SourceFileResolveEventArgs e)
        {
            SourceFileInfo sourceFileInfo = e.SourceFileInfo;
            string newSourceDir = _sourceDir;
            string newRelativeFilePath;
 
            if (String.IsNullOrEmpty(sourceFileInfo.OriginalFilePath))
            {
                newRelativeFilePath = sourceFileInfo.OriginalFilePath;
            }
            else
            {
                newRelativeFilePath = GetResolvedFilePath(sourceFileInfo.OriginalFilePath, ref newSourceDir);
 
                _taskLogger.LogMessageFromResources(MessageImportance.Low, nameof(SR.FileResolved), sourceFileInfo.OriginalFilePath, newRelativeFilePath, newSourceDir);
            }
 
            if (sourceFileInfo.IsXamlFile)
            {
                //
                // For Xaml Source file, we need to remove the .xaml extension part.
                //
                int fileExtIndex = newRelativeFilePath.LastIndexOf(MarkupCompiler.DOTCHAR);
                newRelativeFilePath = newRelativeFilePath.Substring(0, fileExtIndex);
            }
 
            //
            // Update the SourcePath and RelativeSourceFilePath property in SourceFileInfo object.
            //
            sourceFileInfo.SourcePath = newSourceDir;
            sourceFileInfo.RelativeSourceFilePath = newRelativeFilePath;
 
            // Put the stream here.
            sourceFileInfo.Stream = TaskFileService.GetContent(sourceFileInfo.OriginalFilePath);
        }
 
        //
        // Return a new sourceDir and relative filepath for a given filePath.
        // This is for supporting of fullpath or ..\ in the original FilePath.
        //
        private string GetResolvedFilePath(string filePath, ref string newSourceDir)
        {
            // make it an absolute path if not already so
            if (!Path.IsPathRooted(filePath))
            {
                filePath = _sourceDir + filePath;
            }
 
            // get rid of '..' and '.' if any
            string fullFilePath = Path.GetFullPath(filePath);
 
            // Get the relative path based on sourceDir
            string relPath = String.Empty;
            string newRelativeFilePath;
 
            if (fullFilePath.StartsWith(_sourceDir,StringComparison.OrdinalIgnoreCase))
            {
                relPath = fullFilePath.Substring(_sourceDir.Length);
 
                // the original file is relative to the SourceDir.
                newSourceDir = _sourceDir;
                newRelativeFilePath = relPath;
            }
            else
            {
                // the original file is not relative to the SourceDir.
                // it could have its own fullpath or contains "..\" etc.
                //
                // In this case, we want to put the filename as relative filepath
                // and put the deepest directory that file is in as the new
                // SourceDir.
                //
                int pathEndIndex = fullFilePath.LastIndexOf(Path.DirectorySeparatorChar);
 
                newSourceDir = fullFilePath.Substring(0, pathEndIndex + 1);
                newRelativeFilePath = fullFilePath.Substring(pathEndIndex + 1);
            }
 
            return newRelativeFilePath;
        }
 
        #endregion
 
        #region private data
 
        private MarkupCompiler _mc;
        private string         _sourceDir;
        private TaskLoggingHelper _taskLogger;
        private int    _nErrors;
        private string _unknownErrorID;
 
        private FileUnit _applicationMarkup;
 
        private int      _localizationDirectivesToLocFile;
 
        #endregion
 
    }
}