diff --git a/src/RoslynCodeTaskFactory/CodeTaskFactory.cs b/src/RoslynCodeTaskFactory/CodeTaskFactory.cs index 81a5dd6..691aa37 100644 --- a/src/RoslynCodeTaskFactory/CodeTaskFactory.cs +++ b/src/RoslynCodeTaskFactory/CodeTaskFactory.cs @@ -94,6 +94,21 @@ public sealed partial class CodeTaskFactory : ITaskFactory /// private static readonly ConcurrentDictionary CompiledAssemblyCache = new ConcurrentDictionary(); + /// + /// Stores a cache of loaded assemblies by the handler. + /// + private static readonly ConcurrentDictionary LoadedAssemblyCache = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// Stores the path to the directory that this assembly is located in. + /// + private static readonly Lazy ThisAssemblyDirectoryLazy = new Lazy(() => Path.GetDirectoryName(typeof(CodeTaskFactory).GetTypeInfo().Assembly.ManifestModule.FullyQualifiedName)); + + /// + /// Stores the parent directory of this assembly's directory. + /// + private static readonly Lazy ThisAssemblyParentDirectoryLazy = new Lazy(() => Path.GetDirectoryName(ThisAssemblyDirectoryLazy.Value)); + /// /// Stores an instance of a for logging messages. /// @@ -121,6 +136,10 @@ public sealed partial class CodeTaskFactory : ITaskFactory /// public void CleanupTask(ITask task) { +#if NET46 + + AppDomain.CurrentDomain.AssemblyResolve -= AppDomain_AssemblyResolve; +#endif } /// @@ -169,6 +188,11 @@ public bool Initialize(string taskName, IDictionary pa TaskType = assembly.GetExportedTypes().FirstOrDefault(type => type.Name.Equals(taskName)); } +#if NET46 + + AppDomain.CurrentDomain.AssemblyResolve += AppDomain_AssemblyResolve; +#endif + // Initialization succeeded if we found a type matching the task name from the compiled assembly // return TaskType != null; @@ -443,10 +467,6 @@ internal static bool TryLoadTaskBody(TaskLoggingHelper log, string taskName, str /// can compile their own task library. internal static bool TryResolveAssemblyReferences(TaskLoggingHelper log, TaskInfo taskInfo, out ITaskItem[] items) { - // Use the parent directory of this assembly as a starting point for resolving assemblies - // - string thisAssemblyParentDirectory = Path.GetDirectoryName(Path.GetDirectoryName(typeof(CodeTaskFactory).GetTypeInfo().Assembly.ManifestModule.FullyQualifiedName)); - // Store the list of resolved assemblies because a user can specify a short name or a full path // ISet resolvedAssemblyReferences = new HashSet(StringComparer.OrdinalIgnoreCase); @@ -486,7 +506,7 @@ internal static bool TryResolveAssemblyReferences(TaskLoggingHelper log, TaskInf ? reference : $"{reference}.dll"; - string possiblePath = Path.Combine(thisAssemblyParentDirectory, ReferenceAssemblyDirectoryName, assemblyFileName); + string possiblePath = Path.Combine(ThisAssemblyParentDirectoryLazy.Value, ReferenceAssemblyDirectoryName, assemblyFileName); if (File.Exists(possiblePath)) { @@ -570,6 +590,49 @@ private static IEnumerable GetPropertyStatements(string codeLanguage, IE } } +#if NET46 + + /// + /// A custom handler which loads assemblies needed for the CodeTaskFactory to work. + /// + /// + private Assembly AppDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + AssemblyName assemblyName = new AssemblyName(args.Name); + + // Guess the path based on the name + // + string candidateAssemblyPath = Path.Combine(ThisAssemblyDirectoryLazy.Value, $"{assemblyName.Name}.dll"); + + // See if the assembly has already been loaded from this path + // + if (LoadedAssemblyCache.TryGetValue(candidateAssemblyPath, out Assembly loadedAssembly)) + { + return loadedAssembly; + } + + if (File.Exists(candidateAssemblyPath)) + { + AssemblyName candidateAssemblyName = AssemblyName.GetAssemblyName(candidateAssemblyPath); + + // Verify that the requested assembly is the same as this one + // + if (assemblyName.FullName.Equals(candidateAssemblyName.FullName, StringComparison.OrdinalIgnoreCase)) + { + loadedAssembly = Assembly.LoadFrom(candidateAssemblyPath); + + // Cache the loaded assembly for later if necessary + // + LoadedAssemblyCache.TryAdd(candidateAssemblyPath, loadedAssembly); + + return loadedAssembly; + } + } + return null; + } + +#endif + /// /// Loads an assembly from the specified path. /// diff --git a/src/RoslynCodeTaskFactory/RoslynCodeTaskFactory.csproj b/src/RoslynCodeTaskFactory/RoslynCodeTaskFactory.csproj index d5fef2f..5b96466 100644 --- a/src/RoslynCodeTaskFactory/RoslynCodeTaskFactory.csproj +++ b/src/RoslynCodeTaskFactory/RoslynCodeTaskFactory.csproj @@ -76,6 +76,11 @@ Content build\ref + + <_PackageFiles Include="lib\**"> + Content + build\ + diff --git a/src/RoslynCodeTaskFactory/lib/net46/System.IO.FileSystem.dll b/src/RoslynCodeTaskFactory/lib/net46/System.IO.FileSystem.dll new file mode 100644 index 0000000..e4d8cea Binary files /dev/null and b/src/RoslynCodeTaskFactory/lib/net46/System.IO.FileSystem.dll differ