/// <summary> /// This is helper method to provide well-known defaults for some parameters of <see cref="FrameworkMonikerInfo.ReadAssemblyInformationFromRedistXMLFile"/> method. /// </summary> /// <param name="redistXMLFilePath">The location of the <c>FrameworkList.xml</c> file.</param> /// <param name="msCorLibName">This will hold the detected assembly name that acts as <c>mscorlib</c> assembly of the framework-</param> /// <param name="frameworkDisplayName">This will hold the detected display name of the framework.</param> /// <param name="targetFWDir">This will hold the detected full path to the target framework assemblies.</param> /// <param name="defaultTargetFWPath">The default target framework path, if the XML file does not define target framework path. If none provider, a directory up one level from the given XML path will be used.</param> /// <returns>The result of <see cref="FrameworkMonikerInfo.ReadAssemblyInformationFromRedistXMLFile"/> method.</returns> public static IDictionary <String, Tuple <Version, Byte[]> > ReadAssemblyInformationFromRedistXMLFile( String redistXMLFilePath, out String msCorLibName, out String frameworkDisplayName, out String targetFWDir, String defaultTargetFWPath = null ) { var redistListDir = Path.GetDirectoryName(redistXMLFilePath); if (defaultTargetFWPath == null) { defaultTargetFWPath = Directory.GetParent(redistListDir).FullName; } IDictionary <String, Tuple <Version, Byte[]> > retVal; using (var stream = File.Open(redistXMLFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { retVal = FrameworkMonikerInfo.ReadAssemblyInformationFromRedistXMLFile( defaultTargetFWPath, stream, targetFWPathArg => { var targetFWDirInner = ProcessTargetFWDir(redistListDir, targetFWPathArg); return(Directory.EnumerateFiles(targetFWDirInner, "*.dll").Select(curFN => Path.Combine(targetFWDirInner, curFN))); }, () => DotNETReflectionContext.CreateDotNETContext(), assFN => File.Open(assFN, FileMode.Open, FileAccess.Read, FileShare.Read), (targetFWPathArg, simpleAssemblyName) => { return(File.Exists(Path.Combine(targetFWPathArg, simpleAssemblyName + ".dll"))); }, out msCorLibName, out frameworkDisplayName, out targetFWDir ); } targetFWDir = ProcessTargetFWDir(redistListDir, targetFWDir); return(retVal); }
/// <inheritdoc /> public Boolean TryGetFrameworkMoniker(String fwName, String fwVersion, String fwProfile, out FrameworkMonikerInfo moniker) { var retVal = !String.IsNullOrEmpty(fwName) && !String.IsNullOrEmpty(fwVersion); if (retVal) { var key = new FrameworkMonikerID(fwName, fwVersion, fwProfile); retVal = this._monikers.TryGetValue(key, out moniker); if (!retVal) { var dir = this.GetDirectory(fwName, fwVersion, fwProfile); retVal = Directory.Exists(dir); if (retVal) { var redistListDir = Path.Combine(dir, "RedistList"); var fn = Path.Combine(redistListDir, "FrameworkList.xml"); String msCorLibName; String fwDisplayName; String targetFWDir; moniker = new FrameworkMonikerInfo(fwName, fwVersion, fwProfile, DotNETReflectionContext.ReadAssemblyInformationFromRedistXMLFile( fn, out msCorLibName, out fwDisplayName, out targetFWDir ), msCorLibName, fwDisplayName); if (!String.IsNullOrEmpty(targetFWDir)) { this._explicitDirectories.TryAdd(moniker, targetFWDir); } } } } else { moniker = null; } return(retVal); }
/// <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><framework-id>\<framework-version></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 )); }
/// <summary> /// Adds a <see cref="TargetFrameworkAttribute"/> to the <see cref="CILAssembly"/> which represents information for given target framework. /// </summary> /// <param name="assembly">The <see cref="CILAssembly"/>.</param> /// <param name="monikerInfo">The information about target framework, see <see cref="FrameworkMonikerInfo"/>.</param> /// <param name="monikerMapper">The assembly mapper helper, created by <see cref="E_CIL.CreateMapperForFrameworkMoniker"/> method, or by creating <see cref="EmittingArguments"/> by <see cref="EmittingArguments.CreateForEmittingWithMoniker"/> method and accessing its <see cref="EmittingArguments.AssemblyMapper"/> property.</param> /// <exception cref="NullReferenceException">If <paramref name="assembly"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException">If <paramref name="monikerInfo"/> or <paramref name="monikerMapper"/> is <c>null</c>.</exception> public static void AddTargetFrameworkAttributeWithMonikerInfo(this CILAssembly assembly, FrameworkMonikerInfo monikerInfo, EmittingAssemblyMapper monikerMapper) { ArgumentValidator.ValidateNotNull("Moniker info", monikerInfo); ArgumentValidator.ValidateNotNull("Moniker mapper", monikerMapper); var targetFWType = (CILType)monikerMapper.MapTypeBase(ModuleReader.TARGET_FRAMEWORK_ATTRIBUTE_CTOR.DeclaringType.NewWrapper(assembly.ReflectionContext)); var targetFWCtor = targetFWType.Constructors.First(ctor => ctor.Parameters.Count == 1 && ctor.Parameters[0].ParameterType is CILType && ((CILType)ctor.Parameters[0].ParameterType).TypeCode == CILTypeCode.String); var targetFWProp = targetFWType.DeclaredProperties.First(prop => prop.Name == ModuleReader.TARGET_FRAMEWORK_ATTRIBUTE_NAMED_PROPERTY.Name); var fwString = monikerInfo.FrameworkName + ",Version=" + monikerInfo.FrameworkVersion; if (!String.IsNullOrEmpty(monikerInfo.ProfileName)) { fwString += ",Profile=" + monikerInfo.ProfileName; } assembly.AddCustomAttribute( targetFWCtor, new CILCustomAttributeTypedArgument[] { CILCustomAttributeFactory.NewTypedArgument((CILType)targetFWCtor.Parameters[0].ParameterType, fwString) }, new CILCustomAttributeNamedArgument[] { CILCustomAttributeFactory.NewNamedArgument(targetFWProp, CILCustomAttributeFactory.NewTypedArgument((CILType)targetFWProp.GetPropertyType(), monikerInfo.FrameworkDisplayName)) } ); }
/// <summary> /// Creates a new <see cref="EmittingAssemblyMapper"/> to be used to redirect references from currently loaded runtime to other runtime. /// The assemblies of other runtime should be located in given <paramref name="assemblyDirectory"/>. /// </summary> /// <param name="ctx">The <see cref="CILReflectionContext"/>.</param> /// <param name="thisRuntimeRoot">The directory containing assemblies of currently loaded runtime.</param> /// <param name="assemblyDirectory">The directory where moniker assemblies reside. This should have its last character as the directory separator of the current operating system.</param> /// <param name="streamOpener">The callback to open a file given a specific path.</param> /// <param name="monikerInfo">The information about framework moniker.</param> /// <returns>A new <see cref="EmittingAssemblyMapper"/> that will redirect references from currently loaded runtime to other runtime.</returns> /// <exception cref="ArgumentNullException">If <paramref name="thisRuntimeRoot"/>, <paramref name="assemblyDirectory"/>, <paramref name="monikerInfo"/> or <paramref name="streamOpener"/> is <c>null</c>.</exception> /// <exception cref="NullReferenceException">If <paramref name="ctx"/> is <c>null</c>.</exception> public static EmittingAssemblyMapper CreateMapperForFrameworkMoniker(this CILReflectionContext ctx, String thisRuntimeRoot, String assemblyDirectory, Func <String, Stream> streamOpener, FrameworkMonikerInfo monikerInfo) { if (ctx == null) { throw new NullReferenceException(); } ArgumentValidator.ValidateNotNull("This runtime root directory", thisRuntimeRoot); ArgumentValidator.ValidateNotNull("Moniker information.", monikerInfo); var loader = new AssemblyLoaderFromBasePath(ctx, assemblyDirectory, streamOpener, monikerInfo.MsCorLibAssembly); return(new CachingEmittingAssemblyMapper(monikerInfo.Assemblies.ToDictionary( kvp => new CILAssemblyName(kvp.Key, kvp.Value.Item1.Major, kvp.Value.Item1.Minor, kvp.Value.Item1.Build, kvp.Value.Item1.Revision), kvp => new Lazy <CILAssembly>(() => loader.LoadFromStreamFunction(assemblyDirectory + "\\" + kvp.Key + ".dll")), ASSEMBLY_NAME_COMPARER), monikerInfo.Assemblies.ToDictionary( kvp => new CILAssemblyName(kvp.Key, kvp.Value.Item1.Major, kvp.Value.Item1.Minor, kvp.Value.Item1.Build, kvp.Value.Item1.Revision), kvp => new Lazy <CILAssembly>(() => loader.LoadFromStreamFunction(thisRuntimeRoot + "\\" + kvp.Key + ".dll")), ASSEMBLY_NAME_COMPARER) )); }