Example #1
0
        public void ItGeneratesExecutableImage()
        {
            using TestDirectory testDirectory = TestDirectory.Create();
            string sourceAppHostMock   = PrepareAppHostMockFile(testDirectory);
            string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
            string appBinaryFilePath   = "Test/App/Binary/Path.dll";

            // strip executable permissions from this AppHost template binary
            File.SetUnixFileMode(sourceAppHostMock, UnixFileMode.UserRead | UnixFileMode.GroupRead | UnixFileMode.OtherRead);

            // -rwxr-xr-x
            const UnixFileMode expectedPermissions = UnixFileMode.UserRead | UnixFileMode.UserExecute | UnixFileMode.UserWrite |
                                                     UnixFileMode.GroupRead | UnixFileMode.GroupExecute |
                                                     UnixFileMode.OtherRead | UnixFileMode.OtherExecute;

            HostWriter.CreateAppHost(
                sourceAppHostMock,
                destinationFilePath,
                appBinaryFilePath,
                windowsGraphicalUserInterface: true);

            // assert that the generated app has executable permissions
            // despite different permissions on the template binary.
            File.GetUnixFileMode(destinationFilePath)
            .Should()
            .Be(expectedPermissions);
        }
Example #2
0
        public void ItDoesNotCodeSignAppHostByDefault()
        {
            using (TestDirectory testDirectory = TestDirectory.Create())
            {
                string sourceAppHostMock = PrepareAppHostMockFile(testDirectory);
                File.SetAttributes(sourceAppHostMock, FileAttributes.ReadOnly);
                string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
                string appBinaryFilePath   = "Test/App/Binary/Path.dll";
                HostWriter.CreateAppHost(
                    sourceAppHostMock,
                    destinationFilePath,
                    appBinaryFilePath,
                    windowsGraphicalUserInterface: false);

                const string codesign = @"/usr/bin/codesign";
                var          psi      = new ProcessStartInfo()
                {
                    Arguments             = $"-d {destinationFilePath}",
                    FileName              = codesign,
                    RedirectStandardError = true,
                };

                using (var p = Process.Start(psi))
                {
                    p.Start();
                    p.StandardError.ReadToEnd()
                    .Should().Contain($"{Path.GetFullPath(destinationFilePath)}: code object is not signed at all");
                    p.WaitForExit();
                }
            }
        }
Example #3
0
        public void CanCodeSignAppHostOnMacOS(string subdir)
        {
            using (TestDirectory testDirectory = TestDirectory.Create(subdir))
            {
                string sourceAppHostMock = PrepareAppHostMockFile(testDirectory);
                File.SetAttributes(sourceAppHostMock, FileAttributes.ReadOnly);
                string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
                string appBinaryFilePath   = "Test/App/Binary/Path.dll";
                HostWriter.CreateAppHost(
                    sourceAppHostMock,
                    destinationFilePath,
                    appBinaryFilePath,
                    windowsGraphicalUserInterface: false,
                    enableMacOSCodeSign: true);

                const string codesign = @"/usr/bin/codesign";
                var          psi      = new ProcessStartInfo()
                {
                    Arguments             = $"-d \"{destinationFilePath}\"",
                    FileName              = codesign,
                    RedirectStandardError = true,
                };

                using (var p = Process.Start(psi))
                {
                    p.Start();
                    p.StandardError.ReadToEnd()
                    .Should().Contain($"Executable=/private{Path.GetFullPath(destinationFilePath)}");
                    p.WaitForExit();
                    // Successfully signed the apphost.
                    Assert.True(p.ExitCode == 0, $"Expected exit code was '0' but '{codesign}' returned '{p.ExitCode}' instead.");
                }
            }
        }
        public void ItGeneratesExecutableImage()
        {
            using (TestDirectory testDirectory = TestDirectory.Create())
            {
                string sourceAppHostMock   = PrepareAppHostMockFile(testDirectory);
                string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
                string appBinaryFilePath   = "Test/App/Binary/Path.dll";

                chmod(sourceAppHostMock, Convert.ToInt32("755", 8)) // match installed permissions: -rwxr-xr-x
                .Should()
                .NotBe(-1);

                GetLastError()
                .Should()
                .NotBe(4);     // EINTR

                GetFilePermissionValue(sourceAppHostMock)
                .Should()
                .Be(Convert.ToInt32("755", 8));

                HostWriter.CreateAppHost(
                    sourceAppHostMock,
                    destinationFilePath,
                    appBinaryFilePath,
                    windowsGraphicalUserInterface: true);

                GetFilePermissionValue(destinationFilePath)
                .Should()
                .Be(Convert.ToInt32("755", 8));
            }

            int GetLastError() => Marshal.GetLastWin32Error();
        }
        public void ItEmbedsAppBinaryPath()
        {
            using (TestDirectory testDirectory = TestDirectory.Create())
            {
                string sourceAppHostMock   = PrepareAppHostMockFile(testDirectory);
                string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
                string appBinaryFilePath   = "Test/App/Binary/Path.dll";

                HostWriter.CreateAppHost(
                    sourceAppHostMock,
                    destinationFilePath,
                    appBinaryFilePath);

                byte[] binaryPathBlob = Encoding.UTF8.GetBytes(appBinaryFilePath);
                byte[] result         = File.ReadAllBytes(destinationFilePath);
                result
                .Skip(WindowsFileHeader.Length)
                .Take(binaryPathBlob.Length)
                .Should()
                .BeEquivalentTo(binaryPathBlob);

                BitConverter
                .ToUInt16(result, SubsystemOffset)
                .Should()
                .Be(3);
            }
        }
Example #6
0
        public void CodeSigningFailuresThrow()
        {
            using (TestDirectory testDirectory = TestDirectory.Create())
            {
                string sourceAppHostMock = PrepareAppHostMockFile(testDirectory);
                File.SetAttributes(sourceAppHostMock, FileAttributes.ReadOnly);
                string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
                string appBinaryFilePath   = "Test/App/Binary/Path.dll";
                HostWriter.CreateAppHost(
                    sourceAppHostMock,
                    destinationFilePath,
                    appBinaryFilePath,
                    windowsGraphicalUserInterface: false,
                    enableMacOSCodeSign: true);

                // Run CreateAppHost again to sign the apphost a second time,
                // causing codesign to fail.
                var exception = Assert.Throws <AppHostSigningException>(() =>
                                                                        HostWriter.CreateAppHost(
                                                                            sourceAppHostMock,
                                                                            destinationFilePath,
                                                                            appBinaryFilePath,
                                                                            windowsGraphicalUserInterface: false,
                                                                            enableMacOSCodeSign: true));
                Assert.Contains($"{destinationFilePath}: is already signed", exception.Message);
            }
        }
Example #7
0
        // 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));
        }
Example #8
0
        /// <summary>
        /// Extract all files in the bundle to disk
        /// </summary>
        /// <exceptions>
        /// BundleException if the bundle is invalid or malformed.
        /// IOExceptions and ArgumentExceptions from callees flow to the caller.
        /// </exceptions>
        public void ExtractFiles()
        {
            try
            {
                trace.Log($"Bundler version {Bundler.Version}");
                trace.Log($"Extract from file: {BundlePath}");
                trace.Log($"Output Directory: {OutputDir}");

                long headerOffset;
                if (!HostWriter.IsBundle(BundlePath, out headerOffset))
                {
                    throw new BundleException("Extraction failed: Bundle Signature not found.");
                }

                using (BinaryReader reader = new BinaryReader(File.OpenRead(BundlePath)))
                {
                    Manifest manifest = Manifest.Read(reader, headerOffset);

                    foreach (FileEntry entry in manifest.Files)
                    {
                        trace.Log($"Extract: {entry}");

                        string fileRelativePath = entry.RelativePath.Replace(FileEntry.DirectorySeparatorChar, Path.DirectorySeparatorChar);
                        string filePath         = Path.Combine(OutputDir, fileRelativePath);
                        string fileDir          = Path.GetDirectoryName(filePath);

                        if ((fileDir != null) && !fileDir.Equals(String.Empty))
                        {
                            Directory.CreateDirectory(fileDir);
                        }

                        reader.BaseStream.Position = entry.Offset;
                        using (BinaryWriter file = new BinaryWriter(File.Create(filePath)))
                        {
                            long size = entry.Size;
                            do
                            {
                                int copySize = (int)(size <= int.MaxValue ? size : int.MaxValue);
                                file.Write(reader.ReadBytes(copySize));
                                size -= copySize;
                            } while (size > 0);
                        }
                    }
                }
            }
            catch (EndOfStreamException)
            {
                // Trying to read non-existant bits in the bundle
                throw new BundleException("Malformed Bundle");
            }
            catch (ArgumentOutOfRangeException)
            {
                // Trying to set file-stream position to an invalid value
                throw new BundleException("Malformed Bundle");
            }
        }
Example #9
0
        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.GetAppName(testFixture));
            return(publishedHostPath);
        }
Example #10
0
        // 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.GetAppName(testFixture));
            return(publishedHostPath);
        }
        public void CreateApphostShellShim(string entryPoint, string shimPath)
        {
            var    appHostDestinationFilePath = Path.GetFullPath(shimPath);
            string entryPointFullPath         = Path.GetFullPath(entryPoint);
            var    appBinaryFilePath          = Path.GetRelativePath(Path.GetDirectoryName(appHostDestinationFilePath), entryPointFullPath);

            // by passing null to assemblyToCopyResorcesFrom, it will skip copying resources,
            // which is only supported on Windows
            HostWriter.CreateAppHost(appHostSourceFilePath: Path.GetFullPath(_apphost),
                                     appHostDestinationFilePath: appHostDestinationFilePath,
                                     appBinaryFilePath: appBinaryFilePath,
                                     windowsGraphicalUserInterface: false,
                                     assemblyToCopyResorcesFrom: null);
        }
        public void ItFailsToEmbedTooLongAppBinaryPath()
        {
            using (TestDirectory testDirectory = TestDirectory.Create())
            {
                string sourceAppHostMock   = PrepareAppHostMockFile(testDirectory);
                string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
                string appBinaryFilePath   = new string('a', 1024 + 5);

                Assert.Throws <AppNameTooLongException>(() =>
                                                        HostWriter.CreateAppHost(
                                                            sourceAppHostMock,
                                                            destinationFilePath,
                                                            appBinaryFilePath));

                File.Exists(destinationFilePath).Should().BeFalse();
            }
        }
        public void CanCreateAppHost()
        {
            using (TestDirectory testDirectory = TestDirectory.Create())
            {
                string sourceAppHostMock = PrepareAppHostMockFile(testDirectory);
                File.SetAttributes(sourceAppHostMock, FileAttributes.ReadOnly);
                string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
                string appBinaryFilePath   = "Test/App/Binary/Path.dll";
                HostWriter.CreateAppHost(
                    sourceAppHostMock,
                    destinationFilePath,
                    appBinaryFilePath,
                    windowsGraphicalUserInterface: false);

                File.SetAttributes(sourceAppHostMock, FileAttributes.Normal);
            }
        }
        public void ItCanSetWindowsGUISubsystem()
        {
            using (TestDirectory testDirectory = TestDirectory.Create())
            {
                string sourceAppHostMock   = PrepareAppHostMockFile(testDirectory);
                string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
                string appBinaryFilePath   = "Test/App/Binary/Path.dll";

                HostWriter.CreateAppHost(
                    sourceAppHostMock,
                    destinationFilePath,
                    appBinaryFilePath,
                    windowsGraphicalUserInterface: true);

                BitConverter
                .ToUInt16(File.ReadAllBytes(destinationFilePath), SubsystemOffset)
                .Should()
                .Be(2);
            }
        }
Example #15
0
        public void CreateApphostShellShim(FilePath entryPoint, FilePath shimPath)
        {
            string appHostSourcePath;

            if (OperatingSystem.IsWindows())
            {
                appHostSourcePath = Path.Combine(_appHostSourceDirectory, ApphostNameWithoutExtension + ".exe");
            }
            else
            {
                appHostSourcePath = Path.Combine(_appHostSourceDirectory, ApphostNameWithoutExtension);
            }

            var    appHostDestinationFilePath = Path.GetFullPath(shimPath.Value);
            string entryPointFullPath         = Path.GetFullPath(entryPoint.Value);
            var    appBinaryFilePath          = Path.GetRelativePath(Path.GetDirectoryName(appHostDestinationFilePath), entryPointFullPath);


            if (ResourceUpdater.IsSupportedOS())
            {
                var windowsGraphicalUserInterfaceBit = PEUtils.GetWindowsGraphicalUserInterfaceBit(entryPointFullPath);
                HostWriter.CreateAppHost(appHostSourceFilePath: appHostSourcePath,
                                         appHostDestinationFilePath: appHostDestinationFilePath,
                                         appBinaryFilePath: appBinaryFilePath,
                                         windowsGraphicalUserInterface: (windowsGraphicalUserInterfaceBit == WindowsGUISubsystem),
                                         assemblyToCopyResorcesFrom: entryPointFullPath);
            }
            else
            {
                // by passing null to assemblyToCopyResorcesFrom, it will skip copying resources,
                // which is only supported on Windows
                HostWriter.CreateAppHost(appHostSourceFilePath: appHostSourcePath,
                                         appHostDestinationFilePath: appHostDestinationFilePath,
                                         appBinaryFilePath: appBinaryFilePath,
                                         windowsGraphicalUserInterface: false,
                                         assemblyToCopyResorcesFrom: null,
                                         enableMacOSCodeSign: OperatingSystem.IsMacOS());
            }

            _filePermissionSetter.SetUserExecutionPermission(appHostDestinationFilePath);
        }
        public void ItFailsToEmbedAppBinaryIfHashIsWrong()
        {
            using (TestDirectory testDirectory = TestDirectory.Create())
            {
                string sourceAppHostMock = PrepareAppHostMockFile(testDirectory, content =>
                {
                    // Corrupt the hash value
                    content[WindowsFileHeader.Length + 1]++;
                });
                string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
                string appBinaryFilePath   = "Test/App/Binary/Path.dll";

                Assert.Throws <PlaceHolderNotFoundInAppHostException>(() =>
                                                                      HostWriter.CreateAppHost(
                                                                          sourceAppHostMock,
                                                                          destinationFilePath,
                                                                          appBinaryFilePath));

                File.Exists(destinationFilePath).Should().BeFalse();
            }
        }
        public void ItFailsToSetGUISubsystemWithWrongDefault()
        {
            using (TestDirectory testDirectory = TestDirectory.Create())
            {
                string sourceAppHostMock = PrepareAppHostMockFile(testDirectory, content =>
                {
                    // Corrupt the value of the subsystem (the default should be 3)
                    content[SubsystemOffset] = 42;
                });
                string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
                string appBinaryFilePath   = "Test/App/Binary/Path.dll";

                Assert.Throws <AppHostNotCUIException>(() =>
                                                       HostWriter.CreateAppHost(
                                                           sourceAppHostMock,
                                                           destinationFilePath,
                                                           appBinaryFilePath,
                                                           windowsGraphicalUserInterface: true));

                File.Exists(destinationFilePath).Should().BeFalse();
            }
        }
        public void ItFailsToSetGUISubsystemOnNonWindowsPEFile()
        {
            using (TestDirectory testDirectory = TestDirectory.Create())
            {
                string sourceAppHostMock = PrepareAppHostMockFile(testDirectory, content =>
                {
                    // Windows PE files must start with 0x5A4D, so write some other value here.
                    content[0] = 1;
                    content[1] = 2;
                });
                string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
                string appBinaryFilePath   = "Test/App/Binary/Path.dll";

                Assert.Throws <AppHostNotPEFileException>(() =>
                                                          HostWriter.CreateAppHost(
                                                              sourceAppHostMock,
                                                              destinationFilePath,
                                                              appBinaryFilePath,
                                                              windowsGraphicalUserInterface: true));

                File.Exists(destinationFilePath).Should().BeFalse();
            }
        }
Example #19
0
        protected override void ExecuteCore()
        {
            try
            {
                if (ResourceUpdater.IsSupportedOS())
                {
                    HostWriter.CreateAppHost(appHostSourceFilePath: AppHostSourcePath,
                                             appHostDestinationFilePath: AppHostDestinationPath,
                                             appBinaryFilePath: AppBinaryName,
                                             windowsGraphicalUserInterface: WindowsGraphicalUserInterface,
                                             assemblyToCopyResorcesFrom: IntermediateAssembly);
                }
                else
                {
                    // by passing null to assemblyToCopyResorcesFrom, it will skip copying resorces,
                    // which is only supported on Windows
                    if (WindowsGraphicalUserInterface)
                    {
                        Log.LogWarning(Strings.AppHostCustomizationRequiresWindowsHostWarning);
                    }

                    HostWriter.CreateAppHost(appHostSourceFilePath: AppHostSourcePath,
                                             appHostDestinationFilePath: AppHostDestinationPath,
                                             appBinaryFilePath: AppBinaryName,
                                             windowsGraphicalUserInterface: false,
                                             assemblyToCopyResorcesFrom: null);
                }
            }
            catch (AppNameTooLongException ex)
            {
                throw new BuildErrorException(Strings.FileNameIsTooLong, ex.LongName);
            }
            catch (PlaceHolderNotFoundInAppHostException ex)
            {
                throw new BuildErrorException(Strings.AppHostHasBeenModified, AppHostSourcePath, BitConverter.ToString(ex.MissingPattern));
            }
        }
Example #20
0
        protected override void ExecuteCore()
        {
            var embeddedApphostPaths = new List <ITaskItem>();

            foreach (var runtimeIdentifier in ShimRuntimeIdentifiers.Select(r => r.ItemSpec))
            {
                var resolvedApphostAssetPath = GetApphostAsset(ApphostsForShimRuntimeIdentifiers, runtimeIdentifier);

                var packagedShimOutputDirectoryAndRid = Path.Combine(
                    PackagedShimOutputDirectory,
                    runtimeIdentifier);

                var appHostDestinationFilePath = Path.Combine(
                    packagedShimOutputDirectoryAndRid,
                    ToolCommandName + ExecutableExtension.ForRuntimeIdentifier(runtimeIdentifier));

                Directory.CreateDirectory(packagedShimOutputDirectoryAndRid);

                // per https://github.com/dotnet/cli/issues/9870 nuget layout (as in {packageid}/{packageversion}/tools/)is normalized version
                var normalizedPackageVersion = NuGetVersion.Parse(PackageVersion).ToNormalizedString();
                // This is the embedded string. We should normalize it on forward slash, so the file won't be different according to
                // build machine.
                var appBinaryFilePath = string.Join("/",
                                                    new[] {
                    ".store",
                    PackageId.ToLowerInvariant(),
                    normalizedPackageVersion,
                    PackageId.ToLowerInvariant(),
                    normalizedPackageVersion,
                    "tools",
                    NuGetUtils.ParseFrameworkName(TargetFrameworkMoniker).GetShortFolderName(),
                    "any",
                    ToolEntryPoint
                });

                try
                {
                    var windowsGraphicalUserInterface = runtimeIdentifier.StartsWith("win") && "WinExe".Equals(OutputType, StringComparison.OrdinalIgnoreCase);
                    if (ResourceUpdater.IsSupportedOS() && runtimeIdentifier.StartsWith("win"))
                    {
                        HostWriter.CreateAppHost(appHostSourceFilePath: resolvedApphostAssetPath,
                                                 appHostDestinationFilePath: appHostDestinationFilePath,
                                                 appBinaryFilePath: appBinaryFilePath,
                                                 windowsGraphicalUserInterface: windowsGraphicalUserInterface,
                                                 assemblyToCopyResorcesFrom: IntermediateAssembly);
                    }
                    else
                    {
                        // by passing null to assemblyToCopyResorcesFrom, it will skip copying resources,
                        // which is only supported on Windows
                        if (windowsGraphicalUserInterface)
                        {
                            Log.LogWarning(Strings.AppHostCustomizationRequiresWindowsHostWarning);
                        }

                        HostWriter.CreateAppHost(appHostSourceFilePath: resolvedApphostAssetPath,
                                                 appHostDestinationFilePath: appHostDestinationFilePath,
                                                 appBinaryFilePath: appBinaryFilePath,
                                                 windowsGraphicalUserInterface: false,
                                                 assemblyToCopyResorcesFrom: null);
                    }
                }
                catch (AppNameTooLongException ex)
                {
                    throw new BuildErrorException(Strings.FileNameIsTooLong, ex.LongName);
                }
                catch (PlaceHolderNotFoundInAppHostException ex)
                {
                    throw new BuildErrorException(Strings.AppHostHasBeenModified, resolvedApphostAssetPath, BitConverter.ToString(ex.MissingPattern));
                }

                var item = new TaskItem(appHostDestinationFilePath);
                item.SetMetadata(MetadataKeys.ShimRuntimeIdentifier, runtimeIdentifier);
                embeddedApphostPaths.Add(item);
            }

            EmbeddedApphostPaths = embeddedApphostPaths.ToArray();
        }
Example #21
0
        protected override void ExecuteCore()
        {
            try
            {
                var isGUI             = WindowsGraphicalUserInterface;
                var resourcesAssembly = IntermediateAssembly;

                if (!ResourceUpdater.IsSupportedOS())
                {
                    if (isGUI)
                    {
                        Log.LogWarning(Strings.AppHostCustomizationRequiresWindowsHostWarning);
                    }

                    isGUI             = false;
                    resourcesAssembly = null;
                }

                int attempts = 0;

                while (true)
                {
                    try
                    {
                        HostWriter.CreateAppHost(appHostSourceFilePath: AppHostSourcePath,
                                                 appHostDestinationFilePath: AppHostDestinationPath,
                                                 appBinaryFilePath: AppBinaryName,
                                                 windowsGraphicalUserInterface: isGUI,
                                                 assemblyToCopyResorcesFrom: resourcesAssembly);
                        return;
                    }
                    catch (Exception ex) when(ex is IOException ||
                                              ex is UnauthorizedAccessException ||
                                              ex is HResultException ||
                                              (ex is AggregateException && (ex.InnerException is IOException || ex.InnerException is UnauthorizedAccessException)))
                    {
                        if (Retries < 0 || attempts == Retries)
                        {
                            throw;
                        }

                        ++attempts;

                        string message = ex.Message;

                        if (ex is AggregateException)
                        {
                            message = ex.InnerException.Message;
                        }

                        Log.LogWarning(
                            string.Format(Strings.AppHostCreationFailedWithRetry,
                                          attempts,
                                          Retries + 1,
                                          message));

                        if (RetryDelayMilliseconds > 0)
                        {
                            Thread.Sleep(RetryDelayMilliseconds);
                        }
                    }
                }
            }
            catch (AppNameTooLongException ex)
            {
                throw new BuildErrorException(Strings.FileNameIsTooLong, ex.LongName);
            }
            catch (PlaceHolderNotFoundInAppHostException ex)
            {
                throw new BuildErrorException(Strings.AppHostHasBeenModified, AppHostSourcePath, BitConverter.ToString(ex.MissingPattern));
            }
        }
Example #22
0
        /// <summary>
        /// Generate a bundle, given the specification of embedded files
        /// </summary>
        /// <param name="fileSpecs">
        /// An enumeration FileSpecs for the files to be embedded.
        ///
        /// Files in fileSpecs that are not bundled within the single file bundle,
        /// and should be published as separate files are marked as "IsExcluded" by this method.
        /// This doesn't include unbundled files that should be dropped, and not publised as output.
        /// </param>
        /// <returns>
        /// The full path the the generated bundle file
        /// </returns>
        /// <exceptions>
        /// ArgumentException if input is invalid
        /// IOExceptions and ArgumentExceptions from callees flow to the caller.
        /// </exceptions>
        public string GenerateBundle(IReadOnlyList <FileSpec> fileSpecs)
        {
            Tracer.Log($"Bundler Version: {BundlerMajorVersion}.{BundlerMinorVersion}");
            Tracer.Log($"Bundle  Version: {BundleManifest.BundleVersion}");
            Tracer.Log($"Target Runtime: {Target}");
            Tracer.Log($"Bundler Options: {Options}");

            if (fileSpecs.Any(x => !x.IsValid()))
            {
                throw new ArgumentException("Invalid input specification: Found entry with empty source-path or bundle-relative-path.");
            }

            string hostSource;

            try
            {
                hostSource = fileSpecs.Where(x => x.BundleRelativePath.Equals(HostName)).Single().SourcePath;
            }
            catch (InvalidOperationException)
            {
                throw new ArgumentException("Invalid input specification: Must specify the host binary");
            }

            string bundlePath = Path.Combine(OutputDir, HostName);

            if (File.Exists(bundlePath))
            {
                Tracer.Log($"Ovewriting existing File {bundlePath}");
            }

            BinaryUtils.CopyFile(hostSource, bundlePath);

            // Note: We're comparing file paths both on the OS we're running on as well as on the target OS for the app
            // We can't really make assumptions about the file systems (even on Linux there can be case insensitive file systems
            // and vice versa for Windows). So it's safer to do case sensitive comparison everywhere.
            var relativePathToSpec = new Dictionary <string, FileSpec>(StringComparer.Ordinal);

            long headerOffset = 0;

            using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(bundlePath)))
            {
                Stream bundle = writer.BaseStream;
                bundle.Position = bundle.Length;

                foreach (var fileSpec in fileSpecs)
                {
                    string relativePath = fileSpec.BundleRelativePath;

                    if (IsHost(relativePath))
                    {
                        continue;
                    }

                    if (ShouldIgnore(relativePath))
                    {
                        Tracer.Log($"Ignore: {relativePath}");
                        continue;
                    }

                    FileType type = InferType(fileSpec);

                    if (ShouldExclude(type, relativePath))
                    {
                        Tracer.Log($"Exclude [{type}]: {relativePath}");
                        fileSpec.Excluded = true;
                        continue;
                    }

                    if (relativePathToSpec.TryGetValue(fileSpec.BundleRelativePath, out var existingFileSpec))
                    {
                        if (!string.Equals(fileSpec.SourcePath, existingFileSpec.SourcePath, StringComparison.Ordinal))
                        {
                            throw new ArgumentException($"Invalid input specification: Found entries '{fileSpec.SourcePath}' and '{existingFileSpec.SourcePath}' with the same BundleRelativePath '{fileSpec.BundleRelativePath}'");
                        }

                        // Exact duplicate - intentionally skip and don't include a second copy in the bundle
                        continue;
                    }
                    else
                    {
                        relativePathToSpec.Add(fileSpec.BundleRelativePath, fileSpec);
                    }

                    using (FileStream file = File.OpenRead(fileSpec.SourcePath))
                    {
                        FileType targetType = Target.TargetSpecificFileType(type);
                        (long startOffset, long compressedSize) = AddToBundle(bundle, file, targetType);
                        FileEntry entry = BundleManifest.AddEntry(targetType, file, relativePath, startOffset, compressedSize, Target.BundleMajorVersion);
                        Tracer.Log($"Embed: {entry}");
                    }
                }

                // Write the bundle manifest
                headerOffset = BundleManifest.Write(writer);
                Tracer.Log($"Header Offset={headerOffset}");
                Tracer.Log($"Meta-data Size={writer.BaseStream.Position - headerOffset}");
                Tracer.Log($"Bundle: Path={bundlePath}, Size={bundle.Length}");
            }

            HostWriter.SetAsBundle(bundlePath, headerOffset);

            return(bundlePath);
        }
Example #23
0
        /// <summary>
        /// Generate a bundle, given the specification of embedded files
        /// </summary>
        /// <param name="fileSpecs">
        /// An enumeration FileSpecs for the files to be embedded.
        ///
        /// Files in fileSpecs that are not bundled within the single file bundle,
        /// and should be published as separate files are marked as "IsExcluded" by this method.
        /// This doesn't include unbundled files that should be dropped, and not publised as output.
        /// </param>
        /// <returns>
        /// The full path the the generated bundle file
        /// </returns>
        /// <exceptions>
        /// ArgumentException if input is invalid
        /// IOExceptions and ArgumentExceptions from callees flow to the caller.
        /// </exceptions>
        public string GenerateBundle(IReadOnlyList <FileSpec> fileSpecs)
        {
            Tracer.Log($"Bundler version: {Manifest.CurrentVersion}");
            Tracer.Log($"Bundler Header: {BundleManifest.DesiredVersion}");
            Tracer.Log($"Target Runtime: {Target}");
            Tracer.Log($"Bundler Options: {Options}");

            if (fileSpecs.Any(x => !x.IsValid()))
            {
                throw new ArgumentException("Invalid input specification: Found entry with empty source-path or bundle-relative-path.");
            }

            string hostSource;

            try
            {
                hostSource = fileSpecs.Where(x => x.BundleRelativePath.Equals(HostName)).Single().SourcePath;
            }
            catch (InvalidOperationException)
            {
                throw new ArgumentException("Invalid input specification: Must specify the host binary");
            }

            if (fileSpecs.GroupBy(file => file.BundleRelativePath).Where(g => g.Count() > 1).Any())
            {
                throw new ArgumentException("Invalid input specification: Found multiple entries with the same BundleRelativePath");
            }

            string bundlePath = Path.Combine(OutputDir, HostName);

            if (File.Exists(bundlePath))
            {
                Tracer.Log($"Ovewriting existing File {bundlePath}");
            }

            BinaryUtils.CopyFile(hostSource, bundlePath);

            long headerOffset = 0;

            using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(bundlePath)))
            {
                Stream bundle = writer.BaseStream;
                bundle.Position = bundle.Length;

                foreach (var fileSpec in fileSpecs)
                {
                    string relativePath = fileSpec.BundleRelativePath;

                    if (IsHost(relativePath))
                    {
                        continue;
                    }

                    if (ShouldIgnore(relativePath))
                    {
                        Tracer.Log($"Ignore: {relativePath}");
                        continue;
                    }

                    FileType type = InferType(fileSpec);

                    if (ShouldExclude(type, relativePath))
                    {
                        Tracer.Log($"Exclude [{type}]: {relativePath}");
                        fileSpec.Excluded = true;
                        continue;
                    }

                    using (FileStream file = File.OpenRead(fileSpec.SourcePath))
                    {
                        FileType  targetType  = Target.TargetSpecificFileType(type);
                        long      startOffset = AddToBundle(bundle, file, targetType);
                        FileEntry entry       = BundleManifest.AddEntry(targetType, relativePath, startOffset, file.Length);
                        Tracer.Log($"Embed: {entry}");
                    }
                }

                // Write the bundle manifest
                headerOffset = BundleManifest.Write(writer);
                Tracer.Log($"Header Offset={headerOffset}");
                Tracer.Log($"Meta-data Size={writer.BaseStream.Position - headerOffset}");
                Tracer.Log($"Bundle: Path={bundlePath}, Size={bundle.Length}");
            }

            HostWriter.SetAsBundle(bundlePath, headerOffset);

            return(bundlePath);
        }
Example #24
0
        /// <summary>
        /// Generate a bundle, given the specification of embedded files
        /// </summary>
        /// <param name="fileSpecs">
        /// An enumeration FileSpecs for the files to be embedded.
        /// </param>
        /// <returns>
        /// The full path the the generated bundle file
        /// </returns>
        /// <exceptions>
        /// ArgumentException if input is invalid
        /// IOExceptions and ArgumentExceptions from callees flow to the caller.
        /// </exceptions>
        public string GenerateBundle(IReadOnlyList <FileSpec> fileSpecs)
        {
            trace.Log($"Bundler version {Version}");

            if (fileSpecs.Any(x => !x.IsValid()))
            {
                throw new ArgumentException("Invalid input specification: Found entry with empty source-path or bundle-relative-path.");
            }

            string hostSource;

            try
            {
                hostSource = fileSpecs.Where(x => x.BundleRelativePath.Equals(HostName)).Single().SourcePath;
            }
            catch (InvalidOperationException)
            {
                throw new ArgumentException("Invalid input specification: Must specify the host binary");
            }

            if (fileSpecs.GroupBy(file => file.BundleRelativePath).Where(g => g.Count() > 1).Any())
            {
                throw new ArgumentException("Invalid input specification: Found multiple entries with the same BundleRelativePath");
            }

            string bundlePath = Path.Combine(OutputDir, HostName);

            if (File.Exists(bundlePath))
            {
                trace.Log($"Ovewriting existing File {bundlePath}");
            }

            BinaryUtils.CopyFile(hostSource, bundlePath);

            long headerOffset = 0;

            using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(bundlePath)))
            {
                Stream bundle = writer.BaseStream;
                bundle.Position = bundle.Length;

                // Write the files from the specification into the bundle
                foreach (var fileSpec in fileSpecs)
                {
                    if (!ShouldEmbed(fileSpec.BundleRelativePath))
                    {
                        trace.Log($"Skip: {fileSpec.BundleRelativePath}");
                        continue;
                    }

                    using (FileStream file = File.OpenRead(fileSpec.SourcePath))
                    {
                        FileType  type        = InferType(fileSpec.BundleRelativePath, file);
                        long      startOffset = AddToBundle(bundle, file, type);
                        FileEntry entry       = BundleManifest.AddEntry(type, fileSpec.BundleRelativePath, startOffset, file.Length);
                        trace.Log($"Embed: {entry}");
                    }
                }

                // Write the bundle manifest
                headerOffset = BundleManifest.Write(writer);
                trace.Log($"Header Offset={headerOffset}");
                trace.Log($"Meta-data Size={writer.BaseStream.Position - headerOffset}");
                trace.Log($"Bundle: Path={bundlePath}, Size={bundle.Length}");
            }

            HostWriter.SetAsBundle(bundlePath, headerOffset);

            return(bundlePath);
        }
Example #25
0
        public static void Main(string[] args)
        {
            try
            {
                ParseArgs(args);
            }
            catch (ArgumentException e)
            {
                Fail("ERROR", e.Message);
                Usage();
                return;
            }

            if (NeedHelp)
            {
                Usage();
                return;
            }

            if (CreateHost)
            {
                HostWriter.CreateAppHost(Template, Path.Combine(SourceDir, Host), App);
            }

            if (!CreateBundle)
            {
                return;
            }

            Bundler bundler = new Bundler(Host, OutputDir, Options, OS, Framework, Diagnostics);

            // Get all files in the source directory and all sub-directories.
            string[] sources = Directory.GetFiles(SourceDir, searchPattern: "*", searchOption: SearchOption.AllDirectories);

            // Sort the file names to keep the bundle construction deterministic.
            Array.Sort(sources, StringComparer.Ordinal);

            List <FileSpec> fileSpecs = new List <FileSpec>(sources.Length);

            foreach (var file in sources)
            {
                fileSpecs.Add(new FileSpec(file, Path.GetRelativePath(SourceDir, file)));
            }

            bundler.GenerateBundle(fileSpecs);

            if (!CopyExcluded)
            {
                return;
            }

            foreach (var spec in fileSpecs)
            {
                if (spec.Excluded)
                {
                    var outputPath = Path.Combine(OutputDir, spec.BundleRelativePath);
                    Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
                    File.Copy(spec.SourcePath, outputPath, true);
                }
            }
        }
Example #26
0
 public void SetUp()
 {
     theWriter = new HostWriter(new TypeDescriptorCache());
 }
Example #27
0
        /// <summary>
        /// Generate a bundle, given the specification of embedded files
        /// </summary>
        /// <param name="fileSpecs">
        /// An enumeration FileSpecs for the files to be embedded.
        /// </param>
        /// <returns>
        /// The full path the the generated bundle file
        /// </returns>
        /// <exceptions>
        /// ArgumentException if input is invalid
        /// IOExceptions and ArgumentExceptions from callees flow to the caller.
        /// </exceptions>
        public string GenerateBundle(IReadOnlyList <FileSpec> fileSpecs)
        {
            trace.Log($"Bundler version {Version}");

            if (fileSpecs.Any(x => !x.IsValid()))
            {
                throw new ArgumentException("Invalid input specification: Found entry with empty source-path or bundle-relative-path.");
            }

            string hostSource;

            try
            {
                hostSource = fileSpecs.Where(x => x.BundleRelativePath.Equals(HostName)).Single().SourcePath;
            }
            catch (InvalidOperationException)
            {
                throw new ArgumentException("Invalid input specification: Must specify the host binary");
            }

            if (fileSpecs.GroupBy(file => file.BundleRelativePath).Where(g => g.Count() > 1).Any())
            {
                throw new ArgumentException("Invalid input specification: Found multiple entries with the same BundleRelativePath");
            }

            string bundlePath = Path.Combine(OutputDir, HostName);

            if (File.Exists(bundlePath))
            {
                trace.Log($"Ovewriting existing File {bundlePath}");
            }

            BinaryUtils.CopyFile(hostSource, bundlePath);

            long headerOffset = 0;

            using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(bundlePath)))
            {
                Stream bundle = writer.BaseStream;
                bundle.Position = bundle.Length;

                // Write the files from the specification into the bundle
                // Alignment:
                //   Assemblies are written aligned at "AssemblyAlignment" bytes to facilitate loading from bundle
                //   Remaining files (native binaries and other files) are written without alignment.
                //
                // The unaligned files are written first, followed by the aligned files,
                // and finally the bundle manifest.
                // TODO: Order file writes to minimize file size.

                List <Tuple <FileSpec, FileType> > ailgnedFiles = new List <Tuple <FileSpec, FileType> >();
                bool NeedsAlignment(FileType type) => type == FileType.IL || type == FileType.Ready2Run;

                foreach (var fileSpec in fileSpecs)
                {
                    if (!ShouldEmbed(fileSpec.BundleRelativePath))
                    {
                        trace.Log($"Skip: {fileSpec.BundleRelativePath}");
                        continue;
                    }

                    using (FileStream file = File.OpenRead(fileSpec.SourcePath))
                    {
                        FileType type = InferType(fileSpec.BundleRelativePath, file);

                        if (NeedsAlignment(type))
                        {
                            ailgnedFiles.Add(Tuple.Create(fileSpec, type));
                            continue;
                        }

                        long      startOffset = AddToBundle(bundle, file, shouldAlign: false);
                        FileEntry entry       = BundleManifest.AddEntry(type, fileSpec.BundleRelativePath, startOffset, file.Length);
                        trace.Log($"Embed: {entry}");
                    }
                }

                foreach (var tuple in ailgnedFiles)
                {
                    var fileSpec = tuple.Item1;
                    var type     = tuple.Item2;

                    using (FileStream file = File.OpenRead(fileSpec.SourcePath))
                    {
                        long      startOffset = AddToBundle(bundle, file, shouldAlign: true);
                        FileEntry entry       = BundleManifest.AddEntry(type, fileSpec.BundleRelativePath, startOffset, file.Length);
                        trace.Log($"Embed: {entry}");
                    }
                }

                // Write the bundle manifest
                headerOffset = BundleManifest.Write(writer);
                trace.Log($"Header Offset={headerOffset}");
                trace.Log($"Meta-data Size={writer.BaseStream.Position - headerOffset}");
                trace.Log($"Bundle: Path={bundlePath}, Size={bundle.Length}");
            }

            HostWriter.SetAsBundle(bundlePath, headerOffset);

            return(bundlePath);
        }