diff --git a/tools/StaticAnalysis/AnalysisLogger.cs b/tools/StaticAnalysis/AnalysisLogger.cs
new file mode 100644
index 000000000000..f6aa7b493f3e
--- /dev/null
+++ b/tools/StaticAnalysis/AnalysisLogger.cs
@@ -0,0 +1,120 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace StaticAnalysis
+{
+ ///
+ /// Abstract class to implement the report logging structure
+ ///
+ public abstract class AnalysisLogger
+ {
+ string _baseDirectory;
+
+ ///
+ /// Create an analysis logger that will write reports to the given directory
+ ///
+ ///
+ public AnalysisLogger(string baseDirectory)
+ {
+ _baseDirectory = baseDirectory;
+ }
+
+ IList _loggers = new List();
+ protected virtual IList Loggers { get { return _loggers; } }
+
+ ///
+ /// Write an error report.
+ ///
+ /// The message to write
+ public abstract void WriteError(string error);
+
+ public virtual void WriteError(string format, params object[] args)
+ {
+ WriteError(string.Format(format, args));
+ }
+
+ ///
+ /// Write an informational message.
+ ///
+ /// The message to write
+ public abstract void WriteMessage(string message);
+
+ public virtual void WriteMessage(string format, params object[] args)
+ {
+ WriteMessage(string.Format(format, args));
+ }
+
+ ///
+ /// Write a warning.
+ ///
+ /// The warning text
+ public abstract void WriteWarning(string message);
+
+ public virtual void WriteWarning(string format, params object[] args)
+ {
+ WriteWarning(string.Format(format, args));
+ }
+
+ ///
+ /// Write a report file to the given file, using the given file contents.
+ ///
+ /// The path to the file
+ /// The contents of the report
+ public abstract void WriteReport(string name, string contents);
+
+ ///
+ /// Create a logger for a particular report
+ ///
+ /// The type of records written to the log
+ /// The filename (without file path) where the report will be written
+ /// The given logger. Analyzer may write records to this logger and they will be written to
+ /// the report file.
+ public virtual ReportLogger CreateLogger(string fileName) where T : IReportRecord, new()
+ {
+ if (string.IsNullOrWhiteSpace(fileName))
+ {
+ throw new ArgumentNullException("fileName");
+ }
+
+ var filePath = Path.Combine(_baseDirectory, fileName);
+ var logger = new ReportLogger(filePath, this);
+ Loggers.Add(logger);
+ return logger;
+ }
+
+ ///
+ /// Write out the report files for each of the added report loggers.
+ ///
+ public void WriteReports()
+ {
+ foreach (var logger in Loggers.Where(l => l.Records.Any()))
+ {
+ StringBuilder reportText = new StringBuilder();
+ reportText.AppendLine(logger.Records.First().PrintHeaders());
+ foreach (var reportRecord in logger.Records)
+ {
+ reportText.AppendLine(reportRecord.FormatRecord());
+ }
+
+ WriteReport(logger.FileName, reportText.ToString());
+ }
+ }
+ }
+}
diff --git a/tools/StaticAnalysis/ConsoleLogger.cs b/tools/StaticAnalysis/ConsoleLogger.cs
new file mode 100644
index 000000000000..1cb18d070d64
--- /dev/null
+++ b/tools/StaticAnalysis/ConsoleLogger.cs
@@ -0,0 +1,51 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+using System.IO;
+
+namespace StaticAnalysis
+{
+ ///
+ /// Simple class for logging errors and warnings to the console and writing reports to the file system.
+ ///
+ public class ConsoleLogger : AnalysisLogger
+ {
+
+ public ConsoleLogger(string baseDirectory)
+ : base(baseDirectory)
+ {
+ }
+
+ public override void WriteError(string error)
+ {
+ Console.WriteLine("### ERROR {0}", error);
+ }
+
+ public override void WriteMessage(string message)
+ {
+ Console.WriteLine(message);
+ }
+
+ public override void WriteWarning(string message)
+ {
+ Console.WriteLine("Warning: {0}", message);
+ }
+
+ public override void WriteReport(string name, string records)
+ {
+ File.WriteAllText(name, records);
+ }
+ }
+}
diff --git a/tools/StaticAnalysis/Decorator.cs b/tools/StaticAnalysis/Decorator.cs
new file mode 100644
index 000000000000..48ce7600f477
--- /dev/null
+++ b/tools/StaticAnalysis/Decorator.cs
@@ -0,0 +1,88 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+
+namespace StaticAnalysis
+{
+ ///
+ /// Abstract class to implement the Decorator pattern
+ ///
+ ///
+ public class Decorator
+ {
+ Action _action;
+ string _name;
+
+ protected Decorator(Action action, string name)
+ {
+ _action = action;
+ _name = name;
+ Inner = null;
+ }
+
+ public static Decorator Create()
+ {
+ return new Decorator(r => { }, "default");
+ }
+
+ public void Apply(T record)
+ {
+ _action(record);
+ if (Inner != null)
+ {
+ Inner.Apply(record);
+ }
+ }
+
+ public void AddDecorator(Action action, string name)
+ {
+ if (action == null)
+ {
+ throw new ArgumentNullException("action");
+ }
+
+ if (name == null)
+ {
+ throw new ArgumentNullException("name");
+ }
+
+ if (Inner == null)
+ {
+ Inner = new Decorator(action, name);
+ }
+ else
+ {
+ Inner.AddDecorator(action, name);
+ }
+ }
+
+ public void Remove(string name)
+ {
+ if (Inner != null)
+ {
+ if (string.Equals(Inner._name, name))
+ {
+ Inner = Inner.Inner;
+ }
+ else
+ {
+ Inner.Remove(name);
+ }
+ }
+ }
+
+ protected Decorator Inner { get; set; }
+ }
+}
diff --git a/tools/StaticAnalysis/DependencyAnalyzer/AssemblyLoader.cs b/tools/StaticAnalysis/DependencyAnalyzer/AssemblyLoader.cs
new file mode 100644
index 000000000000..15cd6731a4c8
--- /dev/null
+++ b/tools/StaticAnalysis/DependencyAnalyzer/AssemblyLoader.cs
@@ -0,0 +1,101 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+using System.Reflection;
+
+namespace StaticAnalysis.DependencyAnalyzer
+{
+ ///
+ /// A class using .Net Remoting to load assemblies and retrieve information in a separate app domain
+ ///
+ public class AssemblyLoader : MarshalByRefObject
+ {
+ ///
+ /// Load the assembly in the reflection context by name. Will succeed if the referenced assembly name can
+ /// be found using default assembly loading rules (i.e. it is in the current directory or the GAC)
+ ///
+ /// The full name of the assembly
+ /// Information on the given assembly, if it was loaded successfully, or null if there is an
+ /// assembly loading issue.
+ public AssemblyMetadata GetReflectedAssemblyInfo(string assemblyName)
+ {
+ if (string.IsNullOrWhiteSpace(assemblyName))
+ {
+ throw new ArgumentException("assemblyName");
+ }
+
+ AssemblyMetadata result = null;
+ try
+ {
+ result = new AssemblyMetadata(Assembly.ReflectionOnlyLoad(assemblyName));
+ }
+ catch
+ {
+ }
+
+ return result;
+ }
+
+ ///
+ /// Load the assembly found at the given path in the reflection context and return assembly metadata
+ ///
+ /// The full path to the assembly file.
+ /// Assembly metadata if the assembly is loaded successfully, or null if there are load errors.
+ public AssemblyMetadata GetReflectedAssemblyFromFile(string assemblyPath)
+ {
+ if (string.IsNullOrWhiteSpace(assemblyPath))
+ {
+ throw new ArgumentException("assemblyPath");
+ }
+
+ AssemblyMetadata result = null;
+ try
+ {
+ return new AssemblyMetadata(Assembly.ReflectionOnlyLoadFrom(assemblyPath));
+ }
+ catch
+ {
+ }
+
+ return result;
+ }
+
+ ///
+ /// Create a new AppDomain and create a remote instance of AssemblyLoader we can use there
+ ///
+ /// directory containing assemblies
+ /// A new AppDomain, where assemblies can be loaded
+ /// A proxy to the AssemblyLoader running in the newly created app domain
+ public static AssemblyLoader Create(string directoryPath, out AppDomain testDomain)
+ {
+ if (string.IsNullOrWhiteSpace(directoryPath))
+ {
+ throw new ArgumentException("directoryPath");
+ }
+
+ var setup = new AppDomainSetup();
+ setup.ApplicationBase = directoryPath;
+ setup.ApplicationName = "TestDomain";
+ setup.ApplicationTrust = AppDomain.CurrentDomain.ApplicationTrust;
+ setup.DisallowApplicationBaseProbing = false;
+ setup.DisallowCodeDownload = false;
+ setup.DisallowBindingRedirects = false;
+ setup.DisallowPublisherPolicy = false;
+ testDomain = AppDomain.CreateDomain("TestDomain", null, setup);
+ return testDomain.CreateInstanceFromAndUnwrap(typeof(AssemblyLoader).Assembly.Location,
+ typeof(AssemblyLoader).FullName) as AssemblyLoader;
+ }
+ }
+}
diff --git a/tools/StaticAnalysis/DependencyAnalyzer/AssemblyMetadata.cs b/tools/StaticAnalysis/DependencyAnalyzer/AssemblyMetadata.cs
new file mode 100644
index 000000000000..101cd1d99a2a
--- /dev/null
+++ b/tools/StaticAnalysis/DependencyAnalyzer/AssemblyMetadata.cs
@@ -0,0 +1,70 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+
+namespace StaticAnalysis.DependencyAnalyzer
+{
+ ///
+ /// Serializable assembly metadata class, used to return assembly information from a remote AppDomain
+ ///
+ [Serializable]
+ public class AssemblyMetadata
+ {
+ AssemblyName _name;
+ string _location;
+ IList _references;
+
+ public AssemblyMetadata(Assembly assembly)
+ {
+ if (assembly == null)
+ {
+ throw new ArgumentNullException("assembly");
+ }
+
+ _name = assembly.GetName();
+ _location = assembly.Location;
+ _references = new List();
+ foreach (var child in assembly.GetReferencedAssemblies())
+ {
+ _references.Add(child);
+ }
+ }
+
+ ///
+ /// Path to the assembly.
+ ///
+ public string Location { get { return _location; } }
+
+ ///
+ /// The assembly name
+ ///
+ /// The assembly name for this assembly, including name and the version
+ public AssemblyName GetName()
+ {
+ return _name;
+ }
+
+ ///
+ /// The list of referenced assemblies
+ ///
+ /// A list of assembly name references for all assemblies referenced in the assembly manifest
+ public IEnumerable GetReferencedAssemblies()
+ {
+ return _references;
+ }
+ }
+}
diff --git a/tools/StaticAnalysis/DependencyAnalyzer/AssemblyNameComparer.cs b/tools/StaticAnalysis/DependencyAnalyzer/AssemblyNameComparer.cs
new file mode 100644
index 000000000000..1f0d1fa87414
--- /dev/null
+++ b/tools/StaticAnalysis/DependencyAnalyzer/AssemblyNameComparer.cs
@@ -0,0 +1,56 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+
+namespace StaticAnalysis.DependencyAnalyzer
+{
+ ///
+ /// Equality comparer, used to uniquely store assembly records by assembly name
+ ///
+ public class AssemblyNameComparer : IEqualityComparer
+ {
+ private static string GetComparisonName(AssemblyName name)
+ {
+ if (name == null)
+ {
+ throw new ArgumentNullException("name");
+ }
+
+ return string.Format("{0}.v{1}", name.Name, name.Version);
+ }
+
+ public bool Equals(AssemblyName assembly1, AssemblyName assembly2)
+ {
+ if (assembly1 == null)
+ {
+ throw new ArgumentNullException("assembly1");
+ }
+
+ if (assembly2 == null)
+ {
+ throw new ArgumentNullException("assembly2");
+ }
+
+ return StringComparer.OrdinalIgnoreCase.Equals(GetComparisonName(assembly1), GetComparisonName(assembly2));
+ }
+
+ public int GetHashCode(AssemblyName obj)
+ {
+ return StringComparer.OrdinalIgnoreCase.GetHashCode(GetComparisonName(obj));
+ }
+ }
+}
diff --git a/tools/StaticAnalysis/DependencyAnalyzer/AssemblyRecord.cs b/tools/StaticAnalysis/DependencyAnalyzer/AssemblyRecord.cs
new file mode 100644
index 000000000000..a6a0c41e9e53
--- /dev/null
+++ b/tools/StaticAnalysis/DependencyAnalyzer/AssemblyRecord.cs
@@ -0,0 +1,137 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+
+namespace StaticAnalysis.DependencyAnalyzer
+{
+ ///
+ /// Information about assemblies
+ ///
+ public class AssemblyRecord : ICloneable
+ {
+ HashSet _parents = new HashSet();
+ IList _children = new List();
+ ///
+ /// The path to the assembly
+ ///
+ public string Location { get; set; }
+
+ public string Name { get { return AssemblyName.Name; } }
+ public Version Version { get { return AssemblyName.Version; } }
+ public HashSet ReferencingAssembly { get { return _parents; } }
+
+ public IList Children { get { return _children; } }
+
+ public AssemblyName AssemblyName { get; set; }
+
+ ///
+ /// The majorVersion portion of the file version for the assembly. This may or may not match the assembly version
+ ///
+ public int AssemblyFileMajorVersion { get; set; }
+
+ ///
+ /// The minorVersion portion of the file version for the assembly file. This may or may not match the corresponding
+ /// part of the assembly version.
+ ///
+ public int AssemblyFileMinorVersion { get; set; }
+
+ public override bool Equals(object obj)
+ {
+ var assembly = obj as AssemblyRecord;
+ if (assembly != null)
+ {
+ return string.Equals(assembly.Name, Name, StringComparison.OrdinalIgnoreCase) &&
+ assembly.Version == Version;
+ }
+
+ return false;
+ }
+
+ public object Clone()
+ {
+ var copiedParents = new HashSet();
+ foreach (var parent in _parents)
+ {
+ copiedParents.Add(parent.Clone() as AssemblyRecord);
+ }
+
+ return new AssemblyRecord()
+ {
+ _parents = copiedParents,
+ AssemblyFileMajorVersion = AssemblyFileMajorVersion,
+ AssemblyFileMinorVersion = AssemblyFileMinorVersion,
+ AssemblyName = AssemblyName,
+ Location = Location
+ };
+
+ }
+
+ ///
+ /// Compare the assembly record to an assembly name
+ ///
+ /// The assembly name to compare with
+ /// True if the assembly name is a reference to this assembly, otherwise false
+ public bool Equals(AssemblyName assembly)
+ {
+ return string.Equals(assembly.Name, Name, StringComparison.OrdinalIgnoreCase) &&
+ assembly.Version == Version;
+ }
+
+ public bool Equals(AssemblyRecord record)
+ {
+ return Equals(record.AssemblyName)
+ && AssemblyFileMajorVersion == record.AssemblyFileMajorVersion
+ && AssemblyFileMinorVersion == record.AssemblyFileMinorVersion;
+ }
+
+ public override string ToString()
+ {
+ StringBuilder output = new StringBuilder();
+ output.AppendLine(string.Format("AssemblyName: {0}, Version:{1}, FileVersion: {2}.{3}, Location:{4}",
+ Name, Version, AssemblyFileMajorVersion, AssemblyFileMinorVersion, Location));
+ if (ReferencingAssembly.Any())
+ {
+ output.AppendFormat("-> Parents: ({0})", string.Join(", ", ReferencingAssembly));
+ }
+
+ return output.ToString();
+
+ }
+
+ ///
+ /// Get all the ancestors in the ancestor tree
+ ///
+ /// The full set of ancestors in the ancestor tree.
+ public HashSet GetAncestors()
+ {
+ var result = new HashSet();
+ foreach (var parent in ReferencingAssembly)
+ {
+ result.Add(parent);
+ foreach (var grandParent in parent.GetAncestors())
+ {
+ result.Add(grandParent);
+ }
+
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/tools/StaticAnalysis/DependencyAnalyzer/AssemblyVersionConflict.cs b/tools/StaticAnalysis/DependencyAnalyzer/AssemblyVersionConflict.cs
new file mode 100644
index 000000000000..7c54d20f5571
--- /dev/null
+++ b/tools/StaticAnalysis/DependencyAnalyzer/AssemblyVersionConflict.cs
@@ -0,0 +1,82 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+
+namespace StaticAnalysis.DependencyAnalyzer
+{
+ ///
+ /// Indicates a conflict between assembly reference and actual assembly in a directory
+ ///
+ public class AssemblyVersionConflict : IReportRecord
+ {
+ ///
+ /// The directory containing a conflict
+ ///
+ public string Directory { get; set; }
+
+ ///
+ /// The name of the assembly
+ ///
+ public string AssemblyName { get; set; }
+
+ ///
+ /// The version referenced in the parent assembly manifest
+ ///
+ public Version ExpectedVersion { get; set; }
+
+ ///
+ /// The version of the assembly on disk
+ ///
+ public Version ActualVersion { get; set; }
+
+ ///
+ /// The identity of the parent assembly
+ ///
+ public string ParentAssembly { get; set; }
+
+ ///
+ /// A textual description of the problem
+ ///
+ public string Description { get; set; }
+
+ ///
+ /// A textual description of steps to remediate the issue
+ ///
+ public string Remediation { get; set; }
+
+ public int Severity { get; set; }
+
+ public string PrintHeaders()
+ {
+ return
+ "\"Directory\",\"AssemblyName\",\"Expected Version\",\"Actual Version\",\"Parent Assembly\",\"Severity\"," +
+ "\"Description\",\"Remediation\"";
+ }
+
+ public string FormatRecord()
+ {
+ return
+ string.Format(
+ "\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\",\"{7}\"",
+ Directory, AssemblyName, ExpectedVersion, ActualVersion, ParentAssembly, Severity,
+ Description, Remediation);
+ }
+
+ public override string ToString()
+ {
+ return FormatRecord();
+ }
+ }
+}
diff --git a/tools/StaticAnalysis/DependencyAnalyzer/DependencyAnalyzer.cs b/tools/StaticAnalysis/DependencyAnalyzer/DependencyAnalyzer.cs
new file mode 100644
index 000000000000..829b78e3e343
--- /dev/null
+++ b/tools/StaticAnalysis/DependencyAnalyzer/DependencyAnalyzer.cs
@@ -0,0 +1,331 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+
+namespace StaticAnalysis.DependencyAnalyzer
+{
+ ///
+ /// The static analysis tool for ensuring no runtime conflicts in assembly dependencies
+ ///
+ public class DependencyAnalyzer : IStaticAnalyzer
+ {
+ private Dictionary _assemblies =
+ new Dictionary(StringComparer.OrdinalIgnoreCase);
+ private Dictionary _sharedAssemblyReferences =
+ new Dictionary(new AssemblyNameComparer());
+ private Dictionary _identicalSharedAssemblies =
+ new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ private AppDomain _testDomain;
+ private AssemblyLoader _loader;
+ private ReportLogger _versionConflictLogger;
+ private ReportLogger _sharedConflictLogger;
+ private ReportLogger _missingAssemblyLogger;
+ private ReportLogger _extraAssemblyLogger;
+
+ public DependencyAnalyzer()
+ {
+ Name = "Dependency Analyzer";
+ }
+
+ public AnalysisLogger Logger { get; set; }
+ public string Name { get; private set; }
+
+ public void Analyze(IEnumerable directories)
+ {
+ if (directories == null)
+ {
+ throw new ArgumentNullException("directories");
+ }
+
+ _versionConflictLogger = Logger.CreateLogger("AssemblyVersionConflict.csv");
+ _sharedConflictLogger = Logger.CreateLogger("SharedAssemblyConflict.csv");
+ _missingAssemblyLogger = Logger.CreateLogger("MissingAssemblies.csv");
+ _extraAssemblyLogger = Logger.CreateLogger("ExtraAssemblies.csv");
+ foreach (var baseDirectory in directories)
+ {
+ foreach (var directoryPath in Directory.EnumerateDirectories(baseDirectory))
+ {
+ if (!Directory.Exists(directoryPath))
+ {
+ throw new InvalidOperationException("Please pass a valid directory name as the first parameter");
+ }
+
+ Logger.WriteMessage("Processing Directory {0}", directoryPath);
+ _assemblies.Clear();
+ _versionConflictLogger.Decorator.AddDecorator(r => { r.Directory = directoryPath; }, "Directory");
+ _missingAssemblyLogger.Decorator.AddDecorator(r => { r.Directory = directoryPath; }, "Directory");
+ _extraAssemblyLogger.Decorator.AddDecorator(r => { r.Directory = directoryPath; }, "Directory");
+ ProcessDirectory(directoryPath);
+ _versionConflictLogger.Decorator.Remove("Directory");
+ _missingAssemblyLogger.Decorator.Remove("Directory");
+ _extraAssemblyLogger.Decorator.Remove("Directory");
+ }
+ }
+ }
+
+ private AssemblyRecord CreateAssemblyRecord(string path)
+ {
+ AssemblyRecord result = null;
+ var fullPath = Path.GetFullPath(path);
+ try
+ {
+ var assembly = LoadByReflectionFromFile(fullPath);
+ var versionInfo = FileVersionInfo.GetVersionInfo(fullPath);
+ result = new AssemblyRecord()
+ {
+ AssemblyName = assembly.GetName(),
+ AssemblyFileMajorVersion = versionInfo.FileMajorPart,
+ AssemblyFileMinorVersion = versionInfo.FileMinorPart,
+ Location = fullPath
+ };
+
+ foreach (var child in assembly.GetReferencedAssemblies())
+ {
+ result.Children.Add(child);
+ }
+ }
+ catch
+ {
+ Logger.WriteError("Error loading assembly {0}", fullPath);
+ }
+
+ return result;
+ }
+
+ private bool AddSharedAssembly(AssemblyRecord assembly)
+ {
+ if (_sharedAssemblyReferences.ContainsKey(assembly.AssemblyName))
+ {
+ var stored = _sharedAssemblyReferences[assembly.AssemblyName];
+ if (!assembly.Equals(stored) && !(IsFrameworkAssembly(assembly.AssemblyName) && assembly.Version.Major <= 4))
+ {
+ _sharedConflictLogger.LogRecord(new SharedAssemblyConflict
+ {
+ AssemblyName = assembly.Name,
+ AssemblyPathsAndFileVersions = new List>()
+ {
+ new Tuple(assembly.Location, new Version(assembly.AssemblyFileMajorVersion,
+ assembly.AssemblyFileMinorVersion)),
+ new Tuple(stored.Location, new Version(stored.AssemblyFileMajorVersion,
+ stored.AssemblyFileMinorVersion))
+
+ },
+ AssemblyVersion = assembly.Version,
+ Severity = 0,
+ Description = "Shared assembly conflict, shared assemblies with the same assembly " +
+ "version have differing file versions",
+ Remediation = string.Format("Update the assembly reference for {0} in one of the " +
+ "referring assemblies", assembly.Name)
+ });
+
+ return false;
+ }
+ }
+ else
+ {
+ _sharedAssemblyReferences[assembly.AssemblyName] = assembly;
+ }
+
+ return true;
+ }
+
+ private AssemblyMetadata LoadByReflectionFromFile(string assemblyPath)
+ {
+ var info = _loader.GetReflectedAssemblyFromFile(assemblyPath);
+ if (info == null)
+ {
+ throw new InvalidOperationException();
+ }
+
+ return info;
+ }
+
+ private bool AddSharedAssemblyExactVersion(AssemblyRecord record)
+ {
+ if (_identicalSharedAssemblies.ContainsKey(record.Name))
+ {
+ var stored = _identicalSharedAssemblies[record.Name];
+ if (!record.Equals(stored) && !(IsFrameworkAssembly(record.AssemblyName)))
+ {
+ _sharedConflictLogger.LogRecord(new SharedAssemblyConflict
+ {
+ AssemblyName = record.Name,
+ AssemblyVersion = record.Version,
+ Severity = 0,
+ AssemblyPathsAndFileVersions = new List>()
+ {
+ new Tuple(record.Location, new Version(record.AssemblyFileMajorVersion,
+ record.AssemblyFileMinorVersion)),
+ new Tuple(stored.Location, new Version(stored.AssemblyFileMajorVersion,
+ stored.AssemblyFileMinorVersion)),
+ },
+ Description = string.Format("Assembly {0} has multiple versions as specified in 'Target'",
+ record.Name),
+ Remediation = string.Format("Ensure that all packages reference exactly the same package " +
+ "version of {0}", record.Name)
+
+ });
+
+ return false;
+ }
+ }
+ else
+ {
+ _identicalSharedAssemblies[record.Name] = record;
+ }
+
+ return true;
+ }
+
+ private static bool RequiresExactVersionMatch(AssemblyRecord name)
+ {
+ return name.Name.Contains("Microsoft.Azure.Common.Authentication");
+ }
+
+ private static bool IsFrameworkAssembly(AssemblyName name)
+ {
+ return name.Name.StartsWith("System") || name.Name.Equals("mscorlib");
+ }
+
+ private void ProcessDirectory(string directoryPath)
+ {
+ var savedDirectory = Directory.GetCurrentDirectory();
+ Directory.SetCurrentDirectory(directoryPath);
+ _loader = AssemblyLoader.Create(directoryPath, out _testDomain);
+ foreach (var file in Directory.GetFiles(directoryPath).Where(file => file.EndsWith(".dll")))
+ {
+ AssemblyRecord assembly = CreateAssemblyRecord(file);
+ _assemblies[assembly.Name] = assembly;
+ if (RequiresExactVersionMatch(assembly))
+ {
+ AddSharedAssemblyExactVersion(assembly);
+ }
+ else
+ {
+ AddSharedAssembly(assembly);
+ }
+ }
+
+ // Now check for assembly mismatches
+ foreach (var assembly in _assemblies.Values)
+ {
+ foreach (var reference in assembly.Children)
+ {
+ CheckAssemblyReference(reference, assembly);
+ }
+ }
+
+ FindExtraAssemblies();
+
+ AppDomain.Unload(_testDomain);
+ Directory.SetCurrentDirectory(savedDirectory);
+ }
+
+ private static bool IsCommandAssembly(AssemblyRecord assembly)
+ {
+ return assembly.Name.Contains("Commands") || assembly.Name.Contains("Cmdlets");
+ }
+
+ private void FindExtraAssemblies()
+ {
+ if (_assemblies.Values.Any(a => !IsCommandAssembly(a) && (a.ReferencingAssembly == null
+ || a.ReferencingAssembly.Count == 0 || !a.GetAncestors().Any(IsCommandAssembly))))
+ {
+ foreach (
+ var assembly in
+ _assemblies.Values.Where(a => !IsCommandAssembly(a) && (a.ReferencingAssembly == null ||
+ a.ReferencingAssembly.Count == 0 || !a.GetAncestors().Any(IsCommandAssembly))))
+ {
+ _extraAssemblyLogger.LogRecord(new ExtraAssembly
+ {
+ AssemblyName = assembly.Name,
+ Severity = 2,
+ Description = string.Format("Assembly {0} is not referenced from any cmdlets assembly",
+ assembly.Name),
+ Remediation = string.Format("Remove assembly {0} from the project and regenerate the Wix " +
+ "file", assembly.Name)
+ });
+ }
+ }
+ }
+
+ private void CheckAssemblyReference(AssemblyName reference, AssemblyRecord parent)
+ {
+ if (_assemblies.ContainsKey(reference.Name))
+ {
+ var stored = _assemblies[reference.Name];
+ if (stored.Equals(reference))
+ {
+ stored.ReferencingAssembly.Add(parent);
+ }
+ else if (reference.Version.Major == 0 && reference.Version.Minor == 0)
+ {
+ Logger.WriteWarning("{0}.dll has reference to assembly {1} without any version specification.",
+ parent.Name, reference.Name);
+ _versionConflictLogger.LogRecord(new AssemblyVersionConflict()
+ {
+ AssemblyName = reference.Name,
+ ActualVersion = stored.Version,
+ ExpectedVersion = reference.Version,
+ ParentAssembly = parent.Name,
+ Severity = 2,
+ Description = string.Format("Assembly {0} referenced from {1}.dll does not specify any " +
+ "assembly version evidence. The assembly will use version " +
+ "{2} from disk.", reference.Name, parent.Name, stored.Version),
+ Remediation = string.Format("Update the reference to assembly {0} from {1} so that " +
+ "assembly version evidence is supplied", reference.Name,
+ parent.Name)
+ });
+ }
+ else
+ {
+ var minVersion = (stored.Version < reference.Version) ? stored.Version : reference.Version;
+ _versionConflictLogger.LogRecord(new AssemblyVersionConflict()
+ {
+ AssemblyName = reference.Name,
+ ActualVersion = stored.Version,
+ ExpectedVersion = reference.Version,
+ ParentAssembly = parent.Name,
+ Severity = 1,
+ Description = string.Format("Assembly {0} version {1} referenced from {2}.dll does " +
+ "not match assembly version on disk: {3}",
+ reference.Name, reference.Version, parent.Name, stored.Version),
+ Remediation = string.Format("Update any references to version {0} of assembly {1}",
+ minVersion, reference.Name)
+ });
+ }
+ }
+ else if (!IsFrameworkAssembly(reference))
+ {
+ _missingAssemblyLogger.LogRecord(new MissingAssembly
+ {
+ AssemblyName = reference.Name,
+ AssemblyVersion = reference.Version.ToString(),
+ ReferencingAssembly = parent.Name,
+ Severity = 0,
+ Description = string.Format("Missing assembly {0} referenced from {1}", reference.Name,
+ parent.Name),
+ Remediation = "Ensure that the assembly is included in the Wix file or directory"
+ });
+ }
+ }
+ }
+}
diff --git a/tools/StaticAnalysis/DependencyAnalyzer/ExtraAssembly.cs b/tools/StaticAnalysis/DependencyAnalyzer/ExtraAssembly.cs
new file mode 100644
index 000000000000..677bb9271054
--- /dev/null
+++ b/tools/StaticAnalysis/DependencyAnalyzer/ExtraAssembly.cs
@@ -0,0 +1,49 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+namespace StaticAnalysis.DependencyAnalyzer
+{
+ ///
+ /// Record for reporting assemblies that are not used by a Cmdlet assembly or its dependencies
+ ///
+ public class ExtraAssembly : IReportRecord
+ {
+ public string Directory { get; set; }
+
+ public string AssemblyName { get; set; }
+
+ public int Severity { get; set; }
+
+ public string Description { get; set; }
+
+ public string Remediation { get; set; }
+
+
+ public string PrintHeaders()
+ {
+ return "\"Directory\",\"AssemblyName\",\"Severity\",\"Description\",\"Remediation\"";
+ }
+
+ public string FormatRecord()
+ {
+ return string.Format("\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\"",
+ Directory, AssemblyName, Severity, Description, Remediation);
+ }
+
+ public override string ToString()
+ {
+ return FormatRecord();
+ }
+ }
+}
diff --git a/tools/StaticAnalysis/DependencyAnalyzer/MissingAssembly.cs b/tools/StaticAnalysis/DependencyAnalyzer/MissingAssembly.cs
new file mode 100644
index 000000000000..ea73fd482404
--- /dev/null
+++ b/tools/StaticAnalysis/DependencyAnalyzer/MissingAssembly.cs
@@ -0,0 +1,48 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+namespace StaticAnalysis.DependencyAnalyzer
+{
+ ///
+ /// Record to indicate an assembly that is not in the dependency tree for a cmdlet assembly
+ ///
+ public class MissingAssembly : IReportRecord
+ {
+ public string Directory { get; set; }
+ public string AssemblyName { get; set; }
+ public string AssemblyVersion { get; set; }
+ public string ReferencingAssembly { get; set; }
+ public string Description { get; set; }
+ public string Remediation { get; set; }
+ public int Severity { get; set; }
+
+ public string PrintHeaders()
+ {
+ return "\"Directory\",\"Assembly Name\",\"Assembly Version\",\"Referencing Assembly\"," +
+ "\"Severity\",\"Description\",\"Remediation\"";
+ }
+
+ public string FormatRecord()
+ {
+ return string.Format("\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\"",
+ Directory, AssemblyName, AssemblyVersion, ReferencingAssembly,
+ Severity, Description, Remediation);
+ }
+
+ public override string ToString()
+ {
+ return FormatRecord();
+ }
+ }
+}
diff --git a/tools/StaticAnalysis/DependencyAnalyzer/SharedAssemblyConflict.cs b/tools/StaticAnalysis/DependencyAnalyzer/SharedAssemblyConflict.cs
new file mode 100644
index 000000000000..f2b8b0b9b4e6
--- /dev/null
+++ b/tools/StaticAnalysis/DependencyAnalyzer/SharedAssemblyConflict.cs
@@ -0,0 +1,53 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace StaticAnalysis.DependencyAnalyzer
+{
+ ///
+ /// Record to indicate a difference in assembly file versions for shared assemblies with the same
+ /// assembly version. This could result in unexpected behavior, depending on the assembly load order.
+ ///
+ public class SharedAssemblyConflict : IReportRecord
+ {
+ public string AssemblyName { get; set; }
+ public Version AssemblyVersion { get; set; }
+ public List> AssemblyPathsAndFileVersions { get; set; }
+ public string Description { get; set; }
+ public string Remediation { get; set; }
+ public int Severity { get; set; }
+
+ public string PrintHeaders()
+ {
+ return "\"Target\",\"AssemblyName\",\"AssemblyVersion\",\"Severity\",\"Description\",\"Remediation\"";
+ }
+
+ public string FormatRecord()
+ {
+ var targets =
+ AssemblyPathsAndFileVersions.Select(s => string.Format("File version {0} in {1}", s.Item2, s.Item1));
+ var targetString = string.Join(", ", targets);
+ return string.Format("\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\"", targetString, AssemblyName,
+ AssemblyVersion, Severity, Description, Remediation);
+ }
+
+ public override string ToString()
+ {
+ return FormatRecord();
+ }
+ }
+}
diff --git a/tools/StaticAnalysis/IReportRecord.cs b/tools/StaticAnalysis/IReportRecord.cs
new file mode 100644
index 000000000000..ab0c97f50171
--- /dev/null
+++ b/tools/StaticAnalysis/IReportRecord.cs
@@ -0,0 +1,28 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+namespace StaticAnalysis
+{
+ ///
+ /// Abstract interface for static analysis reports.
+ ///
+ public interface IReportRecord
+ {
+ string Description { get; set; }
+ string Remediation { get; set; }
+ int Severity { get; set; }
+ string PrintHeaders();
+ string FormatRecord();
+ }
+}
diff --git a/tools/StaticAnalysis/IStaticAnalyzer.cs b/tools/StaticAnalysis/IStaticAnalyzer.cs
new file mode 100644
index 000000000000..8eb55a0ec43b
--- /dev/null
+++ b/tools/StaticAnalysis/IStaticAnalyzer.cs
@@ -0,0 +1,40 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System.Collections.Generic;
+
+namespace StaticAnalysis
+{
+ ///
+ /// Defines the public object model for a static analysis tool
+ ///
+ public interface IStaticAnalyzer
+ {
+ ///
+ /// The logger where validation records should be written
+ ///
+ AnalysisLogger Logger { get; set; }
+
+ ///
+ /// The display name of the Analyzer
+ ///
+ string Name { get; }
+
+ ///
+ /// Validate the given assembly in the given directory
+ ///
+ /// The analysis targets
+ void Analyze(IEnumerable scopes);
+ }
+}
diff --git a/tools/StaticAnalysis/Program.cs b/tools/StaticAnalysis/Program.cs
new file mode 100644
index 000000000000..e4c94f164179
--- /dev/null
+++ b/tools/StaticAnalysis/Program.cs
@@ -0,0 +1,68 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace StaticAnalysis.DependencyAnalyzer
+{
+ ///
+ /// Runner for all static analysis tools.
+ ///
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ if (args == null || args.Length < 1)
+ {
+ throw new InvalidOperationException("Please pass a valid directory name as the first parameter");
+ }
+
+ var installDir = args[0];
+ if (!Directory.Exists(installDir))
+ {
+ throw new InvalidOperationException("You must pass a valid directory as the first parameter");
+ }
+
+ var directories = new List
+ {
+ Path.Combine(installDir, @"ResourceManager\AzureResourceManager\"),
+ Path.Combine(installDir, @"ServiceManagement\Azure\")
+ };
+
+ var reportsDirectory = Directory.GetCurrentDirectory();
+ bool logReportsDirectoryWarning = true;
+ if (args.Length > 1 && Directory.Exists(args[1]))
+ {
+ reportsDirectory = args[1];
+ logReportsDirectoryWarning = false;
+ }
+
+ var logger = new ConsoleLogger(reportsDirectory);
+
+ if (logReportsDirectoryWarning)
+ {
+ logger.WriteWarning("No logger specified in the second parameter, writing reports to {0}",
+ reportsDirectory);
+ }
+
+ var analyzer = new DependencyAnalyzer { Logger = logger };
+ logger.WriteMessage("Executing analyzer: {0}", analyzer.Name);
+ analyzer.Analyze(directories);
+ logger.WriteReports();
+ logger.WriteMessage("Processing complete for analyzer: {0}", analyzer.Name);
+ }
+ }
+}
diff --git a/tools/StaticAnalysis/Properties/AssemblyInfo.cs b/tools/StaticAnalysis/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000000..8ae4b806c636
--- /dev/null
+++ b/tools/StaticAnalysis/Properties/AssemblyInfo.cs
@@ -0,0 +1,50 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("DependencyChecker")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("DependencyChecker")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("4c8c8997-993d-4351-b7bc-ab423356fd7f")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/tools/StaticAnalysis/ReportLogger.cs b/tools/StaticAnalysis/ReportLogger.cs
new file mode 100644
index 000000000000..c836be697c1c
--- /dev/null
+++ b/tools/StaticAnalysis/ReportLogger.cs
@@ -0,0 +1,83 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using System.Collections.Generic;
+using System.Linq;
+
+namespace StaticAnalysis
+{
+ ///
+ /// Abstract report logger - used as an abstraction over typed loggers.
+ ///
+ public abstract class ReportLogger
+ {
+ private AnalysisLogger _parent;
+ private string _outputFile;
+ public ReportLogger(string fileName, AnalysisLogger parent)
+ {
+ _parent = parent;
+ _outputFile = fileName;
+ }
+
+ protected AnalysisLogger ParentLogger { get { return _parent; } }
+ public string FileName { get { return _outputFile; } }
+ public abstract IList Records { get; }
+
+ public virtual void WriteError(string error)
+ {
+ ParentLogger.WriteError(error);
+ }
+
+ public virtual void WriteMessage(string message)
+ {
+ ParentLogger.WriteMessage(message);
+ }
+
+ public virtual void WriteWarning(string message)
+ {
+ ParentLogger.WriteWarning(message);
+ }
+ }
+
+ ///
+ /// A typed report logger
+ ///
+ /// The type of the report this logger will log.
+ public class ReportLogger : ReportLogger where T : IReportRecord, new()
+ {
+ public ReportLogger(string fileName, AnalysisLogger logger)
+ : base(fileName, logger)
+ {
+ Decorator = Decorator.Create();
+ }
+
+ private IList _records = new List();
+ public Decorator Decorator { get; protected set; }
+
+ ///
+ /// Log a record to the report
+ ///
+ ///
+ public void LogRecord(T record)
+ {
+ Decorator.Apply(record);
+ _records.Add(record);
+ }
+
+ public override IList Records
+ {
+ get { return _records.Select(r => r as IReportRecord).ToList(); }
+ }
+ }
+}
diff --git a/tools/StaticAnalysis/Static Analysis.csproj b/tools/StaticAnalysis/Static Analysis.csproj
new file mode 100644
index 000000000000..63c60ce4ec69
--- /dev/null
+++ b/tools/StaticAnalysis/Static Analysis.csproj
@@ -0,0 +1,70 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {68384B59-BA0C-4B7B-B3F6-9C7988296C16}
+ Exe
+ Properties
+ StaticAnalysis
+ StaticAnalysis
+ v4.5
+ 512
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tools/StaticAnalysis/Static Analysis.sln b/tools/StaticAnalysis/Static Analysis.sln
new file mode 100644
index 000000000000..8b59a0384e02
--- /dev/null
+++ b/tools/StaticAnalysis/Static Analysis.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.40629.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Static Analysis", "Static Analysis.csproj", "{68384B59-BA0C-4B7B-B3F6-9C7988296C16}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {68384B59-BA0C-4B7B-B3F6-9C7988296C16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {68384B59-BA0C-4B7B-B3F6-9C7988296C16}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {68384B59-BA0C-4B7B-B3F6-9C7988296C16}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {68384B59-BA0C-4B7B-B3F6-9C7988296C16}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal