@@ -26,65 +26,34 @@ import scala.reflect.macros.whitebox
2626import org .semver4j .{RangesListFactory , Semver }
2727
2828/**
29- * Shared machinery behind the conditional compilation based on version annotation families:
30- * - single dimension: [[enableIfVer ]] (exactly one dimension, e.g. `@enableIfVer(spark =
31- * ">=3.5.0")`)
32- * - multi-dimension combinators: [[enableIfAllVer ]] (every listed dimension must match) and
33- * [[enableIfAnyVer ]] (at least one must match).
29+ * Shared machinery behind the version annotations [[enableIfVer ]], [[implementIfVer ]] and
30+ * [[enableOverrideIfVer ]].
3431 *
35- * Every annotation performs the same compile-time tree rewrite (drop / empty-body-except-marked /
36- * strip- `override`). the ONLY thing that differs is a single `Boolean` - does the build's
37- * targeted version(s) satisfy the given range(s) ? So the match decision is computed per
38- * annotation and handed to the shared expansions here.
32+ * Every annotation performs the same compile-time tree rewrite (drop / empty-body / strip
33+ * `override`). the ONLY thing that differs is a single `Boolean` - does the build's targeted
34+ * version satisfy the given range? So the match decision is computed per annotation and handed to
35+ * the shared expansions here.
3936 *
4037 * The build feeds the targeted version of each dimension to the macro via
4138 * `-Xmacro-settings:enableIfVer.<dimension>=<version>`, read at expansion time from `c.settings`
4239 * (see [[versionOf ]]). Adding a dimension is therefore just one more build entry - no generated
43- * sources. Comet currently configures a single dimension, `spark`. A dimension may be configured
44- * as `<none>` ([[NONE_VERSION ]]) meaning "known to the build but not enabled".
40+ * sources. Comet currently configures a single dimension, `spark`.
4541 *
4642 * Ranges are matched by <a href="https://github.com/semver4j/semver4j">semver4j</a>: full
4743 * `major.minor.patch` versions with `>` `>=` `<` `<=` `=` `!=`, space = AND, `||` = OR, `A - B`
48- * hyphen ranges and more. The literal range `"<none>"` is special - it matches iff the dimension
49- * is configured but disabled (see [[satisfies ]]).
44+ * hyphen ranges and more.
5045 */
5146object EnableIfVerSupport {
5247
53- /**
54- * Sentinel for a dimension that is known to the build but not enabled. As a configured value it
55- * resolves to `None`. as a range (`@enableIfVer(spark = "<none>")`) it matches only such
56- * disabled dimensions.
57- */
58- val NONE_VERSION = " <none>"
59-
60- /**
61- * Does the configured `version` satisfy `range`?
62- * - `range == "<none>"` matches iff the dimension is disabled (`version` is `None`).
63- * - any other range never matches a disabled dimension.
64- * - otherwise semver logic.
65- */
66- def satisfies (c : whitebox.Context , range : String , version : Option [String ]): Boolean =
67- (range.trim, version) match {
68- case (NONE_VERSION , None ) => true
69- case (NONE_VERSION , Some (_)) => false
70- case (_, None ) => false
71- case (range, _) if range.contains(NONE_VERSION ) =>
72- c.abort(
73- c.enclosingPosition,
74- s " $NONE_VERSION is not supported with ranges, use semverAny* and " +
75- s """ pass $NONE_VERSION explicitly (e.g. """ +
76- s """ @enableIfAnyVer(spark = "<3.5.0", spark = " $NONE_VERSION")) """ )
77- case (range, Some (version)) =>
78- val rangeList = RangesListFactory .create(range)
79-
80- new Semver (version).satisfies(rangeList)
81- }
48+ /** Does the configured `version` satisfy the semver `range`? */
49+ def satisfies (range : String , version : String ): Boolean =
50+ new Semver (version).satisfies(RangesListFactory .create(range.trim))
8251
8352 /** Prefix of the `-Xmacro-settings` keys this macro understands. */
8453 private val SettingPrefix = " enableIfVer."
8554
8655 /** Parse `enableIfVer.<dimension>=<version>` entries out of `-Xmacro-settings`. */
87- private def configuredVersions (c : whitebox.Context ): Map [String , Option [ String ] ] =
56+ private def configuredVersions (c : whitebox.Context ): Map [String , String ] =
8857 c.settings.collect {
8958 case s if s.startsWith(SettingPrefix ) =>
9059 s.stripPrefix(SettingPrefix ).split(" =" , 2 ) match {
@@ -97,20 +66,18 @@ object EnableIfVerSupport {
9766 }
9867 }.toMap
9968
100- private def parseVersionFromSetting (name : String , version : String ): Option [String ] = {
101- if (version == NONE_VERSION ) return None
102-
69+ private def parseVersionFromSetting (name : String , version : String ): String = {
10370 try {
10471 // Not using Semver.parse as it will return null instead of giving us meaningful
10572 // exceptions on invalid input
10673 new Semver (version)
10774
108- Some ( version)
75+ version
10976 } catch {
11077 case e : Throwable =>
11178 sys.error(
112- s " malformed version passed in macro setting for ' $name', expected either a valid " +
113- s " SemVer or ' $NONE_VERSION ' got ' $version' (error: ${e.toString}) " )
79+ s " malformed version passed in macro setting for ' $name', expected a valid " +
80+ s " SemVer got ' $version' (error: ${e.toString}) " )
11481 }
11582 }
11683
@@ -119,14 +86,13 @@ object EnableIfVerSupport {
11986 * extensibility seam: to add a dimension, pass
12087 * `-Xmacro-settings:${SettingPrefix}<dimension>=<version>` from the build.
12188 *
122- * Returns `None` when the dimension is configured as `<none>` (known but disabled), and aborts
123- * compilation when the dimension was not configured at all.
89+ * Aborts compilation when the dimension was not configured at all.
12490 */
125- private def versionOf (c : whitebox.Context , dimension : String ): Option [ String ] = {
91+ private def versionOf (c : whitebox.Context , dimension : String ): String = {
12692 val versions = configuredVersions(c)
12793 versions.getOrElse(
12894 dimension,
129- // we do not treat missing dimension as None since we want to avoid having silent failures
95+ // we do not treat a missing dimension as a match since we want to avoid silent failures
13096 c.abort(
13197 c.enclosingPosition,
13298 s " @enableIfVer: no version configured for dimension ' $dimension' " +
@@ -138,10 +104,10 @@ object EnableIfVerSupport {
138104 object Macros {
139105
140106 /**
141- * Extract the named `dimension = range` arguments of a combined annotation. A named argument
142- * is `name = value`, whose immediate children are `[Ident(name), value]` - matched
143- * structurally so this works on both Scala 2.12 (`AssignOrNamedArg`) and 2.13 (`NamedArg`). A
144- * positional arg (e.g. a bare literal) has no such children and is rejected.
107+ * Extract the named `dimension = range` argument of a version annotation. A named argument is
108+ * `name = value`, whose immediate children are `[Ident(name), value]` - matched structurally
109+ * so this works on both Scala 2.12 (`AssignOrNamedArg`) and 2.13 (`NamedArg`). A positional
110+ * arg (e.g. a bare literal) has no such children and is rejected.
145111 */
146112 private def namedRanges (c : whitebox.Context ): List [(String , String )] = {
147113 import c .universe ._
@@ -162,33 +128,18 @@ object EnableIfVerSupport {
162128 }
163129 }
164130
165- /** Single dimension: require exactly one named arg and match it . */
131+ /** Require exactly one named dimension arg and return whether the build matches its range . */
166132 def singleKeep (c : whitebox.Context , specificMacroPrefix : String ): Boolean = {
167133 val ranges = namedRanges(c)
168134 if (ranges.size != 1 ) {
169135 val ifCase = if (specificMacroPrefix.isEmpty) " enableIf" else " If"
170136 c.abort(
171137 c.enclosingPosition,
172138 s " @ ${specificMacroPrefix}${ifCase}Ver accepts exactly one dimension " +
173- s " (got ${ranges.size}). use @ ${specificMacroPrefix}${ifCase}AllVer / " +
174- s " @ ${specificMacroPrefix}${ifCase}AnyVer for multiple dimensions. " )
139+ s " (got ${ranges.size}). " )
175140 }
176141 val (dim, range) = ranges.head
177- satisfies(c, range, versionOf(c, dim))
178- }
179-
180- /** Combined AND: keep iff every provided dimension matches (requires at least one). */
181- def allKeep (c : whitebox.Context ): Boolean = {
182- val ranges = namedRanges(c)
183- ranges.nonEmpty && ranges.forall { case (dim, range) =>
184- satisfies(c, range, versionOf(c, dim))
185- }
186- }
187-
188- /** Combined OR: keep iff at least one provided dimension matches. */
189- def anyKeep (c : whitebox.Context ): Boolean = {
190- val ranges = namedRanges(c)
191- ranges.exists { case (dim, range) => satisfies(c, range, versionOf(c, dim)) }
142+ satisfies(range, versionOf(c, dim))
192143 }
193144
194145 // ----- generic tree-rewrite expansions (take a precomputed keep) ---------------------------
@@ -245,10 +196,6 @@ object EnableIfVerSupport {
245196 }
246197}
247198
248- // =================================================================================================
249- // Single dimension
250- // =================================================================================================
251-
252199object enableIfVer {
253200 object Macros {
254201 def verEnable (c : whitebox.Context )(annottees : c.Expr [Any ]* ): c.Expr [Any ] =
@@ -264,9 +211,8 @@ object enableIfVer {
264211
265212/**
266213 * Keep the annotated member only when the build matches a single dimension's range. otherwise
267- * drop it entirely. Exactly one dimension must be given as a named semver range (use
268- * [[enableIfAllVer ]] / [[enableIfAnyVer ]] for multiple). Known dimensions are whatever the build
269- * configures via `-Xmacro-settings` (currently `spark`).
214+ * drop it entirely. Exactly one dimension must be given as a named semver range. Known dimensions
215+ * are whatever the build configures via `-Xmacro-settings` (currently `spark`).
270216 *
271217 * Example:
272218 * {{{
@@ -351,96 +297,3 @@ final class implementIfVer(spark: String = "") extends StaticAnnotation {
351297final class enableOverrideIfVer (spark : String = " " ) extends StaticAnnotation {
352298 def macroTransform (annottees : Any * ): Any = macro enableIfVer.Macros .verEnableOverride
353299}
354-
355- // =================================================================================================
356- // Combined: ALL dimensions must match (AND)
357- // =================================================================================================
358-
359- object enableIfAllVer {
360- object Macros {
361- def verEnable (c : whitebox.Context )(annottees : c.Expr [Any ]* ): c.Expr [Any ] =
362- EnableIfVerSupport .Macros .enable(c)(annottees)(EnableIfVerSupport .Macros .allKeep(c))
363- def verImplementIf (c : whitebox.Context )(annottees : c.Expr [Any ]* ): c.Expr [Any ] =
364- EnableIfVerSupport .Macros .implementIf(c)(annottees)(EnableIfVerSupport .Macros .allKeep(c))
365- def verEnableOverride (c : whitebox.Context )(annottees : c.Expr [Any ]* ): c.Expr [Any ] =
366- EnableIfVerSupport .Macros .enableOverride(c)(annottees)(EnableIfVerSupport .Macros .allKeep(c))
367- }
368- }
369-
370- /**
371- * Keep the annotated member only when the build matches EVERY listed dimension (logical AND).
372- * Dimensions are named semver ranges. omitted dimensions are not constrained, and within a single
373- * dimension you can still use `||` for OR. Arguments must be named. Comet currently configures
374- * only the `spark` dimension; this combinator exists for builds that add more.
375- *
376- * Example:
377- * {{{
378- * @enableIfAllVer(spark = ">=3.5.0") // keep on Spark 3.5+
379- * def needsSpark35(): Unit = ...
380- * }}}
381- */
382- @ nowarn(" cat=unused" ) // params are used by the macro
383- @ compileTimeOnly(" enable macro paradise to expand macro annotations" )
384- final class enableIfAllVer (spark : String = " " ) extends StaticAnnotation {
385- def macroTransform (annottees : Any * ): Any = macro enableIfAllVer.Macros .verEnable
386- }
387-
388- /** Like [[implementIfVer ]] but with all condition like [[enableIfAllVer ]]. */
389- @ nowarn(" cat=unused" ) // params are used by the macro
390- @ compileTimeOnly(" enable macro paradise to expand macro annotations" )
391- final class implementIfAllVer (spark : String = " " ) extends StaticAnnotation {
392- def macroTransform (annottees : Any * ): Any = macro enableIfAllVer.Macros .verImplementIf
393- }
394-
395- /** Like [[enableIfAllVer ]], but strips the `override` modifier on a non-matching combination. */
396- @ nowarn(" cat=unused" ) // params are used by the macro
397- @ compileTimeOnly(" enable macro paradise to expand macro annotations" )
398- final class enableOverrideIfAllVer (spark : String = " " ) extends StaticAnnotation {
399- def macroTransform (annottees : Any * ): Any = macro enableIfAllVer.Macros .verEnableOverride
400- }
401-
402- // =================================================================================================
403- // Combined: ANY dimension matches (OR)
404- // =================================================================================================
405-
406- object enableIfAnyVer {
407- object Macros {
408- def verEnable (c : whitebox.Context )(annottees : c.Expr [Any ]* ): c.Expr [Any ] =
409- EnableIfVerSupport .Macros .enable(c)(annottees)(EnableIfVerSupport .Macros .anyKeep(c))
410- def implementIf (c : whitebox.Context )(annottees : c.Expr [Any ]* ): c.Expr [Any ] =
411- EnableIfVerSupport .Macros .implementIf(c)(annottees)(EnableIfVerSupport .Macros .anyKeep(c))
412- def verEnableOverride (c : whitebox.Context )(annottees : c.Expr [Any ]* ): c.Expr [Any ] =
413- EnableIfVerSupport .Macros .enableOverride(c)(annottees)(EnableIfVerSupport .Macros .anyKeep(c))
414- }
415- }
416-
417- /**
418- * Keep the annotated member when the build matches AT LEAST ONE listed dimension (logical OR).
419- * Arguments must be named. Comet currently configures only the `spark` dimension; this combinator
420- * exists for builds that add more.
421- *
422- * Example:
423- * {{{
424- * @enableIfAnyVer(spark = ">=4.0.0") // keep on Spark 4+
425- * def newApi(): Unit = ...
426- * }}}
427- */
428- @ nowarn(" cat=unused" ) // params are used by the macro
429- @ compileTimeOnly(" enable macro paradise to expand macro annotations" )
430- final class enableIfAnyVer (spark : String = " " ) extends StaticAnnotation {
431- def macroTransform (annottees : Any * ): Any = macro enableIfAnyVer.Macros .verEnable
432- }
433-
434- /** Like [[implementIfVer ]] but with any condition like [[enableIfAnyVer ]]. */
435- @ nowarn(" cat=unused" ) // params are used by the macro
436- @ compileTimeOnly(" enable macro paradise to expand macro annotations" )
437- final class implementIfAnyVer (spark : String = " " ) extends StaticAnnotation {
438- def macroTransform (annottees : Any * ): Any = macro enableIfAnyVer.Macros .implementIf
439- }
440-
441- /** Like [[enableIfAnyVer ]], but strips the `override` modifier on a non-matching combination. */
442- @ nowarn(" cat=unused" ) // params are used by the macro
443- @ compileTimeOnly(" enable macro paradise to expand macro annotations" )
444- final class enableOverrideIfAnyVer (spark : String = " " ) extends StaticAnnotation {
445- def macroTransform (annottees : Any * ): Any = macro enableIfAnyVer.Macros .verEnableOverride
446- }
0 commit comments