private void InnerEmbed(string prefix, string fullPath, bool compress, bool addChecksum, bool disableCleanup, Checksums checksums) { if (!disableCleanup) { // in any case we can remove this from the copy local paths, because either it's already embedded, or it will be embedded. // ReferenceCopyLocalPaths.RemoveAll(item => string.Equals(item, fullPath, StringComparison.OrdinalIgnoreCase)); } var resourceName = $"{prefix}{Path.GetFileName(fullPath).ToLowerInvariant()}"; if (manifest.Resources.Any(x => string.Equals(x.Name, resourceName, StringComparison.OrdinalIgnoreCase))) { // - an assembly that is already embedded uncompressed, using <EmbeddedResource> in the project file // - if compress == false: an assembly that appeared twice in the ReferenceCopyLocalPaths, e.g. the same library from different nuget packages (https://github.com/Fody/Costura/issues/332) if (addChecksum && !checksums.ContainsKey(resourceName)) { checksums.Add(resourceName, Checksums.CalculateChecksum(fullPath)); } return; } if (compress) { resourceName += ".compressed"; if (manifest.Resources.Any(x => string.Equals(x.Name, resourceName, StringComparison.OrdinalIgnoreCase))) { // an assembly that appeared twice in the ReferenceCopyLocalPaths, e.g. the same library from different nuget packages (https://github.com/Fody/Costura/issues/332) return; } } var checksum = Checksums.CalculateChecksum(fullPath); var cacheFile = Path.Combine(cachePath, $"{checksum}.{resourceName}"); var memoryStream = BuildMemoryStream(fullPath, compress, cacheFile); streams.Add(memoryStream); ManifestResourceDeclaration resource = new ManifestResourceDeclaration { Name = resourceName, IsPublic = false }; resource.ContentStreamProvider = () => memoryStream; manifest.Resources.Add(resource); if (addChecksum) { checksums.Add(resourceName, checksum); } }
public override bool Execute() { // Find configuration: var annotations = annotationsService.GetAnnotationsOfType(typeof(PackerAttribute), false, true); PackerAttribute config = new PackerAttribute(); if (annotations.MoveNext()) { config = Configuration.Read(annotations.Current); } // Find gatherable assemblies: string[] paths = Project.Properties["ReferenceCopyLocalPaths"]?.Split('|') ?? new string[0]; AssemblyManifestDeclaration manifest = Project.Module.AssemblyManifest; // I have no idea what this is doing: ResourceCaseFixer.FixResourceCase(manifest); // Embed resources: var checksums = new Checksums(); bool unmanagedFromProcessor = NativeResourcesProcessor.ProcessNativeResources(manifest, !config.DisableCompression, checksums); var resourceEmbedder = new ResourceEmbedder(manifest); resourceEmbedder.EmbedResources(config, paths, checksums); bool unmanagedFromEmbedder = resourceEmbedder.HasUnmanaged; // Load references: var assets = new Assets(Project.Module); AssemblyLoaderInfo info = AssemblyLoaderInfo.LoadAssemblyLoader(config.CreateTemporaryAssemblies, unmanagedFromEmbedder || unmanagedFromProcessor, Project.Module); // Alter code: string resourcesHash = ResourceHash.CalculateHash(manifest); new AttachCallSynthesis().SynthesizeCallToAttach(config, Project, info); new ResourceNameFinder(info, manifest, assets).FillInStaticConstructor( config.CreateTemporaryAssemblies, config.PreloadOrder, resourcesHash, checksums); return(true); }
/// <summary> /// Calculates checksums for costura-processed unmanaged resources of the assembly, and returns true if /// any unprocessed resources exist. /// </summary> public static bool ProcessNativeResources(AssemblyManifestDeclaration manifest, bool compress, Checksums checksums) { var unprocessedNameMatch = new Regex(@"^(.*\.)?costura(32|64)\.", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); var processedNameMatch = new Regex(@"^costura(32|64)\.", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); bool hasUnmanaged = false; foreach (var resource in manifest.Resources) { var match = unprocessedNameMatch.Match(resource.Name); if (match.Success) { resource.Name = resource.Name.Substring(match.Groups[1].Length).ToLowerInvariant(); hasUnmanaged = true; } if (processedNameMatch.IsMatch(resource.Name)) { using (var stream = resource.ContentStreamProvider()) { if (compress && resource.Name.EndsWith(".compressed", StringComparison.OrdinalIgnoreCase)) { using (var compressStream = new DeflateStream(stream, CompressionMode.Decompress)) { checksums.Add(resource.Name, Checksums.CalculateChecksum(compressStream)); } } else { checksums.Add(resource.Name, Checksums.CalculateChecksum(stream)); } } } } return(hasUnmanaged); }
public void FillInStaticConstructor(bool createTemporaryAssemblies, string[] preloadOrder, string resourcesHash, Checksums checksums) { var loaderMethod = info.StaticConstructorMethod; InstructionReader reader = loaderMethod.MethodBody.CreateInstructionReader(); reader.EnterInstructionBlock(loaderMethod.MethodBody.RootInstructionBlock); reader.EnterInstructionSequence(loaderMethod.MethodBody.RootInstructionBlock.LastInstructionSequence); while (reader.ReadInstruction()) { if (reader.CurrentInstruction.OpCodeNumber == OpCodeNumber.Ret) { break; } } Console.WriteLine(reader.CurrentInstruction); reader.CurrentInstructionSequence.SplitAroundReaderPosition(reader, out _, out _); var newSequence = reader.CurrentInstructionBlock.AddInstructionSequence(null, NodePosition.Before, reader.CurrentInstructionSequence); InstructionWriter writer = InstructionWriter.GetInstance(); writer.AttachInstructionSequence(newSequence); var orderedResources = preloadOrder .Join(this.manifest.Resources, p => p.ToLowerInvariant(), r => { var parts = r.Name.Split('.'); GetNameAndExt(parts, out var name, out _); return(name); }, (s, r) => r)
public void EmbedResources(PackerAttribute config, string[] referenceCopyLocalPaths, Checksums checksums) { string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); cachePath = tempDirectory; // Path.Combine(Path.GetDirectoryName(AssemblyFilePath), "Costura"); Directory.CreateDirectory(cachePath); var onlyBinaries = referenceCopyLocalPaths.Where(x => x.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || x.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)).ToArray(); var disableCompression = config.DisableCompression; var createTemporaryAssemblies = config.CreateTemporaryAssemblies; foreach (var dependency in GetFilteredReferences(onlyBinaries, config)) { var fullPath = Path.GetFullPath(dependency); if (!config.IgnoreSatelliteAssemblies) { if (dependency.EndsWith(".resources.dll", StringComparison.OrdinalIgnoreCase)) { Embed($"costura.{Path.GetFileName(Path.GetDirectoryName(fullPath))}.", fullPath, !disableCompression, createTemporaryAssemblies, config.DisableCleanup, checksums); continue; } } Embed("costura.", fullPath, !disableCompression, createTemporaryAssemblies, config.DisableCleanup, checksums); if (!config.IncludeDebugSymbols) { continue; } var pdbFullPath = Path.ChangeExtension(fullPath, "pdb"); if (File.Exists(pdbFullPath)) { Embed("costura.", pdbFullPath, !disableCompression, createTemporaryAssemblies, config.DisableCleanup, checksums); } } foreach (var dependency in onlyBinaries) { var prefix = ""; if (config.Unmanaged32Assemblies.Any(x => string.Equals(x, Path.GetFileNameWithoutExtension(dependency), StringComparison.OrdinalIgnoreCase))) { prefix = "costura32."; this.HasUnmanaged = true; } if (config.Unmanaged64Assemblies.Any(x => string.Equals(x, Path.GetFileNameWithoutExtension(dependency), StringComparison.OrdinalIgnoreCase))) { prefix = "costura64."; this.HasUnmanaged = true; } if (string.IsNullOrEmpty(prefix)) { continue; } var fullPath = Path.GetFullPath(dependency); Embed(prefix, fullPath, !disableCompression, true, config.DisableCleanup, checksums); if (!config.IncludeDebugSymbols) { continue; } var pdbFullPath = Path.ChangeExtension(fullPath, "pdb"); if (File.Exists(pdbFullPath)) { Embed(prefix, pdbFullPath, !disableCompression, true, config.DisableCleanup, checksums); } } }
void Embed(string prefix, string fullPath, bool compress, bool addChecksum, bool disableCleanup, Checksums checksums) { try { InnerEmbed(prefix, fullPath, compress, addChecksum, disableCleanup, checksums); } catch (Exception exception) { throw new Exception( innerException: exception, message: $@"Failed to embed. prefix: {prefix} fullPath: {fullPath} compress: {compress} addChecksum: {addChecksum} disableCleanup: {disableCleanup}"); } }