Konstantin Koch 17d0771b03 Placed the importers into different categories for the "importing existing files" dialog in Visual Studio.
Fixed a performance problem in the Visual Studio extension where no importer or processor was selected for an asset.
Fixed that the asset names for Uri encoded in the build output.
Fixed that errors when serializing assets get logged.
Sped up ImporterManager.GuessImporterByFileExtension, which caused performance problems if many assemblies are loaded into the current AppDomain.
Made the AssimpImporter library deploy the binary files again (hopefully just a temporary solution until we've found a better way.)
Provide a extension for TargetPlatform enum for getting the DisplayName of an entry.
Changed that ProcessorManager.GetProcessorDisplayName doesn't throw an exception if no processor with the given name exists, which now mimicks the same behavior as in importerManager.GetImporterDisplayName.
2015-04-26 19:47:26 +02:00

1051 lines
39 KiB
C#

using ANX.Framework.Content.Pipeline;
using ANX.Framework.Content.Pipeline.Helpers;
using ANX.Framework.Content.Pipeline.Tasks;
using ANX.Framework.Design;
using ANX.Framework.Graphics;
using ANX.Framework.VisualStudio;
using ANX.Framework.VisualStudio.Nodes;
using Microsoft.VisualStudio.Project;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing.Design;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.ServiceModel;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
namespace ANX.Framework.Build
{
public sealed class BuildAppDomain : IDisposable
{
public class RemoteProxy : MarshalByRefObject
{
private List<string> processorNames = new List<string>();
private ProcessorManager processorManager = null;
private bool processorNamesInvalid = true;
private ImporterManager importerManager = null;
private bool importerManagerInvalid = true;
private List<string> importerNames = new List<string>();
public override object InitializeLifetimeService()
{
return null;
}
public void LoadProjectAssemblies(IEnumerable<string> assemblies, IEnumerable<Uri> searchPaths, IDictionary<string, Uri> redirectAssemblies, Action<string, Exception> errorCallback)
{
if (assemblies == null)
throw new ArgumentNullException("assemblies");
if (searchPaths == null)
throw new ArgumentNullException("searchPaths");
if (redirectAssemblies == null)
throw new ArgumentNullException("redirectAssemblies");
foreach (string assembly in assemblies)
{
try
{
Assembly assemblyInstance = null;
if (File.Exists(assembly))
{
string assemblyName = Path.GetFileNameWithoutExtension(assembly);
if (IsAssemblyAlreadyLoaded(assemblyName))
continue;
assemblyInstance = LoadFrom(assembly, redirectAssemblies);
}
else
{
if (IsAssemblyAlreadyLoaded(assembly))
continue;
Uri assemblyUri;
if (Uri.TryCreate(assembly.Split(',').First() + ".dll", UriKind.RelativeOrAbsolute, out assemblyUri))
{
foreach (Uri path in searchPaths)
{
Uri temp = new Uri(path, assemblyUri);
if (File.Exists(temp.LocalPath))
{
assemblyInstance = LoadFrom(temp.LocalPath, redirectAssemblies);
break;
}
}
if (assemblyInstance == null)
assemblyInstance = Assembly.Load(assembly);
}
else
{
assemblyInstance = Assembly.Load(assembly);
}
}
ClassPreloader.Preload(assemblyInstance);
}
catch (Exception exc)
{
if (errorCallback != null)
errorCallback(assembly, exc);
else
throw;
}
}
this.processorManager = null;
this.processorNamesInvalid = true;
this.importerManager = null;
this.importerManagerInvalid = true;
}
private bool IsAssemblyAlreadyLoaded(string assemblyName)
{
bool isFullName = assemblyName.Contains(',');
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
if (isFullName)
{
foreach (var loadedAssembly in loadedAssemblies)
if (loadedAssembly.FullName == assemblyName)
{
return true;
}
}
else
{
foreach (var loadedAssembly in loadedAssemblies)
if (loadedAssembly.GetName().Name == assemblyName)
{
return true;
}
}
return false;
}
private Assembly LoadFrom(string assembly, IDictionary<string, Uri> redirectAssemblies)
{
string assemblyName = assembly;
if (Path.IsPathRooted(assembly))
assemblyName = Path.GetFileNameWithoutExtension(assembly);
else if (assembly.Contains(','))
assemblyName = assembly.Split(',').First();
Uri redirectedPath;
if (redirectAssemblies.TryGetValue(assemblyName, out redirectedPath))
{
return Assembly.LoadFrom(redirectedPath.LocalPath);
}
else
{
return Assembly.LoadFrom(assembly);
}
}
public string GetAssemblyRuntimeVersion(string assemblyPath)
{
if (File.Exists(assemblyPath))
{
return Assembly.ReflectionOnlyLoadFrom(assemblyPath).ImageRuntimeVersion;
}
return null;
}
public IEnumerable<AssemblyName> GetReferencedAssemblies(string assembly)
{
if (File.Exists(assembly))
{
return Assembly.ReflectionOnlyLoadFrom(assembly).GetReferencedAssemblies();
}
else
{
return Assembly.ReflectionOnlyLoad(assembly).GetReferencedAssemblies();
}
}
public string GetExistingFilesFilter()
{
ImporterManager importerManager = new ImporterManager();
SortedDictionary<string, List<string>> supportedFileExtensions = new SortedDictionary<string, List<string>>();
List<string> allExtensions = new List<string>();
foreach (var valuePair in importerManager.AvailableImporters)
{
var attribute = valuePair.Value.GetCustomAttribute<ContentImporterAttribute>(true);
IEnumerable<string> fileExtensions = attribute.FileExtensions;
string category = attribute.Category;
if (fileExtensions.Count() == 0)
continue;
if (string.IsNullOrWhiteSpace(category))
{
category = "Other Files";
}
category = category.Trim();
List<string> extensions;
if (!supportedFileExtensions.TryGetValue(category, out extensions))
{
extensions = new List<string>();
supportedFileExtensions.Add(category, extensions);
}
foreach (var fileExtension in fileExtensions)
{
if (!string.IsNullOrWhiteSpace(fileExtension))
{
string usableExtension = "*" + fileExtension;
if (!extensions.Contains(usableExtension))
{
extensions.Add(usableExtension);
if (!allExtensions.Contains(usableExtension))
{
allExtensions.Add(usableExtension);
}
}
}
}
}
string allExtensionsJoined = string.Join(";", allExtensions);
string filter = "Content Project Files|" + allExtensionsJoined;
foreach (var valuePair in supportedFileExtensions)
{
string extensionsJoined = string.Join(";", valuePair.Value);
filter += "|" + valuePair.Key + "|" + extensionsJoined;
}
return filter;
}
public IEnumerable<string> GetProcessorNames()
{
if (processorNamesInvalid)
{
processorNames.Clear();
if (processorManager == null)
{
processorManager = new ProcessorManager();
}
foreach (var value in processorManager.AvailableProcessors)
{
processorNames.Add(value.Key);
}
processorNamesInvalid = false;
}
return processorNames;
}
public IEnumerable<string> GetImporterNames()
{
if (importerManagerInvalid)
{
importerNames.Clear();
if (importerManager == null)
{
importerManager = new ImporterManager();
}
foreach (var value in importerManager.AvailableImporters)
{
importerNames.Add(value.Key);
}
importerManagerInvalid = false;
}
return importerNames;
}
public string GetImporterName(string displayName)
{
if (importerManager == null)
{
importerManager = new ImporterManager();
}
return importerManager.GetImporterName(displayName);
}
public string GetImporterDisplayName(string importer)
{
if (importerManager == null)
{
importerManager = new ImporterManager();
}
return importerManager.GetImporterDisplayName(importer);
}
public string GetProcessorName(string displayName)
{
if (processorManager == null)
{
processorManager = new ProcessorManager();
}
return processorManager.GetProcessorName(displayName);
}
public string GetProcessorDisplayName(string processor)
{
if (processorManager == null)
{
processorManager = new ProcessorManager();
}
return processorManager.GetProcessorDisplayName(processor);
}
public string GetProcessorTypeName(string processor)
{
if (processorManager == null)
{
processorManager = new ProcessorManager();
}
return processorManager.GetInstance(processor).GetType().AssemblyQualifiedName;
}
public string GuessImporterByFileExtension(string fileName)
{
if (importerManager == null)
{
importerManager = new ImporterManager();
}
return importerManager.GuessImporterByFileExtension(fileName);
}
public string GetDefaultProcessorForImporter(string importer)
{
if (importerManager == null)
importerManager = new ImporterManager();
return importerManager.GetDefaultProcessor(importer);
}
public ICollection<ANX.Framework.VisualStudio.ProcessorParameter> GetProcessorParameters(string processor)
{
if (processorManager == null)
processorManager = new ProcessorManager();
List<ANX.Framework.VisualStudio.ProcessorParameter> result = new List<ANX.Framework.VisualStudio.ProcessorParameter>();
if (!string.IsNullOrEmpty(processor))
{
ANX.Framework.Content.Pipeline.ProcessorParameterCollection processorParams = processorManager.GetProcessorParameters(processor);
foreach (var parameter in processorParams)
{
string defaultValueText = null;
string valueText = null;
object processorInstance = processorManager.GetInstance(processor);
Type processorType = processorInstance.GetType();
Type propertyType = TypeHelper.GetType(parameter.PropertyType);
PropertyInfo property = processorType.GetProperty(parameter.PropertyName, propertyType);
TypeConverter converter = property.GetConverter();
if (converter != null)
{
defaultValueText = (string)converter.ConvertTo(parameter.DefaultValue, typeof(string));
valueText = (string)converter.ConvertTo(property.GetValue(processorInstance), typeof(string));
}
result.Add(new ANX.Framework.VisualStudio.ProcessorParameter(parameter.PropertyName, parameter.DisplayName, parameter.PropertyType, valueText, defaultValueText, parameter.Description));
}
}
return result;
}
public string GetConverterTypeName(string containerType, string propertyName)
{
Type type = TypeHelper.GetType(containerType);
PropertyInfo property = type.GetProperty(propertyName);
TypeConverter converter = property.GetConverter();
if (converter != null)
return converter.GetType().AssemblyQualifiedName;
return null;
}
private object CreateInstance(Type targetType, Type typeParam)
{
var parameterTypes = new Type[] { typeof(Type) };
ConstructorInfo constructor = targetType.GetConstructor(parameterTypes);
if (constructor != null)
{
return TypeDescriptor.CreateInstance(null, targetType, parameterTypes, new object[] { typeParam });
}
return TypeDescriptor.CreateInstance(null, targetType, null, null);
}
public IEnumerable<string> GetAssemblyLocations()
{
List<string> locations = new List<string>();
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (assembly.CodeBase == null)
{
locations.Add(assembly.Location);
}
else
{
locations.Add(new Uri(assembly.CodeBase).LocalPath);
}
}
return locations;
}
/// <summary>
/// Returns a proxy for an editor.
/// The returned value should be wrapped by <see cref="UITypeEditorWrapper"/>
/// </summary>
/// <param name="editorBaseType">The wanted base type for the editor. Currently only supports <see cref="UITypeEditor"/>.</param>
/// <param name="typeName"></param>
/// <param name="propertyName"></param>
/// <param name="converterProxy">Must be a proxy to the <see cref="WrappedConverter"/> class.</param>
/// <returns></returns>
public IProxy GetEditor(Type editorBaseType, string typeName, string propertyName, IProxy converterProxy)
{
if (editorBaseType != typeof(UITypeEditor))
{
return null;
}
Type type = TypeHelper.GetType(typeName);
PropertyInfo property = type.GetProperty(propertyName);
UITypeEditor editor;
var attributes = property.GetCustomAttributes<EditorAttribute>(true);
foreach (var attribute in attributes)
{
if (attribute.EditorBaseTypeName == editorBaseType.AssemblyQualifiedName)
{
Type editorType = TypeHelper.GetType(attribute.EditorTypeName);
if (editorType != null && typeof(UITypeEditor).IsAssignableFrom(editorType))
{
editor = (UITypeEditor)this.CreateInstance(editorType, property.PropertyType);
if (editor != null)
{
return UITypeEditorWrapper.CreateProxy(editor, converterProxy);
}
else
{
return null;
}
}
}
}
editor = (UITypeEditor)TypeDescriptor.GetEditor(property.PropertyType, editorBaseType);
if (editor != null)
{
return UITypeEditorWrapper.CreateProxy(editor, converterProxy);
}
else
{
return null;
}
}
public string[] GetPlatformDisplayNames()
{
return Utilities.GetTargetPlatformDisplayNames();
}
public string[] GetGraphicsProfilesNames()
{
return Enum.GetNames(typeof(GraphicsProfile));
}
public bool IsUpDoDate(string projectHome, IEnumerable<BuildItem> buildItems, Configuration activeConfiguration, ErrorLoggingHelper loggingHelper)
{
BuildContentTask task = new BuildContentTask();
task.TargetPlatform = activeConfiguration.Platform;
task.TargetProfile = activeConfiguration.Profile;
var buildCache = new AnxBuildCache(task.ImporterManager, task.ProcessorManager, task.ContentCompiler);
buildCache.ProjectHome = new Uri(projectHome, UriKind.Absolute);
task.BuildCache = buildCache;
string intermediateDirectory = Path.Combine(projectHome, "obj", CreateSafeFileName(activeConfiguration.Platform.ToDisplayName()), CreateSafeFileName(activeConfiguration.Name)) + Path.DirectorySeparatorChar;
var buildCacheUri = new Uri(new Uri(intermediateDirectory, UriKind.Absolute), new Uri("build.cache", UriKind.Relative));
try
{
buildCache.LoadCache(buildCacheUri);
}
catch
{
return false;
}
foreach (var buildItem in buildItems)
{
if (buildCache.IsValid(buildItem, task.GetOutputFileName(buildItem)))
return false;
}
return true;
}
private string CreateSafeFileName(string text)
{
foreach (var unsafeChar in Path.GetInvalidFileNameChars())
text = text.Replace(unsafeChar, '_');
return text;
}
}
//pretty much the same as if one would use lock(appDomain) but as a public api.
public sealed class BuildDomainAquire : IDisposable
{
BuildAppDomain appDomain;
internal BuildDomainAquire(BuildAppDomain appDomain)
{
Monitor.Enter(appDomain);
this.appDomain = appDomain;
}
public RemoteProxy Proxy
{
get
{
return this.appDomain.proxyInstance;
}
}
public void Unload()
{
this.appDomain.Unload();
}
public void Initialize(string identifier)
{
appDomain.Initialize(identifier);
}
public T CreateInstanceAndUnwrap<T>()
{
return appDomain.CreateInstanceAndUnwrap<T>();
}
public List<Uri> SearchPaths
{
get { return appDomain.SearchPaths; }
}
public Dictionary<string, Uri> Redirects
{
get { return appDomain.Redirects; }
}
public String MakeRelativeToSearchPaths(String path)
{
if (path == null)
throw new ArgumentNullException("path");
Uri uri = new Uri(path);
if (!uri.IsAbsoluteUri)
throw new ArgumentException("uri must be absolute.");
return uri.MakeRelativeUri(SearchPaths).OriginalString;
}
public Uri MakeRelativeToSearchPaths(Uri uri)
{
if (uri == null)
throw new ArgumentNullException("uri");
if (!uri.IsAbsoluteUri)
throw new ArgumentException("uri must be absolute.");
return uri.MakeRelativeUri(SearchPaths);
}
public String MakeAbsoluteFromSearchPaths(String path)
{
if (path == null)
throw new ArgumentNullException("path");
var uri = new Uri(path, UriKind.RelativeOrAbsolute);
if (uri.IsAbsoluteUri)
{
return path;
}
foreach (Uri searchPath in SearchPaths)
{
Uri tempPath = new Uri(searchPath, uri);
if (File.Exists(tempPath.LocalPath))
return tempPath.OriginalString;
}
return uri.OriginalString;
}
public Uri MakeAbsoluteFromSearchPaths(Uri uri)
{
if (uri == null)
throw new ArgumentNullException("uri");
if (uri.IsAbsoluteUri)
{
return uri;
}
foreach (Uri path in SearchPaths)
{
Uri tempPath = new Uri(path, uri);
if (File.Exists(tempPath.LocalPath))
return tempPath;
}
return uri;
}
public Uri[] ShadowCopyDirectories
{
get { return appDomain.ShadowCopyDirectories; }
set { appDomain.ShadowCopyDirectories = value; }
}
public void AddShadowCopyDirectory(Uri path)
{
appDomain.AddShadowCopyDirectory(path);
}
public void RemoveShadowCopyDirectory(Uri path)
{
appDomain.RemoveShadowCopyDirectory(path);
}
public void Release()
{
Monitor.Exit(appDomain);
}
void IDisposable.Dispose()
{
this.Release();
}
}
object buildLock = new object();
bool disposed = false;
AppDomain appDomain;
RemoteProxy proxyInstance;
List<Uri> searchPaths = new List<Uri>();
//string shadowCopyPath;
Uri[] sourceShadowCopyDirectories = new Uri[0];
Dictionary<string, Uri> redirects = new Dictionary<string, Uri>();
ServiceHost loggerServiceHost;
ContentProjectNode projectNode;
Process buildProcess;
public BuildAppDomain(ContentProjectNode projectNode)
{
this.SearchPaths.Add(new Uri(Path.GetDirectoryName(typeof(BuildAppDomain.RemoteProxy).Assembly.Location), UriKind.Absolute));
this.Redirects.Add(typeof(Color).Assembly.GetName().Name, new Uri(typeof(Color).Assembly.CodeBase, UriKind.Absolute)); //ANX.Framework
this.Redirects.Add(typeof(IContentProcessor).Assembly.GetName().Name, new Uri(typeof(IContentProcessor).Assembly.CodeBase, UriKind.Absolute)); //ANX.Framework.Content.Pipeline
this.projectNode = projectNode;
}
public BuildDomainAquire Aquire()
{
if (disposed)
throw new ObjectDisposedException("BuildAppDomain");
return new BuildDomainAquire(this);
}
public bool IsDisposed
{
get { return disposed; }
}
private List<Uri> SearchPaths
{
get
{
return searchPaths;
}
}
private Dictionary<string, Uri> Redirects
{
get
{
return redirects;
}
}
public bool IsBuildRunning
{
get
{
return this.buildProcess != null && !this.buildProcess.HasExited;
}
}
private void AddShadowCopyDirectory(Uri path)
{
if (path == null)
throw new ArgumentNullException("path");
if (!path.IsAbsoluteUri)
throw new ArgumentException("path must be absolute.");
var newDirs = new Uri[sourceShadowCopyDirectories.Length + 1];
Array.Copy(sourceShadowCopyDirectories, newDirs, sourceShadowCopyDirectories.Length);
newDirs[newDirs.Length - 1] = path;
ShadowCopyDirectories = newDirs;
}
private void RemoveShadowCopyDirectory(Uri path)
{
if (path == null)
throw new ArgumentNullException("path");
if (!path.IsAbsoluteUri)
throw new ArgumentException("path must be absolute.");
int index = Array.IndexOf(sourceShadowCopyDirectories, path);
if (index == -1)
return;
var newDirs = new Uri[sourceShadowCopyDirectories.Length - 1];
Array.Copy(sourceShadowCopyDirectories, newDirs, index);
if (newDirs.Length - index > 0)
Array.Copy(sourceShadowCopyDirectories, index + 1, newDirs, index, newDirs.Length - index);
ShadowCopyDirectories = newDirs;
}
//Disable obsolete code warning
//Settings the shadow copy path for an appDomain is declared obsolete, it says I should use the shadow copy path in the appDomainSetup.
//Doing that would require to recreate the appDomain whenever a new reference is added and to batch the adding of references.
//Doing it this way was just the easier way.
#pragma warning disable 618
private Uri[] ShadowCopyDirectories
{
get
{
return sourceShadowCopyDirectories.ToArray();
}
set
{
if (value == null)
{
sourceShadowCopyDirectories = new Uri[0];
appDomain.SetShadowCopyPath("");
}
else
{
if (!value.All((x) => x != null && x.IsAbsoluteUri))
{
throw new ArgumentException("All Uri's must be not null and absolute.");
}
sourceShadowCopyDirectories = value;
appDomain.SetShadowCopyPath(string.Join(";", sourceShadowCopyDirectories.Where((x) => x != null).Select((x) => x.LocalPath)));
}
}
}
//Not behind the lock because we only use that has been given in the parameters and we're not using any data from foreign assemblies.
//Another reason is, when we start debugging, some project properties get refreshed and we need access to the lock for that.
public void BuildContent(Configuration activeConfiguration, IEnumerable<string> files, ErrorLoggingHelper loggingHelper, bool debug, string target)
{
if (activeConfiguration == null)
throw new ArgumentNullException("activeConfiguration");
if (loggingHelper == null)
throw new ArgumentNullException("loggingHelper");
string projectHome = this.projectNode.ProjectHome;
List<string> arguments = new List<string>();
VisualStudioContentBuildLogger logger = new VisualStudioContentBuildLogger(loggingHelper);
arguments.Add(this.projectNode.AbsoluteProjectFilePath);
arguments.Add("-c:" + activeConfiguration.Name);
arguments.Add("-t:" + activeConfiguration.Platform);
arguments.Add("-cd:" + projectHome);
if (target == MsBuildTarget.Rebuild)
arguments.Add("-ForceRebuild");
else if (target == MsBuildTarget.Clean)
arguments.Add("-CleanCache");
if (files != null && files.Count() > 0)
{
arguments.Add("-DontAddProjectBuildItems");
foreach (string file in files)
{
if (file.StartsWith(projectHome))
arguments.Add(file.Substring(projectHome.Length));
else
arguments.Add(file);
}
}
StartContentBuilder(arguments, debug, logger);
}
private void StartContentBuilder(IList<string> arguments, bool debug, IContentBuildLogger logger, int? millisecondsTimeOut = null)
{
if (buildProcess != null)
{
if (buildProcess.HasExited)
this.EndBuild();
else
throw new InvalidOperationException("There is still a build process running.");
}
lock (buildLock)
{
string workingDir = Path.GetDirectoryName(this.GetType().Assembly.Location);
string exePath = Path.Combine(workingDir, "ContentBuilder.exe");
Uri loggerUri = new Uri("net.pipe://localhost/VisualStudio/" + Process.GetCurrentProcess().Id + "/");
if (loggerServiceHost != null)
loggerServiceHost.Close();
loggerServiceHost = new ServiceHost(logger, loggerUri);
try
{
loggerServiceHost.AddServiceEndpoint(typeof(IContentBuildLogger), new NetNamedPipeBinding(), "ContentBuildLogger");
loggerServiceHost.Open();
arguments.Add("-l:" + new Uri(loggerUri, new Uri("ContentBuildLogger", UriKind.RelativeOrAbsolute)).OriginalString);
if (debug)
arguments.Add("-Debug");
//Prepare the arguments to be used for process start.
var args = arguments.Select((x) => "\"" + Regex.Replace(x, "(\\\\*)(\\\\$|\")", "$1$1\\$2") + "\"");
if (debug)
{
//May have to change the debugger interface for newer versions of visual studio.
var debugger = this.projectNode.Site.GetService(typeof(SVsShellDebugger)) as IVsDebugger4;
var targetInfo = new VsDebugTargetInfo4()
{
bstrArg = string.Join(" ", args),
bstrCurDir = workingDir,
bstrExe = exePath,
guidLaunchDebugEngine = Microsoft.VisualStudio.VSConstants.DebugEnginesGuids.ManagedOnly_guid,
project = this.projectNode,
dlo = (uint)DEBUG_LAUNCH_OPERATION.DLO_CreateProcess,
};
VsDebugTargetProcessInfo[] result = new VsDebugTargetProcessInfo[1];
debugger.LaunchDebugTargets4(1, new VsDebugTargetInfo4[] { targetInfo }, result);
buildProcess = Process.GetProcessById((int)result[0].dwProcessId);
}
else
{
buildProcess = Process.Start(new ProcessStartInfo(exePath, string.Join(" ", args))
{
CreateNoWindow = true,
UseShellExecute = false,
WorkingDirectory = workingDir,
});
}
if (millisecondsTimeOut.HasValue)
buildProcess.WaitForExit(millisecondsTimeOut.Value);
else
buildProcess.WaitForExit();
}
catch
{
if (loggerServiceHost != null)
loggerServiceHost.Abort();
throw;
}
}
}
public void EndBuild()
{
lock (buildLock)
{
if (buildProcess != null)
{
buildProcess.Close();
buildProcess = null;
}
if (loggerServiceHost != null)
{
loggerServiceHost.Close();
loggerServiceHost = null;
}
}
}
public void PrepareBuild(bool cleanBuild)
{
if (cleanBuild)
return;
lock (buildLock)
{
ErrorLoggingHelper loggingHelper = new ErrorLoggingHelper(this.projectNode, this.projectNode.ErrorListProvider, null);
//Test if ANX.Framework or ANX.Framework.Content.Pipeline are referenced, because we must use our own version.
foreach (var reference in this.projectNode.GetReferenceContainer().EnumReferences())
{
if (Redirects.ContainsKey(reference.Caption))
{
loggingHelper.LogMessage("", null, reference.Url, ErrorLoggingHelper.MessageImportance.Info, PackageResources.GetString(PackageResources.AnxFrameworkAssembliesRedirected), reference.Caption);
}
}
}
}
#pragma warning restore 618
private void Unload()
{
EndBuild();
if (appDomain != null)
{
proxyInstance = null;
AppDomain.Unload(appDomain);
appDomain = null;
}
}
private void Initialize(string identifier)
{
if (appDomain != null)
{
throw new InvalidOperationException("The AppDomain is already initialized.");
}
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationBase = Path.GetDirectoryName(typeof(BuildAppDomain.RemoteProxy).Assembly.Location);
//if (SearchPath != null)
// setup.ApplicationBase = SearchPath.OriginalString;
setup.ApplicationName = "ANX Project " + identifier;
setup.ShadowCopyFiles = "true"; //Shadow copying so that files like dll's of referenced projects can still be updated.
//I can't shadow copy everything, otherwise I wouldn't be able to use the remoteProxy in the other appDomain because if the location of the loaded assembly differs from our own, I can't cast the proxy
//in our appDomain.
setup.ShadowCopyDirectories = string.Join(";", ShadowCopyDirectories.Select((x) => x.LocalPath));
//setup.CachePath = Path.Combine(Path.GetTempPath(), "ANX Project ShadowCopies");
//I might need to setup a custom shadow copy path, because the default path is restricted by space. But if I do that, I would also have to take care of the cleanup.
//this.shadowCopyPath = Path.Combine(setup.CachePath, setup.ApplicationName);
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
appDomain = AppDomain.CreateDomain("ANX Project " + identifier, null, setup);
appDomain.AssemblyResolve += BuildDomainAssemblyResolver.appDomain_AssemblyResolve;
//appDomain.TypeResolve += BuildDomainAssemblyResolver.appDomain_AssemblyResolve;
//appDomain.Load(typeof(ContentProject).Assembly.FullName); //ANX.Framework.Build
proxyInstance = (RemoteProxy)appDomain.CreateInstanceAndUnwrap(typeof(BuildAppDomain.RemoteProxy).Assembly.FullName, typeof(BuildAppDomain.RemoteProxy).FullName);
}
[Serializable]
static class BuildDomainAssemblyResolver
{
//Help placing project assemblies from the loadFrom context into the load context, only that way we can find the contained types later by using Type.GetType(string).
public static Assembly appDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (assembly.FullName == args.Name)
return assembly;
}
return null;
}
}
//Help placing our own assemblies into the load context of the newly created appDomain
Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
var assembly = AppDomain.CurrentDomain.GetAssemblies().Where((x) => x.FullName == args.Name).FirstOrDefault();
if (assembly != null)
return assembly;
return null;
}
private T CreateInstanceAndUnwrap<T>()
{
return (T)appDomain.CreateInstanceAndUnwrap(typeof(T).Assembly.FullName, typeof(T).FullName);
}
public void Dispose()
{
Unload();
disposed = true;
/*if (Directory.Exists(shadowCopyPath))
{
//If the directory is still used by another process, the delete should silently fail.
try
{
Directory.Delete(shadowCopyPath, true);
}
catch
{ }
}*/
}
}
}