public AssemblyProcessorContext(CustomAssemblyResolver assemblyResolver, AssemblyDefinition assembly, PlatformType platform, TextWriter log) { AssemblyResolver = assemblyResolver; Assembly = assembly; Platform = platform; Log = log; }
public CustomAssemblyResolver CreateAssemblyResolver() { var assemblyResolver = new CustomAssemblyResolver(); assemblyResolver.RemoveSearchDirectory("."); foreach (string searchDirectory in SearchDirectories) { assemblyResolver.AddSearchDirectory(searchDirectory); } return(assemblyResolver); }
public CustomAssemblyResolver CreateAssemblyResolver() { var assemblyResolver = new CustomAssemblyResolver(); assemblyResolver.RemoveSearchDirectory("."); foreach (string searchDirectory in SearchDirectories) assemblyResolver.AddSearchDirectory(searchDirectory); return assemblyResolver; }
public static AssemblyDefinition GenerateRoslynAssembly(CustomAssemblyResolver assemblyResolver, AssemblyDefinition assembly, string serializationAssemblyLocation, string signKeyFile, List <string> references, List <AssemblyDefinition> memoryReferences, TextWriter log, IEnumerable <SourceCode> sourceCodes) { var sourceFolder = Path.GetDirectoryName(serializationAssemblyLocation); var syntaxTrees = sourceCodes.Select(x => { // It has a name, let's save it as a file string sourcePath = null; if (x.Name != null) { sourcePath = Path.Combine(sourceFolder, $"{x.Name}.cs"); File.WriteAllText(sourcePath, x.Code); } var result = CSharpSyntaxTree.ParseText(x.Code, null, sourcePath, Encoding.UTF8); return(result); }).ToArray(); var compilerOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true, assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default); // Sign the serialization assembly the same way the source was signed // TODO: Transmit over command line if (assembly.Name.HasPublicKey) { // TODO: If delay signed, we could actually extract the public key and apply it ourself maybe? if (signKeyFile == null) { throw new InvalidOperationException("Generating serialization code for signed assembly, but no key was specified."); } compilerOptions = compilerOptions.WithCryptoKeyFile(signKeyFile).WithStrongNameProvider(new DesktopStrongNameProvider()); if ((assembly.MainModule.Attributes & ModuleAttributes.StrongNameSigned) != ModuleAttributes.StrongNameSigned) { // Delay signed compilerOptions = compilerOptions.WithDelaySign(true); } } // Add references (files and in-memory PE data) var metadataReferences = new List <MetadataReference>(); foreach (var reference in assemblyResolver.References) { metadataReferences.Add(MetadataReference.CreateFromFile(reference)); } foreach (var reference in memoryReferences) { metadataReferences.Add(CreateMetadataReference(assemblyResolver, reference)); } // typeof(Dictionary<,>) // Special case for 4.5: Because Dictionary<,> is forwarded, we need to add a reference to the actual assembly var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assembly); metadataReferences.Add(CreateMetadataReference(assemblyResolver, mscorlibAssembly)); var collectionAssembly = CecilExtensions.FindCollectionsAssembly(assembly); metadataReferences.Add(CreateMetadataReference(assemblyResolver, collectionAssembly)); metadataReferences.Add(CreateMetadataReference(assemblyResolver, assembly)); // In case SiliconStudio.Core was not referenced, let's add it. if (assembly.Name.Name != "SiliconStudio.Core" && !references.Any(x => string.Compare(Path.GetFileNameWithoutExtension(x), "SiliconStudio.Core", StringComparison.OrdinalIgnoreCase) == 0)) { metadataReferences.Add(CreateMetadataReference(assemblyResolver, assemblyResolver.Resolve("SiliconStudio.Core"))); } // Create roslyn compilation object var assemblyName = assembly.Name.Name + ".Serializers"; var compilation = CSharpCompilation.Create(assemblyName, syntaxTrees, metadataReferences, compilerOptions); // Do the actual compilation, and check errors using (var peStream = new FileStream(serializationAssemblyLocation, FileMode.Create, FileAccess.Write)) using (var pdbStream = new FileStream(Path.ChangeExtension(serializationAssemblyLocation, ".pdb"), FileMode.Create, FileAccess.Write)) { var compilationResult = compilation.Emit(peStream, pdbStream); if (!compilationResult.Success) { var errors = new StringBuilder(); errors.AppendLine(string.Format("Serialization assembly compilation: {0} error(s)", compilationResult.Diagnostics.Count(x => x.Severity >= DiagnosticSeverity.Error))); foreach (var error in compilationResult.Diagnostics) { if (error.Severity >= DiagnosticSeverity.Warning) { errors.AppendLine(error.ToString()); } } throw new InvalidOperationException(errors.ToString()); } } var repackOptions = new ILRepacking.RepackOptions(new string[0]) { OutputFile = assembly.MainModule.FullyQualifiedName, DebugInfo = true, CopyAttributes = true, AllowMultipleAssemblyLevelAttributes = true, XmlDocumentation = false, NoRepackRes = true, InputAssemblies = new[] { serializationAssemblyLocation }, SearchDirectories = assemblyResolver.GetSearchDirectories(), SearchAssemblies = references, }; // Run ILMerge var merge = new ILRepacking.ILRepack(repackOptions) { PrimaryAssemblyDefinition = assembly, MemoryOnly = true, //KeepFirstOfMultipleAssemblyLevelAttributes = true, //Log = true, //LogFile = "ilmerge.log", }; try { var consoleWriter = Console.Out; Console.SetOut(TextWriter.Null); try { merge.Repack(); } finally { Console.SetOut(consoleWriter); } } catch (Exception) { log.WriteLine($"Error while ILRepacking {assembly.Name.Name}"); throw; } // Copy name merge.TargetAssemblyDefinition.Name.Name = assembly.Name.Name; merge.TargetAssemblyDefinition.Name.Version = assembly.Name.Version; // Copy assembly characterics. This is necessary especially when targeting a windows app merge.TargetAssemblyMainModule.Characteristics = assembly.MainModule.Characteristics; // Add assembly signing info if (assembly.Name.HasPublicKey) { merge.TargetAssemblyDefinition.Name.PublicKey = assembly.Name.PublicKey; merge.TargetAssemblyDefinition.Name.PublicKeyToken = assembly.Name.PublicKeyToken; merge.TargetAssemblyDefinition.Name.Attributes |= AssemblyAttributes.PublicKey; if ((assembly.MainModule.Attributes & ModuleAttributes.StrongNameSigned) == ModuleAttributes.StrongNameSigned) { merge.TargetAssemblyMainModule.Attributes |= ModuleAttributes.StrongNameSigned; } } try { // Delete serializer dll File.Delete(serializationAssemblyLocation); var serializationAssemblyPdbFilePath = Path.ChangeExtension(serializationAssemblyLocation, "pdb"); if (File.Exists(serializationAssemblyPdbFilePath)) { File.Delete(serializationAssemblyPdbFilePath); } } catch (IOException) { // Mute IOException } return(merge.TargetAssemblyDefinition); }
public RepackAssemblyResolverAdapter(CustomAssemblyResolver assemblyResolver) { this.assemblyResolver = assemblyResolver; }
public bool Run(string inputFile, string outputFile = null) { if (inputFile == null) { throw new ArgumentNullException("inputFile"); } if (outputFile == null) { outputFile = inputFile; } try { var processors = new List <IAssemblyDefinitionProcessor>(); // We are no longer using it so we are deactivating it for now to avoid processing //if (AutoNotifyProperty) //{ // processors.Add(new NotifyPropertyProcessor()); //} if (ParameterKey) { processors.Add(new ParameterKeyProcessor()); } if (NewAssemblyName != null) { processors.Add(new RenameAssemblyProcessor(NewAssemblyName)); } //processors.Add(new AsyncBridgeProcessor()); // Always applies the interop processor processors.Add(new InteropProcessor()); processors.Add(new AssemblyVersionProcessor()); if (SerializationAssembly) { processors.Add(new SerializationProcessor(SignKeyFile)); } if (GenerateUserDocumentation) { processors.Add(new GenerateUserDocumentationProcessor(inputFile)); } if (ModuleInitializer) { processors.Add(new ModuleInitializerProcessor()); } processors.Add(new OpenSourceSignProcessor()); var assemblyResolver = new CustomAssemblyResolver(); assemblyResolver.RemoveSearchDirectory("."); foreach (string searchDirectory in SearchDirectories) { assemblyResolver.AddSearchDirectory(searchDirectory); } var readWriteSymbols = UseSymbols; // Double check that var symbolFile = Path.ChangeExtension(inputFile, "pdb"); if (!File.Exists(symbolFile)) { readWriteSymbols = false; } var assemblyDefinition = AssemblyDefinition.ReadAssembly(inputFile, new ReaderParameters { AssemblyResolver = assemblyResolver, ReadSymbols = readWriteSymbols }); // Check if pdb was actually read readWriteSymbols = assemblyDefinition.MainModule.HasDebugHeader; // Check if there is already a AssemblyProcessedAttribute (in which case we can skip processing, it has already been done). // Note that we should probably also match the command line as well so that we throw an error if processing is different (need to rebuild). if (assemblyDefinition.CustomAttributes.Any(x => x.AttributeType.FullName == "SiliconStudio.Core.AssemblyProcessedAttribute")) { OnInfoAction("Assembly has already been processed, skip it."); return(true); } var targetFrameworkAttribute = assemblyDefinition.CustomAttributes .FirstOrDefault(x => x.AttributeType.FullName == typeof(TargetFrameworkAttribute).FullName); var targetFramework = targetFrameworkAttribute != null ? (string)targetFrameworkAttribute.ConstructorArguments[0].Value : null; // Special handling for MonoAndroid // Default frameworkFolder var frameworkFolder = Path.Combine(CecilExtensions.ProgramFilesx86(), @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\"); switch (Platform) { case PlatformType.Android: { if (string.IsNullOrEmpty(TargetFramework)) { throw new InvalidOperationException("Expecting option target framework for Android"); } var monoAndroidPath = Path.Combine(CecilExtensions.ProgramFilesx86(), @"Reference Assemblies\Microsoft\Framework\MonoAndroid"); frameworkFolder = Path.Combine(monoAndroidPath, "v1.0"); var additionalFrameworkFolder = Path.Combine(monoAndroidPath, TargetFramework); assemblyResolver.AddSearchDirectory(additionalFrameworkFolder); assemblyResolver.AddSearchDirectory(frameworkFolder); break; } case PlatformType.iOS: { if (string.IsNullOrEmpty(TargetFramework)) { throw new InvalidOperationException("Expecting option target framework for iOS"); } var monoAndroidPath = Path.Combine(CecilExtensions.ProgramFilesx86(), @"Reference Assemblies\Microsoft\Framework\MonoTouch"); frameworkFolder = Path.Combine(monoAndroidPath, "v1.0"); var additionalFrameworkFolder = Path.Combine(monoAndroidPath, TargetFramework); assemblyResolver.AddSearchDirectory(additionalFrameworkFolder); assemblyResolver.AddSearchDirectory(frameworkFolder); break; } case PlatformType.WindowsStore: { if (string.IsNullOrEmpty(TargetFramework)) { throw new InvalidOperationException("Expecting option target framework for WindowsStore"); } frameworkFolder = Path.Combine(CecilExtensions.ProgramFilesx86(), @"Reference Assemblies\Microsoft\Framework\.NETCore", TargetFramework); assemblyResolver.AddSearchDirectory(frameworkFolder); // Add path to look for WinRT assemblies (Windows.winmd) var windowsAssemblyPath = Path.Combine(CecilExtensions.ProgramFilesx86(), @"Windows Kits\8.1\References\CommonConfiguration\Neutral\", "Windows.winmd"); var windowsAssembly = AssemblyDefinition.ReadAssembly(windowsAssemblyPath, new ReaderParameters { AssemblyResolver = assemblyResolver, ReadSymbols = false }); assemblyResolver.Register(windowsAssembly); break; } case PlatformType.WindowsPhone: { if (string.IsNullOrEmpty(TargetFramework)) { throw new InvalidOperationException("Expecting option target framework for WindowsPhone"); } // Note: v8.1 is hardcoded because we currently receive v4.5.x as TargetFramework (different from TargetPlatformVersion) frameworkFolder = Path.Combine(CecilExtensions.ProgramFilesx86(), @"Reference Assemblies\Microsoft\Framework\WindowsPhoneApp", "v8.1"); assemblyResolver.AddSearchDirectory(frameworkFolder); // Add path to look for WinRT assemblies (Windows.winmd) var windowsAssemblyPath = Path.Combine(CecilExtensions.ProgramFilesx86(), @"Windows Phone Kits\8.1\References\CommonConfiguration\Neutral\", "Windows.winmd"); var windowsAssembly = AssemblyDefinition.ReadAssembly(windowsAssemblyPath, new ReaderParameters { AssemblyResolver = assemblyResolver, ReadSymbols = false }); assemblyResolver.Register(windowsAssembly); break; } } if (SerializationAssembly) { // Resave a first version of assembly with [InteralsVisibleTo] for future serialization assembly. // It will be used by serialization assembly compilation. // It's recommended to do it in the original code to avoid this extra step. var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assemblyDefinition); var stringType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(string).FullName); var internalsVisibleToAttribute = mscorlibAssembly.MainModule.GetTypeResolved(typeof(InternalsVisibleToAttribute).FullName); var serializationAssemblyName = assemblyDefinition.Name.Name + ".Serializers"; bool internalsVisibleAlreadyApplied = false; // Check if already applied foreach (var customAttribute in assemblyDefinition.CustomAttributes.Where(x => x.AttributeType.FullName == internalsVisibleToAttribute.FullName)) { var assemblyName = (string)customAttribute.ConstructorArguments[0].Value; int publicKeyIndex; if ((publicKeyIndex = assemblyName.IndexOf(", PublicKey=", StringComparison.InvariantCulture)) != -1 || (publicKeyIndex = assemblyName.IndexOf(",PublicKey=", StringComparison.InvariantCulture)) != -1) { assemblyName = assemblyName.Substring(0, publicKeyIndex); } if (assemblyName == serializationAssemblyName) { internalsVisibleAlreadyApplied = true; break; } } if (!internalsVisibleAlreadyApplied) { // Apply public key if (assemblyDefinition.Name.HasPublicKey) { serializationAssemblyName += ", PublicKey=" + ByteArrayToString(assemblyDefinition.Name.PublicKey); } // Add [InteralsVisibleTo] attribute var internalsVisibleToAttributeCtor = assemblyDefinition.MainModule.Import(internalsVisibleToAttribute.GetConstructors().Single()); var internalsVisibleAttribute = new CustomAttribute(internalsVisibleToAttributeCtor) { ConstructorArguments = { new CustomAttributeArgument(assemblyDefinition.MainModule.Import(stringType), serializationAssemblyName) } }; assemblyDefinition.CustomAttributes.Add(internalsVisibleAttribute); // Save updated file assemblyDefinition.Write(inputFile, new WriterParameters() { WriteSymbols = readWriteSymbols }); // Reread file (otherwise it seems Mono Cecil is buggy and generate invalid PDB) assemblyDefinition = AssemblyDefinition.ReadAssembly(inputFile, new ReaderParameters { AssemblyResolver = assemblyResolver, ReadSymbols = readWriteSymbols }); // Check if pdb was actually read readWriteSymbols = assemblyDefinition.MainModule.HasDebugHeader; } } bool modified = false; var assemblyProcessorContext = new AssemblyProcessorContext(assemblyResolver, assemblyDefinition, Platform); foreach (var processor in processors) { modified = processor.Process(assemblyProcessorContext) || modified; } // Assembly might have been recreated (i.e. il-repack), so let's use it from now on assemblyDefinition = assemblyProcessorContext.Assembly; if (modified || inputFile != outputFile) { // In case assembly has been modified, // add AssemblyProcessedAttribute to assembly so that it doesn't get processed again var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assemblyDefinition); if (mscorlibAssembly == null) { OnErrorAction("Missing mscorlib.dll from assembly"); return(false); } var attributeType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(Attribute).FullName); var attributeTypeRef = assemblyDefinition.MainModule.Import(attributeType); var attributeCtorRef = assemblyDefinition.MainModule.Import(attributeType.GetConstructors().Single(x => x.Parameters.Count == 0)); var voidType = assemblyDefinition.MainModule.Import(mscorlibAssembly.MainModule.GetTypeResolved("System.Void")); // Create custom attribute var assemblyProcessedAttributeType = new TypeDefinition("SiliconStudio.Core", "AssemblyProcessedAttribute", TypeAttributes.BeforeFieldInit | TypeAttributes.AnsiClass | TypeAttributes.AutoClass | TypeAttributes.Public, attributeTypeRef); // Add constructor (call parent constructor) var assemblyProcessedAttributeConstructor = new MethodDefinition(".ctor", MethodAttributes.RTSpecialName | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Public, voidType); assemblyProcessedAttributeConstructor.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); assemblyProcessedAttributeConstructor.Body.Instructions.Add(Instruction.Create(OpCodes.Call, attributeCtorRef)); assemblyProcessedAttributeConstructor.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); assemblyProcessedAttributeType.Methods.Add(assemblyProcessedAttributeConstructor); // Make sure output directory is created var outputDirectory = Path.GetDirectoryName(outputFile); if (!string.IsNullOrEmpty(outputDirectory) && !Directory.Exists(outputDirectory)) { Directory.CreateDirectory(outputDirectory); } // Add AssemblyProcessedAttribute to assembly assemblyDefinition.MainModule.Types.Add(assemblyProcessedAttributeType); assemblyDefinition.CustomAttributes.Add(new CustomAttribute(assemblyProcessedAttributeConstructor)); assemblyDefinition.Write(outputFile, new WriterParameters() { WriteSymbols = readWriteSymbols }); } } catch (Exception e) { OnErrorAction(e.Message, e); return(false); } return(true); }
public bool Run(string inputFile, string outputFile = null) { if (inputFile == null) { throw new ArgumentNullException("inputFile"); } if (outputFile == null) { outputFile = inputFile; } CustomAssemblyResolver assemblyResolver = null; AssemblyDefinition assemblyDefinition = null; try { try { assemblyResolver = CreateAssemblyResolver(); var readWriteSymbols = UseSymbols; // Double check that var symbolFile = Path.ChangeExtension(inputFile, "pdb"); if (!File.Exists(symbolFile)) { readWriteSymbols = false; } assemblyDefinition = AssemblyDefinition.ReadAssembly(inputFile, new ReaderParameters { AssemblyResolver = assemblyResolver, ReadSymbols = readWriteSymbols, ReadWrite = true }); bool modified; // Check if pdb was actually read readWriteSymbols = assemblyDefinition.MainModule.SymbolReader != null; var symbolWriterProvider = assemblyDefinition.MainModule.SymbolReader?.GetWriterProvider(); var result = Run(ref assemblyDefinition, ref readWriteSymbols, out modified); if (modified || inputFile != outputFile) { // Make sure output directory is created var outputDirectory = Path.GetDirectoryName(outputFile); if (!string.IsNullOrEmpty(outputDirectory) && !Directory.Exists(outputDirectory)) { Directory.CreateDirectory(outputDirectory); } // Keep the original assembly by adding a .old prefix to the current extension if (KeepOriginal) { var copiedFile = Path.ChangeExtension(inputFile, "old" + Path.GetExtension(inputFile)); File.Copy(inputFile, copiedFile, true); } if (assemblyDefinition.MainModule.FileName != outputFile) { // Note: using FileShare.Read otherwise often had access conflict (maybe with antivirus or window ssearch?) assemblyDefinition.MainModule.Write(outputFile, new WriterParameters() { WriteSymbols = readWriteSymbols, SymbolWriterProvider = symbolWriterProvider }); } else { assemblyDefinition.MainModule.Write(new WriterParameters() { WriteSymbols = readWriteSymbols }); } } return(result); } finally { assemblyResolver?.Dispose(); assemblyDefinition?.Dispose(); } } catch (Exception e) { OnErrorAction(null, e); if (DeleteOutputOnError) { File.Delete(outputFile); } return(false); } }