Skip to content

Comments

Mill1x support#1892

Open
yisraelU wants to merge 14 commits intodisneystreaming:series/0.19from
yisraelU:Mill1xSupport
Open

Mill1x support#1892
yisraelU wants to merge 14 commits intodisneystreaming:series/0.19from
yisraelU:Mill1xSupport

Conversation

@yisraelU
Copy link
Contributor

@yisraelU yisraelU commented Feb 19, 2026

In effort to target Mill 1.x , we need to support Scala 3 in the codegen module which must also support Scala 2.12 .

A bunch of syntax changes were made to be compatible with Scala 3 .
VarArgs expansion is the only syntax that was not supported in Scala 2 , and therefore a config was added to silence the warning in the Scalac options

  • Updated changelog

Copy link

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 support for Mill 1.x build tool alongside existing Mill 0.11 and 0.12 support. It introduces platform-specific configurations to handle differences between Mill versions, particularly addressing Scala version requirements (Mill 1.x uses Scala 3.8.1 vs Scala 2.13 for legacy versions) and dependency management challenges. The changes also include Scala 3 compatibility improvements in the codegen module, updating type casting patterns and compiler options to work with newer Scala 3 versions, and refactoring string literal rendering to avoid scala-reflect dependency.

Changes:

  • Introduced MillPlatformConfig abstraction to handle version-specific Mill configurations including Scala versions, dependencies, and conflict resolution settings
  • Added Mill 1.x support with Scala 3.8.1, new mill-libs dependency, and classpath conflict suppression
  • Updated Scala 3 compiler options to support both 3.3.x (-Ykind-projector, -release 8) and 3.8.x (-Xkind-projector, -release 17, -Werror) versions
  • Refactored string literal rendering in codegen to use manual escaping instead of scala-reflect for Scala 3 compatibility
  • Added Mill 1.x specific source files with updated Mill API usage and test implementations

Reviewed changes

Copilot reviewed 13 out of 17 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
project/Smithy4sBuildPlugin.scala Introduces MillPlatformConfig abstraction and Mill 1.x support with platform-specific settings
project/Dependencies.scala Adds Mill 1.x mill-libs dependency with necessary exclusions
modules/mill-codegen-plugin/src-mill-1/smithy4s/codegen/mill/Smithy4sModule.scala Mill 1.x implementation of Smithy4s codegen plugin with updated Mill API
modules/mill-codegen-plugin/src-mill-1/smithy4s/codegen/LSP.scala Mill 1.x LSP configuration module using new Mill API
modules/mill-codegen-plugin/test/src-mill-1/smithy4s/codegen/mill/Smithy4sModuleSpec.scala Comprehensive test suite for Mill 1.x plugin functionality
modules/mill-codegen-plugin/test/src-mill-1/smithy4s/codegen/mill/SmithyLSPConfigSpec.scala Tests for LSP configuration generation in Mill 1.x
modules/mill-codegen-plugin/src-mill-shared/smithy4s/codegen/mill/Smithy4sModuleCommon.scala Simplified type matching using pattern matching instead of asInstanceOf
modules/codegen/src/smithy4s/codegen/internals/Renderer.scala Refactored string literal rendering to use custom escaping for Scala 3 compatibility
modules/codegen/src/smithy4s/codegen/internals/SmithyToIR.scala Added Scala 3-compatible type casting with @SuppressWarnings and @unchecked annotations
modules/codegen/src/smithy4s/codegen/transformers/ValidatedNewtypesTransformer.scala Updated type casting pattern for Scala 3 compatibility
modules/codegen/src-3/smithy4s/codegen/internal/MapTypeCompanionPlatform.scala New Scala 3-specific platform code for Map type companion
modules/codegen/test/src/smithy4s/codegen/internals/ScaladocSpec.scala Removed unused empty statement
build.sbt Updated codegen module to support Scala 3 and Mill Scala 3 versions with appropriate dependencies
modules/bootstrapped/src/generated/... Updated generated code with improved string escaping (contractions properly escaped)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

aws.auth.Sigv4(name = "dynamodb"),
aws.protocols.AwsJson1_0(http = None, eventStreamHttp = None),
Hints.dynamic(ShapeId("smithy.api", "documentation"), smithy4s.Document.fromString("<fullname>Amazon DynamoDB</fullname>\n\n\n <p>Amazon DynamoDB is a fully managed NoSQL database service that provides fast and\n predictable performance with seamless scalability. DynamoDB lets you offload the\n administrative burdens of operating and scaling a distributed database, so that you don\'t have\n to worry about hardware provisioning, setup and configuration, replication, software patching,\n or cluster scaling.</p>\n\n <p>With DynamoDB, you can create database tables that can store and retrieve any amount of\n data, and serve any level of request traffic. You can scale up or scale down your tables\'\n throughput capacity without downtime or performance degradation, and use the AWS Management\n Console to monitor resource utilization and performance metrics.</p>\n\n <p>DynamoDB automatically spreads the data and traffic for your tables over a sufficient\n number of servers to handle your throughput and storage requirements, while maintaining\n consistent and fast performance. All of your data is stored on solid state disks (SSDs) and\n automatically replicated across multiple Availability Zones in an AWS region, providing\n built-in high availability and data durability. </p>")),
Hints.dynamic(ShapeId("smithy.api", "documentation"), smithy4s.Document.fromString("<fullname>Amazon DynamoDB</fullname>\n\n\n <p>Amazon DynamoDB is a fully managed NoSQL database service that provides fast and\n predictable performance with seamless scalability. DynamoDB lets you offload the\n administrative burdens of operating and scaling a distributed database, so that you don't have\n to worry about hardware provisioning, setup and configuration, replication, software patching,\n or cluster scaling.</p>\n\n <p>With DynamoDB, you can create database tables that can store and retrieve any amount of\n data, and serve any level of request traffic. You can scale up or scale down your tables'\n throughput capacity without downtime or performance degradation, and use the AWS Management\n Console to monitor resource utilization and performance metrics.</p>\n\n <p>DynamoDB automatically spreads the data and traffic for your tables over a sufficient\n number of servers to handle your throughput and storage requirements, while maintaining\n consistent and fast performance. All of your data is stored on solid state disks (SSDs) and\n automatically replicated across multiple Availability Zones in an AWS region, providing\n built-in high availability and data durability. </p>")),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

we used to escape a single quote , which was not necessary

val id: ShapeId = ShapeId("smithy4s.example", "OrderType")

val hints: Hints = Hints(
Hints.dynamic(ShapeId("smithy.api", "documentation"), smithy4s.Document.fromString("Our order types have different ways to identify a product\nExcept for preview orders, these don\'t have an ID")),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

ditto

val id: ShapeId = ShapeId("smithy4s.example", "InStoreOrder")

val hints: Hints = Hints(
Hints.dynamic(ShapeId("smithy.api", "documentation"), smithy4s.Document.fromString("For an InStoreOrder a location ID isn\'t needed")),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

ditto

import LineSegment.NameRef

trait MapTypeCompanionPlatform {
val seqMapRef = NameRef("scala.collection.immutable.SeqMap")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

same as scala 2 , just for cross compilation purposes

@yisraelU yisraelU marked this pull request as ready for review February 19, 2026 20:40
})
}

private def escapeForStringLiteral(raw: String): String = {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This replaced the Scala 2 version of storing source code string literals without using reflection.

scalaVersion: String,
millVersions: Seq[String]
millVersions: Seq[String],
deps: ProjectMatrix*
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since sbt-projectmatrix uses exact Scala version matching for .dependsOn(), the Mill 1.x row (3.8.1) can't find a matching codegen row (3.3.6).
To work around this, millPlatforms now accepts project matrix dependencies explicitly and wires them per-row using depScalaVersion

Copy link
Contributor

@lewisjkl lewisjkl left a comment

Choose a reason for hiding this comment

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

Looks great, I think we should also update the documentation mill example to show how to setup for mill 1.x now as well

(duration: java.time.Duration) => line"$duration_(${renderStringLiteral(duration.toString)})"
case Primitive.Document => (node: Node) => renderNodeToLine(node)
case Primitive.Nothing => (_: Any) => sys.error("unreachable") // this case can't happen
}).asInstanceOf[T => Line]
Copy link

@rochala rochala Feb 20, 2026

Choose a reason for hiding this comment

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

This could be simplified by migrating away from Aux pattern of Primitive to normal parameterized trait like:

private[internals] sealed trait Primitive[T]
private[internals] object Primitive {
  case object Unit extends Primitive[Unit]
  case object Blob extends Primitive[Array[Byte]]

Then Scala 3 typechecker will be satisfied with

  private def renderPrimitive[T](prim: Primitive[T]): T => Line =
    prim match {
      case Primitive.BigDecimal =>
        bd => line"scala.math.BigDecimal(${bd.toString})"
      case Primitive.BigInteger => bi => line"scala.math.BigInt(${bi.toString})"
      case Primitive.Unit       => _ => line"()"
      case Primitive.Double     => t => line"${t.toString}d"
      case Primitive.Float      => t => line"${t.toString}f"
      case Primitive.Long       => t => line"${t.toString}L"
      case Primitive.Int        => t => line"${t.toString}"
      case Primitive.Short      => t => line"${t.toString}"
      case Primitive.Bool       => t => line"${t.toString}"
      case Primitive.Uuid       => uuid => line"java.util.UUID.fromString(${renderStringLiteral(uuid.toString)})"
      case Primitive.String     => s => renderStringLiteral(s)
      case Primitive.Byte       => b => line"${b.toString}"
      case Primitive.Blob =>
        ba =>
          val blob = NameRef("smithy4s", "Blob")
          if (ba.isEmpty) line"$blob.empty"
          else
            line"$blob(Array[Byte](${ba.mkString(", ")}))"
      case Primitive.Timestamp =>
        ts => line"${NameRef("smithy4s", "Timestamp")}(${ts.getEpochSecond()}L, ${ts.getNano()})"
      case Primitive.Document => renderNodeToLine(_)
      case Primitive.Nothing  => v => (v: Nothing)
    }

But confirm me here, I may be wrong why Aux pattern was used here in the first place.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the input. Yeah it might be possible to move this direction, I'm not entirely sure. The reason it was done with Aux originally iirc has to do with the usage of recursion schemes in SmithyToIr

private def unfoldNodeAndType(layer: NodeAndType): TypedNode[NodeAndType] =
and TypedNode. So I think it would be a larger refactor if we didn't have a GADT for primitive

Copy link
Contributor

@lewisjkl lewisjkl left a comment

Choose a reason for hiding this comment

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

We can leave the PR open for the weekend in case anyone else wants to review, but I think it's good to go 🎉

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.

3 participants