File: Microsoft\Activities\Build\Expressions\ExpressionsBuildExtension.cs
Project: ndp\cdf\src\NetFx40\Microsoft.Activities.Build\Microsoft.Activities.Build.csproj (Microsoft.Activities.Build)
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------
 
[module: System.Diagnostics.CodeAnalysis.SuppressMessage(System.Runtime.FxCop.Category.Performance, System.Runtime.FxCop.Rule.AvoidUncalledPrivateCode, Scope = "member", Target = "Microsoft.Activities.Build.SR.get_InvalidValueForDisableWorkflowCompiledExpressions():System.String", Justification = "This resource string is only referenced from the targets file.")]
 
namespace Microsoft.Activities.Build.Expressions
{
    using System;
    using System.Collections.Generic;
    using Microsoft.Build.Tasks.Xaml;
    using System.Activities;
    using System.Activities.XamlIntegration;
    using System.Reflection;
    using System.IO;
    using System.Runtime;
    using System.CodeDom.Compiler;
    using Microsoft.Activities.Build.Validation;
 
    public class ExpressionsBuildExtension : IXamlBuildTypeInspectionExtension
    {
        static string fileNameSuffix = "_CompiledExpressionRoot";
        XamlBuildTypeInspectionExtensionContext buildContext;
        List<string> generatedFiles;
        List<Tuple<string, bool>> messages;
 
        public ExpressionsBuildExtension()
        {
        }
 
        public bool Execute(XamlBuildTypeInspectionExtensionContext buildContext)
        {
            if (buildContext == null)
            {
                throw FxTrace.Exception.AsError(new ArgumentNullException("buildContext"));
            }
 
            this.buildContext = buildContext;
            string deferredValidationErrorsFilePath = Path.Combine(this.buildContext.OutputPath, ValidationBuildExtension.DeferredValidationErrorsFileName);
            if (string.Equals(this.buildContext.Language, "VB", StringComparison.OrdinalIgnoreCase) && File.Exists(deferredValidationErrorsFilePath))
            {                
                List<ValidationBuildExtension.Violation> violations = ReportDeferredValidationErrorsTask.LoadDeferredValidationErrors(deferredValidationErrorsFilePath);
                if (violations != null && violations.Count > 0)
                {                    
                    // ValidationBuildExtension must have run prior to ExpressionBuildExtension.
                    // If ValidationBuildExtension had cached any validation errors including VB Hosted compiler errors, 
                    //  then we do not generate the compiled expression code for VB.
                    return true;
                }
            }
            
            this.generatedFiles = new List<string>();
            this.messages = new List<Tuple<string, bool>>();
 
            try
            {
                bool success = Execute();
 
                foreach (string fileName in this.generatedFiles)
                {
                    buildContext.AddGeneratedFile(fileName);
                }
 
                foreach (Tuple<string, bool> message in this.messages)
                {
                    if (message.Item2)
                    {
                        buildContext.XamlBuildLogger.LogError(message.Item1);
                    }
                    else
                    {
                        buildContext.XamlBuildLogger.LogMessage(message.Item1);
                    }
                }
 
                return success;
            }
            catch (BadImageFormatException bex)
            {
                buildContext.XamlBuildLogger.LogWarning(SR.BadImageFormat_Expression(bex.FileName));
 
                // We don't want to add the generated files to the project, since compilation was incomplete;
                // so we're responsible for cleaning them up ourselves.
                try
                {
                    foreach (string fileName in this.generatedFiles)
                    {
                        File.Delete(fileName);
                    }
                }
                catch (IOException)
                {
                    // cleanup is best-effort
                }
 
                return true;
            }
        }
 
        bool Execute()
        {
            Assembly localAssembly = Utilities.GetLocalAssembly(buildContext, SR.LocalAssemblyNotLoaded_Expressions);            
 
            foreach (Type type in Utilities.GetTypes(localAssembly))
            {
                if (Utilities.IsTypeAuthoredInXaml(type))
                {
                    Exception ctorException;
                    Activity activity = Utilities.CreateActivity(type, out ctorException);
                    if (ctorException != null)
                    {
                        LogError(SR.ExpressionBuildExtensionConstructorFailed(type.FullName, ctorException != null ? ctorException.Message : string.Empty));
                    }
                    else if (activity != null)
                    {
                        string activityName = activity.GetType().Name;
 
                        string activityNamespace = "";
                        string fullActivityNamespace = activity.GetType().Namespace; 
                        if (string.Equals(buildContext.Language, "VB", StringComparison.OrdinalIgnoreCase))
                        {
                            if (string.IsNullOrWhiteSpace(buildContext.RootNamespace))
                            {
                                activityNamespace = fullActivityNamespace;
                            }
                            else
                            {
                                int firstIndex = fullActivityNamespace.IndexOf(buildContext.RootNamespace, StringComparison.Ordinal);                                
                                if (firstIndex != -1)
                                {
                                    int subStringIndex = firstIndex + buildContext.RootNamespace.Length + 1;
                                    if (subStringIndex < fullActivityNamespace.Length)
                                    {
                                        activityNamespace = fullActivityNamespace.Substring(subStringIndex);
                                    }
                                    else
                                    {
                                        activityNamespace = "";
                                    }
                                }
                                else
                                {
                                    activityNamespace = "";
                                }
                            }
                        }
                        else
                        {
                            activityNamespace = fullActivityNamespace;
                        }
                        
 
                        TextExpressionCompiler compiler = new TextExpressionCompiler(
                            new TextExpressionCompilerSettings()
                            {
                                Activity = activity,
                                ActivityName = activityName,
                                ActivityNamespace = activityNamespace,
                                Language = buildContext.Language,
                                RootNamespace = buildContext.RootNamespace,
                                LogSourceGenerationMessage = LogMessage,
                                AlwaysGenerateSource = false
                            });
                        
                        string filePath = Path.GetFullPath(buildContext.OutputPath);
                        string codeFileName = Path.Combine(filePath, activityNamespace + "_" + activityName + fileNameSuffix + "." + CodeDomProvider.CreateProvider(this.buildContext.Language).FileExtension);
 
                        bool fileWritten = false;
                        using (StreamWriter fileStream = new StreamWriter(codeFileName))
                        {
                            try
                            {
                                fileWritten = compiler.GenerateSource(fileStream);
                            }
                            catch (Exception ex)
                            {
                                if (Fx.IsFatal(ex))
                                {
                                    throw;
                                }
                                LogError(ex.Message);
                            }
                        }
 
                        if (fileWritten)
                        {
                            // Batch up all file generation until the end, because we don't want to emit 
                            // any source if we can't complete compilation due to an unloadable reference.
                            this.generatedFiles.Add(codeFileName);
                        }
                    }
                }
            }
            return true;
        }
 
        // Batch up all errors and warnings, because we don't want to emit any messages
        // if we can't complete compilation due to an unloadable reference.
        void LogMessage(string message)
        {
            this.messages.Add(Tuple.Create(message, false));
        }
 
        void LogError(string message)
        {
            this.messages.Add(Tuple.Create(message, true));
        }
    }
}