diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 036a24e2e..9259163f5 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -7,8 +7,8 @@ only tracks infrastructure issues regarding the following items:
* Reference Source Browser (http://referencesource.microsoft.com/)
* Debugging the .NET Framework using Reference Source
-For issues regarding functionality, please use the following resources:
+This repository does not accept feature requests or bug reports. To submit
+those, you need to go elsewhere:
-* BCL. https://github.com/dotnet/corefx
-* ASP.NET. https://github.com/aspnet/home
-* Everything else. https://connect.microsoft.com/VisualStudio/feedback/LoadSubmitFeedbackForm
+* [.NET Framework](https://developercommunity.visualstudio.com/dotnet)
+* [.NET Core](https://github.com/dotnet/core)
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 000000000..1eb3c758b
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,3 @@
+# READ BEFORE FILING
+
+This repository is read-only and doesn't accept community pull requests.
\ No newline at end of file
diff --git a/Microsoft.Activities.Build/Microsoft.Activities.Build.csproj b/Microsoft.Activities.Build/Microsoft.Activities.Build.csproj
new file mode 100644
index 000000000..95bfe6105
--- /dev/null
+++ b/Microsoft.Activities.Build/Microsoft.Activities.Build.csproj
@@ -0,0 +1,60 @@
+ Copyright (c) Microsoft Corporation. All rights reserved.?>
+
+
+
+ $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), 'Microsoft.CDF.Settings.targets'))
+
+
+
+ true
+ Microsoft.Activities.Build
+ Library
+ $(AssemblyName)
+ true
+ MSSHARED
+ false
+ true
+ {B3C9BB78-FC5F-4644-B898-E237C8ACF854}
+ $(ADPDocumentationPath)\$(AssemblyName).xml
+ $(DefineConstants);NONAPTCA
+
+
+ SR.resx
+ $(AssemblyName)
+ $(AssemblyName)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Microsoft\Activities\Build\Validation
+ Microsoft\Activities\Build\Expressions
+ Microsoft\Activities\Build\Debugger
+ Microsoft\Activities\Build
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/Microsoft.Activities.Build/Microsoft.WorkflowBuildExtensions.targets b/Microsoft.Activities.Build/Microsoft.WorkflowBuildExtensions.targets
new file mode 100644
index 000000000..8878dbd98
--- /dev/null
+++ b/Microsoft.Activities.Build/Microsoft.WorkflowBuildExtensions.targets
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+ ValidationExtension;
+ ExpressionBuildExtension;
+ $(PrepareResourcesDependsOn)
+
+
+
+
+
+ GenerateCompiledExpressionsTempFile;
+ $(CoreCompileDependsOn)
+
+
+
+
+ 4.0.0.0
+ 31bf3856ad364e35
+ Microsoft.Activities.Build, Version=$(WorkflowBuildExtensionVersion), Culture=neutral, PublicKeyToken=$(WorkflowBuildExtensionKeyToken)
+
+ $(IntermediateOutputPath)\TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs
+ $(IntermediateOutputPath)\TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs
+ $(IntermediateOutputPath)\TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs
+
+ $(IntermediateOutputPath)\AC2C1ABA-CCF6-44D4-8127-588FD4D0A860-DeferredValidationErrors.xml
+
+
+
+
+
+
+
+
+ $(WorkflowBuildExtensionAssemblyName)
+ false
+
+
+ $(WorkflowBuildExtensionAssemblyName)
+ false
+
+
+
+
+
+
+
+
+ $(WorkflowBuildExtensionAssemblyName)
+ false
+
+
+
+
+
+
+
+
+
+
+ $(WorkflowBuildExtensionAssemblyName)
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Microsoft.Activities.Build/Microsoft/Activities/Build/BeforeInitializeComponentExtension.cs b/Microsoft.Activities.Build/Microsoft/Activities/Build/BeforeInitializeComponentExtension.cs
new file mode 100644
index 000000000..f9be54d97
--- /dev/null
+++ b/Microsoft.Activities.Build/Microsoft/Activities/Build/BeforeInitializeComponentExtension.cs
@@ -0,0 +1,242 @@
+//------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------
+
+namespace Microsoft.Activities.Build
+{
+ using System;
+ using System.Activities;
+ using System.CodeDom;
+ using System.CodeDom.Compiler;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.IO;
+ using System.Linq;
+ using System.Reflection;
+ using System.Xaml;
+ using System.Xaml.Schema;
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Tasks.Xaml;
+ using System.Runtime;
+ using System.Text;
+
+ class BeforeInitializeComponentExtension : IXamlBuildTypeGenerationExtension
+ {
+ const string FileNameSuffix = "BeforeInitializeComponentHelper";
+
+ XamlSchemaContext schemaContext;
+
+ public bool Execute(ClassData classData, XamlBuildTypeGenerationExtensionContext buildContext)
+ {
+ string className = !string.IsNullOrEmpty(classData.Namespace) ? classData.Namespace + "." + classData.Name : classData.Name;
+ buildContext.XamlBuildLogger.LogMessage(MessageImportance.Low, SR.InspectingClass(typeof(BeforeInitializeComponentExtension).Name, className));
+
+ this.schemaContext = classData.EmbeddedResourceXaml.Writer.SchemaContext;
+
+ if (!IsAssignableTo(classData.BaseType, typeof(Activity)))
+ {
+ return true;
+ }
+
+ if (ArePartialMethodsSupported(buildContext))
+ {
+ buildContext.XamlBuildLogger.LogMessage(MessageImportance.Low, SR.GeneratingBeforeInitializeComponent(typeof(BeforeInitializeComponentExtension).Name, className));
+
+ CodeCompileUnit myCodeCompileUnit = GenerateCompileUnit(classData.Namespace ?? string.Empty, classData, buildContext.Language);
+ WriteGeneratedCode(classData, buildContext, myCodeCompileUnit, buildContext.Language);
+ }
+ else
+ {
+ buildContext.XamlBuildLogger.LogWarning(SR.UnsupportedLanguage(typeof(BeforeInitializeComponentExtension).Name, className));
+ }
+
+ return true;
+ }
+
+ static void WriteGeneratedCode(ClassData classData, XamlBuildTypeGenerationExtensionContext buildContext, CodeCompileUnit compileUnit, string language)
+ {
+ using (CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider(buildContext.Language))
+ {
+ string codeFileName = string.Format(CultureInfo.InvariantCulture, "{0}_{1}_{2}.{3}", classData.Namespace, classData.Name, FileNameSuffix, codeDomProvider.FileExtension);
+ string codeFilePath = Path.Combine(buildContext.OutputPath, codeFileName);
+
+ using (StreamWriter fileStream = new StreamWriter(codeFilePath))
+ {
+ using (IndentedTextWriter tw = new IndentedTextWriter(fileStream))
+ {
+ codeDomProvider.GenerateCodeFromCompileUnit(compileUnit, tw, new CodeGeneratorOptions());
+ }
+ }
+
+ buildContext.AddGeneratedFile(codeFilePath);
+
+ // Generate a resource file that contains the name of the resource holding the XAML.
+ string resourceFilePath = Path.Combine(buildContext.OutputPath, GenerateHelperResourceFilename(classData, buildContext, language));
+
+ string xamlResourceName = classData.EmbeddedResourceFileName;
+
+ using (StreamWriter fileStream = new StreamWriter(resourceFilePath))
+ {
+ // The first line of the resource is the Xaml resource name.
+ fileStream.WriteLine(xamlResourceName);
+ // The second line of the resource is the full class name of the Xaml helper class.
+ // In VB, that name is prepended with the root namespace.
+ string helperClassName = null;
+ if (string.Equals(language, "VB", StringComparison.OrdinalIgnoreCase))
+ {
+ helperClassName = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", buildContext.RootNamespace, classData.HelperClassFullName);
+ }
+ else
+ {
+ helperClassName = classData.HelperClassFullName;
+ }
+ fileStream.WriteLine(helperClassName);
+ }
+ buildContext.AddGeneratedResourceFile(resourceFilePath);
+
+ }
+ }
+
+ static string GenerateHelperResourceFilename(ClassData classData, XamlBuildTypeGenerationExtensionContext buildContext, string language)
+ {
+ // Generate a resource file that contains the name of the resource holding the XAML.
+ // [ 0)
+ {
+ builder.Append(".");
+ }
+ builder.Append(classData.Namespace);
+ }
+
+ if (builder.Length > 0)
+ {
+ builder.Append("_");
+ }
+ }
+ else
+ {
+ if (!string.IsNullOrWhiteSpace(classData.Namespace))
+ {
+ builder.Append(classData.Namespace);
+ }
+
+ if (builder.Length > 0)
+ {
+ builder.Append("_");
+ }
+ }
+ builder.Append(string.Format(CultureInfo.InvariantCulture, "{0}_{1}.{2}", classData.Name, FileNameSuffix, "txt"));
+ return builder.ToString();
+ }
+
+ [SuppressMessage(FxCop.Category.Globalization, FxCop.Rule.DoNotPassLiteralsAsLocalizedParameters,
+ Justification = "The string literals are code snippets, not localizable values.")]
+ static CodeCompileUnit GenerateCompileUnit(string clrNamespace, ClassData classData, string language)
+ {
+ CodeTypeDeclaration typeDeclaration = new CodeTypeDeclaration
+ {
+ Name = classData.Name,
+ IsPartial = true,
+ TypeAttributes = classData.IsPublic ? TypeAttributes.Public : TypeAttributes.NotPublic,
+ };
+
+ // Partial declarations are only supported in C# and VB
+ string namespaceAndClassNameSnippet = null;
+ if (string.Equals(language, "C#", StringComparison.OrdinalIgnoreCase))
+ {
+ namespaceAndClassNameSnippet = CodeDomSnippets.BeforeInitializeComponentCS;
+ }
+ else if (string.Equals(language, "VB", StringComparison.OrdinalIgnoreCase))
+ {
+ namespaceAndClassNameSnippet = CodeDomSnippets.BeforeInitializeComponentVB;
+ }
+ else
+ {
+ throw Fx.AssertAndThrow("This method should only have been called for VB or C#");
+ }
+
+ namespaceAndClassNameSnippet = namespaceAndClassNameSnippet.Replace("ClassNameGoesHere", classData.Name);
+ typeDeclaration.Members.Add(new CodeSnippetTypeMember(namespaceAndClassNameSnippet));
+
+ return new CodeCompileUnit
+ {
+ Namespaces =
+ {
+ new CodeNamespace
+ {
+ Name = clrNamespace,
+ Types =
+ {
+ typeDeclaration
+ }
+ }
+ }
+ };
+ }
+
+ bool IsAssignableTo(XamlType type, Type assignableTo)
+ {
+ XamlType liveXamlType = this.schemaContext.GetXamlType(assignableTo);
+ XamlType rolXamlType = this.schemaContext.GetXamlType(new XamlTypeName(liveXamlType));
+ if (rolXamlType == null)
+ {
+ // assignableTo is not in the project references, so project must not be deriving from it
+ return false;
+ }
+ return type.CanAssignTo(rolXamlType);
+ }
+
+ bool ArePartialMethodsSupported(XamlBuildTypeGenerationExtensionContext buildContext)
+ {
+ return string.Equals(buildContext.Language, "C#", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(buildContext.Language, "VB", StringComparison.OrdinalIgnoreCase);
+ }
+
+ static class CodeDomSnippets
+ {
+ public const string BeforeInitializeComponentCS =
+@" partial void BeforeInitializeComponent(ref bool isInitialized)
+ {
+ if (isInitialized == true) {
+ return;
+ }
+
+ System.Activities.XamlIntegration.ActivityXamlServices.InitializeComponent(
+ typeof(ClassNameGoesHere),
+ this
+ );
+
+ // Setting this will turn InitializeComponent into a no-op.
+ isInitialized = true;
+ }
+";
+
+ public const string BeforeInitializeComponentVB =
+@" Private Sub BeforeInitializeComponent(ByRef isInitialized as Boolean)
+ If (isInitialized)
+ return
+ End If
+
+ System.Activities.XamlIntegration.ActivityXamlServices.InitializeComponent(
+ GetType(ClassNameGoesHere),
+ Me
+ )
+
+ isInitialized = true
+ End Sub
+";
+ }
+ }
+}
+
diff --git a/Microsoft.Activities.Build/Microsoft/Activities/Build/Debugger/DebugBuildExtension.cs b/Microsoft.Activities.Build/Microsoft/Activities/Build/Debugger/DebugBuildExtension.cs
new file mode 100644
index 000000000..0ac5cc912
--- /dev/null
+++ b/Microsoft.Activities.Build/Microsoft/Activities/Build/Debugger/DebugBuildExtension.cs
@@ -0,0 +1,101 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+
+namespace Microsoft.Activities.Build.Debugger
+{
+ using System;
+ using System.Activities.Debugger.Symbol;
+ using System.CodeDom;
+ using System.IO;
+ using System.Runtime;
+ using System.Xaml;
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Tasks.Xaml;
+
+ ///
+ /// Build Extension for Workflow debugger support.
+ ///
+ public class DebugBuildExtension : IXamlBuildTypeGenerationExtension
+ {
+ private static string debugSymbolTypeFullName = typeof(DebugSymbol).FullName;
+
+ ///
+ /// Update debug symbol with check sum.
+ ///
+ /// Class data to add the checksum.
+ /// Build context
+ /// Whether the execution succeeds
+ public bool Execute(ClassData classData, XamlBuildTypeGenerationExtensionContext buildContext)
+ {
+ string className = !string.IsNullOrEmpty(classData.Namespace) ? classData.Namespace + "." + classData.Name : classData.Name;
+ buildContext.XamlBuildLogger.LogMessage(MessageImportance.Low, SR.InspectingClass(typeof(DebugBuildExtension).Name, className));
+ this.UpdateDebugSymbol(classData, buildContext);
+ return true;
+ }
+
+ private void UpdateDebugSymbol(ClassData classData, XamlBuildTypeGenerationExtensionContext buildContext)
+ {
+ string path = Path.GetFullPath(classData.FileName);
+ try
+ {
+ using (XamlReader reader = classData.EmbeddedResourceXaml.GetReader())
+ {
+ XamlNodeList newList = new XamlNodeList(reader.SchemaContext);
+ using (XamlWriter writer = newList.Writer)
+ {
+ bool nodesAvailable = reader.Read();
+ while (nodesAvailable)
+ {
+ if (reader.NodeType == XamlNodeType.StartMember)
+ {
+ writer.WriteNode(reader);
+ if (reader.Member.DeclaringType != null &&
+ reader.Member.DeclaringType.UnderlyingType != null &&
+ string.CompareOrdinal(reader.Member.DeclaringType.UnderlyingType.FullName, debugSymbolTypeFullName) == 0)
+ {
+ reader.Read();
+ string symbolString = reader.Value as string;
+ if (!string.IsNullOrEmpty(symbolString))
+ {
+ WorkflowSymbol symbol = WorkflowSymbol.Decode(symbolString);
+ symbol.FileName = path;
+ symbol.CalculateChecksum();
+ writer.WriteValue(symbol.Encode());
+ }
+ else
+ {
+ writer.WriteValue(reader.Value);
+ }
+ }
+
+ nodesAvailable = reader.Read();
+ }
+ else
+ {
+ writer.WriteNode(reader);
+ nodesAvailable = reader.Read();
+ }
+ }
+ }
+
+ classData.EmbeddedResourceXaml = newList;
+ }
+ }
+ catch (Exception e)
+ {
+ if (Fx.IsFatal(e))
+ {
+ throw;
+ }
+
+ buildContext.XamlBuildLogger.LogMessage(
+ MessageImportance.High,
+ SR.DebugBuildExtensionExceptionPrefix(
+ typeof(DebugBuildExtension).Name,
+ path,
+ e.Message));
+ }
+ }
+ }
+}
diff --git a/Microsoft.Activities.Build/Microsoft/Activities/Build/Expressions/ExpressionsBuildExtension.cs b/Microsoft.Activities.Build/Microsoft/Activities/Build/Expressions/ExpressionsBuildExtension.cs
new file mode 100644
index 000000000..5eae886d6
--- /dev/null
+++ b/Microsoft.Activities.Build/Microsoft/Activities/Build/Expressions/ExpressionsBuildExtension.cs
@@ -0,0 +1,209 @@
+//----------------------------------------------------------------
+// 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 generatedFiles;
+ List> 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 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();
+ this.messages = new List>();
+
+ try
+ {
+ bool success = Execute();
+
+ foreach (string fileName in this.generatedFiles)
+ {
+ buildContext.AddGeneratedFile(fileName);
+ }
+
+ foreach (Tuple 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));
+ }
+ }
+}
diff --git a/Microsoft.Activities.Build/Microsoft/Activities/Build/Utilities.cs b/Microsoft.Activities.Build/Microsoft/Activities/Build/Utilities.cs
new file mode 100644
index 000000000..acbb5be36
--- /dev/null
+++ b/Microsoft.Activities.Build/Microsoft/Activities/Build/Utilities.cs
@@ -0,0 +1,111 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+
+namespace Microsoft.Activities.Build
+{
+ using System;
+ using System.Activities;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.IO;
+ using System.Linq;
+ using System.Reflection;
+ using System.Runtime;
+ using Microsoft.Build.Tasks.Xaml;
+
+ internal static class Utilities
+ {
+ private const string InitializeComponentMethodName = "InitializeComponent";
+
+ internal static Activity CreateActivity(Type type, out Exception ctorException)
+ {
+ try
+ {
+ ctorException = null;
+ Activity result = null;
+ if (!type.ContainsGenericParameters)
+ {
+ ConstructorInfo defaultConstructor = type.GetConstructor(Type.EmptyTypes);
+ if (defaultConstructor != null)
+ {
+ result = (Activity)defaultConstructor.Invoke(null);
+ }
+ }
+
+ return result;
+ }
+ catch (TargetInvocationException tie)
+ {
+ Exception ex = tie;
+ while (ex != null && ex is TargetInvocationException)
+ {
+ ex = ex.InnerException;
+ }
+
+ if (ex is BadImageFormatException)
+ {
+ // there's an unloadable reference, this will be handled by the Task's Execute method
+ throw Fx.Exception.AsError(ex);
+ }
+
+ ctorException = ex;
+ return null;
+ }
+ }
+
+ [SuppressMessage(FxCop.Category.Reliability, FxCop.Rule.AvoidCallingProblematicMethods,
+ Justification = "Using LoadFile to avoid loading through Fusion and load the exact local assembly")]
+ internal static Assembly GetLocalAssembly(BuildExtensionContext context, string errorMessage)
+ {
+ try
+ {
+ string path = Path.GetFullPath(context.LocalAssembly);
+ return Assembly.LoadFile(path);
+ }
+ catch (Exception e)
+ {
+ if (Fx.IsFatal(e) || e is BadImageFormatException)
+ {
+ throw;
+ }
+
+ throw FxTrace.Exception.AsError(new FileLoadException(errorMessage));
+ }
+ }
+
+ internal static Type[] GetTypes(Assembly assembly)
+ {
+ try
+ {
+ return assembly.GetTypes();
+ }
+ catch (ReflectionTypeLoadException rtle)
+ {
+ foreach (Exception exception in rtle.LoaderExceptions)
+ {
+ if (exception is BadImageFormatException)
+ {
+ throw FxTrace.Exception.AsError(exception);
+ }
+ }
+
+ throw;
+ }
+ }
+
+ internal static bool IsTypeAuthoredInXaml(Type type)
+ {
+ if (type.BaseType != null && (type.BaseType == typeof(Activity) ||
+ (type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(Activity<>))))
+ {
+ if (type.GetMethod(InitializeComponentMethodName, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance) != null)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/Microsoft.Activities.Build/Microsoft/Activities/Build/Validation/DeferredValidationTask.cs b/Microsoft.Activities.Build/Microsoft/Activities/Build/Validation/DeferredValidationTask.cs
new file mode 100644
index 000000000..9b1e8a79d
--- /dev/null
+++ b/Microsoft.Activities.Build/Microsoft/Activities/Build/Validation/DeferredValidationTask.cs
@@ -0,0 +1,67 @@
+//----------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+//----------------------------------------------------------------
+
+namespace Microsoft.Activities.Build.Validation
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Runtime;
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Utilities;
+
+ ///
+ /// Wait until Compile target runs successfully,
+ /// and then if validation errors's been reported, simply return failure
+ /// so that the entire build process can terminate.
+ ///
+ public class DeferredValidationTask : Task
+ {
+ ///
+ /// Gets or sets the DeferredValidationErrorsFilePath property.
+ ///
+ [Required]
+ public string DeferredValidationErrorsFilePath { get; set; }
+
+ ///
+ /// Executes to check to see if validation errors' been already reported,
+ /// and if yes, fail the entire build now.
+ ///
+ /// Returns true if validation errors's already been reported. Returns false otherwise.
+ public override bool Execute()
+ {
+ if (File.Exists(this.DeferredValidationErrorsFilePath))
+ {
+ List violations = ReportDeferredValidationErrorsTask.LoadDeferredValidationErrors(this.DeferredValidationErrorsFilePath);
+
+ if (ErrorExists(violations))
+ {
+ // the validation errors must have already been reported/emitted in ReportDeferredValidationErrorsTask.
+ // the goal of this task is to simply fail the entire build process after CoreCompile target has succeeded.
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static bool ErrorExists(List violations)
+ {
+ if (violations != null && violations.Count > 0)
+ {
+ foreach (ValidationBuildExtension.Violation violation in violations)
+ {
+ if (!violation.IsWarning)
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/Microsoft.Activities.Build/Microsoft/Activities/Build/Validation/ReportDeferredValidationErrorsTask.cs b/Microsoft.Activities.Build/Microsoft/Activities/Build/Validation/ReportDeferredValidationErrorsTask.cs
new file mode 100644
index 000000000..710f7e66e
--- /dev/null
+++ b/Microsoft.Activities.Build/Microsoft/Activities/Build/Validation/ReportDeferredValidationErrorsTask.cs
@@ -0,0 +1,64 @@
+//----------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+//----------------------------------------------------------------
+
+namespace Microsoft.Activities.Build.Validation
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Runtime;
+ using System.Runtime.Serialization;
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Utilities;
+
+ ///
+ /// Reports validation errors cached immediately following XamlMarkupCompilePass2,
+ /// where ValidationBuildExtesion ran,
+ /// and always return success so the build contineus.
+ ///
+ public class ReportDeferredValidationErrorsTask : Task
+ {
+ ///
+ /// Gets or sets the DeferredValidationErrorsFilePath property.
+ ///
+ [Required]
+ public string DeferredValidationErrorsFilePath { get; set; }
+
+ ///
+ /// Executes to report validation error.
+ ///
+ /// Always returns true.
+ public override bool Execute()
+ {
+ if (File.Exists(this.DeferredValidationErrorsFilePath))
+ {
+ List violations = LoadDeferredValidationErrors(this.DeferredValidationErrorsFilePath);
+
+ if (violations != null && violations.Count > 0)
+ {
+ foreach (ValidationBuildExtension.Violation violation in violations)
+ {
+ violation.Emit(this.Log);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ internal static List LoadDeferredValidationErrors(string filePath)
+ {
+ List violations = null;
+ using (FileStream stream = new FileStream(filePath, FileMode.Open))
+ {
+ DataContractSerializer serializer = new DataContractSerializer(typeof(List));
+ violations = (List)serializer.ReadObject(stream);
+ }
+
+ return violations;
+ }
+ }
+}
diff --git a/Microsoft.Activities.Build/Microsoft/Activities/Build/Validation/ValidationBuildExtension.cs b/Microsoft.Activities.Build/Microsoft/Activities/Build/Validation/ValidationBuildExtension.cs
new file mode 100644
index 000000000..4e21be367
--- /dev/null
+++ b/Microsoft.Activities.Build/Microsoft/Activities/Build/Validation/ValidationBuildExtension.cs
@@ -0,0 +1,354 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+namespace Microsoft.Activities.Build.Validation
+{
+ using System;
+ using System.Activities;
+ using System.Activities.Debugger;
+ using System.Activities.Debugger.Symbol;
+ using System.Activities.Validation;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.IO;
+ using System.Reflection;
+ using System.Runtime;
+ using System.Runtime.Serialization;
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Tasks.Xaml;
+ using Microsoft.Build.Utilities;
+
+ [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUninstantiatedInternalClasses,
+ Justification = "This type is used in targets file. It is instantiated while running XBT pass 2 extensions.")]
+ class ValidationBuildExtension : IXamlBuildTypeInspectionExtension
+ {
+ public const string DeferredValidationErrorsFileName = "AC2C1ABA-CCF6-44D4-8127-588FD4D0A860-DeferredValidationErrors.xml";
+
+ XamlBuildTypeInspectionExtensionContext buildContext;
+ List violations;
+
+ public ValidationBuildExtension()
+ {
+ }
+
+ public bool Execute(XamlBuildTypeInspectionExtensionContext buildContext)
+ {
+ if (buildContext == null)
+ {
+ throw FxTrace.Exception.AsError(new ArgumentNullException("buildContext"));
+ }
+ this.buildContext = buildContext;
+ this.violations = new List();
+
+ try
+ {
+ this.Execute();
+
+ // Delay validation report and always returns true in order to let CoreCompile to compile first.
+ // CoreCompile will report expression compile errors and duplicated errors will be merged by Microsoft.VisualStudio.Activities.BuildHelper later.
+ // ReportValidationBuildExtensionErrors will report validation errors after CoreCompile done.
+ this.WriteViolationsToDeferredErrorsFile();
+
+ return true;
+ }
+ catch (BadImageFormatException bex)
+ {
+ buildContext.XamlBuildLogger.LogWarning(SR.BadImageFormat_Validation(bex.FileName));
+ return true;
+ }
+ }
+
+ void WriteViolationsToDeferredErrorsFile()
+ {
+ // OutputPath here is the intermediate output path
+ string filePath = Path.Combine(this.buildContext.OutputPath, DeferredValidationErrorsFileName);
+ using (FileStream stream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
+ {
+ DataContractSerializer serializer = new DataContractSerializer(typeof(List));
+ serializer.WriteObject(stream, this.violations);
+ }
+ }
+
+ void Execute()
+ {
+ Assembly localAssembly = Utilities.GetLocalAssembly(this.buildContext, SR.LocalAssemblyNotLoaded);
+
+ foreach (Type type in Utilities.GetTypes(localAssembly))
+ {
+ // Check if the type is authored in xaml
+ if (Utilities.IsTypeAuthoredInXaml(type))
+ {
+ // Check if the file is marked with SkipWorkflowValidation = true
+ ITaskItem inputTaskItem = null;
+ buildContext.MarkupItemsByTypeName.TryGetValue(type.FullName, out inputTaskItem);
+ if (!SkipValidationForFile(inputTaskItem))
+ {
+ string fileName = inputTaskItem != null ? inputTaskItem.ItemSpec : String.Empty;
+ Exception ex;
+ Activity activity = Utilities.CreateActivity(type, out ex);
+ if (ex != null)
+ {
+ string message = SR.ValidationBuildExtensionConstructorFailed(type.FullName, ex != null ? ex.Message : string.Empty);
+ this.violations.Add(new Violation(fileName, message));
+ }
+ else if (activity != null)
+ {
+ this.Validate(activity, fileName);
+ }
+ }
+ }
+ }
+ }
+
+ static IDictionary GetParentChildRelationships(Activity activity)
+ {
+ IDictionary parentChildMappings = new Dictionary();
+ InternalGetParentChildRelationships(activity, null, parentChildMappings);
+ return parentChildMappings;
+ }
+
+ static void InternalGetParentChildRelationships(Activity activity, Activity parent, IDictionary parentChildMappings)
+ {
+ if (!parentChildMappings.ContainsKey(activity))
+ {
+ // the very first parent declaring the child activity
+ // and the rest are parent to reference child relationships
+ parentChildMappings.Add(activity, parent);
+ }
+
+ foreach (Activity child in WorkflowInspectionServices.GetActivities(activity))
+ {
+ InternalGetParentChildRelationships(child, activity, parentChildMappings);
+ }
+ }
+
+ void Validate(Activity activity, string fileName)
+ {
+ List validationErrors = new List();
+
+ ValidationSettings settings = new ValidationSettings()
+ {
+ SkipValidatingRootConfiguration = true
+ };
+ ValidationResults results = null;
+ try
+ {
+ results = ActivityValidationServices.Validate(activity, settings);
+ }
+ catch (Exception e)
+ {
+ if (Fx.IsFatal(e))
+ {
+ throw;
+ }
+
+ ValidationError error = new ValidationError(SR.ValidationBuildExtensionExceptionPrefix(typeof(ValidationBuildExtension).Name, activity.DisplayName, e.Message));
+ validationErrors.Add(error);
+ }
+
+ if (results != null)
+ {
+ validationErrors.AddRange(results.Errors);
+ validationErrors.AddRange(results.Warnings);
+ }
+
+ if (validationErrors.Count > 0)
+ {
+ Dictionary