Skip to content

v2.0.0-M2

Pre-release
Pre-release
Compare
Choose a tag to compare
@ckipp01 ckipp01 released this 31 Oct 19:40
· 123 commits to main since this release
df1149b

This marks the first usable Milestone release for the 2.x series of scoverage!

Before going over any details, I don't want to miss an opportunity to thank @sksamuel, @gslowikowski, and others for their incredible work on scoverage over the years. Countless Scala devs rely on scoverage as their go-to Scala code coverage tooling, with over 1.5 million downloads a month. I'm happy they've trusted me with trying to usher in a 2.0.0 release that also unlocks code coverage for Scala 3.

These notes will be most useful for those interfacing with scoverage and for those that are just generally curious about how all this works. For users of the plugins, like sbt-scoverage for example, you should notice no difference. All this will be abstracted away. Also note that the Milestone releases are attached to the V2 branch, not main.

Intro to the changes

Support for code coverage in Scala 3 will be built right into the compiler. This is fantastic in order to ensure we don't need to publish support for every single release of Scala 3 and it also helps quickly catch regressions and additions that might be harder to spot if the tooling was outside of the compiler. With that being said, we still have Scala 2 to support, and the mechanism for aggregating, deserializing, and reporting will be identical for Scala 2 and Scala 3 meaning their's a lot of code we'd like to re-use in scoverage between 2 and 3. This introduces a bit of a problem right off the bat as scoverage is a compiler plugin, and everything except for the runtime was packaged together. If you're unfamiliar with how scoverage works, the general flow is something like this:

  • scoverage plugin is included in your scalacOptions and passed into the compiler
  • during compilation your AST is traversed and instrumented inserting method calls to an invoker that trigger every time they are hit
// For example here we are wrapping the thenp and elsep in an If tree
case i: If =>
  treeCopy.If(
    i,
    process(i.cond),
    instrument(process(i.thenp), i.thenp, branch = true),
    instrument(process(i.elsep), i.elsep, branch = true)
  )
  • all the instrumented parts of your code are written to an scoverage.coverage file
  • after our code is instrumented during runtime when your tests are ran, the invoker is called writing to disk the ids the correspond to what what we instrumented and writing those to disk
  • the reporting then picks up those ids, matching them to the ids in the scoverage.coverage file and creates your report

In Scala 3 the plan is basically the same, but done in the compiler right up until the last step. Then the shared code will be in charge of reading the files, deserializing them, and creating the reports. There are breaking changes that were needed to accomplish this.

Breaking changes

There are a variety of breaking changes necessary in order to make this happen. Since we were making breaking changes I also took the liberty to tackle a few other small things that were in themselves technically breaking changes.

Dropping support for Scala 2.11

Starting in the scoverage 2.x release, Scala 2.11 will no longer be supported. The numbers are already quite low, and if you're stuck on Scala 2.11, there is really no need to bump the version of your scoverage. You should be able to continue to use the old version with the old version of whatever build tool plugin you're using. Plus, if you're still on 2.11 I doubt you're on the cutting edge of plugins anyways.

Default to utf-8 all over the place

Previously for some of the serialization/deserialization, utf-8 was not always defaulted to. Some of the writers allowed for you to pass this in as a parameter whereas all others defaulted to the system settings. In the 2.x series all writers will default to utf-8 unless another encoding is passed in.

Coverage data format version 3 and relative paths

The format version is basically identical to version 2, but the source path is now relative. You can see some more context for why this was done in #275, but what this means for plugins is that instead of just passing in a dataDir for scoverage you'll now also need to pass in a sourceRoot to correctly translate paths in the serialization/deserialization process.

New artifact structure

In the past there were just two artifacts being produced from scoverage:

  1. scalac-scoverage-plugin
  2. scalac-scoverage-runtime (JVM and JS)

In 2.x this has now changed to the following:

  1. scalac-scoverage-domain (2.12, 2.13, 3)
  2. scalac-scoverage-serializer (2.12, 2.13, 3)
  3. scalac-scoverage-reporter (2.12, 2.13, 3)
  4. scalac-scoverage-plugin (for all supported 2.12 and 2.13 versions full cross version)
  5. scalac-scoverage-runtime (2.12, 2.13) (JVM and JS)

There will be a few changes that need to happen in order to correctly use the plugin for 2.12 and 2.13. Mainly, you'll need to ensure that the path being passed into -Xplugin: will need to include plugin:domain:serializer.

For all deserializing and reporting, you'll now rely on scalac-scoverage-domain for you domain classes like Coverage, scalac-scoverage-serialize for your deserializing methods, and scalac-scoverage-reporter for your various reporting methods.

This fine grained split is quite a change, but it allows for a couple things:

  1. deserializing and reporting to be fully re-used by Scala 2 and Scala 3
  2. scala-xml will no longer be added to your compile classpath like it was previously, but only being used by your plugin for reporting

Next steps

This is the first step that will set everything else in motion. In the coming week or so you'll see the following:

  1. sbt-scoverage Milestone released with all the above changes. This is to ensure everything continues to work for those 2.12 and 2.13 users. The goal is for none of them to even notice. Secondly, this release has hardcoded support for 3.1.2-RC1-bin-SNAPSHOT which will need to be published locally from the pr I'll also send in.
  2. A pr will be introduced to lampepfl/dotty. There are still things that still need to be polished, but the core is working and ready for feedback and iteration. (scala/scala3#13880)
  3. Once 2 is merged in, scoverage will move to a RC release where things should freeze until the first release of Dotty with coverage support. Once that happens, 2.0.0 will be released.
  4. During all the above I hope to also help figure out what's needed for the other build tools in the ecosystem.

If you're still reading, thanks for following along. This has been quite a bit more work than originally expected, but I'm excited to start seeing the light at he end of the tunnel.

Cheers.