/// <summary> /// Helper method to call <see cref="ApplicationModel{T}.GenerateCode(CILReflectionContext, Boolean)"/> and save the resulting Qi4CS generated assemblies to specific folder. /// </summary> /// <typeparam name="TInstance">The type of the Qi4CS application instance of <paramref name="model"/>.</typeparam> /// <param name="model">The <see cref="ApplicationModel{T}"/>.</param> /// <param name="path">The path where generated Qi4CS assemblies should reside. If <c>null</c>, value of <see cref="Environment.CurrentDirectory"/> will be used.</param> /// <param name="isWP8OrSL5Emit">Whether emit assemblies compatible with Windows Phone 8 or Silverlight 5.</param> /// <param name="emittingInfoCreator"> /// The callback to create an <see cref="EmittingArguments"/> for emitting an assembly. /// It receives the existing <see cref="System.Reflection.Assembly"/> from which the Qi4CS assembly was generated, corresponding generated <see cref="CILAssembly"/>. /// It should return <see cref="EmittingArguments"/> to be used to emit the Qi4CS assembly. /// If the callback is <c>null</c> or returns <c>null</c>, the result of <see cref="EmittingArguments.CreateForEmittingDLL(StrongNameKeyPair, ImageFileMachine, TargetRuntime, ModuleFlags)"/> is used, with no strong name and <see cref="ImageFileMachine.I386"/> and <see cref="TargetRuntime.Net_4_0"/> as parameters. /// </param> /// <returns> /// The mapping from assembly used in Qi4CS model to the full path of corresponding generated Qi4CS assembly. /// </returns> public static IDictionary <System.Reflection.Assembly, String> GenerateAndSaveAssemblies <TInstance>(this ApplicationModel <TInstance> model, String path = null, Boolean isWP8OrSL5Emit = false, Func <System.Reflection.Assembly, CILAssembly, EmittingArguments> emittingInfoCreator = null) where TInstance : ApplicationSPI { if (path == null) { path = Environment.CurrentDirectory; } else { if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } if (!path.EndsWith("" + Path.DirectorySeparatorChar)) { path += Path.DirectorySeparatorChar; } path = Path.GetDirectoryName(Path.GetFullPath(path)); } //var refDic = new ConcurrentDictionary<String, Tuple<CILAssemblyName, Boolean>[]>(); var fileNames = DotNETReflectionContext.UseDotNETContext(ctx => { var genDic = model.GenerateCode(ctx, isWP8OrSL5Emit); var eArgsDic = new Dictionary <CILAssembly, EmittingArguments>(); // Before emitting, we must set the public keys of the generated assemblies, in order for cross-references to work properly. // Either CreateDefaultEmittingArguments or emittingInfoCreator should set the public key, if the strong name key pair is container name. foreach (var kvp in genDic) { var genAss = kvp.Value; var nAss = kvp.Key; EmittingArguments eArgs; if (emittingInfoCreator == null) { eArgs = CreateDefaultEmittingArguments(); } else { eArgs = (emittingInfoCreator(nAss, genAss) ?? CreateDefaultEmittingArguments()); } if (eArgs.StrongName != null && !eArgs.StrongName.KeyPair.IsNullOrEmpty() && genAss.Name.PublicKey.IsNullOrEmpty()) { // Have to set the public key genAss.Name.PublicKey = Utils.ExtractPublicKey(eArgs.StrongName.KeyPair.ToArray(), (eArgs.SigningAlgorithm ?? AssemblyHashAlgorithm.SHA1)); genAss.Name.Flags |= AssemblyFlags.PublicKey; } eArgsDic.Add(genAss, eArgs); } var resultDic = new ConcurrentDictionary <System.Reflection.Assembly, String>(); System.Threading.Tasks.Parallel.ForEach(genDic, kvp => { foreach (var mod in kvp.Value.Modules) { var fileName = Path.Combine(path, mod.Name); using (var stream = File.Open(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Read)) { var eArgs = eArgsDic[kvp.Value]; mod.EmitModule(stream, eArgs); stream.Flush(); //refDic.TryAdd( fileName, eArgs.AssemblyRefs.Select( aRef => Tuple.Create( aRef, IsDotNETAssembly( aRef, portabilityHelper, eArgs.PCLProfile ) ) ).ToArray() ); resultDic.TryAdd(kvp.Key, fileName); } } }); return(resultDic); }); return(fileNames); }