From a2feaec177a0fe3d697d1df43283fb02155b63e6 Mon Sep 17 00:00:00 2001 From: Mateo Torres Ruiz Date: Thu, 17 Sep 2020 22:44:04 -0700 Subject: [PATCH 01/17] Extract bundled files when IncludeAllContentForSelfExtract is set --- .../corehost/cli/bundle/extractor.cpp | 2 +- .../corehost/cli/bundle/manifest.cpp | 8 +- src/installer/corehost/cli/bundle/manifest.h | 21 +++++- src/installer/corehost/cli/bundle/runner.cpp | 2 +- .../NetCoreApp3CompatModeTests.cs | 73 +++++++++++++++++++ 5 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs diff --git a/src/installer/corehost/cli/bundle/extractor.cpp b/src/installer/corehost/cli/bundle/extractor.cpp index 56f36812200a2..8feaf315d264c 100644 --- a/src/installer/corehost/cli/bundle/extractor.cpp +++ b/src/installer/corehost/cli/bundle/extractor.cpp @@ -204,7 +204,7 @@ void extractor_t::extract_new(reader_t& reader) begin(); for (const file_entry_t& entry : m_manifest.files) { - if (entry.needs_extraction()) + if (m_manifest.is_netcoreapp3_compat_mode() || entry.needs_extraction()) { extract(entry, reader); } diff --git a/src/installer/corehost/cli/bundle/manifest.cpp b/src/installer/corehost/cli/bundle/manifest.cpp index 23678a563d59a..436a3bf680515 100644 --- a/src/installer/corehost/cli/bundle/manifest.cpp +++ b/src/installer/corehost/cli/bundle/manifest.cpp @@ -5,16 +5,18 @@ using namespace bundle; -manifest_t manifest_t::read(reader_t& reader, int32_t num_files) +manifest_t manifest_t::read(reader_t& reader, header_t& header) { manifest_t manifest; + manifest.m_netcoreapp3_compat_mode = header.is_netcoreapp3_compat_mode(); - for (int32_t i = 0; i < num_files; i++) + for (int32_t i = 0; i < header.num_embedded_files(); i++) { file_entry_t entry = file_entry_t::read(reader); manifest.files.push_back(std::move(entry)); - manifest.m_need_extraction |= entry.needs_extraction(); + manifest.m_files_need_extraction |= entry.needs_extraction(); } + manifest.m_files_need_extraction |= manifest.is_netcoreapp3_compat_mode(); return manifest; } diff --git a/src/installer/corehost/cli/bundle/manifest.h b/src/installer/corehost/cli/bundle/manifest.h index 159f810189cf2..ce901e57ac21a 100644 --- a/src/installer/corehost/cli/bundle/manifest.h +++ b/src/installer/corehost/cli/bundle/manifest.h @@ -6,6 +6,7 @@ #include #include "file_entry.h" +#include "header.h" namespace bundle { @@ -16,16 +17,28 @@ namespace bundle { public: manifest_t() - : m_need_extraction(false) {} + : m_files_need_extraction(false) + , m_netcoreapp3_compat_mode(false) + { + } std::vector files; - static manifest_t read(reader_t &reader, int32_t num_files); + static manifest_t read(reader_t &reader, header_t &header); - bool files_need_extraction() { return m_need_extraction; } + bool files_need_extraction() const + { + return m_files_need_extraction; + } + + bool is_netcoreapp3_compat_mode() const + { + return m_netcoreapp3_compat_mode; + } private: - bool m_need_extraction; + bool m_files_need_extraction; + bool m_netcoreapp3_compat_mode; }; } #endif // __MANIFEST_H__ diff --git a/src/installer/corehost/cli/bundle/runner.cpp b/src/installer/corehost/cli/bundle/runner.cpp index 012274cc9ed0e..40c76de09db18 100644 --- a/src/installer/corehost/cli/bundle/runner.cpp +++ b/src/installer/corehost/cli/bundle/runner.cpp @@ -28,7 +28,7 @@ StatusCode runner_t::extract() m_runtimeconfig_json.set_location(&m_header.runtimeconfig_json_location()); // Read the bundle manifest - m_manifest = manifest_t::read(reader, m_header.num_embedded_files()); + m_manifest = manifest_t::read(reader, m_header); // Extract the files if necessary if (m_manifest.files_need_extraction()) diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs new file mode 100644 index 0000000000000..952bf0970a4cd --- /dev/null +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs @@ -0,0 +1,73 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +using System; +using System.IO; +using System.Linq; +using BundleTests.Helpers; +using Microsoft.DotNet.Cli.Build.Framework; +using Microsoft.DotNet.CoreSetup.Test; +using Microsoft.NET.HostModel.Bundle; +using Xunit; + +namespace AppHost.Bundle.Tests +{ + public class NetCoreApp3CompatModeTests : IClassFixture + { + private SharedTestState sharedTestState; + + public NetCoreApp3CompatModeTests(SharedTestState fixture) + { + sharedTestState = fixture; + } + + [Fact] + public void Bundle_Is_Extracted() + { + var fixture = sharedTestState.TestFixture.Copy(); + string singleFile; + Bundler bundler = BundleHelper.BundleApp(fixture, out singleFile, BundleOptions.BundleAllContent); + var extractionBaseDir = BundleHelper.GetExtractionRootDir(fixture); + + Command.Create(singleFile) + .CaptureStdErr() + .CaptureStdOut() + .EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractionBaseDir.FullName) + .Execute() + .Should() + .Pass() + .And + .HaveStdOutContaining("Hello World"); + + var extractionDir = BundleHelper.GetExtractionDir(fixture, bundler); + var bundleFiles = BundleHelper.GetBundleDir(fixture).GetFiles().Select(file => file.Name).ToArray(); + var publishedFiles = Directory.GetFiles(BundleHelper.GetPublishPath(fixture), searchPattern: "*", searchOption: SearchOption.AllDirectories) + .Select(file => Path.GetFileName(file)) + .Except(bundleFiles) + .ToArray(); + var bundlerFiles = BundleHelper.GetBundleDir(fixture).GetFiles(); + extractionDir.Should().HaveFiles(publishedFiles); + } + + public class SharedTestState : IDisposable + { + public TestProjectFixture TestFixture { get; set; } + public RepoDirectoriesProvider RepoDirectories { get; set; } + + public SharedTestState() + { + RepoDirectories = new RepoDirectoriesProvider(); + TestFixture = new TestProjectFixture("StandaloneApp", RepoDirectories); + TestFixture + .EnsureRestoredForRid(TestFixture.CurrentRid, RepoDirectories.CorehostPackages) + .PublishProject(runtime: TestFixture.CurrentRid, outputDirectory: BundleHelper.GetPublishPath(TestFixture)); + } + + public void Dispose() + { + TestFixture.Dispose(); + } + } + } +} From 83a11095e4f83da4e44fb4646978de7c92a39c0b Mon Sep 17 00:00:00 2001 From: Mateo Torres Ruiz Date: Mon, 21 Sep 2020 00:22:35 -0700 Subject: [PATCH 02/17] Don't pass bundle probe to the runtime for netcoreapp3 compat mode --- .../corehost/cli/bundle/extractor.cpp | 2 +- .../corehost/cli/bundle/file_entry.cpp | 7 ++++-- .../corehost/cli/bundle/file_entry.h | 11 +++++++--- .../corehost/cli/bundle/manifest.cpp | 4 +--- src/installer/corehost/cli/bundle/manifest.h | 7 ------ .../corehost/cli/hostpolicy/hostpolicy.cpp | 13 ++++++++++- .../cli/hostpolicy/hostpolicy_context.cpp | 2 +- .../SingleFileApiTests/Program.cs | 22 +++++++++++++------ .../NetCoreApp3CompatModeTests.cs | 6 ++--- 9 files changed, 46 insertions(+), 28 deletions(-) diff --git a/src/installer/corehost/cli/bundle/extractor.cpp b/src/installer/corehost/cli/bundle/extractor.cpp index 8feaf315d264c..56f36812200a2 100644 --- a/src/installer/corehost/cli/bundle/extractor.cpp +++ b/src/installer/corehost/cli/bundle/extractor.cpp @@ -204,7 +204,7 @@ void extractor_t::extract_new(reader_t& reader) begin(); for (const file_entry_t& entry : m_manifest.files) { - if (m_manifest.is_netcoreapp3_compat_mode() || entry.needs_extraction()) + if (entry.needs_extraction()) { extract(entry, reader); } diff --git a/src/installer/corehost/cli/bundle/file_entry.cpp b/src/installer/corehost/cli/bundle/file_entry.cpp index 0249b249a0359..e33b2cfa32dcf 100644 --- a/src/installer/corehost/cli/bundle/file_entry.cpp +++ b/src/installer/corehost/cli/bundle/file_entry.cpp @@ -14,11 +14,11 @@ bool file_entry_t::is_valid() const static_cast(m_type) < file_type_t::__last; } -file_entry_t file_entry_t::read(reader_t &reader) +file_entry_t file_entry_t::read(reader_t &reader, bool force_extraction) { // First read the fixed-sized portion of file-entry const file_entry_fixed_t* fixed_data = reinterpret_cast(reader.read_direct(sizeof(file_entry_fixed_t))); - file_entry_t entry(fixed_data); + file_entry_t entry(fixed_data, force_extraction); if (!entry.is_valid()) { @@ -35,6 +35,9 @@ file_entry_t file_entry_t::read(reader_t &reader) bool file_entry_t::needs_extraction() const { + if (m_force_extraction) + return true; + switch (m_type) { case file_type_t::deps_json: diff --git a/src/installer/corehost/cli/bundle/file_entry.h b/src/installer/corehost/cli/bundle/file_entry.h index 9d45eaecb317f..eb122efe86835 100644 --- a/src/installer/corehost/cli/bundle/file_entry.h +++ b/src/installer/corehost/cli/bundle/file_entry.h @@ -38,12 +38,16 @@ namespace bundle , m_type(file_type_t::__last) , m_relative_path() , m_disabled(false) + , m_force_extraction(false) { } - file_entry_t(const file_entry_fixed_t *fixed_data) - :m_relative_path() + file_entry_t( + const file_entry_fixed_t *fixed_data, + const bool force_extraction = false) + : m_relative_path() , m_disabled(false) + , m_force_extraction(force_extraction) { // File_entries in the bundle-manifest are expected to be used // beyond startup (for loading files directly from bundle, lazy extraction, etc.). @@ -64,7 +68,7 @@ namespace bundle bool needs_extraction() const; bool matches(const pal::string_t& path) const { return (pal::pathcmp(relative_path(), path) == 0) && !is_disabled(); } - static file_entry_t read(reader_t &reader); + static file_entry_t read(reader_t &reader, bool force_extraction); private: int64_t m_offset; @@ -76,6 +80,7 @@ namespace bundle // So in order to make sure that the servicing location is used, the file entry in the bundle is marked as "disabled" // in such case, and the lookup logic will behave as if the file is not present in the bundle. bool m_disabled; + bool m_force_extraction; bool is_valid() const; }; } diff --git a/src/installer/corehost/cli/bundle/manifest.cpp b/src/installer/corehost/cli/bundle/manifest.cpp index 436a3bf680515..58b7041519f45 100644 --- a/src/installer/corehost/cli/bundle/manifest.cpp +++ b/src/installer/corehost/cli/bundle/manifest.cpp @@ -8,15 +8,13 @@ using namespace bundle; manifest_t manifest_t::read(reader_t& reader, header_t& header) { manifest_t manifest; - manifest.m_netcoreapp3_compat_mode = header.is_netcoreapp3_compat_mode(); for (int32_t i = 0; i < header.num_embedded_files(); i++) { - file_entry_t entry = file_entry_t::read(reader); + file_entry_t entry = file_entry_t::read(reader, header.is_netcoreapp3_compat_mode()); manifest.files.push_back(std::move(entry)); manifest.m_files_need_extraction |= entry.needs_extraction(); } - manifest.m_files_need_extraction |= manifest.is_netcoreapp3_compat_mode(); return manifest; } diff --git a/src/installer/corehost/cli/bundle/manifest.h b/src/installer/corehost/cli/bundle/manifest.h index ce901e57ac21a..28ed725cfdabb 100644 --- a/src/installer/corehost/cli/bundle/manifest.h +++ b/src/installer/corehost/cli/bundle/manifest.h @@ -18,7 +18,6 @@ namespace bundle public: manifest_t() : m_files_need_extraction(false) - , m_netcoreapp3_compat_mode(false) { } @@ -31,14 +30,8 @@ namespace bundle return m_files_need_extraction; } - bool is_netcoreapp3_compat_mode() const - { - return m_netcoreapp3_compat_mode; - } - private: bool m_files_need_extraction; - bool m_netcoreapp3_compat_mode; }; } #endif // __MANIFEST_H__ diff --git a/src/installer/corehost/cli/hostpolicy/hostpolicy.cpp b/src/installer/corehost/cli/hostpolicy/hostpolicy.cpp index dbe883eb8410f..7b316aac5b710 100644 --- a/src/installer/corehost/cli/hostpolicy/hostpolicy.cpp +++ b/src/installer/corehost/cli/hostpolicy/hostpolicy.cpp @@ -369,11 +369,22 @@ int corehost_main_init( if (bundle::info_t::is_single_file_bundle()) { - StatusCode status = bundle::runner_t::process_manifest_and_extract(); + const bundle::runner_t* bundle = bundle::runner_t::app(); + StatusCode status = bundle->process_manifest_and_extract(); if (status != StatusCode::Success) { return status; } + + if (bundle->is_netcoreapp3_compat_mode()) + { + auto extracted_assembly = bundle->extraction_path(); + auto app_name = hostpolicy_init.host_info.get_app_name() + _X(".dll"); + append_path(&extracted_assembly, app_name.c_str()); + remove_trailing_dir_seperator(&extracted_assembly); + assert(pal::file_exists(extracted_assembly)); + hostpolicy_init.host_info.app_path = extracted_assembly; + } } return corehost_init(hostpolicy_init, argc, argv, location, args); diff --git a/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp b/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp index ee8ad4293abb3..a407f63b5d73a 100644 --- a/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp +++ b/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp @@ -225,7 +225,7 @@ int hostpolicy_context_t::initialize(hostpolicy_init_t &hostpolicy_init, const a } // Single-File Bundle Probe - if (bundle::info_t::is_single_file_bundle()) + if (bundle::info_t::is_single_file_bundle() && !(bundle::runner_t::app()->is_netcoreapp3_compat_mode())) { // Encode the bundle_probe function pointer as a string, and pass it to the runtime. pal::stringstream_t ptr_stream; diff --git a/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs b/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs index e5ed6805d5c12..9f0bc34901415 100644 --- a/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs +++ b/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; namespace SingleFileApiTests { @@ -8,10 +9,13 @@ public static void Main(string[] args) { switch (args[0]) { - case "fullyqualifiedname": - var module = typeof(object).Assembly.GetModules()[0]; - Console.WriteLine("FullyQualifiedName: " + module.FullyQualifiedName); - Console.WriteLine("Name: " + module.Name); + case "appcontext": + var deps_files = AppContext.GetData("APP_CONTEXT_DEPS_FILES"); + Console.WriteLine("APP_CONTEXT_DEPS_FILES: " + deps_files); + return; + + case "assembly_location": + Console.WriteLine(Assembly.GetExecutingAssembly().Location); return; case "cmdlineargs": @@ -32,10 +36,14 @@ public static void Main(string[] args) } break; - case "appcontext": - var deps_files = AppContext.GetData("APP_CONTEXT_DEPS_FILES"); - Console.WriteLine("APP_CONTEXT_DEPS_FILES: " + deps_files); + case "fullyqualifiedname": + var module = typeof(object).Assembly.GetModules()[0]; + Console.WriteLine("FullyQualifiedName: " + module.FullyQualifiedName); + Console.WriteLine("Name: " + module.Name); return; + + default: + break; } Console.WriteLine("test failure"); diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs index 952bf0970a4cd..bc73ffabb366c 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs @@ -30,7 +30,7 @@ public void Bundle_Is_Extracted() Bundler bundler = BundleHelper.BundleApp(fixture, out singleFile, BundleOptions.BundleAllContent); var extractionBaseDir = BundleHelper.GetExtractionRootDir(fixture); - Command.Create(singleFile) + Command.Create(singleFile, "assembly_location") .CaptureStdErr() .CaptureStdOut() .EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractionBaseDir.FullName) @@ -38,7 +38,7 @@ public void Bundle_Is_Extracted() .Should() .Pass() .And - .HaveStdOutContaining("Hello World"); + .HaveStdOutContaining(extractionBaseDir.FullName); var extractionDir = BundleHelper.GetExtractionDir(fixture, bundler); var bundleFiles = BundleHelper.GetBundleDir(fixture).GetFiles().Select(file => file.Name).ToArray(); @@ -58,7 +58,7 @@ public class SharedTestState : IDisposable public SharedTestState() { RepoDirectories = new RepoDirectoriesProvider(); - TestFixture = new TestProjectFixture("StandaloneApp", RepoDirectories); + TestFixture = new TestProjectFixture("SingleFileApiTests", RepoDirectories); TestFixture .EnsureRestoredForRid(TestFixture.CurrentRid, RepoDirectories.CorehostPackages) .PublishProject(runtime: TestFixture.CurrentRid, outputDirectory: BundleHelper.GetPublishPath(TestFixture)); From fab74ef2779263a15d2a472ba0449798e761519f Mon Sep 17 00:00:00 2001 From: vitek-karas Date: Mon, 21 Sep 2020 08:05:08 -0700 Subject: [PATCH 03/17] Bundle probe should not report extracted files This also means we can still pass bundle probe to the runtime even in full-extract case as all files will be marked as extracted in that situation (so the probe should return false all the time). This might be benefitial as the runtime will still "know" it's single-file (we don't really use that knowledge today though). Improve the test to actually validate locally built binaries and add validation of TPA and framework assembly loading. --- src/installer/corehost/cli/bundle/runner.cpp | 3 +- .../corehost/cli/hostpolicy/hostpolicy.cpp | 1 - .../cli/hostpolicy/hostpolicy_context.cpp | 2 +- .../SingleFileApiTests/Program.cs | 95 +++++++++++-------- .../NetCoreApp3CompatModeTests.cs | 26 ++++- 5 files changed, 82 insertions(+), 45 deletions(-) diff --git a/src/installer/corehost/cli/bundle/runner.cpp b/src/installer/corehost/cli/bundle/runner.cpp index 40c76de09db18..36edd791fa44d 100644 --- a/src/installer/corehost/cli/bundle/runner.cpp +++ b/src/installer/corehost/cli/bundle/runner.cpp @@ -65,7 +65,8 @@ bool runner_t::probe(const pal::string_t& relative_path, int64_t* offset, int64_ { const bundle::file_entry_t* entry = probe(relative_path); - if (entry == nullptr) + // Do not report extracted entries - those should be reported through either TPA or resource paths + if (entry == nullptr || entry->needs_extraction()) { return false; } diff --git a/src/installer/corehost/cli/hostpolicy/hostpolicy.cpp b/src/installer/corehost/cli/hostpolicy/hostpolicy.cpp index 7b316aac5b710..9939fe4d71c76 100644 --- a/src/installer/corehost/cli/hostpolicy/hostpolicy.cpp +++ b/src/installer/corehost/cli/hostpolicy/hostpolicy.cpp @@ -381,7 +381,6 @@ int corehost_main_init( auto extracted_assembly = bundle->extraction_path(); auto app_name = hostpolicy_init.host_info.get_app_name() + _X(".dll"); append_path(&extracted_assembly, app_name.c_str()); - remove_trailing_dir_seperator(&extracted_assembly); assert(pal::file_exists(extracted_assembly)); hostpolicy_init.host_info.app_path = extracted_assembly; } diff --git a/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp b/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp index a407f63b5d73a..ee8ad4293abb3 100644 --- a/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp +++ b/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp @@ -225,7 +225,7 @@ int hostpolicy_context_t::initialize(hostpolicy_init_t &hostpolicy_init, const a } // Single-File Bundle Probe - if (bundle::info_t::is_single_file_bundle() && !(bundle::runner_t::app()->is_netcoreapp3_compat_mode())) + if (bundle::info_t::is_single_file_bundle()) { // Encode the bundle_probe function pointer as a string, and pass it to the runtime. pal::stringstream_t ptr_stream; diff --git a/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs b/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs index 9f0bc34901415..983479522cd23 100644 --- a/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs +++ b/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs @@ -1,52 +1,71 @@ using System; +using System.IO; using System.Reflection; namespace SingleFileApiTests { public class Program { - public static void Main(string[] args) + public static int Main(string[] args) { - switch (args[0]) + for (int i = 0; i < args.Length; i++) { - case "appcontext": - var deps_files = AppContext.GetData("APP_CONTEXT_DEPS_FILES"); - Console.WriteLine("APP_CONTEXT_DEPS_FILES: " + deps_files); - return; - - case "assembly_location": - Console.WriteLine(Assembly.GetExecutingAssembly().Location); - return; - - case "cmdlineargs": - Console.WriteLine(Environment.GetCommandLineArgs()[0]); - return; - - case "codebase": - try - { - #pragma warning disable SYSLIB0012 - _ = typeof(Program).Assembly.CodeBase; - #pragma warning restore SYSLIB0012 - } - catch (NotSupportedException) - { - Console.WriteLine("CodeBase NotSupported"); - return; - } - break; - - case "fullyqualifiedname": - var module = typeof(object).Assembly.GetModules()[0]; - Console.WriteLine("FullyQualifiedName: " + module.FullyQualifiedName); - Console.WriteLine("Name: " + module.Name); - return; - - default: - break; + string arg = args[i]; + switch (arg) + { + case "appcontext": + var deps_files = AppContext.GetData("APP_CONTEXT_DEPS_FILES"); + Console.WriteLine("APP_CONTEXT_DEPS_FILES: " + deps_files); + break; + + case "executing_assembly_location": + Console.WriteLine("ExecutingAssembly.Location: " + Assembly.GetExecutingAssembly().Location); + break; + + case "assembly_location": + string assemblyName = args[++i]; + Console.WriteLine(assemblyName + " location: " + Assembly.Load(assemblyName).Location); + break; + + case "cmdlineargs": + Console.WriteLine(Environment.GetCommandLineArgs()[0]); + break; + + case "codebase": + try + { + #pragma warning disable SYSLIB0012 + _ = typeof(Program).Assembly.CodeBase; + #pragma warning restore SYSLIB0012 + } + catch (NotSupportedException) + { + Console.WriteLine("CodeBase NotSupported"); + return -1; + } + break; + + case "fullyqualifiedname": + var module = typeof(object).Assembly.GetModules()[0]; + Console.WriteLine("FullyQualifiedName: " + module.FullyQualifiedName); + Console.WriteLine("Name: " + module.Name); + break; + + case "trusted_platform_assemblies": + Console.WriteLine("TRUSTED_PLATFORM_ASSEMBLIES:"); + foreach (var assemblyPath in ((string)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES")).Split(Path.PathSeparator)) + { + Console.WriteLine(assemblyPath); + } + break; + + default: + Console.WriteLine("test failure"); + return -1; + } } - Console.WriteLine("test failure"); + return 0; } } } diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs index bc73ffabb366c..e0f124be8d608 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs @@ -30,15 +30,19 @@ public void Bundle_Is_Extracted() Bundler bundler = BundleHelper.BundleApp(fixture, out singleFile, BundleOptions.BundleAllContent); var extractionBaseDir = BundleHelper.GetExtractionRootDir(fixture); - Command.Create(singleFile, "assembly_location") - .CaptureStdErr() + Command.Create(singleFile, "executing_assembly_location trusted_platform_assemblies assembly_location System.Console") .CaptureStdOut() + .CaptureStdErr() .EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractionBaseDir.FullName) .Execute() .Should() .Pass() - .And - .HaveStdOutContaining(extractionBaseDir.FullName); + // Validate that the main assembly is running from disk (and not from bundle) + .And.HaveStdOutContaining("ExecutingAssembly.Location: " + extractionBaseDir.FullName) + // Validate that TPA contains at least one framework assembly from the extraction directory + .And.HaveStdOutContaining("System.Runtime.dll") + // Validate that framework assembly is actually loaded from the extraction directory + .And.HaveStdOutContaining("System.Console location: " + extractionBaseDir.FullName); var extractionDir = BundleHelper.GetExtractionDir(fixture, bundler); var bundleFiles = BundleHelper.GetBundleDir(fixture).GetFiles().Select(file => file.Name).ToArray(); @@ -62,6 +66,20 @@ public SharedTestState() TestFixture .EnsureRestoredForRid(TestFixture.CurrentRid, RepoDirectories.CorehostPackages) .PublishProject(runtime: TestFixture.CurrentRid, outputDirectory: BundleHelper.GetPublishPath(TestFixture)); + + // The above will publish the app as self-contained using the stock .NET Core SDK (typically from Program Files) + // In order to test the hosting components we need the hostpolicy and hostfxr built by the repo + string hostFxrFileName = RuntimeInformationExtensions.GetSharedLibraryFileNameForCurrentPlatform("hostfxr"); + File.Copy( + Path.Combine(RepoDirectories.Artifacts, "corehost", hostFxrFileName), + Path.Combine(BundleHelper.GetPublishPath(TestFixture), hostFxrFileName), + overwrite: true); + + string hostPolicyFileName = RuntimeInformationExtensions.GetSharedLibraryFileNameForCurrentPlatform("hostpolicy"); + File.Copy( + Path.Combine(RepoDirectories.Artifacts, "corehost", hostPolicyFileName), + Path.Combine(BundleHelper.GetPublishPath(TestFixture), hostPolicyFileName), + overwrite: true); } public void Dispose() From 788e04642b3f94d968833804aa33c20842909808 Mon Sep 17 00:00:00 2001 From: Mateo Torres Ruiz Date: Mon, 21 Sep 2020 10:28:04 -0700 Subject: [PATCH 04/17] CodeBaseThrows should fail --- .../AppHost.Bundle.Tests/SingleFileApiTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs index 5d5f6b8c786d4..fc88a5897f7ef 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs @@ -44,7 +44,7 @@ public void CodeBaseThrows() .CaptureStdOut() .Execute() .Should() - .Pass() + .Fail() .And .HaveStdOutContaining("CodeBase NotSupported"); } From 459c10ea6f3f4e95cf7c34e1731420c05a7705fb Mon Sep 17 00:00:00 2001 From: vitek-karas Date: Mon, 21 Sep 2020 15:04:31 -0700 Subject: [PATCH 05/17] Normalize app base path --- .../corehost/cli/hostpolicy/deps_resolver.h | 16 ++++++++++++---- .../cli/hostpolicy/hostpolicy_context.cpp | 3 ++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/installer/corehost/cli/hostpolicy/deps_resolver.h b/src/installer/corehost/cli/hostpolicy/deps_resolver.h index 300973ce2a4cc..65e1bbc561b10 100644 --- a/src/installer/corehost/cli/hostpolicy/deps_resolver.h +++ b/src/installer/corehost/cli/hostpolicy/deps_resolver.h @@ -172,13 +172,15 @@ class deps_resolver_t return m_is_framework_dependent; } - const pal::string_t &get_app_dir() const + const void get_app_dir(pal::string_t *app_dir) const { if (m_host_mode == host_mode_t::libhost) { static const pal::string_t s_empty; - return s_empty; + *app_dir = s_empty; + return; } + *app_dir = m_app_dir; if (m_host_mode == host_mode_t::apphost) { if (bundle::info_t::is_single_file_bundle()) @@ -186,12 +188,18 @@ class deps_resolver_t const bundle::runner_t* app = bundle::runner_t::app(); if (app->is_netcoreapp3_compat_mode()) { - return app->extraction_path(); + *app_dir = app->extraction_path(); } } } - return m_app_dir; + // Make sure the path ends with a directory separator + // This has been the behavior for a long time, and we should make it consistent + // for all cases. + if (app_dir->back() != DIR_SEPARATOR) + { + app_dir->append(1, DIR_SEPARATOR); + } } private: diff --git a/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp b/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp index ee8ad4293abb3..05a3b8fb9e2db 100644 --- a/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp +++ b/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp @@ -166,7 +166,8 @@ int hostpolicy_context_t::initialize(hostpolicy_init_t &hostpolicy_init, const a } // Build properties for CoreCLR instantiation - pal::string_t app_base = resolver.get_app_dir(); + pal::string_t app_base; + resolver.get_app_dir(&app_base); coreclr_properties.add(common_property::TrustedPlatformAssemblies, probe_paths.tpa.c_str()); coreclr_properties.add(common_property::NativeDllSearchDirectories, probe_paths.native.c_str()); coreclr_properties.add(common_property::PlatformResourceRoots, probe_paths.resources.c_str()); From 42bc99bf929ad2494b6adbf979d9cb70cd0ee7bd Mon Sep 17 00:00:00 2001 From: vitek-karas Date: Wed, 23 Sep 2020 08:21:00 -0700 Subject: [PATCH 06/17] Search for CoreLib in TPA --- src/coreclr/src/binder/applicationcontext.cpp | 123 +--------------- src/coreclr/src/binder/assemblybinder.cpp | 58 +++++++- src/coreclr/src/binder/inc/utils.hpp | 3 + src/coreclr/src/binder/utils.cpp | 137 ++++++++++++++++++ 4 files changed, 195 insertions(+), 126 deletions(-) diff --git a/src/coreclr/src/binder/applicationcontext.cpp b/src/coreclr/src/binder/applicationcontext.cpp index d190295485410..f865b4240490d 100644 --- a/src/coreclr/src/binder/applicationcontext.cpp +++ b/src/coreclr/src/binder/applicationcontext.cpp @@ -127,74 +127,6 @@ namespace BINDER_SPACE return hr; } - HRESULT GetNextPath(SString& paths, SString::Iterator& startPos, SString& outPath) - { - HRESULT hr = S_OK; - - bool wrappedWithQuotes = false; - - // Skip any leading spaces or path separators - while (paths.Skip(startPos, W(' ')) || paths.Skip(startPos, PATH_SEPARATOR_CHAR_W)) {} - - if (startPos == paths.End()) - { - // No more paths in the string and we just skipped over some white space - outPath.Set(W("")); - return S_FALSE; - } - - // Support paths being wrapped with quotations - if (paths.Skip(startPos, W('\"'))) - { - wrappedWithQuotes = true; - } - - SString::Iterator iEnd = startPos; // Where current path ends - SString::Iterator iNext; // Where next path starts - if (wrappedWithQuotes) - { - if (paths.Find(iEnd, W('\"'))) - { - iNext = iEnd; - // Find where the next path starts - there should be a path separator right after the closing quotation mark - if (paths.Find(iNext, PATH_SEPARATOR_CHAR_W)) - { - iNext++; - } - else - { - iNext = paths.End(); - } - } - else - { - // There was no terminating quotation mark - that's bad - GO_WITH_HRESULT(E_INVALIDARG); - } - } - else if (paths.Find(iEnd, PATH_SEPARATOR_CHAR_W)) - { - iNext = iEnd + 1; - } - else - { - iNext = iEnd = paths.End(); - } - - // Skip any trailing spaces - while (iEnd[-1] == W(' ')) - { - iEnd--; - } - - _ASSERTE(startPos < iEnd); - - outPath.Set(paths, startPos, iEnd); - startPos = iNext; - Exit: - return hr; - } - HRESULT ApplicationContext::SetupBindingPaths(SString &sTrustedPlatformAssemblies, SString &sPlatformResourceRoots, SString &sAppPaths, @@ -221,64 +153,15 @@ namespace BINDER_SPACE for (SString::Iterator i = sTrustedPlatformAssemblies.Begin(); i != sTrustedPlatformAssemblies.End(); ) { SString fileName; + SString simpleName; + bool isNativeImage = false; HRESULT pathResult = S_OK; - IF_FAIL_GO(pathResult = GetNextPath(sTrustedPlatformAssemblies, i, fileName)); + IF_FAIL_GO(pathResult = GetNextTPAPath(sTrustedPlatformAssemblies, i, /*dllOnly*/ false, fileName, simpleName, isNativeImage)); if (pathResult == S_FALSE) { break; } -#ifndef CROSSGEN_COMPILE - if (Path::IsRelative(fileName)) - { - GO_WITH_HRESULT(E_INVALIDARG); - } -#endif - - // Find the beginning of the simple name - SString::Iterator iSimpleNameStart = fileName.End(); - - if (!fileName.FindBack(iSimpleNameStart, DIRECTORY_SEPARATOR_CHAR_W)) - { - iSimpleNameStart = fileName.Begin(); - } - else - { - // Advance past the directory separator to the first character of the file name - iSimpleNameStart++; - } - - if (iSimpleNameStart == fileName.End()) - { - GO_WITH_HRESULT(E_INVALIDARG); - } - - SString simpleName; - bool isNativeImage = false; - - // GCC complains if we create SStrings inline as part of a function call - SString sNiDll(W(".ni.dll")); - SString sNiExe(W(".ni.exe")); - SString sDll(W(".dll")); - SString sExe(W(".exe")); - - if (fileName.EndsWithCaseInsensitive(sNiDll) || - fileName.EndsWithCaseInsensitive(sNiExe)) - { - simpleName.Set(fileName, iSimpleNameStart, fileName.End() - 7); - isNativeImage = true; - } - else if (fileName.EndsWithCaseInsensitive(sDll) || - fileName.EndsWithCaseInsensitive(sExe)) - { - simpleName.Set(fileName, iSimpleNameStart, fileName.End() - 4); - } - else - { - // Invalid filename - GO_WITH_HRESULT(E_INVALIDARG); - } - const SimpleNameToFileNameMapEntry *pExistingEntry = m_pTrustedPlatformAssemblyMap->LookupPtr(simpleName.GetUnicode()); if (pExistingEntry != nullptr) diff --git a/src/coreclr/src/binder/assemblybinder.cpp b/src/coreclr/src/binder/assemblybinder.cpp index 02a3646dab4a1..aa29465af824e 100644 --- a/src/coreclr/src/binder/assemblybinder.cpp +++ b/src/coreclr/src/binder/assemblybinder.cpp @@ -22,6 +22,7 @@ #include "utils.hpp" #include "variables.hpp" #include "stringarraylist.h" +#include "configuration.h" #define APP_DOMAIN_LOCKED_UNLOCKED 0x02 #define APP_DOMAIN_LOCKED_CONTEXT 0x04 @@ -401,14 +402,59 @@ namespace BINDER_SPACE sCoreLib.Set(systemDirectory); CombinePath(sCoreLib, sCoreLibName, sCoreLib); - IF_FAIL_GO(AssemblyBinder::GetAssembly(sCoreLib, - TRUE /* fIsInGAC */, - fBindToNativeImage, - &pSystemAssembly, - NULL /* szMDAssemblyPath */, - bundleFileLocation)); + hr = AssemblyBinder::GetAssembly(sCoreLib, + TRUE /* fIsInGAC */, + fBindToNativeImage, + &pSystemAssembly, + NULL /* szMDAssemblyPath */, + bundleFileLocation); BinderTracing::PathProbed(sCoreLib, pathSource, hr); + if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + // Try to find corelib in the TPA + StackSString sCoreLibSimpleName(CoreLibName_W); + StackSString sTrustedPlatformAssemblies = Configuration::GetKnobStringValue(W("TRUSTED_PLATFORM_ASSEMBLIES")); + sTrustedPlatformAssemblies.Normalize(); + + bool found = false; + for (SString::Iterator i = sTrustedPlatformAssemblies.Begin(); i != sTrustedPlatformAssemblies.End(); ) + { + SString fileName; + SString simpleName; + bool isNativeImage = false; + HRESULT pathResult = S_OK; + IF_FAIL_GO(pathResult = GetNextTPAPath(sTrustedPlatformAssemblies, i, /*dllOnly*/ true, fileName, simpleName, isNativeImage)); + if (pathResult == S_FALSE) + { + break; + } + + if (simpleName.EqualsCaseInsensitive(sCoreLibSimpleName)) + { + sCoreLib = fileName; + found = true; + break; + } + } + + if (!found) + { + GO_WITH_HRESULT(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + } + + hr = AssemblyBinder::GetAssembly(sCoreLib, + TRUE /* fIsInGAC */, + fBindToNativeImage, + &pSystemAssembly, + NULL /* szMDAssemblyPath */, + bundleFileLocation); + + BinderTracing::PathProbed(sCoreLib, BinderTracing::PathSource::ApplicationAssemblies, hr); + } + + IF_FAIL_GO(hr); + *ppSystemAssembly = pSystemAssembly.Extract(); Exit: diff --git a/src/coreclr/src/binder/inc/utils.hpp b/src/coreclr/src/binder/inc/utils.hpp index 6108d3aea22c3..1c8ea114f5e6c 100644 --- a/src/coreclr/src/binder/inc/utils.hpp +++ b/src/coreclr/src/binder/inc/utils.hpp @@ -39,6 +39,9 @@ namespace BINDER_SPACE SBuffer &publicKeyTokenBLOB); BOOL IsFileNotFound(HRESULT hr); + + HRESULT GetNextPath(SString& paths, SString::Iterator& startPos, SString& outPath); + HRESULT GetNextTPAPath(SString& paths, SString::Iterator& startPos, bool dllOnly, SString& outPath, SString& simpleName, bool& isNativeImage); }; #endif diff --git a/src/coreclr/src/binder/utils.cpp b/src/coreclr/src/binder/utils.cpp index c98d7f71bf831..21fc115a77662 100644 --- a/src/coreclr/src/binder/utils.cpp +++ b/src/coreclr/src/binder/utils.cpp @@ -15,6 +15,8 @@ #include "strongnameinternal.h" #include "corpriv.h" +#include "clr/fs/path.h" +using namespace clr::fs; namespace BINDER_SPACE { @@ -115,4 +117,139 @@ namespace BINDER_SPACE { return RuntimeFileNotFound(hr); } + + HRESULT GetNextPath(SString& paths, SString::Iterator& startPos, SString& outPath) + { + HRESULT hr = S_OK; + + bool wrappedWithQuotes = false; + + // Skip any leading spaces or path separators + while (paths.Skip(startPos, W(' ')) || paths.Skip(startPos, PATH_SEPARATOR_CHAR_W)) {} + + if (startPos == paths.End()) + { + // No more paths in the string and we just skipped over some white space + outPath.Set(W("")); + return S_FALSE; + } + + // Support paths being wrapped with quotations + if (paths.Skip(startPos, W('\"'))) + { + wrappedWithQuotes = true; + } + + SString::Iterator iEnd = startPos; // Where current path ends + SString::Iterator iNext; // Where next path starts + if (wrappedWithQuotes) + { + if (paths.Find(iEnd, W('\"'))) + { + iNext = iEnd; + // Find where the next path starts - there should be a path separator right after the closing quotation mark + if (paths.Find(iNext, PATH_SEPARATOR_CHAR_W)) + { + iNext++; + } + else + { + iNext = paths.End(); + } + } + else + { + // There was no terminating quotation mark - that's bad + GO_WITH_HRESULT(E_INVALIDARG); + } + } + else if (paths.Find(iEnd, PATH_SEPARATOR_CHAR_W)) + { + iNext = iEnd + 1; + } + else + { + iNext = iEnd = paths.End(); + } + + // Skip any trailing spaces + while (iEnd[-1] == W(' ')) + { + iEnd--; + } + + _ASSERTE(startPos < iEnd); + + outPath.Set(paths, startPos, iEnd); + startPos = iNext; + Exit: + return hr; + } + + HRESULT GetNextTPAPath(SString& paths, SString::Iterator& startPos, bool dllOnly, SString& outPath, SString& simpleName, bool& isNativeImage) + { + HRESULT hr = S_OK; + isNativeImage = false; + + HRESULT pathResult = S_OK; + IF_FAIL_GO(pathResult = GetNextPath(paths, startPos, outPath)); + if (pathResult == S_FALSE) + { + return S_FALSE; + } + +#ifndef CROSSGEN_COMPILE + if (Path::IsRelative(outPath)) + { + GO_WITH_HRESULT(E_INVALIDARG); + } +#endif + + { + // Find the beginning of the simple name + SString::Iterator iSimpleNameStart = outPath.End(); + + if (!outPath.FindBack(iSimpleNameStart, DIRECTORY_SEPARATOR_CHAR_W)) + { + iSimpleNameStart = outPath.Begin(); + } + else + { + // Advance past the directory separator to the first character of the file name + iSimpleNameStart++; + } + + if (iSimpleNameStart == outPath.End()) + { + GO_WITH_HRESULT(E_INVALIDARG); + } + + // GCC complains if we create SStrings inline as part of a function call + SString sNiDll(W(".ni.dll")); + SString sNiExe(W(".ni.exe")); + SString sDll(W(".dll")); + SString sExe(W(".exe")); + + if (!dllOnly && (outPath.EndsWithCaseInsensitive(sNiDll) || + outPath.EndsWithCaseInsensitive(sNiExe))) + { + simpleName.Set(outPath, iSimpleNameStart, outPath.End() - 7); + isNativeImage = true; + } + else if (outPath.EndsWithCaseInsensitive(sDll) || + (!dllOnly && outPath.EndsWithCaseInsensitive(sExe))) + { + simpleName.Set(outPath, iSimpleNameStart, outPath.End() - 4); + } + else + { + // Invalid filename + GO_WITH_HRESULT(E_INVALIDARG); + } + } + + Exit: + return hr; + } + }; From ac2b539b5cbbb78dc1d12e5406eefbda9f668efc Mon Sep 17 00:00:00 2001 From: vitek-karas Date: Thu, 24 Sep 2020 05:23:50 -0700 Subject: [PATCH 07/17] PR feedback --- src/installer/corehost/cli/bundle/manifest.cpp | 2 +- src/installer/corehost/cli/bundle/manifest.h | 2 +- src/installer/corehost/cli/hostpolicy/deps_resolver.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/installer/corehost/cli/bundle/manifest.cpp b/src/installer/corehost/cli/bundle/manifest.cpp index 58b7041519f45..cf439952871c7 100644 --- a/src/installer/corehost/cli/bundle/manifest.cpp +++ b/src/installer/corehost/cli/bundle/manifest.cpp @@ -5,7 +5,7 @@ using namespace bundle; -manifest_t manifest_t::read(reader_t& reader, header_t& header) +manifest_t manifest_t::read(reader_t& reader, const header_t& header) { manifest_t manifest; diff --git a/src/installer/corehost/cli/bundle/manifest.h b/src/installer/corehost/cli/bundle/manifest.h index 28ed725cfdabb..c744e6290ee30 100644 --- a/src/installer/corehost/cli/bundle/manifest.h +++ b/src/installer/corehost/cli/bundle/manifest.h @@ -23,7 +23,7 @@ namespace bundle std::vector files; - static manifest_t read(reader_t &reader, header_t &header); + static manifest_t read(reader_t &reader, const header_t &header); bool files_need_extraction() const { diff --git a/src/installer/corehost/cli/hostpolicy/deps_resolver.h b/src/installer/corehost/cli/hostpolicy/deps_resolver.h index 65e1bbc561b10..92749558e78ce 100644 --- a/src/installer/corehost/cli/hostpolicy/deps_resolver.h +++ b/src/installer/corehost/cli/hostpolicy/deps_resolver.h @@ -172,7 +172,7 @@ class deps_resolver_t return m_is_framework_dependent; } - const void get_app_dir(pal::string_t *app_dir) const + void get_app_dir(pal::string_t *app_dir) const { if (m_host_mode == host_mode_t::libhost) { From fe7c2cdfcd51885254eff8120def5e850e8180a1 Mon Sep 17 00:00:00 2001 From: vitek-karas Date: Thu, 24 Sep 2020 05:29:44 -0700 Subject: [PATCH 08/17] Fix bundle tests to actually test single-file bundles The tests were basically running everything using apphost.exe - the reason the tests worked is basically a weird coincidence (the bundle was always self-contained so it had everything managed in it and all the native dlls were beside the .exe) - so from host's point of view this looked like a self-contained app - and then hostpolicy/hostfxr noticed it's also a bundle and treated it as such. This change introduces new test baseclass to reduce duplication and standardizes the tests to mostly use self-contained publish then bundled with singlefilehost.exe. Currently they do leave hostpolicy.dll and hostfxr.dll next to the exe (they're written in .deps.json so they must exist on disk) but at runtime they won't be used since the singlefilehost.exe has them bundled in itself. --- .../AppWithSubDirs/AppWithSubDirs.csproj | 1 - .../BundleExtractToSpecificPath.cs | 27 +++---- .../BundleLocalizedApp.cs | 15 +--- .../AppHost.Bundle.Tests/BundleProbe.cs | 20 ++--- .../AppHost.Bundle.Tests/BundleRename.cs | 20 ++--- .../AppHost.Bundle.Tests/BundleTestBase.cs | 81 +++++++++++++++++++ .../BundledAppWithSubDirs.cs | 36 +++++---- .../AppHost.Bundle.Tests/HammerServiceTest.cs | 13 +-- .../NetCoreApp3CompatModeTests.cs | 36 ++------- .../SingleFileApiTests.cs | 19 ++--- .../AppHost.Bundle.Tests/StaticHost.cs | 40 ++------- 11 files changed, 154 insertions(+), 154 deletions(-) create mode 100644 src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs diff --git a/src/installer/tests/Assets/TestProjects/AppWithSubDirs/AppWithSubDirs.csproj b/src/installer/tests/Assets/TestProjects/AppWithSubDirs/AppWithSubDirs.csproj index 92aed3cea064c..95842521601eb 100644 --- a/src/installer/tests/Assets/TestProjects/AppWithSubDirs/AppWithSubDirs.csproj +++ b/src/installer/tests/Assets/TestProjects/AppWithSubDirs/AppWithSubDirs.csproj @@ -3,7 +3,6 @@ $(NetCoreAppCurrent) Exe - $(TestTargetRid) $(MNAVersion) diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs index d6af83a7df819..b932662f8d077 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleExtractToSpecificPath.cs @@ -12,7 +12,7 @@ namespace AppHost.Bundle.Tests { - public class BundleExtractToSpecificPath : IClassFixture + public class BundleExtractToSpecificPath : BundleTestBase, IClassFixture { private SharedTestState sharedTestState; @@ -28,8 +28,8 @@ private void Bundle_Extraction_To_Specific_Path_Succeeds() var hostName = BundleHelper.GetHostName(fixture); // Publish the bundle - string singleFile; - Bundler bundler = BundleHelper.BundleApp(fixture, out singleFile, options: BundleOptions.BundleNativeBinaries); + UseSingleFileSelfContainedHost(fixture); + Bundler bundler = BundleHelper.BundleApp(fixture, out string singleFile, options: BundleOptions.BundleNativeBinaries); // Verify expected files in the bundle directory var bundleDir = BundleHelper.GetBundleDir(fixture); @@ -65,7 +65,7 @@ private void Bundle_Extraction_To_Specific_Path_Succeeds() private void Bundle_Extraction_To_Relative_Path_Succeeds (string relativePath) { var fixture = sharedTestState.TestFixture.Copy(); - var singleFile = BundleHelper.BundleApp(fixture, BundleOptions.None); + var singleFile = BundleSelfContainedApp(fixture, BundleOptions.None); // Run the bundled app (extract files to ) Command.Create(singleFile) @@ -85,8 +85,8 @@ private void Bundle_extraction_is_reused() var fixture = sharedTestState.TestFixture.Copy(); // Publish the bundle - string singleFile; - Bundler bundler = BundleHelper.BundleApp(fixture, out singleFile, BundleOptions.BundleNativeBinaries); + UseSingleFileSelfContainedHost(fixture); + Bundler bundler = BundleHelper.BundleApp(fixture, out string singleFile, BundleOptions.BundleNativeBinaries); // Create a directory for extraction. var extractBaseDir = BundleHelper.GetExtractionRootDir(fixture); @@ -103,7 +103,6 @@ private void Bundle_extraction_is_reused() .And .HaveStdOutContaining("Hello World"); - var appBaseName = BundleHelper.GetAppBaseName(fixture); var extractDir = BundleHelper.GetExtractionDir(fixture, bundler); extractDir.Refresh(); @@ -136,8 +135,8 @@ private void Bundle_extraction_can_recover_missing_files() var appName = Path.GetFileNameWithoutExtension(hostName); // Publish the bundle - string singleFile; - Bundler bundler = BundleHelper.BundleApp(fixture, out singleFile, BundleOptions.BundleNativeBinaries); + UseSingleFileSelfContainedHost(fixture); + Bundler bundler = BundleHelper.BundleApp(fixture, out string singleFile, BundleOptions.BundleNativeBinaries); // Create a directory for extraction. var extractBaseDir = BundleHelper.GetExtractionRootDir(fixture); @@ -178,19 +177,13 @@ private void Bundle_extraction_can_recover_missing_files() extractDir.Should().HaveFiles(extractedFiles); } - public class SharedTestState : IDisposable + public class SharedTestState : SharedTestStateBase, IDisposable { public TestProjectFixture TestFixture { get; set; } - public RepoDirectoriesProvider RepoDirectories { get; set; } public SharedTestState() { - RepoDirectories = new RepoDirectoriesProvider(); - TestFixture = new TestProjectFixture("StandaloneApp", RepoDirectories); - TestFixture - .EnsureRestoredForRid(TestFixture.CurrentRid, RepoDirectories.CorehostPackages) - .PublishProject(runtime: TestFixture.CurrentRid, - outputDirectory: BundleHelper.GetPublishPath(TestFixture)); + TestFixture = PreparePublishedSelfContainedTestProject("StandaloneApp"); } public void Dispose() diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleLocalizedApp.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleLocalizedApp.cs index e713e6f7d8c32..c1c8fbe28d9f8 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleLocalizedApp.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleLocalizedApp.cs @@ -11,7 +11,7 @@ namespace AppHost.Bundle.Tests { - public class BundleLocalizedApp : IClassFixture + public class BundleLocalizedApp : BundleTestBase, IClassFixture { private SharedTestState sharedTestState; @@ -24,7 +24,7 @@ public BundleLocalizedApp(SharedTestState fixture) public void Bundled_Localized_App_Run_Succeeds() { var fixture = sharedTestState.TestFixture.Copy(); - var singleFile = BundleHelper.BundleApp(fixture); + var singleFile = BundleSelfContainedApp(fixture); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { @@ -42,20 +42,13 @@ public void Bundled_Localized_App_Run_Succeeds() .HaveStdOutContaining("ನಮಸ್ಕಾರ! வணக்கம்! Hello!"); } - public class SharedTestState : IDisposable + public class SharedTestState : SharedTestStateBase, IDisposable { public TestProjectFixture TestFixture { get; set; } - public RepoDirectoriesProvider RepoDirectories { get; set; } public SharedTestState() { - RepoDirectories = new RepoDirectoriesProvider(); - - TestFixture = new TestProjectFixture("LocalizedApp", RepoDirectories); - TestFixture - .EnsureRestoredForRid(TestFixture.CurrentRid, RepoDirectories.CorehostPackages) - .PublishProject(runtime: TestFixture.CurrentRid, - outputDirectory: BundleHelper.GetPublishPath(TestFixture)); + TestFixture = PreparePublishedSelfContainedTestProject("LocalizedApp"); } public void Dispose() diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleProbe.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleProbe.cs index f9d0d04e8b8e9..8679f834a638a 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleProbe.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleProbe.cs @@ -2,16 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.IO; -using Xunit; +using BundleTests.Helpers; using Microsoft.DotNet.Cli.Build.Framework; using Microsoft.DotNet.CoreSetup.Test; -using BundleTests.Helpers; -using System.Threading; +using Xunit; namespace AppHost.Bundle.Tests { - public class BundleProbe : IClassFixture + public class BundleProbe : BundleTestBase, IClassFixture { private SharedTestState sharedTestState; @@ -40,7 +38,7 @@ private void Bundle_Probe_Not_Passed_For_Non_Single_File_App() private void Bundle_Probe_Passed_For_Single_File_App() { var fixture = sharedTestState.TestFixture.Copy(); - string singleFile = BundleHelper.BundleApp(fixture); + string singleFile = BundleSelfContainedApp(fixture); Command.Create(singleFile, "SingleFile") .CaptureStdErr() @@ -52,19 +50,13 @@ private void Bundle_Probe_Passed_For_Single_File_App() .HaveStdOutContaining("BUNDLE_PROBE OK"); } - public class SharedTestState : IDisposable + public class SharedTestState : SharedTestStateBase, IDisposable { public TestProjectFixture TestFixture { get; set; } - public RepoDirectoriesProvider RepoDirectories { get; set; } public SharedTestState() { - RepoDirectories = new RepoDirectoriesProvider(); - TestFixture = new TestProjectFixture("BundleProbeTester", RepoDirectories); - TestFixture - .EnsureRestoredForRid(TestFixture.CurrentRid, RepoDirectories.CorehostPackages) - .PublishProject(runtime: TestFixture.CurrentRid, - outputDirectory: BundleHelper.GetPublishPath(TestFixture)); + TestFixture = PreparePublishedSelfContainedTestProject("BundleProbeTester"); } public void Dispose() diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleRename.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleRename.cs index 006df6e3dc518..22e27c578523c 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleRename.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleRename.cs @@ -3,16 +3,16 @@ using System; using System.IO; -using Xunit; +using System.Threading; +using BundleTests.Helpers; using Microsoft.DotNet.Cli.Build.Framework; using Microsoft.DotNet.CoreSetup.Test; using Microsoft.NET.HostModel.Bundle; -using BundleTests.Helpers; -using System.Threading; +using Xunit; namespace AppHost.Bundle.Tests { - public class BundleRename : IClassFixture + public class BundleRename : BundleTestBase, IClassFixture { private SharedTestState sharedTestState; @@ -29,7 +29,7 @@ private void Bundle_can_be_renamed_while_running(bool testExtraction) { var fixture = sharedTestState.TestFixture.Copy(); BundleOptions options = testExtraction ? BundleOptions.BundleAllContent : BundleOptions.None; - string singleFile = BundleHelper.BundleApp(fixture, options); + string singleFile = BundleSelfContainedApp(fixture, options); string outputDir = Path.GetDirectoryName(singleFile); string renameFile = Path.Combine(outputDir, Path.GetRandomFileName()); string waitFile = Path.Combine(outputDir, "wait"); @@ -63,19 +63,13 @@ private void Bundle_can_be_renamed_while_running(bool testExtraction) .HaveStdOutContaining("Hello World!"); } - public class SharedTestState : IDisposable + public class SharedTestState : SharedTestStateBase, IDisposable { public TestProjectFixture TestFixture { get; set; } - public RepoDirectoriesProvider RepoDirectories { get; set; } public SharedTestState() { - RepoDirectories = new RepoDirectoriesProvider(); - TestFixture = new TestProjectFixture("AppWithWait", RepoDirectories); - TestFixture - .EnsureRestoredForRid(TestFixture.CurrentRid, RepoDirectories.CorehostPackages) - .PublishProject(runtime: TestFixture.CurrentRid, - outputDirectory: BundleHelper.GetPublishPath(TestFixture)); + TestFixture = PreparePublishedSelfContainedTestProject("AppWithWait"); } public void Dispose() diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs new file mode 100644 index 0000000000000..410ef683328ff --- /dev/null +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +using System; +using System.IO; +using BundleTests.Helpers; +using Microsoft.DotNet.CoreSetup.Test; +using Microsoft.NET.HostModel.AppHost; +using Microsoft.NET.HostModel.Bundle; + +namespace AppHost.Bundle.Tests +{ + public abstract class BundleTestBase + { + // This helper is used in lieu of SDK support for publishing apps using the singlefilehost. + // It replaces the apphost with singlefilehost, and along with appropriate app.dll updates in the host. + // For now, we leave behind the hostpolicy and hostfxr DLLs in the publish directory, because + // removing them requires deps.json update. + public static string UseSingleFileSelfContainedHost(TestProjectFixture testFixture) + { + var singleFileHost = Path.Combine( + testFixture.RepoDirProvider.HostArtifacts, + RuntimeInformationExtensions.GetExeFileNameForCurrentPlatform("singlefilehost")); + var publishedHostPath = BundleHelper.GetHostPath(testFixture); + HostWriter.CreateAppHost(singleFileHost, + publishedHostPath, + BundleHelper.GetAppPath(testFixture)); + return publishedHostPath; + } + + public static string UseFrameworkDependentHost(TestProjectFixture testFixture) + { + var appHost = Path.Combine( + testFixture.RepoDirProvider.HostArtifacts, + RuntimeInformationExtensions.GetExeFileNameForCurrentPlatform("apphost")); + var publishedHostPath = BundleHelper.GetHostPath(testFixture); + HostWriter.CreateAppHost(appHost, + publishedHostPath, + BundleHelper.GetAppPath(testFixture)); + return publishedHostPath; + } + + public static string BundleSelfContainedApp( + TestProjectFixture testFixture, + BundleOptions options = BundleOptions.None, + Version targetFrameworkVersion = null) + { + UseSingleFileSelfContainedHost(testFixture); + return BundleHelper.BundleApp(testFixture, options, targetFrameworkVersion); + } + + public abstract class SharedTestStateBase + { + public RepoDirectoriesProvider RepoDirectories { get; set; } + + public SharedTestStateBase() + { + RepoDirectories = new RepoDirectoriesProvider(); + } + + public TestProjectFixture PreparePublishedSelfContainedTestProject(string projectName) + { + var testFixture = new TestProjectFixture(projectName, RepoDirectories); + testFixture + .EnsureRestoredForRid(testFixture.CurrentRid, RepoDirectories.CorehostPackages) + .PublishProject(runtime: testFixture.CurrentRid, + outputDirectory: BundleHelper.GetPublishPath(testFixture)); + + return testFixture; + } + + public TestProjectFixture PrepareSingleFileSelfContainedTestProject(string projectName) + { + var testFixture = PreparePublishedSelfContainedTestProject(projectName); + UseSingleFileSelfContainedHost(testFixture); + return testFixture; + } + } + } +} diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs index e336de81dd5fa..4fdfaec59feb6 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs @@ -1,16 +1,16 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using BundleTests.Helpers; using Microsoft.DotNet.Cli.Build.Framework; -using Microsoft.NET.HostModel.Bundle; using Microsoft.DotNet.CoreSetup.Test; -using System; +using Microsoft.NET.HostModel.Bundle; using Xunit; namespace AppHost.Bundle.Tests { - public class BundledAppWithSubDirs : IClassFixture + public class BundledAppWithSubDirs : BundleTestBase, IClassFixture { private SharedTestState sharedTestState; @@ -19,11 +19,15 @@ public BundledAppWithSubDirs(SharedTestState fixture) sharedTestState = fixture; } - private void RunTheApp(string path) + private void RunTheApp(string path, TestProjectFixture fixture) { Command.Create(path) + .EnvironmentVariable("COREHOST_TRACE", "1") .CaptureStdErr() .CaptureStdOut() + .EnvironmentVariable("DOTNET_ROOT", fixture.BuiltDotnet.BinPath) + .EnvironmentVariable("DOTNET_ROOT(x86)", fixture.BuiltDotnet.BinPath) + .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0") .Execute() .Should() .Pass() @@ -38,13 +42,14 @@ private void RunTheApp(string path) public void Bundled_Framework_dependent_App_Run_Succeeds(BundleOptions options) { var fixture = sharedTestState.TestFrameworkDependentFixture.Copy(); + UseFrameworkDependentHost(fixture); var singleFile = BundleHelper.BundleApp(fixture, options); // Run the bundled app (extract files) - RunTheApp(singleFile); + RunTheApp(singleFile, fixture); // Run the bundled app again (reuse extracted files) - RunTheApp(singleFile); + RunTheApp(singleFile, fixture); } [InlineData(BundleOptions.None)] @@ -57,10 +62,10 @@ public void Bundled_Self_Contained_App_Run_Succeeds(BundleOptions options) var singleFile = BundleHelper.BundleApp(fixture, options); // Run the bundled app (extract files) - RunTheApp(singleFile); + RunTheApp(singleFile, fixture); // Run the bundled app again (reuse extracted files) - RunTheApp(singleFile); + RunTheApp(singleFile, fixture); } [InlineData(BundleOptions.None)] @@ -73,25 +78,26 @@ public void Bundled_With_Empty_File_Succeeds(BundleOptions options) var singleFile = BundleHelper.BundleApp(fixture, options); // Run the app - RunTheApp(singleFile); + RunTheApp(singleFile, fixture); } - public class SharedTestState : IDisposable + public class SharedTestState : SharedTestStateBase, IDisposable { public TestProjectFixture TestFrameworkDependentFixture { get; set; } public TestProjectFixture TestSelfContainedFixture { get; set; } public TestProjectFixture TestAppWithEmptyFileFixture { get; set; } - public RepoDirectoriesProvider RepoDirectories { get; set; } public SharedTestState() { - RepoDirectories = new RepoDirectoriesProvider(); - TestFrameworkDependentFixture = new TestProjectFixture("AppWithSubDirs", RepoDirectories); BundleHelper.AddLongNameContentToAppWithSubDirs(TestFrameworkDependentFixture); + // This is a bit of a cheating - we know that the AppWithSubDirs doesn't have any RID specific assets + // so we can build it as a portable app and bundle it (since the built output will look the same) + // This is to workaround a problem where publishing RID specific FDD apps for some reason brings in + // hostfxr.dll to the built output - which breaks everything. TestFrameworkDependentFixture - .EnsureRestoredForRid(TestFrameworkDependentFixture.CurrentRid, RepoDirectories.CorehostPackages) - .PublishProject(runtime: TestFrameworkDependentFixture.CurrentRid, + .EnsureRestored(RepoDirectories.CorehostPackages) + .PublishProject(selfContained: false, outputDirectory: BundleHelper.GetPublishPath(TestFrameworkDependentFixture)); TestSelfContainedFixture = new TestProjectFixture("AppWithSubDirs", RepoDirectories); diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/HammerServiceTest.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/HammerServiceTest.cs index 5c0206573c47d..77c657433809b 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/HammerServiceTest.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/HammerServiceTest.cs @@ -11,7 +11,7 @@ namespace AppHost.Bundle.Tests { - public class HammerServiceTest : IClassFixture + public class HammerServiceTest : BundleTestBase, IClassFixture { private SharedTestState sharedTestState; @@ -38,7 +38,7 @@ private void SingleFile_Apps_Are_Serviced() // Annotate the app as servicible, and then publish to a single-file. string depsjson = BundleHelper.GetDepsJsonPath(fixture); File.WriteAllText(depsjson, File.ReadAllText(depsjson).Replace("\"serviceable\": false", "\"serviceable\": true")); - var singleFile = BundleHelper.BundleApp(fixture); + var singleFile = BundleSelfContainedApp(fixture); // Create the servicing directory, and copy the servived DLL from service fixture to the servicing directory. var serviceBasePath = Path.Combine(fixture.TestProject.ProjectDirectory, "coreservicing"); @@ -68,20 +68,15 @@ private void SingleFile_Apps_Are_Serviced() .HaveStdOutContaining("Hi Bengaluru!"); } - public class SharedTestState : IDisposable + public class SharedTestState : SharedTestStateBase, IDisposable { public TestProjectFixture TestFixture { get; set; } public TestProjectFixture ServiceFixture { get; set; } - public RepoDirectoriesProvider RepoDirectories { get; set; } public SharedTestState() { RepoDirectories = new RepoDirectoriesProvider(); - TestFixture = new TestProjectFixture("HammerServiceApp", RepoDirectories); - TestFixture - .EnsureRestoredForRid(TestFixture.CurrentRid, RepoDirectories.CorehostPackages) - .PublishProject(runtime: TestFixture.CurrentRid, - outputDirectory: BundleHelper.GetPublishPath(TestFixture)); + TestFixture = PreparePublishedSelfContainedTestProject("HammerServiceApp"); ServiceFixture = new TestProjectFixture("ServicedLocation", RepoDirectories, assemblyName: "Location"); ServiceFixture diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs index e0f124be8d608..dc64be2b47dbc 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/NetCoreApp3CompatModeTests.cs @@ -13,7 +13,7 @@ namespace AppHost.Bundle.Tests { - public class NetCoreApp3CompatModeTests : IClassFixture + public class NetCoreApp3CompatModeTests : BundleTestBase, IClassFixture { private SharedTestState sharedTestState; @@ -25,9 +25,9 @@ public NetCoreApp3CompatModeTests(SharedTestState fixture) [Fact] public void Bundle_Is_Extracted() { - var fixture = sharedTestState.TestFixture.Copy(); - string singleFile; - Bundler bundler = BundleHelper.BundleApp(fixture, out singleFile, BundleOptions.BundleAllContent); + var fixture = sharedTestState.SingleFileTestFixture.Copy(); + UseSingleFileSelfContainedHost(fixture); + Bundler bundler = BundleHelper.BundleApp(fixture, out string singleFile, BundleOptions.BundleAllContent); var extractionBaseDir = BundleHelper.GetExtractionRootDir(fixture); Command.Create(singleFile, "executing_assembly_location trusted_platform_assemblies assembly_location System.Console") @@ -50,41 +50,21 @@ public void Bundle_Is_Extracted() .Select(file => Path.GetFileName(file)) .Except(bundleFiles) .ToArray(); - var bundlerFiles = BundleHelper.GetBundleDir(fixture).GetFiles(); extractionDir.Should().HaveFiles(publishedFiles); } - public class SharedTestState : IDisposable + public class SharedTestState : SharedTestStateBase, IDisposable { - public TestProjectFixture TestFixture { get; set; } - public RepoDirectoriesProvider RepoDirectories { get; set; } + public TestProjectFixture SingleFileTestFixture { get; set; } public SharedTestState() { - RepoDirectories = new RepoDirectoriesProvider(); - TestFixture = new TestProjectFixture("SingleFileApiTests", RepoDirectories); - TestFixture - .EnsureRestoredForRid(TestFixture.CurrentRid, RepoDirectories.CorehostPackages) - .PublishProject(runtime: TestFixture.CurrentRid, outputDirectory: BundleHelper.GetPublishPath(TestFixture)); - - // The above will publish the app as self-contained using the stock .NET Core SDK (typically from Program Files) - // In order to test the hosting components we need the hostpolicy and hostfxr built by the repo - string hostFxrFileName = RuntimeInformationExtensions.GetSharedLibraryFileNameForCurrentPlatform("hostfxr"); - File.Copy( - Path.Combine(RepoDirectories.Artifacts, "corehost", hostFxrFileName), - Path.Combine(BundleHelper.GetPublishPath(TestFixture), hostFxrFileName), - overwrite: true); - - string hostPolicyFileName = RuntimeInformationExtensions.GetSharedLibraryFileNameForCurrentPlatform("hostpolicy"); - File.Copy( - Path.Combine(RepoDirectories.Artifacts, "corehost", hostPolicyFileName), - Path.Combine(BundleHelper.GetPublishPath(TestFixture), hostPolicyFileName), - overwrite: true); + SingleFileTestFixture = PreparePublishedSelfContainedTestProject("SingleFileApiTests"); } public void Dispose() { - TestFixture.Dispose(); + SingleFileTestFixture.Dispose(); } } } diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs index fc88a5897f7ef..ade9edaf0a5a6 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs @@ -6,7 +6,7 @@ namespace AppHost.Bundle.Tests { - public class SingleFileApiTests : IClassFixture + public class SingleFileApiTests : BundleTestBase, IClassFixture { private SharedTestState sharedTestState; @@ -19,7 +19,7 @@ public SingleFileApiTests(SharedTestState fixture) public void FullyQualifiedName() { var fixture = sharedTestState.TestFixture.Copy(); - var singleFile = BundleHelper.BundleApp(fixture); + var singleFile = BundleSelfContainedApp(fixture); Command.Create(singleFile, "fullyqualifiedname") .CaptureStdErr() @@ -37,7 +37,7 @@ public void FullyQualifiedName() public void CodeBaseThrows() { var fixture = sharedTestState.TestFixture.Copy(); - var singleFile = BundleHelper.BundleApp(fixture); + var singleFile = BundleSelfContainedApp(fixture); Command.Create(singleFile, "codebase") .CaptureStdErr() @@ -53,7 +53,7 @@ public void CodeBaseThrows() public void AppContext_Deps_Files_Bundled_Self_Contained() { var fixture = sharedTestState.TestFixture.Copy(); - var singleFile = BundleHelper.BundleApp(fixture); + var singleFile = BundleSelfContainedApp(fixture); Command.Create(singleFile, "appcontext") .CaptureStdErr() @@ -71,7 +71,7 @@ public void AppContext_Deps_Files_Bundled_Self_Contained() public void GetEnvironmentArgs_0_Returns_Bundled_Executable_Path() { var fixture = sharedTestState.TestFixture.Copy(); - var singleFile = BundleHelper.BundleApp(fixture); + var singleFile = BundleSelfContainedApp(fixture); // For single-file, Environment.GetCommandLineArgs[0] // should return the file path of the host. @@ -104,18 +104,13 @@ public void GetEnvironmentArgs_0_Non_Bundled_App() .HaveStdOutContaining(appPath); } - public class SharedTestState : IDisposable + public class SharedTestState : SharedTestStateBase, IDisposable { public TestProjectFixture TestFixture { get; set; } - public RepoDirectoriesProvider RepoDirectories { get; set; } public SharedTestState() { - RepoDirectories = new RepoDirectoriesProvider(); - TestFixture = new TestProjectFixture("SingleFileApiTests", RepoDirectories); - TestFixture - .EnsureRestoredForRid(TestFixture.CurrentRid, RepoDirectories.CorehostPackages) - .PublishProject(runtime: TestFixture.CurrentRid, outputDirectory: BundleHelper.GetPublishPath(TestFixture)); + TestFixture = PreparePublishedSelfContainedTestProject("SingleFileApiTests"); } public void Dispose() diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/StaticHost.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/StaticHost.cs index b1111e9c9b027..71d365e93bb65 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/StaticHost.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/StaticHost.cs @@ -1,19 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using BundleTests.Helpers; +using System; using Microsoft.DotNet.Cli.Build.Framework; using Microsoft.DotNet.CoreSetup.Test; -using Microsoft.NET.HostModel.AppHost; -using Microsoft.NET.HostModel.Bundle; -using System; -using System.IO; -using System.Threading; using Xunit; namespace AppHost.Bundle.Tests { - public class StaticHost : IClassFixture + public class StaticHost : BundleTestBase, IClassFixture { private SharedTestState sharedTestState; @@ -22,27 +17,12 @@ public StaticHost(SharedTestState fixture) sharedTestState = fixture; } - // This helper is used in lieu of SDK support for publishing apps using the singlefilehost. - // It replaces the apphost with singlefilehost, and along with appropriate app.dll updates in the host. - // For now, we leave behind the hostpolicy and hostfxr DLLs in the publish directory, because - // removing them requires deps.json update. - void ReplaceApphostWithStaticHost(TestProjectFixture fixture) - { - var staticHost = Path.Combine(fixture.RepoDirProvider.HostArtifacts, - RuntimeInformationExtensions.GetExeFileNameForCurrentPlatform("singlefilehost")); - HostWriter.CreateAppHost(staticHost, - BundleHelper.GetHostPath(fixture), - BundleHelper.GetAppPath(fixture)); - - } - [Fact] private void Can_Run_App_With_StatiHost() { var fixture = sharedTestState.TestFixture.Copy(); - var appExe = BundleHelper.GetHostPath(fixture); - ReplaceApphostWithStaticHost(fixture); + var appExe = UseSingleFileSelfContainedHost(fixture); Command.Create(appExe) .CaptureStdErr() @@ -59,9 +39,7 @@ private void Can_Run_SingleFile_App_With_StatiHost() { var fixture = sharedTestState.TestFixture.Copy(); - ReplaceApphostWithStaticHost(fixture); - - string singleFile = BundleHelper.BundleApp(fixture); + string singleFile = BundleSelfContainedApp(fixture); Command.Create(singleFile) .CaptureStdErr() @@ -73,19 +51,13 @@ private void Can_Run_SingleFile_App_With_StatiHost() .HaveStdOutContaining("Hello World"); } - public class SharedTestState : IDisposable + public class SharedTestState : SharedTestStateBase, IDisposable { public TestProjectFixture TestFixture { get; set; } - public RepoDirectoriesProvider RepoDirectories { get; set; } public SharedTestState() { - RepoDirectories = new RepoDirectoriesProvider(); - TestFixture = new TestProjectFixture("StandaloneApp", RepoDirectories); - TestFixture - .EnsureRestoredForRid(TestFixture.CurrentRid, RepoDirectories.CorehostPackages) - .PublishProject(runtime: TestFixture.CurrentRid, - outputDirectory: BundleHelper.GetPublishPath(TestFixture)); + TestFixture = PreparePublishedSelfContainedTestProject("StandaloneApp"); } public void Dispose() From 9334a7df953c39425798308db6a6ad4e9a5f7dfa Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 24 Sep 2020 15:14:35 -0700 Subject: [PATCH 09/17] Don't use Microsoft.NETCore.App.Internal package From 3.0+, Microsoft.NETCore.App just chains in the appropriate runtime packs. The .Internal package was necessary for tests to use when we were building with a older SDK, but targeting 3.0+. We should be able to get rid of the workaround now. This actually causes problems with the full extraction single-file mode because pre-5.0 and in the .Internal package, System.Private.CoreLib.dll was not listed as a runtime assembly (only under native). As a result, it would not end up in the TPA for a single-file bundle. --- .../TestProjects/AppWithWait/Directory.Build.props | 9 +++++++++ .../tests/Assets/TestProjects/Directory.Build.targets | 5 ++++- .../SingleFileApiTests/Directory.Build.props | 9 +++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/installer/tests/Assets/TestProjects/AppWithWait/Directory.Build.props create mode 100644 src/installer/tests/Assets/TestProjects/SingleFileApiTests/Directory.Build.props diff --git a/src/installer/tests/Assets/TestProjects/AppWithWait/Directory.Build.props b/src/installer/tests/Assets/TestProjects/AppWithWait/Directory.Build.props new file mode 100644 index 0000000000000..de2e4580b9bea --- /dev/null +++ b/src/installer/tests/Assets/TestProjects/AppWithWait/Directory.Build.props @@ -0,0 +1,9 @@ + + + + false + + + + + diff --git a/src/installer/tests/Assets/TestProjects/Directory.Build.targets b/src/installer/tests/Assets/TestProjects/Directory.Build.targets index 099990097e245..71982e083164d 100644 --- a/src/installer/tests/Assets/TestProjects/Directory.Build.targets +++ b/src/installer/tests/Assets/TestProjects/Directory.Build.targets @@ -8,7 +8,10 @@ - + + + + diff --git a/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Directory.Build.props b/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Directory.Build.props new file mode 100644 index 0000000000000..de2e4580b9bea --- /dev/null +++ b/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Directory.Build.props @@ -0,0 +1,9 @@ + + + + false + + + + + From 928968731924e7d109aeafcdb28526ae40b8d93d Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 24 Sep 2020 15:34:46 -0700 Subject: [PATCH 10/17] AppWithSubDirs: add back RuntimeIdentifier, don't use .Internal package The test assets are all restored - passing in TestTargetRid - before the test runs. Since we do a rid-specific publish later, it needs to be restored with the approriate rid. Stop using .Internal package. This resulted in host binaries getting published even for framework-dependent publishes. --- .../TestProjects/AppWithSubDirs/AppWithSubDirs.csproj | 1 + .../TestProjects/AppWithSubDirs/Directory.Build.props | 9 +++++++++ .../AppHost.Bundle.Tests/BundledAppWithSubDirs.cs | 9 +++------ 3 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 src/installer/tests/Assets/TestProjects/AppWithSubDirs/Directory.Build.props diff --git a/src/installer/tests/Assets/TestProjects/AppWithSubDirs/AppWithSubDirs.csproj b/src/installer/tests/Assets/TestProjects/AppWithSubDirs/AppWithSubDirs.csproj index 95842521601eb..719f295f06ff4 100644 --- a/src/installer/tests/Assets/TestProjects/AppWithSubDirs/AppWithSubDirs.csproj +++ b/src/installer/tests/Assets/TestProjects/AppWithSubDirs/AppWithSubDirs.csproj @@ -4,6 +4,7 @@ $(NetCoreAppCurrent) Exe $(MNAVersion) + $(TestTargetRid) diff --git a/src/installer/tests/Assets/TestProjects/AppWithSubDirs/Directory.Build.props b/src/installer/tests/Assets/TestProjects/AppWithSubDirs/Directory.Build.props new file mode 100644 index 0000000000000..de2e4580b9bea --- /dev/null +++ b/src/installer/tests/Assets/TestProjects/AppWithSubDirs/Directory.Build.props @@ -0,0 +1,9 @@ + + + + false + + + + + diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs index 4fdfaec59feb6..503f755ecb296 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs @@ -91,13 +91,10 @@ public SharedTestState() { TestFrameworkDependentFixture = new TestProjectFixture("AppWithSubDirs", RepoDirectories); BundleHelper.AddLongNameContentToAppWithSubDirs(TestFrameworkDependentFixture); - // This is a bit of a cheating - we know that the AppWithSubDirs doesn't have any RID specific assets - // so we can build it as a portable app and bundle it (since the built output will look the same) - // This is to workaround a problem where publishing RID specific FDD apps for some reason brings in - // hostfxr.dll to the built output - which breaks everything. TestFrameworkDependentFixture - .EnsureRestored(RepoDirectories.CorehostPackages) - .PublishProject(selfContained: false, + .EnsureRestoredForRid(TestFrameworkDependentFixture.CurrentRid, RepoDirectories.CorehostPackages) + .PublishProject(runtime: TestFrameworkDependentFixture.CurrentRid, + selfContained: false, outputDirectory: BundleHelper.GetPublishPath(TestFrameworkDependentFixture)); TestSelfContainedFixture = new TestProjectFixture("AppWithSubDirs", RepoDirectories); From fcc222f24332d24e233e92296499fbfce4ba9a26 Mon Sep 17 00:00:00 2001 From: vitek-karas Date: Fri, 25 Sep 2020 08:17:48 -0700 Subject: [PATCH 11/17] Remove unused method --- .../AppHost.Bundle.Tests/BundleTestBase.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs index 410ef683328ff..e01d71d8dcb35 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs @@ -69,13 +69,6 @@ public TestProjectFixture PreparePublishedSelfContainedTestProject(string projec return testFixture; } - - public TestProjectFixture PrepareSingleFileSelfContainedTestProject(string projectName) - { - var testFixture = PreparePublishedSelfContainedTestProject(projectName); - UseSingleFileSelfContainedHost(testFixture); - return testFixture; - } } } } From 48563cf46e219ca2f78bb43d4d781ebe1e654764 Mon Sep 17 00:00:00 2001 From: Mateo Torres Ruiz Date: Tue, 22 Sep 2020 15:08:04 -0700 Subject: [PATCH 12/17] Keep deps.json in APP_CONTEXT_DEPS_FILES for .NET Core 3.x compat mode --- .../corehost/cli/hostpolicy/deps_resolver.cpp | 3 ++- .../SingleFileApiTests.cs | 19 +++++++++++++++++++ .../Helpers/BundleHelper.cs | 13 +++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/installer/corehost/cli/hostpolicy/deps_resolver.cpp b/src/installer/corehost/cli/hostpolicy/deps_resolver.cpp index ceffb77f3b8a0..d406497e7be1b 100644 --- a/src/installer/corehost/cli/hostpolicy/deps_resolver.cpp +++ b/src/installer/corehost/cli/hostpolicy/deps_resolver.cpp @@ -736,7 +736,8 @@ void deps_resolver_t::get_app_context_deps_files_range(fx_definition_vector_t::i auto begin_iter = m_fx_definitions.begin(); auto end_iter = m_fx_definitions.end(); - if ((m_host_mode == host_mode_t::libhost || bundle::info_t::is_single_file_bundle()) + if ((m_host_mode == host_mode_t::libhost + || (bundle::info_t::is_single_file_bundle() && !bundle::runner_t::app()->is_netcoreapp3_compat_mode())) && begin_iter != end_iter) { // Neither in a libhost scenario nor in a bundled app diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs index ade9edaf0a5a6..56b2d6f94baeb 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs @@ -2,6 +2,7 @@ using BundleTests.Helpers; using Microsoft.DotNet.Cli.Build.Framework; using Microsoft.DotNet.CoreSetup.Test; +using Microsoft.NET.HostModel.Bundle; using Xunit; namespace AppHost.Bundle.Tests @@ -67,6 +68,24 @@ public void AppContext_Deps_Files_Bundled_Self_Contained() .NotHaveStdOutContaining("Microsoft.NETCore.App.deps.json"); } + [Fact] + public void AppContext_Deps_Files_Bundled_Self_Contained_NetCoreApp3_CompatMode() + { + var fixture = sharedTestState.TestFixture.Copy(); + string singleFile = BundleHelper.BundleApp(fixture, BundleOptions.BundleAllContent); + + Command.Create(singleFile, "appcontext") + .CaptureStdErr() + .CaptureStdOut() + .Execute() + .Should() + .Pass() + .And + .HaveStdOutContaining("SingleFileApiTests.deps.json") + .And + .HaveStdOutContaining("Microsoft.NETCore.App.deps.json"); + } + [Fact] public void GetEnvironmentArgs_0_Returns_Bundled_Executable_Path() { diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs index 53c8d244038d8..fe4b5e4d3b193 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs @@ -122,6 +122,15 @@ public static Architecture GetTargetArch(string runtimeIdentifier) throw new ArgumentException(nameof (runtimeIdentifier)); } + private static void UseOwnSharedLibrary(TestProjectFixture fixture, string libraryName) + { + string sharedLibraryName = RuntimeInformationExtensions.GetSharedLibraryFileNameForCurrentPlatform(libraryName); + File.Copy( + Path.Combine(fixture.RepoDirProvider.HostArtifacts, sharedLibraryName), + Path.Combine(BundleHelper.GetPublishPath(fixture), sharedLibraryName), + overwrite: true); + } + /// Generate a bundle containind the (embeddable) files in sourceDir public static string GenerateBundle(Bundler bundler, string sourceDir, string outputDir, bool copyExludedFiles=true) { @@ -175,6 +184,10 @@ public static Bundler BundleApp(TestProjectFixture fixture, var targetOS = GetTargetOS(fixture.CurrentRid); var targetArch = GetTargetArch(fixture.CurrentRid); + // In order to test the hosting components we need the hostpolicy and hostfxr built by the repo + UseOwnSharedLibrary(fixture, "hostfxr"); + UseOwnSharedLibrary(fixture, "hostpolicy"); + var bundler = new Bundler(hostName, bundleDir.FullName, options, targetOS, targetArch, targetFrameworkVersion); singleFile = GenerateBundle(bundler, publishPath, bundleDir.FullName, copyExcludedFiles); From 7595e5f4078c97fe40c73c32245d87dd5671a706 Mon Sep 17 00:00:00 2001 From: Mateo Torres Ruiz Date: Tue, 22 Sep 2020 20:47:42 -0700 Subject: [PATCH 13/17] Only expect app.deps.json --- .../AppHost.Bundle.Tests/SingleFileApiTests.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs index 56b2d6f94baeb..d5acc26c93a35 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs @@ -81,9 +81,7 @@ public void AppContext_Deps_Files_Bundled_Self_Contained_NetCoreApp3_CompatMode( .Should() .Pass() .And - .HaveStdOutContaining("SingleFileApiTests.deps.json") - .And - .HaveStdOutContaining("Microsoft.NETCore.App.deps.json"); + .HaveStdOutContaining("SingleFileApiTests.deps.json"); } [Fact] From 9449607011c5222ddcd1f1dfca3eb45744e1613e Mon Sep 17 00:00:00 2001 From: vitek-karas Date: Fri, 25 Sep 2020 08:46:15 -0700 Subject: [PATCH 14/17] Adapt to recent test changes --- .../AppHost.Bundle.Tests/SingleFileApiTests.cs | 2 +- .../Helpers/BundleHelper.cs | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs index d5acc26c93a35..afd987e9e25d7 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs @@ -72,7 +72,7 @@ public void AppContext_Deps_Files_Bundled_Self_Contained() public void AppContext_Deps_Files_Bundled_Self_Contained_NetCoreApp3_CompatMode() { var fixture = sharedTestState.TestFixture.Copy(); - string singleFile = BundleHelper.BundleApp(fixture, BundleOptions.BundleAllContent); + string singleFile = BundleSelfContainedApp(fixture, BundleOptions.BundleAllContent); Command.Create(singleFile, "appcontext") .CaptureStdErr() diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs index fe4b5e4d3b193..53c8d244038d8 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/Helpers/BundleHelper.cs @@ -122,15 +122,6 @@ public static Architecture GetTargetArch(string runtimeIdentifier) throw new ArgumentException(nameof (runtimeIdentifier)); } - private static void UseOwnSharedLibrary(TestProjectFixture fixture, string libraryName) - { - string sharedLibraryName = RuntimeInformationExtensions.GetSharedLibraryFileNameForCurrentPlatform(libraryName); - File.Copy( - Path.Combine(fixture.RepoDirProvider.HostArtifacts, sharedLibraryName), - Path.Combine(BundleHelper.GetPublishPath(fixture), sharedLibraryName), - overwrite: true); - } - /// Generate a bundle containind the (embeddable) files in sourceDir public static string GenerateBundle(Bundler bundler, string sourceDir, string outputDir, bool copyExludedFiles=true) { @@ -184,10 +175,6 @@ public static Bundler BundleApp(TestProjectFixture fixture, var targetOS = GetTargetOS(fixture.CurrentRid); var targetArch = GetTargetArch(fixture.CurrentRid); - // In order to test the hosting components we need the hostpolicy and hostfxr built by the repo - UseOwnSharedLibrary(fixture, "hostfxr"); - UseOwnSharedLibrary(fixture, "hostpolicy"); - var bundler = new Bundler(hostName, bundleDir.FullName, options, targetOS, targetArch, targetFrameworkVersion); singleFile = GenerateBundle(bundler, publishPath, bundleDir.FullName, copyExcludedFiles); From e52837ccae79a230057b0b4846d1a6aae845cfa7 Mon Sep 17 00:00:00 2001 From: vitek-karas Date: Fri, 25 Sep 2020 09:11:02 -0700 Subject: [PATCH 15/17] Rework the single-file API tests for better coverage The tests now validate behavior of all of the interesting APIs in both single-file and backcompat mode. Added the AppContext.BaseDirectory to the tests. --- .../SingleFileApiTests/Program.cs | 10 +- .../SingleFileApiTests.cs | 92 ++++++------------- 2 files changed, 35 insertions(+), 67 deletions(-) diff --git a/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs b/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs index 983479522cd23..d7f3eb1524eaa 100644 --- a/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs +++ b/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs @@ -28,20 +28,20 @@ public static int Main(string[] args) break; case "cmdlineargs": - Console.WriteLine(Environment.GetCommandLineArgs()[0]); + Console.WriteLine("Command line args: " + Environment.GetCommandLineArgs()[0]); break; case "codebase": try { #pragma warning disable SYSLIB0012 - _ = typeof(Program).Assembly.CodeBase; + var codeBase = typeof(Program).Assembly.CodeBase; #pragma warning restore SYSLIB0012 + Console.WriteLine("CodeBase " + codeBase); } catch (NotSupportedException) { Console.WriteLine("CodeBase NotSupported"); - return -1; } break; @@ -59,6 +59,10 @@ public static int Main(string[] args) } break; + case "basedirectory": + Console.WriteLine("AppContext.BaseDirectory: " + AppContext.BaseDirectory); + break; + default: Console.WriteLine("test failure"); return -1; diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs index afd987e9e25d7..37f8b0fff088f 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/SingleFileApiTests.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using BundleTests.Helpers; using Microsoft.DotNet.Cli.Build.Framework; using Microsoft.DotNet.CoreSetup.Test; @@ -17,93 +18,56 @@ public SingleFileApiTests(SharedTestState fixture) } [Fact] - public void FullyQualifiedName() + public void SelfContained_SingleFile_APITests() { var fixture = sharedTestState.TestFixture.Copy(); var singleFile = BundleSelfContainedApp(fixture); - Command.Create(singleFile, "fullyqualifiedname") + Command.Create(singleFile, "fullyqualifiedname codebase appcontext cmdlineargs executing_assembly_location basedirectory") .CaptureStdErr() .CaptureStdOut() .Execute() .Should() .Pass() - .And - .HaveStdOutContaining("FullyQualifiedName: " + - Environment.NewLine + - "Name: "); + .And.HaveStdOutContaining("FullyQualifiedName: ") + .And.HaveStdOutContaining("Name: ") + .And.HaveStdOutContaining("CodeBase NotSupported") + .And.NotHaveStdOutContaining("SingleFileApiTests.deps.json") + .And.NotHaveStdOutContaining("Microsoft.NETCore.App.deps.json") + // For single-file, Environment.GetCommandLineArgs[0] should return the file path of the host. + .And.HaveStdOutContaining("Command line args: " + singleFile) + .And.HaveStdOutContaining("ExecutingAssembly.Location: " + Environment.NewLine) + .And.HaveStdOutContaining("AppContext.BaseDirectory: " + Path.GetDirectoryName(singleFile)); } [Fact] - public void CodeBaseThrows() + public void SelfContained_NetCoreApp3_CompatMode_SingleFile_APITests() { var fixture = sharedTestState.TestFixture.Copy(); - var singleFile = BundleSelfContainedApp(fixture); + var singleFile = BundleSelfContainedApp(fixture, BundleOptions.BundleAllContent); + var extractionBaseDir = BundleHelper.GetExtractionRootDir(fixture); - Command.Create(singleFile, "codebase") - .CaptureStdErr() - .CaptureStdOut() - .Execute() - .Should() - .Fail() - .And - .HaveStdOutContaining("CodeBase NotSupported"); - } - - [Fact] - public void AppContext_Deps_Files_Bundled_Self_Contained() - { - var fixture = sharedTestState.TestFixture.Copy(); - var singleFile = BundleSelfContainedApp(fixture); - - Command.Create(singleFile, "appcontext") + Command.Create(singleFile, "fullyqualifiedname codebase appcontext cmdlineargs executing_assembly_location basedirectory") .CaptureStdErr() .CaptureStdOut() + .EnvironmentVariable(BundleHelper.DotnetBundleExtractBaseEnvVariable, extractionBaseDir.FullName) .Execute() .Should() .Pass() - .And - .NotHaveStdOutContaining("SingleFileApiTests.deps.json") - .And - .NotHaveStdOutContaining("Microsoft.NETCore.App.deps.json"); - } - - [Fact] - public void AppContext_Deps_Files_Bundled_Self_Contained_NetCoreApp3_CompatMode() - { - var fixture = sharedTestState.TestFixture.Copy(); - string singleFile = BundleSelfContainedApp(fixture, BundleOptions.BundleAllContent); - - Command.Create(singleFile, "appcontext") - .CaptureStdErr() - .CaptureStdOut() - .Execute() - .Should() - .Pass() - .And - .HaveStdOutContaining("SingleFileApiTests.deps.json"); - } - - [Fact] - public void GetEnvironmentArgs_0_Returns_Bundled_Executable_Path() - { - var fixture = sharedTestState.TestFixture.Copy(); - var singleFile = BundleSelfContainedApp(fixture); - - // For single-file, Environment.GetCommandLineArgs[0] - // should return the file path of the host. - Command.Create(singleFile, "cmdlineargs") - .CaptureStdErr() - .CaptureStdOut() - .Execute() - .Should() - .Pass() - .And - .HaveStdOutContaining(singleFile); + .And.HaveStdOutContaining(Path.DirectorySeparatorChar + "System.Private.CoreLib.dll") // In extraction directory + .And.HaveStdOutContaining("System.Private.CoreLib.dll") // In extraction directory + .And.NotHaveStdOutContaining("CodeBase NotSupported") // CodeBase should point to extraction directory + .And.HaveStdOutContaining("SingleFileApiTests.dll") + .And.HaveStdOutContaining("SingleFileApiTests.deps.json") // The app's .deps.json should be available + .And.NotHaveStdOutContaining("Microsoft.NETCore.App.deps.json") // No framework - it's self-contained + // For single-file, Environment.GetCommandLineArgs[0] should return the file path of the host. + .And.HaveStdOutContaining("Command line args: " + singleFile) + .And.HaveStdOutContaining("ExecutingAssembly.Location: " + extractionBaseDir.FullName) // Should point to the app's dll + .And.HaveStdOutContaining("AppContext.BaseDirectory: " + extractionBaseDir.FullName); // Should point to the extraction directory } [Fact] - public void GetEnvironmentArgs_0_Non_Bundled_App() + public void GetCommandLineArgs_0_Non_Bundled_App() { var fixture = sharedTestState.TestFixture.Copy(); var dotnet = fixture.BuiltDotnet; From 7ec7fd364d5629824cb53bf717cf907f17921da0 Mon Sep 17 00:00:00 2001 From: vitek-karas Date: Fri, 25 Sep 2020 16:18:07 -0700 Subject: [PATCH 16/17] Fix the reported path to .deps.json for back compat mode The path must point to the extraction folder and not use the "fake" in-bundle path. --- .../cli/hostpolicy/hostpolicy_context.cpp | 15 ++++++++++++++- .../TestProjects/SingleFileApiTests/Program.cs | 10 +++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp b/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp index 05a3b8fb9e2db..502d3a1b4a66b 100644 --- a/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp +++ b/src/installer/corehost/cli/hostpolicy/hostpolicy_context.cpp @@ -161,7 +161,20 @@ int hostpolicy_context_t::initialize(hostpolicy_init_t &hostpolicy_init, const a if (fx_curr != fx_begin) app_context_deps_str += _X(';'); - app_context_deps_str += (*fx_curr)->get_deps_file(); + // For the application's .deps.json if this is single file, 3.1 backward compat + // then the path used internally is the bundle path, but externally we need to report + // the path to the extraction folder. + if (fx_curr == fx_begin && bundle::info_t::is_single_file_bundle() && bundle::runner_t::app()->is_netcoreapp3_compat_mode()) + { + pal::string_t deps_path = bundle::runner_t::app()->extraction_path(); + append_path(&deps_path, get_filename((*fx_curr)->get_deps_file()).c_str()); + app_context_deps_str += deps_path; + } + else + { + app_context_deps_str += (*fx_curr)->get_deps_file(); + } + ++fx_curr; } diff --git a/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs b/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs index d7f3eb1524eaa..4c52801dcb23e 100644 --- a/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs +++ b/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs @@ -14,8 +14,16 @@ public static int Main(string[] args) switch (arg) { case "appcontext": - var deps_files = AppContext.GetData("APP_CONTEXT_DEPS_FILES"); + var deps_files = (string)AppContext.GetData("APP_CONTEXT_DEPS_FILES"); Console.WriteLine("APP_CONTEXT_DEPS_FILES: " + deps_files); + foreach (string deps_file_path in deps_files.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)) + { + if (!File.Exists(deps_file_path)) + { + Console.WriteLine($"APP_CONTEXT_DEPS_FILES contains path which doesn't exist: '{deps_file_path}'"); + return -1; + } + } break; case "executing_assembly_location": From 19ea932d8dcf6060f917a8ba760c4a8ed5f57d67 Mon Sep 17 00:00:00 2001 From: vitek-karas Date: Mon, 28 Sep 2020 04:33:40 -0700 Subject: [PATCH 17/17] Fix a test bug on Linux Actually we should be using path separator char in the APP_CONTEXT_DEPS_FILES, but that's something we won't be able to change really (too much chance of breaking something without too much value). --- .../tests/Assets/TestProjects/SingleFileApiTests/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs b/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs index 4c52801dcb23e..56428b14cacad 100644 --- a/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs +++ b/src/installer/tests/Assets/TestProjects/SingleFileApiTests/Program.cs @@ -16,7 +16,7 @@ public static int Main(string[] args) case "appcontext": var deps_files = (string)AppContext.GetData("APP_CONTEXT_DEPS_FILES"); Console.WriteLine("APP_CONTEXT_DEPS_FILES: " + deps_files); - foreach (string deps_file_path in deps_files.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)) + foreach (string deps_file_path in deps_files.Split(";", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)) { if (!File.Exists(deps_file_path)) {