示例#1
0
    /// <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);
    }
        /// <summary>
        /// This method will generate the Qi4CS assemblies.
        /// </summary>
        /// <param name="projectDir">The project directory.</param>
        /// <param name="targetFWID">The target framework identifier.</param>
        /// <param name="targetFWVersion">The target framework version.</param>
        /// <param name="targetFWProfile">The target framework profile.</param>
        /// <param name="referenceAssembliesDir">The base directory where target framework assemblies reside. Should be the directory where target framework assemblies are found under subdirectory <c>&lt;framework-id&gt;\&lt;framework-version&gt;</c>, e.g. <c>".NETFramework\v4.0"</c>.</param>
        /// <param name="targetPlatform">The textual name of target platform.</param>
        /// <param name="path">The path where to store assemblies.</param>
        /// <param name="assemblySNInfo">The file containing strongname information about the assemblies to be emitted.</param>
        /// <param name="qi4CSDir">The directory where Qi4CS assemblies actually used by the application reside.</param>
        /// <param name="verify">Whether to run PEVerify on generated Qi4CS assemblies.</param>
        /// <param name="winSDKDir">The directory where the Windows SDK resides, needed to detect PEVerify executable.</param>
        public IDictionary <String, String> GenerateAssemblies(String projectDir, String targetFWID, String targetFWVersion, String targetFWProfile, String referenceAssembliesDir, String targetPlatform, String path, String assemblySNInfo, String qi4CSDir, Boolean verify, String winSDKDir)
        {
            qi4CSDir = Path.GetFullPath(qi4CSDir);
            path     = Path.GetFullPath(path);

            XElement assemblyInfo = null;

            if (!String.IsNullOrEmpty(assemblySNInfo))
            {
                try
                {
                    assemblyInfo = XElement.Load(new StringReader(assemblySNInfo));
                }
                catch (Exception exc)
                {
                    throw new Qi4CSBuildException("Invalid assembly info element " + assemblySNInfo + ".\n" + exc);
                }
            }
            Func <String, Stream> streamOpener = str => File.Open(str, FileMode.Open, FileAccess.Read, FileShare.Read);

            referenceAssembliesDir = Path.Combine(referenceAssembliesDir, targetFWID, targetFWVersion);
            if (!String.IsNullOrEmpty(targetFWProfile))
            {
                referenceAssembliesDir = Path.Combine(referenceAssembliesDir, "Profile", targetFWProfile);
            }

            String msCorLibName; String fwDisplayName; String targetFWDir;
            var    thisFWMoniker = new FrameworkMonikerInfo(targetFWID, targetFWVersion, targetFWProfile, DotNETReflectionContext.ReadAssemblyInformationFromRedistXMLFile(Path.Combine(referenceAssembliesDir, "RedistList", "FrameworkList.xml"), out msCorLibName, out fwDisplayName, out targetFWDir), msCorLibName, fwDisplayName);

            if (!String.IsNullOrEmpty(targetFWDir))
            {
                referenceAssembliesDir = targetFWDir;
            }

            if (!Directory.Exists(referenceAssembliesDir))
            {
                throw new Qi4CSBuildException("The reference assemblies directory " + referenceAssembliesDir + " does not exist.");
            }

            referenceAssembliesDir += Path.DirectorySeparatorChar;

            var isX86         = X86.Equals(targetPlatform, StringComparison.InvariantCultureIgnoreCase);
            var targetMachine = String.IsNullOrEmpty(targetPlatform) || ANYCPU.Equals(targetPlatform, StringComparison.InvariantCultureIgnoreCase) || isX86 ?
                                ImageFileMachine.I386 :
                                ImageFileMachine.AMD64; // TODO more machines
            var mFlags = ModuleFlags.ILOnly;

            if (isX86)
            {
                mFlags |= ModuleFlags.Required32Bit;
            }

            var snDic = new ConcurrentDictionary <String, Tuple <StrongNameKeyPair, AssemblyHashAlgorithm> >();

            if (assemblyInfo != null)
            {
                foreach (var elem in assemblyInfo.XPathSelectElements("assembly"))
                {
                    snDic[elem.Attribute("name").Value] = Tuple.Create(
                        SubElementTextOrFallback(elem, "sn", (XElement snElem, out StrongNameKeyPair sn) =>
                    {
                        var type = snElem.Attribute("type");
                        sn       = "container".Equals(type.Value, StringComparison.InvariantCultureIgnoreCase) ?
                                   new StrongNameKeyPair(snElem.Value) :
                                   new StrongNameKeyPair("inline".Equals(type.Value, StringComparison.InvariantCultureIgnoreCase) ? StringConversions.HexStr2ByteArray(snElem.Value) : ReadAllBytes(projectDir, snElem.Value));
                        return(true);
                    }, null),
                        SubElementAttributeOrFallback(elem, "hashAlgorithm", (String algoStr, out AssemblyHashAlgorithm algo) =>
                    {
                        return(Enum.TryParse <AssemblyHashAlgorithm>(algoStr, out algo));
                    }, AssemblyHashAlgorithm.SHA1));
                }
            }

            var runtimeRootDir = Path.GetDirectoryName(new Uri(typeof(Object).Assembly.CodeBase).LocalPath);


            var actualPath = path;
            var needToMove = verify && !String.Equals(actualPath, qi4CSDir);

            if (needToMove)
            {
                // When verifying strong-named assemblies in different location than application's out dir, the .dll.config file
                // should hold recursively all non-.NET references, which is quite complicated and maybe not even enough.
                // Instead, emit Qi4CS assemblies into out dir and move them after verifying
                actualPath = qi4CSDir;
            }

            IDictionary <System.Reflection.Assembly, String> genAssFilenames;

            try
            {
                genAssFilenames = this._modelFactory.Model.GenerateAndSaveAssemblies(
                    actualPath,
                    this.IsSilverlight,
                    (nAss, gAss) =>
                {
                    Tuple <StrongNameKeyPair, AssemblyHashAlgorithm> snTuple;
                    snDic.TryGetValue(nAss.GetName().Name, out snTuple);
                    var sn    = snTuple == null ? null : snTuple.Item1;
                    var eArgs = EmittingArguments.CreateForEmittingWithMoniker(gAss.ReflectionContext, targetMachine, TargetRuntime.Net_4_0, ModuleKind.Dll, null, runtimeRootDir, referenceAssembliesDir, streamOpener, sn, thisFWMoniker, String.Equals(thisFWMoniker.FrameworkName, PCL_FW_NAME), mFlags);

                    gAss.AddTargetFrameworkAttributeWithMonikerInfo(thisFWMoniker, eArgs.AssemblyMapper);
                    if (snTuple != null)
                    {
                        eArgs.SigningAlgorithm = snTuple.Item2;
                    }
                    return(eArgs);
                });
            }
            catch (InvalidApplicationModelException apme)
            {
                throw new Qi4CSBuildException("The Qi4CS model was not valid:\n" + apme.ValidationResult, apme);
            }

            if (verify)
            {
                try
                {
                    winSDKDir = FindWinSDKBinPath(winSDKDir);
                    foreach (var fn in genAssFilenames)
                    {
                        Verify(winSDKDir, fn.Value, snDic != null && snDic.ContainsKey(fn.Key.GetName().Name));
                    }
                }
                finally
                {
                    if (!Directory.Exists(path))
                    {
                        Directory.CreateDirectory(path);
                    }

                    foreach (var kvp in genAssFilenames.ToArray())
                    {
                        var fn = kvp.Value;
                        try
                        {
                            var targetFn = Path.Combine(path, Path.GetFileName(fn));
                            if (!String.Equals(fn, targetFn))
                            {
                                if (File.Exists(targetFn))
                                {
                                    try
                                    {
                                        File.Delete(targetFn);
                                    }
                                    catch
                                    {
                                        // Ignore
                                    }
                                }
                                File.Move(fn, targetFn);
                                genAssFilenames.Remove(kvp.Key);
                                genAssFilenames.Add(kvp.Key, targetFn);
                            }
                        }
                        catch
                        {
                            // Ignore
                        }
                    }
                }
            }

            return(genAssFilenames.ToDictionary(
                       kvp => kvp.Key.CodeBase,
                       kvp => kvp.Value
                       ));
        }