Skip to content

Add Bundler.ComputeBundleContents API#127962

Open
elinor-fung wants to merge 1 commit intodotnet:mainfrom
elinor-fung:compute-bundle-contents
Open

Add Bundler.ComputeBundleContents API#127962
elinor-fung wants to merge 1 commit intodotnet:mainfrom
elinor-fung:compute-bundle-contents

Conversation

@elinor-fung
Copy link
Copy Markdown
Member

Note

This PR description was generated with assistance from GitHub Copilot.

Addresses #124051

Adds an API to compute which files would be included in or excluded from a single-file bundle without actually generating the bundle. This enables the SDK to properly support build incrementality by determining bundle membership ahead of time.

New API

namespace Microsoft.NET.HostModel.Bundle;

public sealed class BundleContents
{
    public FileSpec Host { get; }
    public IReadOnlyList<FileSpec> IncludedFiles { get; }
    public IReadOnlyList<FileSpec> ExcludedFiles { get; }
}

public class Bundler
{
    public BundleContents ComputeBundleContents(IReadOnlyList<FileSpec> fileSpecs);
    public string GenerateBundle(BundleContents bundleContents);
}

Changes

  • Added BundleContents class with Host, IncludedFiles, and ExcludedFiles properties
  • Added Bundler.ComputeBundleContents() to determine bundle membership without generating the bundle
  • Added Bundler.GenerateBundle(BundleContents) overload to generate a bundle from pre-computed contents (skipping re-filtering and type inference)
  • Refactored existing GenerateBundle(IReadOnlyList<FileSpec>) to delegate through ComputeBundleContents
  • Moved host pre-filtering out of GetFilteredFileSpecs into both callers for clarity
  • Added tests for ComputeBundleContents covering all BundleOptions variants and round-trip equivalence with GenerateBundle

Add a public ComputeBundleContents method to the Bundler class that
allows callers to compute which files would be included in or excluded
from a bundle without actually generating the bundle. This enables the
SDK to properly support incrementality for bundle generation by knowing
the included/excluded file lists ahead of time.

- Add BundleContents class with Host, FilesToBundle, and ExcludedFromBundle
- Add ComputeBundleContents method that reuses existing filtering logic
- Add GenerateBundle(BundleContents) overload that skips filtering
- Remove IsHost check from GetFilteredFileSpecs (callers pre-filter)
- Add tests covering various BundleOptions, duplicates, error cases,
  and round-trip through GenerateBundle(BundleContents)

Fixes dotnet#124051

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke, @elinor-fung, @VSadov
See info in area-owners.md if you want to be subscribed.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new public API to the HostModel single-file bundler to precompute bundle membership (included vs. excluded files) without emitting a bundle, and refactors bundle generation to reuse that computation path.

Changes:

  • Introduces BundleContents (host + included/excluded file lists) as a new public type in Microsoft.NET.HostModel.Bundle.
  • Adds Bundler.ComputeBundleContents(IReadOnlyList<FileSpec>) and a Bundler.GenerateBundle(BundleContents) overload, and refactors GenerateBundle(IReadOnlyList<FileSpec>) to delegate through the new API.
  • Adds tests for ComputeBundleContents and a round-trip equivalence check between the old and new generation paths.
Show a summary per file
File Description
src/installer/tests/Microsoft.NET.HostModel.Tests/Bundle/BundlerConsistencyTests.cs Adds coverage for the new precompute API and compares manifest output between direct vs. precomputed generation.
src/installer/managed/Microsoft.NET.HostModel/Bundle/Bundler.cs Adds the precompute API, new overload, and refactors generation to reuse the computed contents.
src/installer/managed/Microsoft.NET.HostModel/Bundle/BundleContents.cs Adds the new public BundleContents type used to carry precomputed bundle membership.

Copilot's findings

  • Files reviewed: 3/3 changed files
  • Comments generated: 5

Comment on lines +11 to +26
public sealed class BundleContents
{
/// <summary>
/// The host binary that serves as the bundle container.
/// </summary>
public FileSpec Host { get; }

/// <summary>
/// Files that will be embedded in the bundle.
/// </summary>
public IReadOnlyList<FileSpec> IncludedFiles { get; }

/// <summary>
/// Files that are excluded from the bundle and should be published alongside the host.
/// </summary>
public IReadOnlyList<FileSpec> ExcludedFiles { get; }
Comment on lines +467 to +472
FileSpec? hostSpec = fileSpecs.FirstOrDefault(x => IsHost(x.BundleRelativePath));
if (hostSpec is null)
{
throw new ArgumentException("Invalid input specification: Must specify the host binary");
}

Comment on lines +460 to +478
public BundleContents ComputeBundleContents(IReadOnlyList<FileSpec> fileSpecs)
{
if (fileSpecs.Any(x => !x.IsValid()))
{
throw new ArgumentException("Invalid input specification: Found entry with empty source-path or bundle-relative-path.");
}

FileSpec? hostSpec = fileSpecs.FirstOrDefault(x => IsHost(x.BundleRelativePath));
if (hostSpec is null)
{
throw new ArgumentException("Invalid input specification: Must specify the host binary");
}

var included = GetFilteredFileSpecs(fileSpecs.Where(x => !IsHost(x.BundleRelativePath)));
return new BundleContents(
hostSpec,
included,
fileSpecs.Where(x => x.Excluded).ToArray());
}
Comment on lines +269 to +287
/// <summary>
/// Generate a bundle from the given <see cref="BundleContents"/>,
/// as computed by <see cref="ComputeBundleContents"/>.
/// </summary>
/// <param name="bundleContents">
/// The bundle contents from <see cref="ComputeBundleContents"/>.
/// </param>
/// <returns>
/// The full path of the generated bundle file.
/// </returns>
public string GenerateBundle(BundleContents bundleContents)
{
_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");
}

(FileSpec Spec, FileType Type)[] relativePathToSpec = GetFilteredFileSpecs(fileSpecs);
string hostSource = bundleContents.Host.SourcePath;
(FileSpec Spec, FileType Type)[] relativePathToSpec = bundleContents.TypedIncludedFiles;
Comment on lines +412 to +417
string directBundlePath = directBundler.GenerateBundle(fileSpecs);

// Generate bundle via ComputeBundleContents + GenerateBundle(BundleContents)
Bundler computedBundler = CreateBundlerInstance();
BundleContents contents = computedBundler.ComputeBundleContents(fileSpecs);
string computedBundlePath = computedBundler.GenerateBundle(contents);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants