@@ -31,15 +31,20 @@ val usageMessage = """
31
31
|The optional <bisect-options> may be any combination of:
32
32
|* --dry-run
33
33
| Don't try to bisect - just make sure the validation command works correctly
34
+ |
34
35
|* --releases <releases-range>
35
36
| Bisect only releases from the given range (defaults to all releases).
36
37
| The range format is <first>...<last>, where both <first> and <last> are optional, e.g.
37
38
| * 3.1.0-RC1-bin-20210827-427d313-NIGHTLY..3.2.1-RC1-bin-20220716-bb9c8ff-NIGHTLY
38
39
| * 3.2.1-RC1-bin-20220620-de3a82c-NIGHTLY..
39
40
| * ..3.3.0-RC1-bin-20221124-e25362d-NIGHTLY
40
41
| The ranges are treated as inclusive.
42
+ |
41
43
|* --bootstrapped
42
- | Publish locally and test a bootstrapped compiler rather than a nonboostrapped one
44
+ | Publish locally and test a bootstrapped compiler rather than a nonboostrapped one.
45
+ |
46
+ |* --should-fail
47
+ | Expect the validation command to fail rather that succeed. This can be used e.g. to find out when some illegal code started to compile.
43
48
|
44
49
|Warning: The bisect script should not be run multiple times in parallel because of a potential race condition while publishing artifacts locally.
45
50
@@ -54,7 +59,7 @@ val usageMessage = """
54
59
55
60
val validationScript = scriptOptions.validationCommand.validationScript
56
61
val releases = Releases .fromRange(scriptOptions.releasesRange)
57
- val releaseBisect = ReleaseBisect (validationScript, releases)
62
+ val releaseBisect = ReleaseBisect (validationScript, shouldFail = scriptOptions.shouldFail, releases)
58
63
59
64
releaseBisect.verifyEdgeReleases()
60
65
@@ -64,18 +69,19 @@ val usageMessage = """
64
69
println(s " First bad release: ${firstBadRelease.version}" )
65
70
println(" \n Finished bisecting releases\n " )
66
71
67
- val commitBisect = CommitBisect (validationScript, bootstrapped = scriptOptions.bootstrapped, lastGoodRelease.hash, firstBadRelease.hash)
72
+ val commitBisect = CommitBisect (validationScript, shouldFail = scriptOptions.shouldFail, bootstrapped = scriptOptions.bootstrapped, lastGoodRelease.hash, firstBadRelease.hash)
68
73
commitBisect.bisect()
69
74
70
75
71
- case class ScriptOptions (validationCommand : ValidationCommand , dryRun : Boolean , bootstrapped : Boolean , releasesRange : ReleasesRange )
76
+ case class ScriptOptions (validationCommand : ValidationCommand , dryRun : Boolean , bootstrapped : Boolean , releasesRange : ReleasesRange , shouldFail : Boolean )
72
77
object ScriptOptions :
73
78
def fromArgs (args : Seq [String ]) =
74
79
val defaultOptions = ScriptOptions (
75
80
validationCommand = null ,
76
81
dryRun = false ,
77
82
bootstrapped = false ,
78
- ReleasesRange (first = None , last = None )
83
+ ReleasesRange (first = None , last = None ),
84
+ shouldFail = false
79
85
)
80
86
parseArgs(args, defaultOptions)
81
87
@@ -86,6 +92,7 @@ object ScriptOptions:
86
92
case " --releases" :: argsRest =>
87
93
val range = ReleasesRange .tryParse(argsRest.head).get
88
94
parseArgs(argsRest.tail, options.copy(releasesRange = range))
95
+ case " --should-fail" :: argsRest => parseArgs(argsRest, options.copy(shouldFail = true ))
89
96
case _ =>
90
97
val command = ValidationCommand .fromArgs(args)
91
98
options.copy(validationCommand = command)
@@ -182,7 +189,7 @@ case class Release(version: String):
182
189
override def toString : String = version
183
190
184
191
185
- class ReleaseBisect (validationScript : File , allReleases : Vector [Release ]):
192
+ class ReleaseBisect (validationScript : File , shouldFail : Boolean , allReleases : Vector [Release ]):
186
193
assert(allReleases.length > 1 , " Need at least 2 releases to bisect" )
187
194
188
195
private val isGoodReleaseCache = collection.mutable.Map .empty[Release , Boolean ]
@@ -217,21 +224,22 @@ class ReleaseBisect(validationScript: File, allReleases: Vector[Release]):
217
224
isGoodReleaseCache.getOrElseUpdate(release, {
218
225
println(s " Testing ${release.version}" )
219
226
val result = Seq (validationScript.getAbsolutePath, release.version).!
220
- val isGood = result == 0
227
+ val isGood = if (shouldFail) result != 0 else result == 0 // invert the process status if failure was expected
221
228
println(s " Test result: ${release.version} is a ${if isGood then " good" else " bad" } release \n " )
222
229
isGood
223
230
})
224
231
225
- class CommitBisect (validationScript : File , bootstrapped : Boolean , lastGoodHash : String , fistBadHash : String ):
232
+ class CommitBisect (validationScript : File , shouldFail : Boolean , bootstrapped : Boolean , lastGoodHash : String , fistBadHash : String ):
226
233
def bisect (): Unit =
227
234
println(s " Starting bisecting commits $lastGoodHash.. $fistBadHash\n " )
228
235
val scala3CompilerProject = if bootstrapped then " scala3-compiler-bootstrapped" else " scala3-compiler"
229
236
val scala3Project = if bootstrapped then " scala3-bootstrapped" else " scala3"
237
+ val validationCommandStatusModifier = if shouldFail then " ! " else " " // invert the process status if failure was expected
230
238
val bisectRunScript = s """
231
239
|scalaVersion= $$ (sbt "print ${scala3CompilerProject}/version" | tail -n1)
232
240
|rm -r out
233
241
|sbt "clean; ${scala3Project}/publishLocal"
234
- | ${validationScript.getAbsolutePath} " $$ scalaVersion"
242
+ | ${validationCommandStatusModifier}${ validationScript.getAbsolutePath} " $$ scalaVersion"
235
243
""" .stripMargin
236
244
" git bisect start" .!
237
245
s " git bisect bad $fistBadHash" .!
0 commit comments