// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace Microsoft.NET.HostModel.Bundle { /// /// BundleManifest is a description of the contents of a bundle file. /// This class handles creation and consumption of bundle-manifests. /// /// Here is the description of the Bundle Layout: /// _______________________________________________ /// AppHost /// /// /// ------------Embedded Files --------------------- /// The embedded files including the app, its /// configuration files, dependencies, and /// possibly the runtime. /// /// /// /// /// /// /// /// ------------ Bundle Header ------------- /// MajorVersion /// MinorVersion /// NumEmbeddedFiles /// ExtractionID /// DepsJson Location [Version 2+] /// Offset /// Size /// RuntimeConfigJson Location [Version 2+] /// Offset /// Size /// Flags [Version 2+] /// - - - - - - Manifest Entries - - - - - - - - - - - /// Series of FileEntries (for each embedded file) /// [File Type, Name, Offset, Size information] /// /// /// /// _________________________________________________ /// public class Manifest { // NetcoreApp3CompatMode flag is set on a .net5 app, // which chooses to build single-file apps in .netcore3.x compat mode, // by constructing the bundler with BundleAllConent option. // This mode is expected to be deprecated in future versions of .NET. [Flags] enum HeaderFlags : ulong { None = 0, NetcoreApp3CompatMode = 2 } // Bundle ID is a string that is used to uniquely // identify this bundle. It is choosen to be compatible // with path-names so that the AppHost can use it in // extraction path. public readonly string BundleID; public const uint CurrentMajorVersion = 2; public readonly uint DesiredMajorVersion; // The Minor version is currently unused, and is always zero public const uint MinorVersion = 0; public static string CurrentVersion => $"{CurrentMajorVersion}.{MinorVersion}"; public string DesiredVersion => $"{DesiredMajorVersion}.{MinorVersion}"; FileEntry DepsJsonEntry = null; FileEntry RuntimeConfigJsonEntry = null; HeaderFlags Flags; public List Files; public Manifest(uint desiredVersion, bool netcoreapp3CompatMode = false) { DesiredMajorVersion = desiredVersion; Files = new List(); BundleID = Path.GetRandomFileName(); Flags = (netcoreapp3CompatMode) ? HeaderFlags.NetcoreApp3CompatMode: HeaderFlags.None; } public FileEntry AddEntry(FileType type, string relativePath, long offset, long size) { FileEntry entry = new FileEntry(type, relativePath, offset, size); Files.Add(entry); switch(entry.Type) { case FileType.DepsJson: DepsJsonEntry = entry; break; case FileType.RuntimeConfigJson: RuntimeConfigJsonEntry = entry; break; case FileType.Assembly: break; default: break; } return entry; } public long Write(BinaryWriter writer) { long startOffset = writer.BaseStream.Position; // Write the bundle header writer.Write(DesiredMajorVersion); writer.Write(MinorVersion); writer.Write(Files.Count()); writer.Write(BundleID); if (DesiredMajorVersion == 2) { writer.Write((DepsJsonEntry != null) ? DepsJsonEntry.Offset : 0); writer.Write((DepsJsonEntry != null) ? DepsJsonEntry.Size : 0); writer.Write((RuntimeConfigJsonEntry != null) ? RuntimeConfigJsonEntry.Offset : 0); writer.Write((RuntimeConfigJsonEntry != null) ? RuntimeConfigJsonEntry.Size : 0); writer.Write((ulong)Flags); } // Write the manifest entries foreach (FileEntry entry in Files) { entry.Write(writer); } return startOffset; } public bool Contains(string relativePath) { return Files.Any(entry => relativePath.Equals(entry.RelativePath)); } } }