Support for Runtime Patches via Harmony
This commit is contained in:
parent
9f54ff8169
commit
0c05236bee
|
@ -3,7 +3,7 @@
|
||||||
# Uncomment this to turn on verbose mode.
|
# Uncomment this to turn on verbose mode.
|
||||||
#export DH_VERBOSE=1
|
#export DH_VERBOSE=1
|
||||||
|
|
||||||
EXCLUDE_MODULEREFS = crypt32 httpapi __Internal
|
EXCLUDE_MODULEREFS = crypt32 httpapi __Internal ole32.dll libmonosgen-2.0
|
||||||
|
|
||||||
%:
|
%:
|
||||||
dh $@ --with=systemd --with=cli
|
dh $@ --with=systemd --with=cli
|
||||||
|
|
|
@ -102,12 +102,6 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
||||||
o.Debug = false;
|
o.Debug = false;
|
||||||
o.DiagnosticsLevel = SentryLevel.Debug;
|
o.DiagnosticsLevel = SentryLevel.Debug;
|
||||||
o.Release = BuildInfo.Release;
|
o.Release = BuildInfo.Release;
|
||||||
if (PlatformInfo.IsMono)
|
|
||||||
{
|
|
||||||
// Mono 6.0 broke GzipStream.WriteAsync
|
|
||||||
// TODO: Check specific version
|
|
||||||
o.RequestBodyCompressionLevel = System.IO.Compression.CompressionLevel.NoCompression;
|
|
||||||
}
|
|
||||||
o.BeforeSend = x => SentryCleanser.CleanseEvent(x);
|
o.BeforeSend = x => SentryCleanser.CleanseEvent(x);
|
||||||
o.BeforeBreadcrumb = x => SentryCleanser.CleanseBreadcrumb(x);
|
o.BeforeBreadcrumb = x => SentryCleanser.CleanseBreadcrumb(x);
|
||||||
o.Environment = BuildInfo.Branch;
|
o.Environment = BuildInfo.Branch;
|
||||||
|
|
|
@ -6,6 +6,7 @@ using NzbDrone.Common.Exceptions;
|
||||||
using NzbDrone.Common.Instrumentation;
|
using NzbDrone.Common.Instrumentation;
|
||||||
using NzbDrone.Host;
|
using NzbDrone.Host;
|
||||||
using NzbDrone.Host.AccessControl;
|
using NzbDrone.Host.AccessControl;
|
||||||
|
using NzbDrone.RuntimePatches;
|
||||||
|
|
||||||
namespace NzbDrone.Console
|
namespace NzbDrone.Console
|
||||||
{
|
{
|
||||||
|
@ -23,6 +24,8 @@ namespace NzbDrone.Console
|
||||||
|
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
|
RuntimePatcher.Initialize();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var startupArgs = new StartupContext(args);
|
var startupArgs = new StartupContext(args);
|
||||||
|
|
|
@ -9,5 +9,6 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\NzbDrone.Host\Sonarr.Host.csproj" />
|
<ProjectReference Include="..\NzbDrone.Host\Sonarr.Host.csproj" />
|
||||||
|
<ProjectReference Include="..\Sonarr.RuntimePatches\Sonarr.RuntimePatches.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -0,0 +1,96 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
using HarmonyLib;
|
||||||
|
|
||||||
|
namespace NzbDrone.RuntimePatches.Mono
|
||||||
|
{
|
||||||
|
// Mono 6.0 - 6.x bug 16122
|
||||||
|
// Unimplemented method used in GzipStream initiated via the http stack, the method existed as far back as 5.10
|
||||||
|
public class DeflateStreamAsyncPatch : MonoRuntimePatchBase
|
||||||
|
{
|
||||||
|
private static DeflateStreamAsyncPatch Instance;
|
||||||
|
|
||||||
|
public override Version MonoMinVersion => new Version(6, 0);
|
||||||
|
public override Version MonoMaxVersion => new Version(6, 0, 0, 334);
|
||||||
|
|
||||||
|
protected override void Patch()
|
||||||
|
{
|
||||||
|
Instance = this;
|
||||||
|
|
||||||
|
TryPatchMethod(typeof(DeflateStream), "ReadAsyncMemory", "Memory<Byte>", "CancellationToken");
|
||||||
|
TryPatchMethod(typeof(DeflateStream), "WriteAsyncMemory", "ReadOnlyMemory<Byte>", "CancellationToken");
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need a Transpiler coz these methods are for net4.7.2 so we cannot access the types directly
|
||||||
|
|
||||||
|
// internal ValueTask<int> ReadAsyncMemory(Memory<byte> destination, CancellationToken cancellationToken)
|
||||||
|
// {
|
||||||
|
// - throw new NotImplementedException();
|
||||||
|
// + return base.ReadAsync(destination, cancellationToken);
|
||||||
|
// }
|
||||||
|
static IEnumerable<CodeInstruction> Transpiler_ReadAsyncMemory(IEnumerable<CodeInstruction> instructions, MethodBase method)
|
||||||
|
{
|
||||||
|
var codes = instructions.ToList();
|
||||||
|
|
||||||
|
var patchable = codes.Matches(OpCodes.Newobj, OpCodes.Throw);
|
||||||
|
|
||||||
|
var readAsync = method.DeclaringType.BaseType.GetMethod("ReadAsync", method.GetParameterTypes());
|
||||||
|
|
||||||
|
if (patchable && readAsync != null)
|
||||||
|
{
|
||||||
|
codes.Clear();
|
||||||
|
|
||||||
|
codes.Add(new CodeInstruction(OpCodes.Ldarg_0));
|
||||||
|
codes.Add(new CodeInstruction(OpCodes.Ldarg_1));
|
||||||
|
codes.Add(new CodeInstruction(OpCodes.Ldarg_2));
|
||||||
|
codes.Add(new CodeInstruction(OpCodes.Call, readAsync));
|
||||||
|
codes.Add(new CodeInstruction(OpCodes.Ret));
|
||||||
|
|
||||||
|
Instance.Debug($"Patch applied to method {method.GetSimplifiedName()}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Instance.Error($"Skipped patching method {method.GetSimplifiedName()}: Method construct different than expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
return codes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// internal ValueTask WriteAsyncMemory(ReadOnlyMemory<byte> source, CancellationToken cancellationToken)
|
||||||
|
// {
|
||||||
|
// - throw new NotImplementedException();
|
||||||
|
// + return base.WriteAsync(source, cancellationToken);
|
||||||
|
// }
|
||||||
|
static IEnumerable<CodeInstruction> Transpiler_WriteAsyncMemory(IEnumerable<CodeInstruction> instructions, MethodBase method)
|
||||||
|
{
|
||||||
|
var codes = instructions.ToList();
|
||||||
|
|
||||||
|
var patchable = codes.Matches(OpCodes.Newobj, OpCodes.Throw);
|
||||||
|
|
||||||
|
var writeAsync = method.DeclaringType.BaseType.GetMethod("WriteAsync", method.GetParameterTypes());
|
||||||
|
|
||||||
|
if (patchable && writeAsync != null)
|
||||||
|
{
|
||||||
|
codes.Clear();
|
||||||
|
|
||||||
|
codes.Add(new CodeInstruction(OpCodes.Ldarg_0));
|
||||||
|
codes.Add(new CodeInstruction(OpCodes.Ldarg_1));
|
||||||
|
codes.Add(new CodeInstruction(OpCodes.Ldarg_2));
|
||||||
|
codes.Add(new CodeInstruction(OpCodes.Call, writeAsync));
|
||||||
|
codes.Add(new CodeInstruction(OpCodes.Ret));
|
||||||
|
|
||||||
|
Instance.Debug($"Patch applied to method {method.GetSimplifiedName()}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Instance.Error($"Skipped patching method {method.GetSimplifiedName()}: Method construct different than expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
return codes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace NzbDrone.RuntimePatches
|
||||||
|
{
|
||||||
|
public abstract class MonoRuntimePatchBase : RuntimePatchBase
|
||||||
|
{
|
||||||
|
private static readonly Regex VersionRegex = new Regex(@"(?<=\W|^)(?<version>\d+\.\d+(\.\d+)?(\.\d+)?)(?=\W)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
public static readonly Version MonoVersion;
|
||||||
|
public virtual Version MonoMinVersion => new Version(0, 0);
|
||||||
|
public virtual Version MonoMaxVersion => new Version(100, 0);
|
||||||
|
|
||||||
|
static MonoRuntimePatchBase()
|
||||||
|
{
|
||||||
|
// Copied from MonoPlatformInfo, coz we want to load as little as possible at this stage.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var type = Type.GetType("Mono.Runtime");
|
||||||
|
|
||||||
|
if (type != null)
|
||||||
|
{
|
||||||
|
var displayNameMethod = type.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static);
|
||||||
|
if (displayNameMethod != null)
|
||||||
|
{
|
||||||
|
var displayName = displayNameMethod.Invoke(null, null).ToString();
|
||||||
|
var versionMatch = VersionRegex.Match(displayName);
|
||||||
|
|
||||||
|
if (versionMatch.Success)
|
||||||
|
{
|
||||||
|
MonoVersion = new Version(versionMatch.Groups["version"].Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool ShouldPatch()
|
||||||
|
{
|
||||||
|
if (MonoVersion == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MonoVersion >= MonoMinVersion && MonoVersion < MonoMaxVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Log(string log)
|
||||||
|
{
|
||||||
|
base.Log($"{log} (Mono {MonoVersion})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using HarmonyLib;
|
||||||
|
|
||||||
|
namespace NzbDrone.RuntimePatches
|
||||||
|
{
|
||||||
|
public abstract class RuntimePatchBase
|
||||||
|
{
|
||||||
|
private Harmony _harmony;
|
||||||
|
|
||||||
|
public virtual bool ShouldPatch() => true;
|
||||||
|
protected abstract void Patch();
|
||||||
|
|
||||||
|
public void Patch(Harmony harmony)
|
||||||
|
{
|
||||||
|
_harmony = harmony;
|
||||||
|
|
||||||
|
if (ShouldPatch())
|
||||||
|
{
|
||||||
|
Patch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected const BindingFlags DefaultBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
|
||||||
|
|
||||||
|
protected static MethodInfo FindMethod(Type type, string methodName, params string[] paramTypes)
|
||||||
|
{
|
||||||
|
foreach (var methodInfo in type.GetMethods(DefaultBindingFlags))
|
||||||
|
{
|
||||||
|
if (methodInfo.Name != methodName) continue;
|
||||||
|
|
||||||
|
var parameters = methodInfo.GetParameters();
|
||||||
|
|
||||||
|
if (parameters.Length != paramTypes.Length) continue;
|
||||||
|
|
||||||
|
var parametersMatch = true;
|
||||||
|
for (var i = 0; i < parameters.Length; i++)
|
||||||
|
{
|
||||||
|
if (parameters[i].ParameterType.Name != paramTypes[i] &&
|
||||||
|
parameters[i].ParameterType.FullName != paramTypes[i] &&
|
||||||
|
parameters[i].ParameterType.GetSimplifiedName() != paramTypes[i] &&
|
||||||
|
parameters[i].ParameterType.GetSimplifiedName(true) != paramTypes[i])
|
||||||
|
{
|
||||||
|
parametersMatch = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parametersMatch) continue;
|
||||||
|
|
||||||
|
return methodInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void PatchMethod(MethodInfo methodInfo)
|
||||||
|
{
|
||||||
|
var prefix = GetPatchMethod("Prefix_" + methodInfo.Name);
|
||||||
|
var postfix = GetPatchMethod("Postfix_" + methodInfo.Name);
|
||||||
|
var transpiler = GetPatchMethod("Transpiler_" + methodInfo.Name);
|
||||||
|
|
||||||
|
_harmony.Patch(methodInfo, prefix, postfix, transpiler);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void TryPatchMethod(string typeName, string methodName, params string[] paramTypes)
|
||||||
|
{
|
||||||
|
var type = Type.GetType(typeName);
|
||||||
|
|
||||||
|
if (type != null)
|
||||||
|
{
|
||||||
|
TryPatchMethod(type, "GetSslServer");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug($"Skipped patching method {typeName}.{methodName}: Type not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void TryPatchMethod(Type type, string methodName, params string[] paramTypes)
|
||||||
|
{
|
||||||
|
var methodInfo = FindMethod(type, methodName, paramTypes);
|
||||||
|
if (methodInfo != null)
|
||||||
|
{
|
||||||
|
PatchMethod(methodInfo);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug($"Skipped patching method {type.GetSimplifiedName()}.{methodName}: Method not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private HarmonyMethod GetPatchMethod(string name)
|
||||||
|
{
|
||||||
|
var patch = GetType().GetMethod(name, DefaultBindingFlags);
|
||||||
|
if (patch != null)
|
||||||
|
{
|
||||||
|
return new HarmonyMethod(patch);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void Debug(string log)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Log(log);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void Error(string log)
|
||||||
|
{
|
||||||
|
Log(log);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Log(string log)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"RuntimePatch {GetType().Name}: {log}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
using System.Text;
|
||||||
|
using HarmonyLib;
|
||||||
|
|
||||||
|
namespace NzbDrone.RuntimePatches
|
||||||
|
{
|
||||||
|
public static class RuntimePatchExtensions
|
||||||
|
{
|
||||||
|
public static bool Matches(this List<CodeInstruction> instructions, params OpCode[] opcodes)
|
||||||
|
{
|
||||||
|
var codes = instructions.Select(v => v.opcode).Where(v => v != OpCodes.Nop).ToList();
|
||||||
|
|
||||||
|
if (codes.Count != opcodes.Length) return false;
|
||||||
|
|
||||||
|
for (var i = 0; i < codes.Count; i++)
|
||||||
|
{
|
||||||
|
if (codes[i] != opcodes[i]) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type[] GetParameterTypes(this MethodBase method)
|
||||||
|
{
|
||||||
|
return Array.ConvertAll(method.GetParameters(), v => v.ParameterType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetSimplifiedName(this MethodBase method, bool includeNamespace = false)
|
||||||
|
{
|
||||||
|
return $"{method.DeclaringType.GetSimplifiedName()}.{method.Name}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetSimplifiedName(this Type t, bool includeNamespace = false)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
if (includeNamespace && string.IsNullOrEmpty(t.Namespace))
|
||||||
|
{
|
||||||
|
sb.Append(t.Namespace);
|
||||||
|
sb.Append('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.IsGenericType)
|
||||||
|
{
|
||||||
|
sb.Append(t.Name, 0, t.Name.LastIndexOf('`'));
|
||||||
|
sb.Append('<');
|
||||||
|
var args = t.GetGenericArguments();
|
||||||
|
for (int i = 0; i < args.Length; i++)
|
||||||
|
{
|
||||||
|
if (i != 0)
|
||||||
|
sb.Append(", ");
|
||||||
|
|
||||||
|
sb.Append(GetSimplifiedName(args[i], includeNamespace));
|
||||||
|
}
|
||||||
|
sb.Append('>');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Append(t.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using HarmonyLib;
|
||||||
|
|
||||||
|
namespace NzbDrone.RuntimePatches
|
||||||
|
{
|
||||||
|
public static class RuntimePatcher
|
||||||
|
{
|
||||||
|
public static void Initialize()
|
||||||
|
{
|
||||||
|
var env = Environment.GetEnvironmentVariable("DISABLE_RUNTIMEPATCHES");
|
||||||
|
if (env != "1")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ApplyPatches();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Failed to apply runtime patches, attempting to continue normally.\r\n" + ex.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ApplyPatches()
|
||||||
|
{
|
||||||
|
var patches = Assembly.GetExecutingAssembly()
|
||||||
|
.GetExportedTypes()
|
||||||
|
.Where(type => !type.IsAbstract && typeof(RuntimePatchBase).IsAssignableFrom(type))
|
||||||
|
.Select(Activator.CreateInstance)
|
||||||
|
.Cast<RuntimePatchBase>()
|
||||||
|
.Where(patch => patch.ShouldPatch())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (patches.Any())
|
||||||
|
{
|
||||||
|
var harmony = new Harmony("tv.sonarr");
|
||||||
|
|
||||||
|
foreach (var patch in patches)
|
||||||
|
{
|
||||||
|
patch.Patch(harmony);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net462</TargetFramework>
|
||||||
|
<Platforms>x86</Platforms>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Lib.Harmony" Version="2.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio Version 16
|
||||||
VisualStudioVersion = 15.0.27130.2010
|
VisualStudioVersion = 16.0.29806.167
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sonarr.Console", "NzbDrone.Console\Sonarr.Console.csproj", "{3DCA7B58-B8B3-49AC-9D9E-56F4A0460976}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sonarr.Console", "NzbDrone.Console\Sonarr.Console.csproj", "{3DCA7B58-B8B3-49AC-9D9E-56F4A0460976}"
|
||||||
ProjectSection(ProjectDependencies) = postProject
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
@ -97,6 +97,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sonarr.Http", "Sonarr.Http\
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sonarr.Host.Test", "NzbDrone.Host.Test\Sonarr.Host.Test.csproj", "{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sonarr.Host.Test", "NzbDrone.Host.Test\Sonarr.Host.Test.csproj", "{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sonarr.RuntimePatches", "Sonarr.RuntimePatches\Sonarr.RuntimePatches.csproj", "{F3F63718-63C6-432F-BDDC-C960AD95EC82}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|x86 = Debug|x86
|
Debug|x86 = Debug|x86
|
||||||
|
@ -284,6 +286,12 @@ Global
|
||||||
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Mono|x86.Build.0 = Release|x86
|
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Mono|x86.Build.0 = Release|x86
|
||||||
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|x86.ActiveCfg = Release|x86
|
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|x86.ActiveCfg = Release|x86
|
||||||
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|x86.Build.0 = Release|x86
|
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|x86.Build.0 = Release|x86
|
||||||
|
{F3F63718-63C6-432F-BDDC-C960AD95EC82}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
|
{F3F63718-63C6-432F-BDDC-C960AD95EC82}.Debug|x86.Build.0 = Debug|x86
|
||||||
|
{F3F63718-63C6-432F-BDDC-C960AD95EC82}.Mono|x86.ActiveCfg = Release|x86
|
||||||
|
{F3F63718-63C6-432F-BDDC-C960AD95EC82}.Mono|x86.Build.0 = Release|x86
|
||||||
|
{F3F63718-63C6-432F-BDDC-C960AD95EC82}.Release|x86.ActiveCfg = Release|x86
|
||||||
|
{F3F63718-63C6-432F-BDDC-C960AD95EC82}.Release|x86.Build.0 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -315,6 +323,7 @@ Global
|
||||||
{90D6E9FC-7B88-4E1B-B018-8FA742274558} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}
|
{90D6E9FC-7B88-4E1B-B018-8FA742274558} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}
|
||||||
{9DC31DE3-79FF-47A8-96B4-6BA18F6BB1CB} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}
|
{9DC31DE3-79FF-47A8-96B4-6BA18F6BB1CB} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}
|
||||||
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5} = {57A04B72-8088-4F75-A582-1158CF8291F7}
|
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5} = {57A04B72-8088-4F75-A582-1158CF8291F7}
|
||||||
|
{F3F63718-63C6-432F-BDDC-C960AD95EC82} = {0F0D4998-8F5D-4467-A909-BB192C4B3B4B}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
EnterpriseLibraryConfigurationToolBinariesPath = packages\Unity.2.1.505.0\lib\NET35;packages\Unity.2.1.505.2\lib\NET35
|
EnterpriseLibraryConfigurationToolBinariesPath = packages\Unity.2.1.505.0\lib\NET35;packages\Unity.2.1.505.2\lib\NET35
|
||||||
|
|
Loading…
Reference in New Issue