Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ComplexSerializerGenerator.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.CodeDom.Compiler;
5 using System.Collections.Generic;
6 using System.IO;
7 using System.Linq;
8 using System.Runtime.Remoting.Contexts;
9 using System.Security.Policy;
10 using System.Text;
11 using System.Text.RegularExpressions;
12 using Mono.Cecil;
13 using Mono.Cecil.Cil;
14 using Mono.Cecil.Rocks;
15 using SiliconStudio.Core;
16 
17 namespace SiliconStudio.AssemblyProcessor
18 {
19  /// <summary>
20  /// Various helper functions to generate complex serializer.
21  /// </summary>
22  public static class ComplexSerializerGenerator
23  {
24  public static CodeDomProvider codeDomProvider = new Microsoft.CSharp.CSharpCodeProvider();
25 
26  public static AssemblyDefinition GenerateSerializationAssembly(PlatformType platformType, BaseAssemblyResolver assemblyResolver, AssemblyDefinition assembly, string serializationAssemblyLocation, string signKeyFile = null)
27  {
28  // Create the serializer code generator
29  var serializerGenerator = new ComplexSerializerCodeGenerator(assemblyResolver, assembly);
30 
31  // Register default serialization profile (to help AOT generic instantiation of serializers)
32  RegisterDefaultSerializationProfile(assemblyResolver, assembly, serializerGenerator);
33 
34  // Generate serializer code
35  var serializerGeneratedCode = serializerGenerator.TransformText();
36 
37  var compilerParameters = new CompilerParameters();
38  compilerParameters.IncludeDebugInformation = false;
39  compilerParameters.GenerateInMemory = false;
40  compilerParameters.CompilerOptions = "/nostdlib";
41  compilerParameters.TreatWarningsAsErrors = false;
42  compilerParameters.TempFiles.KeepFiles = false;
43  //Console.WriteLine("Generating {0}", serializationAssemblyLocation);
44 
45  // Add reference from source assembly
46  // Use a hash set because it seems including twice mscorlib (2.0 and 4.0) seems to be a problem.
47  var skipWindows = "Windows, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null";
48 
49  // Sign the serialization assembly the same way the source was signed
50  // TODO: Transmit over command line
51  if (assembly.Name.HasPublicKey)
52  {
53  // TODO: If delay signed, we could actually extract the public key and apply it ourself maybe?
54  if (signKeyFile == null)
55  throw new InvalidOperationException("Generating serialization code for signed assembly, but no key was specified.");
56 
57  var assemblyPath = Path.GetDirectoryName(assembly.MainModule.FullyQualifiedName);
58  if ((assembly.MainModule.Attributes & ModuleAttributes.StrongNameSigned) == ModuleAttributes.StrongNameSigned)
59  {
60  // Strongly signed
61  compilerParameters.CompilerOptions += string.Format(" /keyfile:\"{0}\"", signKeyFile);
62  }
63  else
64  {
65  // Delay signed
66  compilerParameters.CompilerOptions += string.Format(" /delaysign+ /keyfile:\"{0}\"", signKeyFile);
67  }
68  }
69 
70  var assemblyLocations = new HashSet<string>();
71  foreach (var referencedAssemblyName in assembly.MainModule.AssemblyReferences)
72  {
73  if (referencedAssemblyName.FullName != skipWindows)
74  {
75  if (assemblyLocations.Add(referencedAssemblyName.Name))
76  {
77  //Console.WriteLine("Resolve Assembly for serialization [{0}]", referencedAssemblyName.FullName);
78  compilerParameters.ReferencedAssemblies.Add(assemblyResolver.Resolve(referencedAssemblyName).MainModule.FullyQualifiedName);
79  }
80  }
81  }
82 
83  // typeof(Dictionary<,>)
84  // Special case for 4.5: Because Dictionary<,> is forwarded, we need to add a reference to the actual assembly
85  var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assembly);
86  compilerParameters.ReferencedAssemblies.Add(mscorlibAssembly.MainModule.FullyQualifiedName);
87  var collectionType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(Dictionary<,>).FullName);
88  compilerParameters.ReferencedAssemblies.Add(collectionType.Module.FullyQualifiedName);
89 
90  // Make sure System and System.Reflection are added
91  // TODO: Maybe we should do that for .NETCore and PCL too? (instead of WinRT only)
92  if (platformType == PlatformType.WindowsStore || platformType == PlatformType.WindowsPhone)
93  {
94  if (assemblyLocations.Add("System"))
95  {
96  compilerParameters.ReferencedAssemblies.Add(assemblyResolver.Resolve("System").MainModule.FullyQualifiedName);
97  }
98  if (assemblyLocations.Add("System.Reflection"))
99  {
100  compilerParameters.ReferencedAssemblies.Add(assemblyResolver.Resolve("System.Reflection").MainModule.FullyQualifiedName);
101  }
102  }
103 
104  compilerParameters.ReferencedAssemblies.Add(assembly.MainModule.FullyQualifiedName);
105  assemblyLocations.Add(assembly.Name.Name);
106 
107  // In case Paradox.Framework.Serialization was not referenced, let's add it.
108  if (!assemblyLocations.Contains("SiliconStudio.Core"))
109  {
110  compilerParameters.ReferencedAssemblies.Add(assemblyResolver.Resolve("SiliconStudio.Core").MainModule.FullyQualifiedName);
111  }
112 
113  compilerParameters.OutputAssembly = serializationAssemblyLocation;
114  var compilerResults = codeDomProvider.CompileAssemblyFromSource(compilerParameters, serializerGeneratedCode);
115 
116  if (compilerResults.Errors.HasErrors)
117  {
118  var errors = new StringBuilder();
119  errors.AppendLine(string.Format("Serialization assembly compilation: {0} error(s)", compilerResults.Errors.Count));
120  foreach (var error in compilerResults.Errors)
121  errors.AppendLine(error.ToString());
122  throw new InvalidOperationException(errors.ToString());
123  }
124 
125  // Run ILMerge
126  var merge = new ILRepacking.ILRepack()
127  {
128  OutputFile = assembly.MainModule.FullyQualifiedName,
129  DebugInfo = true,
130  CopyAttributes = true,
131  AllowMultipleAssemblyLevelAttributes = true,
132  XmlDocumentation = false,
133  NoRepackRes = true,
134  PrimaryAssemblyDefinition = assembly,
135  WriteToDisk = false,
136  //KeepFirstOfMultipleAssemblyLevelAttributes = true,
137  //Log = true,
138  //LogFile = "ilmerge.log",
139  };
140  merge.SetInputAssemblies(new string[] { serializationAssemblyLocation });
141 
142  // Force to use the correct framework
143  //merge.SetTargetPlatform("v4", frameworkFolder);
144  merge.SetSearchDirectories(assemblyResolver.GetSearchDirectories());
145  merge.Merge();
146 
147  // Add assembly signing info
148  if (assembly.Name.HasPublicKey)
149  {
150  merge.TargetAssemblyDefinition.Name.PublicKey = assembly.Name.PublicKey;
151  merge.TargetAssemblyDefinition.Name.PublicKeyToken = assembly.Name.PublicKeyToken;
152  merge.TargetAssemblyDefinition.Name.Attributes |= AssemblyAttributes.PublicKey;
153  if ((assembly.MainModule.Attributes & ModuleAttributes.StrongNameSigned) == ModuleAttributes.StrongNameSigned)
154  merge.TargetAssemblyMainModule.Attributes |= ModuleAttributes.StrongNameSigned;
155  }
156 
157  try
158  {
159  // Delete serializer dll
160  File.Delete(serializationAssemblyLocation);
161 
162  var serializationAssemblyPdbFilePath = Path.ChangeExtension(serializationAssemblyLocation, "pdb");
163  if (File.Exists(serializationAssemblyPdbFilePath))
164  {
165  File.Delete(serializationAssemblyPdbFilePath);
166  }
167  }
168  catch (IOException)
169  {
170  // Mute IOException
171  }
172 
173  return merge.TargetAssemblyDefinition;
174  }
175 
176  private static void RegisterDefaultSerializationProfile(IAssemblyResolver assemblyResolver, AssemblyDefinition assembly, ComplexSerializerCodeGenerator generator)
177  {
178  var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assembly);
179  if (mscorlibAssembly == null)
180  {
181  Console.WriteLine("Missing mscorlib.dll from assembly {0}", assembly.FullName);
182  throw new InvalidOperationException("Missing mscorlib.dll from assembly");
183  }
184 
185  var coreSerializationAssembly = assemblyResolver.Resolve("SiliconStudio.Core");
186 
187  // Register serializer factories (determine which type requires which serializer)
188  generator.SerializerFactories.Add(new CecilGenericSerializerFactory(typeof(IList<>), coreSerializationAssembly.MainModule.GetTypeResolved("SiliconStudio.Core.Serialization.Serializers.ListInterfaceSerializer`1")));
189  generator.SerializerFactories.Add(new CecilGenericSerializerFactory(typeof(List<>), coreSerializationAssembly.MainModule.GetTypeResolved("SiliconStudio.Core.Serialization.Serializers.ListSerializer`1")));
190  generator.SerializerFactories.Add(new CecilGenericSerializerFactory(typeof(KeyValuePair<,>), coreSerializationAssembly.MainModule.GetTypeResolved("SiliconStudio.Core.Serialization.Serializers.KeyValuePairSerializer`2")));
191  generator.SerializerFactories.Add(new CecilGenericSerializerFactory(typeof(IDictionary<,>), coreSerializationAssembly.MainModule.GetTypeResolved("SiliconStudio.Core.Serialization.Serializers.DictionaryInterfaceSerializer`2")));
192  generator.SerializerFactories.Add(new CecilGenericSerializerFactory(typeof(Dictionary<,>), coreSerializationAssembly.MainModule.GetTypeResolved("SiliconStudio.Core.Serialization.Serializers.DictionarySerializer`2")));
193  generator.SerializerFactories.Add(new CecilGenericSerializerFactory(typeof(Nullable<>), coreSerializationAssembly.MainModule.GetTypeResolved("SiliconStudio.Core.Serialization.Serializers.NullableSerializer`1")));
194  generator.SerializerFactories.Add(new CecilEnumSerializerFactory(coreSerializationAssembly.MainModule.GetTypeResolved("SiliconStudio.Core.Serialization.Serializers.EnumSerializer`1")));
195  generator.SerializerFactories.Add(new CecilArraySerializerFactory(coreSerializationAssembly.MainModule.GetTypeResolved("SiliconStudio.Core.Serialization.Serializers.ArraySerializer`1")));
196 
197  // Iterate over tuple size
198  for (int i = 1; i <= 4; ++i)
199  {
200  generator.SerializerDependencies.Add(new CecilSerializerDependency(
201  string.Format("System.Tuple`{0}", i),
202  coreSerializationAssembly.MainModule.GetTypeResolved(string.Format("SiliconStudio.Core.Serialization.Serializers.TupleSerializer`{0}", i))));
203 
204  generator.SerializerDependencies.Add(new CecilSerializerDependency(string.Format("SiliconStudio.Core.Serialization.Serializers.TupleSerializer`{0}", i)));
205  }
206 
207  // Register serializer dependencies (determine which serializer serializes which sub-type)
208  generator.SerializerDependencies.Add(new CecilSerializerDependency("SiliconStudio.Core.Serialization.Serializers.ArraySerializer`1"));
209  generator.SerializerDependencies.Add(new CecilSerializerDependency("SiliconStudio.Core.Serialization.Serializers.KeyValuePairSerializer`2"));
210  generator.SerializerDependencies.Add(new CecilSerializerDependency("SiliconStudio.Core.Serialization.Serializers.ListSerializer`1"));
211  generator.SerializerDependencies.Add(new CecilSerializerDependency("SiliconStudio.Core.Serialization.Serializers.ListInterfaceSerializer`1"));
212  generator.SerializerDependencies.Add(new CecilSerializerDependency("SiliconStudio.Core.Serialization.Serializers.NullableSerializer`1"));
213  generator.SerializerDependencies.Add(new CecilSerializerDependency("SiliconStudio.Core.Serialization.Serializers.DictionarySerializer`2",
214  mscorlibAssembly.MainModule.GetTypeResolved(typeof(KeyValuePair<,>).FullName)));
215  generator.SerializerDependencies.Add(new CecilSerializerDependency("SiliconStudio.Core.Serialization.Serializers.DictionaryInterfaceSerializer`2",
216  mscorlibAssembly.MainModule.GetTypeResolved(typeof(KeyValuePair<,>).FullName)));
217  }
218 
219  public static string GenerateSerializationAssemblyLocation(string assemblyLocation)
220  {
221  if (!Regex.IsMatch(assemblyLocation, ".dll|.exe", RegexOptions.IgnoreCase))
222  throw new InvalidOperationException();
223 
224  return Regex.Replace(assemblyLocation, ".dll|.exe", ".Serializers.dll", RegexOptions.IgnoreCase);
225  }
226 
227  private static bool CheckSerializationAssembly(string assemblyLocation, string serializationAssemblyLocation)
228  {
229  return (File.Exists(serializationAssemblyLocation)
230  && File.GetLastWriteTimeUtc(serializationAssemblyLocation) >= File.GetLastWriteTimeUtc(assemblyLocation));
231  }
232  }
233 }
PlatformType
Describes the platform operating system.
Definition: PlatformType.cs:9
static string GenerateSerializationAssemblyLocation(string assemblyLocation)
Various helper functions to generate complex serializer.
System.IO.File File
static AssemblyDefinition GenerateSerializationAssembly(PlatformType platformType, BaseAssemblyResolver assemblyResolver, AssemblyDefinition assembly, string serializationAssemblyLocation, string signKeyFile=null)