Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
+# Visual Studio Version 15
VisualStudioVersion = 15.0.27527.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5A29E8E3-A0FC-4C57-81DD-297B56D1A119}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtils", "src\test\TestUtils\TestUtils.csproj", "{D6676666-D14D-4DFA-88FB-76E3E823E2E1}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.Build.Bundle", "src\managed\Microsoft.NET.Build.Bundle\Microsoft.NET.Build.Bundle.csproj", "{37B7E731-BEDE-45BF-AEC7-457333F23E53}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.HostModel", "src\managed\Microsoft.NET.HostModel\Microsoft.NET.HostModel.csproj", "{325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.Build.Bundle.Tests", "src\test\Microsoft.NET.Build.Bundle.Tests\Microsoft.NET.Build.Bundle.Tests.csproj", "{8E21F355-54D0-46C3-ACC6-B1BC5D11FDCF}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.HostModel.Tests", "src\test\Microsoft.NET.HostModel.Tests\Microsoft.NET.HostModel.Tests.csproj", "{3D07933E-8A4B-4C9A-92FD-473B5C4F71E2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
{D6676666-D14D-4DFA-88FB-76E3E823E2E1}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{D6676666-D14D-4DFA-88FB-76E3E823E2E1}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{D6676666-D14D-4DFA-88FB-76E3E823E2E1}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
- {37B7E731-BEDE-45BF-AEC7-457333F23E53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {37B7E731-BEDE-45BF-AEC7-457333F23E53}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {37B7E731-BEDE-45BF-AEC7-457333F23E53}.Debug|x64.ActiveCfg = Debug|Any CPU
- {37B7E731-BEDE-45BF-AEC7-457333F23E53}.Debug|x64.Build.0 = Debug|Any CPU
- {37B7E731-BEDE-45BF-AEC7-457333F23E53}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
- {37B7E731-BEDE-45BF-AEC7-457333F23E53}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
- {37B7E731-BEDE-45BF-AEC7-457333F23E53}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
- {37B7E731-BEDE-45BF-AEC7-457333F23E53}.MinSizeRel|x64.Build.0 = Debug|Any CPU
- {37B7E731-BEDE-45BF-AEC7-457333F23E53}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {37B7E731-BEDE-45BF-AEC7-457333F23E53}.Release|Any CPU.Build.0 = Release|Any CPU
- {37B7E731-BEDE-45BF-AEC7-457333F23E53}.Release|x64.ActiveCfg = Release|Any CPU
- {37B7E731-BEDE-45BF-AEC7-457333F23E53}.Release|x64.Build.0 = Release|Any CPU
- {37B7E731-BEDE-45BF-AEC7-457333F23E53}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
- {37B7E731-BEDE-45BF-AEC7-457333F23E53}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
- {37B7E731-BEDE-45BF-AEC7-457333F23E53}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
- {37B7E731-BEDE-45BF-AEC7-457333F23E53}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
- {8E21F355-54D0-46C3-ACC6-B1BC5D11FDCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {8E21F355-54D0-46C3-ACC6-B1BC5D11FDCF}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {8E21F355-54D0-46C3-ACC6-B1BC5D11FDCF}.Debug|x64.ActiveCfg = Debug|Any CPU
- {8E21F355-54D0-46C3-ACC6-B1BC5D11FDCF}.Debug|x64.Build.0 = Debug|Any CPU
- {8E21F355-54D0-46C3-ACC6-B1BC5D11FDCF}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
- {8E21F355-54D0-46C3-ACC6-B1BC5D11FDCF}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
- {8E21F355-54D0-46C3-ACC6-B1BC5D11FDCF}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
- {8E21F355-54D0-46C3-ACC6-B1BC5D11FDCF}.MinSizeRel|x64.Build.0 = Debug|Any CPU
- {8E21F355-54D0-46C3-ACC6-B1BC5D11FDCF}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {8E21F355-54D0-46C3-ACC6-B1BC5D11FDCF}.Release|Any CPU.Build.0 = Release|Any CPU
- {8E21F355-54D0-46C3-ACC6-B1BC5D11FDCF}.Release|x64.ActiveCfg = Release|Any CPU
- {8E21F355-54D0-46C3-ACC6-B1BC5D11FDCF}.Release|x64.Build.0 = Release|Any CPU
- {8E21F355-54D0-46C3-ACC6-B1BC5D11FDCF}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
- {8E21F355-54D0-46C3-ACC6-B1BC5D11FDCF}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
- {8E21F355-54D0-46C3-ACC6-B1BC5D11FDCF}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
- {8E21F355-54D0-46C3-ACC6-B1BC5D11FDCF}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
+ {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}.Debug|x64.Build.0 = Debug|Any CPU
+ {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
+ {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
+ {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
+ {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}.MinSizeRel|x64.Build.0 = Debug|Any CPU
+ {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}.Release|Any CPU.Build.0 = Release|Any CPU
+ {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}.Release|x64.ActiveCfg = Release|Any CPU
+ {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}.Release|x64.Build.0 = Release|Any CPU
+ {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
+ {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
+ {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
+ {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
+ {3D07933E-8A4B-4C9A-92FD-473B5C4F71E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3D07933E-8A4B-4C9A-92FD-473B5C4F71E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3D07933E-8A4B-4C9A-92FD-473B5C4F71E2}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {3D07933E-8A4B-4C9A-92FD-473B5C4F71E2}.Debug|x64.Build.0 = Debug|Any CPU
+ {3D07933E-8A4B-4C9A-92FD-473B5C4F71E2}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
+ {3D07933E-8A4B-4C9A-92FD-473B5C4F71E2}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
+ {3D07933E-8A4B-4C9A-92FD-473B5C4F71E2}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
+ {3D07933E-8A4B-4C9A-92FD-473B5C4F71E2}.MinSizeRel|x64.Build.0 = Debug|Any CPU
+ {3D07933E-8A4B-4C9A-92FD-473B5C4F71E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3D07933E-8A4B-4C9A-92FD-473B5C4F71E2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3D07933E-8A4B-4C9A-92FD-473B5C4F71E2}.Release|x64.ActiveCfg = Release|Any CPU
+ {3D07933E-8A4B-4C9A-92FD-473B5C4F71E2}.Release|x64.Build.0 = Release|Any CPU
+ {3D07933E-8A4B-4C9A-92FD-473B5C4F71E2}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
+ {3D07933E-8A4B-4C9A-92FD-473B5C4F71E2}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
+ {3D07933E-8A4B-4C9A-92FD-473B5C4F71E2}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
+ {3D07933E-8A4B-4C9A-92FD-473B5C4F71E2}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
{23F4AB97-D15C-4C51-A641-DF5C5D5EF70F} = {5CE8410C-3100-4F41-8FA9-E6B4132D9703}
{D86A859D-E6FA-4E73-A255-5776FC473A25} = {5CE8410C-3100-4F41-8FA9-E6B4132D9703}
{D6676666-D14D-4DFA-88FB-76E3E823E2E1} = {5CE8410C-3100-4F41-8FA9-E6B4132D9703}
- {37B7E731-BEDE-45BF-AEC7-457333F23E53} = {FAA448DA-7D1C-4481-915D-5765BF906332}
- {8E21F355-54D0-46C3-ACC6-B1BC5D11FDCF} = {5CE8410C-3100-4F41-8FA9-E6B4132D9703}
+ {325FB7F2-2E2E-422D-ADAA-F0B63E84CF24} = {FAA448DA-7D1C-4481-915D-5765BF906332}
+ {3D07933E-8A4B-4C9A-92FD-473B5C4F71E2} = {5CE8410C-3100-4F41-8FA9-E6B4132D9703}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {28B9726D-802B-478D-AF7A-B9243B9E180B}
+++ /dev/null
-<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <OutputType>Exe</OutputType>
- <TargetFramework>netcoreapp2.0</TargetFramework>
- <Description>.Net Core Single File Bundler</Description>
- <IsTool>true</IsTool>
- </PropertyGroup>
-
- <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), CommonManaged.props))\CommonManaged.props" />
-
- <ItemGroup>
- <Content Include="Sdk.props">
- <Pack>true</Pack>
- <PackagePath>Sdk</PackagePath>
- </Content>
- </ItemGroup>
-
-</Project>
-
-
+++ /dev/null
-// 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;
-
-namespace Microsoft.NET.Build.Bundle
-{
- /// <summary>
- /// The main driver for Bundle and Extract operations.
- /// </summary>
- public static class Program
- {
- enum RunMode
- {
- Help,
- Bundle,
- Extract
- };
-
- static RunMode Mode = RunMode.Bundle;
-
- // Common Options:
- static bool Verbose = false;
- static string OutputDir;
-
- // Bundle options:
- static bool EmbedPDBs = false;
- static string HostName;
- static string SourceDir;
-
- // Extract options:
- static string BundleToExtract;
-
- static void Usage()
- {
- Console.WriteLine($".NET Core Bundler (version {Bundler.Version})");
- Console.WriteLine("Usage: bundle <options>");
- Console.WriteLine("");
- Console.WriteLine("Bundle options:");
- Console.WriteLine(" --source <PATH> Directory containing files to bundle (required).");
- Console.WriteLine(" --apphost <NAME> Application host within source directory (required).");
- Console.WriteLine(" --pdb Embed PDB files.");
- Console.WriteLine("");
- Console.WriteLine("Extract options:");
- Console.WriteLine(" --extract <PATH> Extract files from the specified bundle.");
- Console.WriteLine("");
- Console.WriteLine("Common options:");
- Console.WriteLine(" -o|--output <PATH> Output directory (default: current).");
- Console.WriteLine(" -d|--diagnostics Enable diagnostic output.");
- Console.WriteLine(" -?|-h|--help Display usage information.");
- Console.WriteLine("");
- Console.WriteLine("Examples:");
- Console.WriteLine("Bundle: bundle --source <publish-dir> --apphost <host-exe> -o <output-dir>");
- Console.WriteLine("Extract: bundle --extract <bundle-exe> -o <output-dir>");
- }
-
- public static void Log(string fmt, params object[] args)
- {
- if (Verbose)
- {
- Console.WriteLine("LOG: " + fmt, args);
- }
- }
-
- static void Fail(string type, string message)
- {
- Console.Error.WriteLine($"{type}: {message}");
- }
-
- static void ParseArgs(string[] args)
- {
- int i = 0;
- Func<string, string> NextArg = (string option) =>
- {
- if (++i >= args.Length)
- {
- throw new BundleException("Argument missing for" + option);
- }
- return args[i];
- };
-
- for (; i < args.Length; i++)
- {
- string arg = args[i];
- switch (arg.ToLower())
- {
- case "-?":
- case "-h":
- case "--help":
- Mode = RunMode.Help;
- break;
-
- case "--extract":
- Mode = RunMode.Extract;
- BundleToExtract = NextArg(arg);
- break;
-
- case "-d":
- case "--diagnostics":
- Verbose = true;
- break;
-
- case "--apphost":
- HostName = NextArg(arg);
- break;
-
- case "--source":
- SourceDir = NextArg(arg);
- break;
-
- case "-o":
- case "--output":
- OutputDir = NextArg(arg);
- break;
-
- case "--pdb":
- EmbedPDBs = true;
- break;
-
- default:
- throw new BundleException("Invalid option: " + arg);
- }
- }
-
- if (Mode == RunMode.Bundle)
- {
- if (SourceDir == null)
- {
- throw new BundleException("Missing argument: source directory");
- }
-
- if (HostName == null)
- {
- throw new BundleException("Missing argument: host");
- }
- }
-
- if (OutputDir == null)
- {
- OutputDir = Environment.CurrentDirectory;
- }
- }
-
- static void Run()
- {
- switch (Mode)
- {
- case RunMode.Help:
- Usage();
- break;
-
- case RunMode.Bundle:
- Log($"Bundle from dir: {SourceDir}");
- Log($"Output Directory: {OutputDir}");
- Bundler bundle = new Bundler(HostName, SourceDir, OutputDir, EmbedPDBs);
- bundle.MakeBundle();
- break;
-
- case RunMode.Extract:
- Log($"Extract from file: {BundleToExtract}");
- Log($"Output Directory: {OutputDir}");
- Extractor extract = new Extractor(BundleToExtract, OutputDir);
- extract.Spill();
- break;
- }
- }
-
- public static int Main(string[] args)
- {
- try
- {
- Log($"Bundler version: {Bundler.Version}");
-
- try
- {
- ParseArgs(args);
- }
- catch (BundleException e)
- {
- Fail("ERROR", e.Message);
- Usage();
- return -1;
- }
-
- try
- {
- Run();
- }
- catch (BundleException e)
- {
- Fail("ERROR", e.Message);
- return -2;
- }
- }
- catch (Exception e)
- {
- Fail("INTERNAL ERROR", e.Message);
- return -3;
- }
-
- return 0;
- }
- }
-}
-
+++ /dev/null
-.NET Core Bundler
-===================================
-
-The Bundler is a tool that embeds an application and its dependencies into the AppHost executable. This tool is used to publish apps as a single-file, as described in this [design document](https://github.com/dotnet/designs/blob/master/accepted/single-file/design.md).
-
-### Why is the Bundler in core-setup repo?
-
-The bundler is an independent tool for merging several files into one.
-The bundler code lives in the core-setup repo because:
-* It is closely related to the AppHost code, which facilitates easy development, update, and testing.
-* The `dotnet/cli` and `dotnet/sdk` repos were considered unsuitable because of repo ownership and maintainence concerns.
-* It is not worth creating an managing an independent repo just for the tool.
-
-### Why is the Bundler a tool, not a managed library?
-
-Users typically only interact with the bundler via dotnet CLI (`dotnet publish /p:PublishSingleFile=true`). The connection between the bundler tool and msbuild is facilitated by MsBuild artifacts in the SDK. The Bundler itself is an executable tool with a command-line interface because:
-
-1. A library hosted in-proc by the MSBuild process needs to carefully address concerns such as target framework / multitargeting requirements, dependency collision with other tasks / libraries, etc.
-2. It forces a crisp contract of inputs and outputs of the MSBuild task.
-3. It can be run without spinning up a full build in "ad-hoc" scenarios.
-4. It facilitates easy testing.
-
-The [IL Linker](https://github.com/mono/linker) and [Crossgen compiler](https://github.com/dotnet/coreclr/tree/master/src/tools/crossgen) are similarly implemented as an independent command line tools.
\ No newline at end of file
+++ /dev/null
-<!--
-***********************************************************************************************
-Sdk.props
-
-WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and have
- created a backup copy. Incorrect changes to this file will make it
- impossible to load or build your projects from the command-line or the IDE.
-
-Copyright (c) .NET Foundation. All rights reserved.
-***********************************************************************************************
--->
-<Project ToolsVersion="14.0">
-
- <PropertyGroup>
- <MicrosoftDotnetBundle>$(MSBuildThisFileDirectory)..\tools\Microsoft.NET.Build.Bundle.dll</MicrosoftDotnetBundle>
- </PropertyGroup>
-
-</Project>
using System;
-namespace Microsoft.NET.Build.Bundle
+namespace Microsoft.NET.HostModel.Bundle
{
/// <summary>
/// This exception is thrown when a bundle/extraction
// See the LICENSE file in the project root for more information.
using System;
+using System.Collections.Generic;
+using System.Linq;
using System.IO;
using System.Reflection.PortableExecutable;
-namespace Microsoft.NET.Build.Bundle
+namespace Microsoft.NET.HostModel.Bundle
{
/// <summary>
/// Bundler: Functionality to embed the managed app and its dependencies
/// </summary>
public class Bundler
{
- string HostName;
- string SourceDir;
- string OutputDir;
- bool EmbedPDBs;
+ readonly string HostName;
+ readonly string OutputDir;
+ readonly bool EmbedPDBs;
+ readonly string DepsJson;
+ readonly string RuntimeConfigJson;
+ readonly string RuntimeConfigDevJson;
- string Application;
- string DepsJson;
- string RuntimeConfigJson;
- string RuntimeConfigDevJson;
+ readonly Trace trace;
/// <summary>
/// Align embedded assemblies such that they can be loaded
public static string Version => (Manifest.MajorVersion + "." + Manifest.MinorVersion);
- public Bundler(string hostName, string sourceDir, string outputDir, bool embedPDBs)
+ public Bundler(string hostName, string outputDir, bool embedPDBs = false, bool diagnosticOutput = false)
{
- SourceDir = sourceDir;
- OutputDir = outputDir;
HostName = hostName;
- EmbedPDBs = embedPDBs;
- }
+ OutputDir = Path.GetFullPath(string.IsNullOrEmpty(outputDir) ? Environment.CurrentDirectory : outputDir);
- void ValidateFiles()
- {
- // Check required directories
- if (!Directory.Exists(SourceDir))
- {
- throw new BundleException("Dirctory not found: " + SourceDir);
- }
- if (!Directory.Exists(OutputDir))
- {
- throw new BundleException("Dirctory not found: " + OutputDir);
- }
-
- // Convert relative paths to absolute paths.
- SourceDir = Path.GetFullPath(SourceDir);
- OutputDir = Path.GetFullPath(OutputDir);
-
- // Set default names
string baseName = Path.GetFileNameWithoutExtension(HostName);
- Application = baseName + ".dll";
DepsJson = baseName + ".deps.json";
RuntimeConfigJson = baseName + ".runtimeconfig.json";
RuntimeConfigDevJson = baseName + ".runtimeconfig.dev.json";
- // Check that required files exist on disk.
- Action<string> checkFileExists = (string name) =>
- {
- string path = Path.Combine(SourceDir, name);
- if (!File.Exists(path))
- {
- throw new BundleException("File not found: " + path);
- }
- };
-
- checkFileExists(HostName);
- checkFileExists(Application);
- // The *.json files may or may not exist.
+ EmbedPDBs = embedPDBs;
+ trace = new Trace(diagnosticOutput);
}
/// <summary>
return FileType.RuntimeConfigJson;
}
- if (fileRelativePath.Equals(Application))
- {
- return FileType.Application;
- }
-
try
{
PEReader peReader = new PEReader(file);
return FileType.Extract;
}
- void GenerateBundle()
+ /// <summary>
+ /// Generate a bundle, given the specification of embedded files
+ /// </summary>
+ /// <param name="fileSpecs">
+ /// An enumeration FileSpecs for the files to be embedded.
+ /// </param>
+ /// <returns>
+ /// The full path the the generated bundle file
+ /// </returns>
+ /// <exceptions>
+ /// ArgumentException if input is invalid
+ /// IOExceptions and ArgumentExceptions from callees flow to the caller.
+ /// </exceptions>
+ public string GenerateBundle(IReadOnlyList<FileSpec> fileSpecs)
{
- string bundlePath = Path.Combine(OutputDir, HostName);
+ trace.Log($"Bundler version {Version}");
+ string bundlePath = Path.Combine(OutputDir, HostName);
if (File.Exists(bundlePath))
{
- Program.Log($"Ovewriting existing File {bundlePath}");
+ trace.Log($"Ovewriting existing File {bundlePath}");
+ }
+
+ string hostSource;
+ try
+ {
+ hostSource = fileSpecs.Where(x => x.BundleRelativePath.Equals(HostName)).Single().SourcePath;
+ }
+ catch (InvalidOperationException)
+ {
+ throw new ArgumentException("Input must uniquely specify the host binary");
}
// Start with a copy of the host executable.
// Copy the file to preserve its permissions.
- File.Copy(Path.Combine(SourceDir, HostName), bundlePath, overwrite: true);
+ File.Copy(hostSource, bundlePath, overwrite: true);
using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(bundlePath)))
{
- Stream bundle = writer.BaseStream;
Manifest manifest = new Manifest();
-
+ Stream bundle = writer.BaseStream;
bundle.Position = bundle.Length;
- // Get all files in the source directory and all sub-directories.
- string[] sources = Directory.GetFiles(SourceDir, searchPattern: "*", searchOption: SearchOption.AllDirectories);
-
- // Sort the file names to keep the bundle construction deterministic.
- Array.Sort(sources, StringComparer.Ordinal);
-
- foreach (string filePath in sources)
+ // Write the files from the specification into the bundle
+ foreach (var fileSpec in fileSpecs)
{
- // filePath is the full-path of files within source directory, and any of its sub-directories.
- // We need the relative paths with respect to the source directory.
- string relativePath = Path.GetRelativePath(SourceDir, filePath);
-
- if (!ShouldEmbed(relativePath))
+ if (!ShouldEmbed(fileSpec.BundleRelativePath))
{
- Program.Log($"Skip: {relativePath}");
+ trace.Log($"Skip: {fileSpec.BundleRelativePath}");
continue;
}
- using (FileStream file = File.OpenRead(filePath))
+ using (FileStream file = File.OpenRead(fileSpec.SourcePath))
{
- FileType type = InferType(relativePath, file);
+ FileType type = InferType(fileSpec.BundleRelativePath, file);
long startOffset = AddToBundle(bundle, file, type);
- FileEntry entry = new FileEntry(type, relativePath, startOffset, file.Length);
+ FileEntry entry = new FileEntry(type, fileSpec.BundleRelativePath, startOffset, file.Length);
manifest.Files.Add(entry);
- Program.Log($"Embed: {entry}");
+ trace.Log($"Embed: {entry}");
}
}
- manifest.Write(writer);
- Program.Log($"Bundle: Path={bundlePath} Size={bundle.Length}");
+ // Write the bundle manifest
+ long manifestOffset = manifest.Write(writer);
+ trace.Log($"Manifest: Offset={manifestOffset}, Size={writer.BaseStream.Position - manifestOffset}");
+ trace.Log($"Bundle: Path={bundlePath} Size={bundle.Length}");
}
+
+ return bundlePath;
}
- public void MakeBundle()
+ string RelativePath(string dirFullPath, string fileFullPath)
{
- ValidateFiles();
- GenerateBundle();
+ // This function is used in lieu of Path.GetRelativePath because
+ // * Path.GetRelativePath() doesn't exist in netstandard2.0
+ // * This implementation is pretty much only intended for testing.
+ // SDK integration invokes GenerateBundle(fileSpecs) directly.
+ //
+ // In later revisions, we should target netstandard2.1, and replace
+ // this function with Path.GetRelativePath().
+
+ return fileFullPath.Substring(dirFullPath.TrimEnd(Path.DirectorySeparatorChar).Length).TrimStart(Path.DirectorySeparatorChar);
+ }
+
+ /// <summary>
+ /// Generate a bundle containind the (embeddable) files in sourceDir
+ /// </summary>
+ public string GenerateBundle(string sourceDir)
+ {
+ // Convert sourceDir to absolute path
+ sourceDir = Path.GetFullPath(sourceDir);
+
+ // Get all files in the source directory and all sub-directories.
+ string[] sources = Directory.GetFiles(sourceDir, searchPattern: "*", searchOption: SearchOption.AllDirectories);
+
+ // Sort the file names to keep the bundle construction deterministic.
+ Array.Sort(sources, StringComparer.Ordinal);
+
+ List<FileSpec> fileSpecs = new List<FileSpec>(sources.Length);
+ foreach(var file in sources)
+ {
+ fileSpecs.Add(new FileSpec(file, RelativePath(sourceDir, file)));
+ }
+
+ return GenerateBundle(fileSpecs);
}
}
}
using System;
using System.IO;
-namespace Microsoft.NET.Build.Bundle
+namespace Microsoft.NET.HostModel.Bundle
{
/// <summary>
/// Extractor: The functionality to extract the files embedded
string OutputDir;
string BundlePath;
- public Extractor(string bundlePath, string outputDir)
+ readonly Trace trace;
+
+ public Extractor(string bundlePath, string outputDir,
+ bool diagnosticOutput = false)
{
BundlePath = bundlePath;
- OutputDir = outputDir;
+ OutputDir = Path.GetFullPath(string.IsNullOrEmpty(outputDir) ? Environment.CurrentDirectory : outputDir);
+ trace = new Trace(diagnosticOutput);
}
- public void Spill()
+ /// <summary>
+ /// Extract all files in the bundle to disk
+ /// </summary>
+ /// <exceptions>
+ /// BundleException if the bundle is invalid or malformed.
+ /// IOExceptions and ArgumentExceptions from callees flow to the caller.
+ /// </exceptions>
+ public void ExtractFiles()
{
try
{
- if (!File.Exists(BundlePath))
- {
- throw new BundleException("File not found: " + BundlePath);
- }
+ trace.Log($"Bundler version {Bundler.Version}");
+ trace.Log($"Extract from file: {BundlePath}");
+ trace.Log($"Output Directory: {OutputDir}");
using (BinaryReader reader = new BinaryReader(File.OpenRead(BundlePath)))
{
foreach (FileEntry entry in manifest.Files)
{
- Program.Log($"Spill: {entry}");
+ trace.Log($"Extract: {entry}");
string fileRelativePath = entry.RelativePath.Replace(Manifest.DirectorySeparatorChar, Path.DirectorySeparatorChar);
string filePath = Path.Combine(OutputDir, fileRelativePath);
}
}
}
- catch (IOException)
+ catch (EndOfStreamException)
{
+ // Trying to read non-existant bits in the bundle
throw new BundleException("Malformed Bundle");
}
catch (ArgumentOutOfRangeException)
using System;
using System.IO;
-namespace Microsoft.NET.Build.Bundle
+namespace Microsoft.NET.HostModel.Bundle
{
/// <summary>
/// FileEntry: Records information about embedded files.
public class FileEntry
{
public FileType Type;
- public string RelativePath; // Path of an embedded file, relative to the Bundle source-directory.
+ public string RelativePath; // Path of an embedded file, relative to the <app> dll.
public long Offset;
public long Size;
--- /dev/null
+// 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;
+
+namespace Microsoft.NET.HostModel.Bundle
+{
+ /// <summary>
+ /// Information about files to embed into the Bundle (input to the Bundler).
+ ///
+ /// SourcePath: path to the file to be bundled at compile time
+ /// BundleRelativePath: path where the file is expected at run time,
+ /// relative to the app DLL.
+ /// </summary>
+ public struct FileSpec
+ {
+ public string SourcePath;
+ public string BundleRelativePath;
+
+ public FileSpec(string sourcePath, string bundleRelativePath)
+ {
+ SourcePath = sourcePath;
+ BundleRelativePath = bundleRelativePath;
+ }
+ }
+}
+
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-namespace Microsoft.NET.Build.Bundle
+namespace Microsoft.NET.HostModel.Bundle
{
/// <summary>
/// FileType: Identifies the type of file embedded into the bundle.
/// </summary>
public enum FileType : byte
{
- Application, // Represents the main app, also an assembly
Assembly, // IL Assemblies, which will be processed from bundle
Ready2Run, // R2R assemblies, currently unused, spilled to disk.
DepsJson, // Configuration file, processed from bundle
RuntimeConfigJson, // Configuration file, processed from bundle
- Extract // Files spilled to disk by the host
+ Extract // Files spilled to disk by the host
};
}
// 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.Build.Bundle
+namespace Microsoft.NET.HostModel.Bundle
{
/// <summary>
/// BundleManifest is a description of the contents of a bundle file.
Files = new List<FileEntry>();
}
- public void Write(BinaryWriter writer)
+ public long Write(BinaryWriter writer)
{
long startOffset = writer.BaseStream.Position;
writer.Write(startOffset);
writer.Write(Signature);
- long size = writer.BaseStream.Position - startOffset;
- Program.Log($"Manifest: Offset={startOffset}, Size={size}");
+ return startOffset;
}
public static Manifest Read(BinaryReader reader)
--- /dev/null
+.NET Core Bundler
+===================================
+
+The Bundler is a tool that embeds an application and its dependencies into the AppHost executable. This tool is used to publish apps as a single-file, as described in this [design document](https://github.com/dotnet/designs/blob/master/accepted/single-file/design.md).
+
+### Why is the Bundler in core-setup repo?
+
+The bundler is an independent tool for merging several files into one.
+The bundler code lives in the core-setup repo because:
+* It is closely related to the AppHost code, which facilitates easy development, update, and testing.
+* The `dotnet/cli` and `dotnet/sdk` repos were considered unsuitable because of repo ownership and maintainence concerns.
+* It is not worth creating an managing an independent repo just for the tool.
--- /dev/null
+// 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;
+
+namespace Microsoft.NET.HostModel.Bundle
+{
+ /// <summary>
+ /// Tracing utilities for diagnostic output
+ /// </summary>
+ public class Trace
+ {
+ readonly bool Verbose = false;
+
+ public Trace(bool verbose)
+ {
+ Verbose = verbose;
+ }
+
+ public void Log(string fmt, params object[] args)
+ {
+ if (Verbose)
+ {
+ Console.WriteLine("LOG: " + fmt, args);
+ }
+ }
+
+ public void Error(string type, string message)
+ {
+ Console.Error.WriteLine($"ERROR: {message}");
+ }
+ }
+}
+
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ <Description>Abstractions for modifying .net core host-binaries</Description>
+ </PropertyGroup>
+
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), CommonManaged.props))\CommonManaged.props" />
+
+ <ItemGroup>
+ <PackageReference Include="System.Reflection.Metadata">
+ <Version>1.5.0</Version>
+ </PackageReference>
+ </ItemGroup>
+
+</Project>
+
+
<ItemGroup>
<PackageProjects Include="$(MSBuildThisFileDirectory)Microsoft.DotNet.PlatformAbstractions/Microsoft.DotNet.PlatformAbstractions.csproj" />
<PackageProjects Include="$(MSBuildThisFileDirectory)Microsoft.Extensions.DependencyModel/Microsoft.Extensions.DependencyModel.csproj" />
- <PackageProjects Include="$(MSBuildThisFileDirectory)Microsoft.NET.Build.Bundle/Microsoft.NET.Build.Bundle.csproj" />
+ <PackageProjects Include="$(MSBuildThisFileDirectory)Microsoft.NET.HostModel/Microsoft.NET.HostModel.csproj" />
</ItemGroup>
<PropertyGroup>
<!-- The list of packages we are servicing -->
<PackageProjects Include="$(ProjectDir)src\managed\Microsoft.DotNet.PlatformAbstractions\Microsoft.DotNet.PlatformAbstractions.csproj" />
<PackageProjects Include="$(ProjectDir)src\managed\Microsoft.Extensions.DependencyModel\Microsoft.Extensions.DependencyModel.csproj" />
- <PackageProjects Include="$(ProjectDir)src\managed\Microsoft.NET.Build.Bundle\Microsoft.NET.Build.Bundle.csproj" />
+ <PackageProjects Include="$(ProjectDir)src\managed\Microsoft.NET.HostModel\Microsoft.NET.HostModel.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(BuildAllPackages)' == 'true'">
using System.IO;
using Xunit;
using Microsoft.DotNet.Cli.Build.Framework;
+using Microsoft.NET.HostModel.Bundle;
namespace Microsoft.DotNet.CoreSetup.Test.BundleTests.BundleExtract
{
sharedTestState = fixture;
}
-
private void Run(TestProjectFixture fixture, string publishDir, string singleFileDir)
{
var dotnet = fixture.SdkDotnet;
.HaveStdOutContaining("Wow! We now say hello to the big world and you.");
// Bundle to a single-file
- string bundleDll = Path.Combine(sharedTestState.RepoDirectories.Artifacts,
- "Microsoft.NET.Build.Bundle",
- "netcoreapp2.0",
- "Microsoft.NET.Build.Bundle.dll");
- string[] bundleArgs = { "--source", publishDir,
- "--apphost", hostName,
- "--output", singleFileDir };
-
- dotnet.Exec(bundleDll, bundleArgs)
- .CaptureStdErr()
- .CaptureStdOut()
- .Execute()
- .Should()
- .Pass();
+ Bundler bundler = new Bundler(hostName, singleFileDir);
+ string singleFile = bundler.GenerateBundle(publishDir);
- // Extract the contents
- string singleFile = Path.Combine(singleFileDir, hostName);
- string[] extractArgs = { "--extract", singleFile,
- "--output", singleFileDir };
-
- dotnet.Exec(bundleDll, extractArgs)
- .CaptureStdErr()
- .CaptureStdOut()
- .Execute()
- .Should()
- .Pass();
+ // Extract the file
+ Extractor extractor = new Extractor(singleFile, singleFileDir);
+ extractor.ExtractFiles();
// Run the extracted app
Command.Create(singleFile)
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <Description>Microsoft.NET.Build.Bundle.Tests</Description>
+ <Description>Microsoft.NET.HostModel.Tests</Description>
<TargetFramework>netcoreapp2.0</TargetFramework>
- <AssemblyName>Microsoft.NET.Build.Bundle.Tests</AssemblyName>
- <PackageId>Microsoft.NET.Build.Bundle.Tests</PackageId>
+ <AssemblyName>Microsoft.NET.HostModel.Tests</AssemblyName>
+ <PackageId>Microsoft.NET.HostModel.Tests</PackageId>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
</PropertyGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
- <ProjectReference Include="..\..\managed\Microsoft.NET.Build.Bundle\Microsoft.NET.Build.Bundle.csproj" />
+ <ProjectReference Include="..\..\managed\Microsoft.NET.HostModel\Microsoft.NET.HostModel.csproj" />
</ItemGroup>
</Project>
</PropertyGroup>
<ItemGroup>
- <TestProjects Include="$(TestDir)/Microsoft.NET.Build.Bundle.Tests/Microsoft.NET.Build.Bundle.Tests.csproj" />
<TestProjects Include="$(TestDir)/HostActivationTests/HostActivationTests.csproj" />
<TestProjects Include="$(TestDir)/Microsoft.Extensions.DependencyModel.Tests/Microsoft.Extensions.DependencyModel.Tests.csproj" />
+ <TestProjects Include="$(TestDir)/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.Tests.csproj" />
</ItemGroup>
<ItemGroup>
<RestoreTestFallbackSource Include="$(CoreHostOutputDir.TrimEnd('/').TrimEnd('\'))" />