Skip to content

Conversation

@bripeticca
Copy link
Contributor

@bripeticca bripeticca commented Oct 21, 2025

This PR introduces the EnabledTrait model and associated infrastructure to properly track and manage trait enablement throughout the dependency resolution process. The new system replaces simple string-based trait tracking with a rich model that maintains provenance information about how and why each trait was enabled.

Core Changes

New EnabledTrait Model (EnabledTrait.swift)

Introduces three main components for tracking enabled traits for packages:

EnabledTrait - Represents an individual enabled trait with:

  • Trait name (used as the identifier)
  • Set of Setter values tracking how the trait was enabled (via command-line config, parent package, or another trait)
  • Support for unifying traits with the same name by merging their setter lists - it's important to note that this is usually done with an EnabledTrait for a specific package, and that many packages can define their own (unique) trait that shares a name with a trait from another package but are treated as fundamentally different instances.

EnabledTraits - Collection wrapper around IdentifiableSet<EnabledTrait> that:

  • Automatically unifies traits with the same name when inserted
  • Provides convenient set operations (union, intersection)
  • Maintains enablement metadata while treating traits with the same name as equal (merges Setters for traits of the same name)

EnabledTraitsMap - Thread-safe map storing enabled traits per package that:

  • Maps PackageIdentity to EnabledTraits
  • Implicitly returns ["default"] for packages with no explicitly enabled traits
  • Uses union behaviour: setting traits for the same package multiple times accumulates all traits
  • Tracks disablers to distinguish "no explicitly enabled traits" from "default traits explicitly disabled"

The introduction of these models have also provided some API conveniences:

String Interoperability - EnabledTrait and EnabledTraits provide seamless string integration:

  • ExpressibleByStringLiteral and ExpressibleByArrayLiteral for natural initialization of enabled traits and sets of enabled traits: let enabledTrait: EnabledTrait = "foo" or let enabledTraits: EnabledTraits = ["foo", "bar"]
  • Bidirectional string equality: enabledTrait == "foo" and "foo" == enabledTrait both work
  • EnabledTraitConvertible protocol allows APIs to accept string collections and auto-convert them to EnabledTrait

Collection Protocol Support - EnabledTraits conforms to Collection with set-like operations (union, intersection, contains) and overloaded methods that accept either Collection<String> or Collection<EnabledTrait>, enabling flexible APIs that work with both types

Refactored Trait Handling (Manifest+Traits.swift)

  • Replaced all Set<String> trait representations with richer EnabledTraits model
  • Updated validation methods to work with EnabledTrait instead of raw strings
  • Removed parentPackage parameters from validation (now tracked via EnabledTrait.Setter)
  • Improved type safety and metadata tracking throughout trait calculation

Workspace+Traits.swift (new file)

  • Extracted trait-specific workspace operations
  • updateEnabledTraits(for:) method determines enabled traits for loaded manifests
  • updateEnabledTraits(forDependency:_:) handles dependency-specific trait propagation

Bug Fixes

Required Dependencies Calculation

Problem: During PubGrub dependency resolution, enabled traits were being reset to defaults instead of propagating previously calculated values when creating DependencyResolutionNode instances, which relied on an accurate set of enabledTraits.

Impact: Wrong trait sets were considered when evaluating required dependencies, leading to incorrect dependency graphs.

Solution: Properly propagate enabled traits through the nodes() method for PackageContainerConstraint, which creates instances of DependencyResolutionNode from these constraints during PubGrub dependency resolution.

IdentifiableSet Intersection

Problem: The intersection method created an empty set instead of starting with self, causing loss of element data.

Solution: Changed var result = Self() to var result = self in IdentifiableSet.swift:86, ensuring proper preservation of element data during intersections.

Test Improvements

  • Separated traits tests into dedicated files:
    • ModulesGraphTests+Traits.swift
    • WorkspaceTests+Traits.swift
    • EnabledTraitTests.swift

Additional Improvements

  • Enhanced IdentifiableSet with remove(id:) and remove(_:) methods
  • Added Workspace+Traits.swift for trait-specific workspace operations
  • Updated all package container implementations for proper trait handling

The enabled traits property was essentially being erased to defaults
rather than propagating what was calculated earlier due to how we
were returning the DependencyResolutionNode during the pub grub
stage.

This fix should now assure that we are passing the appropriate
enabled traits to these nodes, and are being considered when making
calls to PackageContainer's dependency-related methods.
* an EnabledTrait model has been created to store additional
  data about an enabled trait, namely the origins of its enablement.
* EnabledTraits is a wrapper for a Set<EnabledTrait> that handles the
  special edge cases when dealing with traits.
* move traits-related tests in the WorkspaceTests to their own file
* Add new file to PackageModelTests/ for EnabledTrait tests
  - Tests EnabledTrait, EnabledTraits, and EnabledTraitsMap
  - Extensions to EnabledTraits added as helpers for testing
* Cleanup some TODOs; added some other prints to help debug, will
  remove once ready for review + tests passing
@bripeticca
Copy link
Contributor Author

@swift-ci please test

@bripeticca
Copy link
Contributor Author

@swift-ci please test

@bripeticca
Copy link
Contributor Author

@swift-ci please test

* Augment EnabledTraits and EnabledTraitsMap to consider the case
  where a package's default traits are disabled through some parent
  configuration.
* Update methods in Manifest+Traits.swift
* Add tests for disable default traits behaviours in EnabledTraitTests.swift
  and WorkspaceTests+Traits.swift.
@bripeticca
Copy link
Contributor Author

@swift-ci please test

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant