@@ -31,15 +31,20 @@ val usageMessage = """
3131 |The optional <bisect-options> may be any combination of:
3232 |* --dry-run
3333 | Don't try to bisect - just make sure the validation command works correctly
34+ |
3435 |* --releases <releases-range>
3536 | Bisect only releases from the given range (defaults to all releases).
3637 | The range format is <first>...<last>, where both <first> and <last> are optional, e.g.
3738 | * 3.1.0-RC1-bin-20210827-427d313-NIGHTLY..3.2.1-RC1-bin-20220716-bb9c8ff-NIGHTLY
3839 | * 3.2.1-RC1-bin-20220620-de3a82c-NIGHTLY..
3940 | * ..3.3.0-RC1-bin-20221124-e25362d-NIGHTLY
4041 | The ranges are treated as inclusive.
42+ |
4143 |* --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.
4348 |
4449 |Warning: The bisect script should not be run multiple times in parallel because of a potential race condition while publishing artifacts locally.
4550
@@ -54,7 +59,7 @@ val usageMessage = """
5459
5560 val validationScript = scriptOptions.validationCommand.validationScript
5661 val releases = Releases .fromRange(scriptOptions.releasesRange)
57- val releaseBisect = ReleaseBisect (validationScript, releases)
62+ val releaseBisect = ReleaseBisect (validationScript, shouldFail = scriptOptions.shouldFail, releases)
5863
5964 releaseBisect.verifyEdgeReleases()
6065
@@ -64,18 +69,19 @@ val usageMessage = """
6469 println(s " First bad release: ${firstBadRelease.version}" )
6570 println(" \n Finished bisecting releases\n " )
6671
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)
6873 commitBisect.bisect()
6974
7075
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 )
7277object ScriptOptions :
7378 def fromArgs (args : Seq [String ]) =
7479 val defaultOptions = ScriptOptions (
7580 validationCommand = null ,
7681 dryRun = false ,
7782 bootstrapped = false ,
78- ReleasesRange (first = None , last = None )
83+ ReleasesRange (first = None , last = None ),
84+ shouldFail = false
7985 )
8086 parseArgs(args, defaultOptions)
8187
@@ -86,6 +92,7 @@ object ScriptOptions:
8692 case " --releases" :: argsRest =>
8793 val range = ReleasesRange .tryParse(argsRest.head).get
8894 parseArgs(argsRest.tail, options.copy(releasesRange = range))
95+ case " --should-fail" :: argsRest => parseArgs(argsRest, options.copy(shouldFail = true ))
8996 case _ =>
9097 val command = ValidationCommand .fromArgs(args)
9198 options.copy(validationCommand = command)
@@ -182,7 +189,7 @@ case class Release(version: String):
182189 override def toString : String = version
183190
184191
185- class ReleaseBisect (validationScript : File , allReleases : Vector [Release ]):
192+ class ReleaseBisect (validationScript : File , shouldFail : Boolean , allReleases : Vector [Release ]):
186193 assert(allReleases.length > 1 , " Need at least 2 releases to bisect" )
187194
188195 private val isGoodReleaseCache = collection.mutable.Map .empty[Release , Boolean ]
@@ -217,21 +224,22 @@ class ReleaseBisect(validationScript: File, allReleases: Vector[Release]):
217224 isGoodReleaseCache.getOrElseUpdate(release, {
218225 println(s " Testing ${release.version}" )
219226 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
221228 println(s " Test result: ${release.version} is a ${if isGood then " good" else " bad" } release \n " )
222229 isGood
223230 })
224231
225- class CommitBisect (validationScript : File , bootstrapped : Boolean , lastGoodHash : String , fistBadHash : String ):
232+ class CommitBisect (validationScript : File , shouldFail : Boolean , bootstrapped : Boolean , lastGoodHash : String , fistBadHash : String ):
226233 def bisect (): Unit =
227234 println(s " Starting bisecting commits $lastGoodHash.. $fistBadHash\n " )
228235 val scala3CompilerProject = if bootstrapped then " scala3-compiler-bootstrapped" else " scala3-compiler"
229236 val scala3Project = if bootstrapped then " scala3-bootstrapped" else " scala3"
237+ val validationCommandStatusModifier = if shouldFail then " ! " else " " // invert the process status if failure was expected
230238 val bisectRunScript = s """
231239 |scalaVersion= $$ (sbt "print ${scala3CompilerProject}/version" | tail -n1)
232240 |rm -r out
233241 |sbt "clean; ${scala3Project}/publishLocal"
234- | ${validationScript.getAbsolutePath} " $$ scalaVersion"
242+ | ${validationCommandStatusModifier}${ validationScript.getAbsolutePath} " $$ scalaVersion"
235243 """ .stripMargin
236244 " git bisect start" .!
237245 s " git bisect bad $fistBadHash" .!
0 commit comments