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;
 }
Exemple #4
0
        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);
        }
Exemple #5
0
 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);
        }
Exemple #7
0
        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);
            }
        }