Skip to content

Commit 960adf9

Browse files
committed
Support the --test flag with the publish & publish local sub-commands
1 parent 49c6696 commit 960adf9

File tree

6 files changed

+261
-189
lines changed

6 files changed

+261
-189
lines changed

modules/cli/src/main/scala/scala/cli/commands/publish/Publish.scala

Lines changed: 78 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,8 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
267267
isCi = options.publishParams.isCi,
268268
() => configDb,
269269
options.mainClass,
270-
dummy = options.sharedPublish.dummy
270+
dummy = options.sharedPublish.dummy,
271+
buildTests = options.sharedPublish.scope.test
271272
)
272273
}
273274

@@ -289,25 +290,26 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
289290
isCi: Boolean,
290291
configDb: () => ConfigDb,
291292
mainClassOptions: MainClassOptions,
292-
dummy: Boolean
293+
dummy: Boolean,
294+
buildTests: Boolean
293295
): Unit = {
294296

295297
val actionableDiagnostics = configDb().get(Keys.actions).getOrElse(None)
296298

297-
if (watch) {
299+
if watch then {
298300
val watcher = Build.watch(
299301
inputs,
300302
initialBuildOptions,
301303
compilerMaker,
302304
docCompilerMaker,
303305
logger,
304306
crossBuilds = cross,
305-
buildTests = false,
307+
buildTests = buildTests,
306308
partial = None,
307309
actionableDiagnostics = actionableDiagnostics,
308310
postAction = () => WatchUtil.printWatchMessage()
309-
) { res =>
310-
res.orReport(logger).foreach { builds =>
311+
) {
312+
_.orReport(logger).foreach { builds =>
311313
maybePublish(
312314
builds,
313315
workingDir,
@@ -336,7 +338,7 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
336338
docCompilerMaker,
337339
logger,
338340
crossBuilds = cross,
339-
buildTests = false,
341+
buildTests = buildTests,
340342
partial = None,
341343
actionableDiagnostics = actionableDiagnostics
342344
).orExit(logger)
@@ -384,14 +386,14 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
384386
name
385387
}
386388

387-
def defaultComputeVersion(mayDefaultToGitTag: Boolean): Option[ComputeVersion] =
389+
private def defaultComputeVersion(mayDefaultToGitTag: Boolean): Option[ComputeVersion] =
388390
if (mayDefaultToGitTag) Some(ComputeVersion.GitTag(os.rel, dynVer = false, positions = Nil))
389391
else None
390392

391-
def defaultVersionError =
393+
private def defaultVersionError =
392394
new MissingPublishOptionError("version", "--project-version", "publish.version")
393395

394-
def defaultVersion: Either[BuildException, String] =
396+
private def defaultVersion: Either[BuildException, String] =
395397
Left(defaultVersionError)
396398

397399
/** Check if all builds are successful and proceed with preparing files to be uploaded OR print
@@ -422,17 +424,20 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
422424
case _: Build.Cancelled => true
423425
case _: Build.Failed => false
424426
}
425-
if (allOk && allDocsOk) {
427+
if allOk && allDocsOk then {
426428
val builds0 = builds.all.collect {
427429
case s: Build.Successful => s
428430
}
429431
val docBuilds0 = builds.allDoc.collect {
430432
case s: Build.Successful => s
431433
}
432434
val res: Either[BuildException, Unit] =
433-
builds.main match {
434-
case s: Build.Successful if mainClassOptions.mainClassLs.contains(true) =>
435-
mainClassOptions.maybePrintMainClasses(s.foundMainClasses(), shouldExit = allowExit)
435+
builds.builds match {
436+
case b if b.forall(_.success) && mainClassOptions.mainClassLs.contains(true) =>
437+
mainClassOptions.maybePrintMainClasses(
438+
builds0.flatMap(_.foundMainClasses()).distinct,
439+
shouldExit = allowExit
440+
)
436441
case _ => prepareFilesAndUpload(
437442
builds0,
438443
docBuilds0,
@@ -447,16 +452,12 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
447452
dummy
448453
)
449454
}
450-
if (allowExit)
451-
res.orExit(logger)
452-
else
453-
res.orReport(logger)
455+
if allowExit then res.orExit(logger) else res.orReport(logger)
454456
}
455457
else {
456-
val msg = if (allOk) "Scaladoc generation failed" else "Compilation failed"
458+
val msg = if allOk then "Scaladoc generation failed" else "Compilation failed"
457459
System.err.println(msg)
458-
if (allowExit)
459-
sys.exit(1)
460+
if allowExit then sys.exit(1)
460461
}
461462
}
462463

@@ -529,8 +530,8 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
529530
}
530531

531532
private def buildFileSet(
532-
build: Build.Successful,
533-
docBuildOpt: Option[Build.Successful],
533+
builds: Seq[Build.Successful],
534+
docBuilds: Seq[Build.Successful],
534535
workingDir: os.Path,
535536
now: Instant,
536537
isIvy2LocalLike: Boolean,
@@ -539,41 +540,56 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
539540
logger: Logger
540541
): Either[BuildException, (FileSet, (coursier.core.Module, String))] = either {
541542

542-
logger.debug(s"Preparing project ${build.project.projectName}")
543+
logger.debug(s"Preparing project ${builds.head.project.projectName}")
543544

544-
val publishOptions = build.options.notForBloopOptions.publishOptions
545+
val publishOptions = builds.head.options.notForBloopOptions.publishOptions
545546

546547
val (org, moduleName, ver) = value {
547548
orgNameVersion(
548549
publishOptions,
549-
build.inputs.workspace,
550+
builds.head.inputs.workspace,
550551
logger,
551-
build.artifacts.scalaOpt,
552+
builds.head.artifacts.scalaOpt,
552553
isCi
553554
)
554555
}
555556

556557
logger.message(s"Publishing $org:$moduleName:$ver")
557558

558559
val mainJar = {
559-
val mainClassOpt = build.options.mainClass.orElse {
560-
build.retainedMainClass(logger) match {
561-
case Left(_: NoMainClassFoundError) => None
562-
case Left(err) =>
563-
logger.debug(s"Error while looking for main class: $err")
564-
None
565-
case Right(cls) => Some(cls)
566-
}
567-
}
568-
val libraryJar = Library.libraryJar(Seq(build), mainClassOpt)
560+
val mainClassOpt: Option[String] =
561+
(builds.head.options.mainClass.filter(_.nonEmpty) match {
562+
case Some(cls) => Right(cls)
563+
case None =>
564+
val potentialMainClasses = builds.flatMap(_.foundMainClasses()).distinct
565+
builds
566+
.map { build =>
567+
build.retainedMainClass(logger, potentialMainClasses)
568+
.map(mainClass => build.scope -> mainClass)
569+
}
570+
.sequence
571+
.left
572+
.map(CompositeBuildException(_))
573+
.map(_.toMap)
574+
.map { retainedMainClassesByScope =>
575+
if retainedMainClassesByScope.size == 1 then retainedMainClassesByScope.head._2
576+
else
577+
retainedMainClassesByScope
578+
.get(Scope.Main)
579+
.orElse(retainedMainClassesByScope.get(Scope.Test))
580+
.get
581+
}
582+
583+
}).toOption
584+
val libraryJar = Library.libraryJar(builds, mainClassOpt)
569585
val dest = workingDir / org / s"$moduleName-$ver.jar"
570586
os.copy.over(libraryJar, dest, createFolders = true)
571587
dest
572588
}
573589

574590
val sourceJarOpt =
575591
if publishOptions.contextual(isCi).sourceJar.getOrElse(true) then {
576-
val content = PackageCmd.sourceJar(Seq(build), now.toEpochMilli)
592+
val content = PackageCmd.sourceJar(builds, now.toEpochMilli)
577593
val sourceJar = workingDir / org / s"$moduleName-$ver-sources.jar"
578594
os.write.over(sourceJar, content, createFolders = true)
579595
Some(sourceJar)
@@ -582,25 +598,27 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
582598

583599
val docJarOpt =
584600
if publishOptions.contextual(isCi).docJar.getOrElse(true) then
585-
docBuildOpt match {
586-
case None => None
587-
case Some(docBuild) =>
588-
val docJarPath = value(PackageCmd.docJar(Seq(docBuild), logger, Nil))
601+
docBuilds match {
602+
case Nil => None
603+
case docBuilds =>
604+
val docJarPath = value(PackageCmd.docJar(docBuilds, logger, Nil))
589605
val docJar = workingDir / org / s"$moduleName-$ver-javadoc.jar"
590606
os.copy.over(docJarPath, docJar, createFolders = true)
591607
Some(docJar)
592608
}
593609
else None
594610

595-
val dependencies = build.artifacts.userDependencies
596-
.map(_.toCs(build.artifacts.scalaOpt.map(_.params)))
611+
val dependencies = builds.flatMap(_.artifacts.userDependencies)
612+
.map(_.toCs(builds.head.artifacts.scalaOpt.map(_.params)))
597613
.sequence
598614
.left.map(CompositeBuildException(_))
599615
.orExit(logger)
600616
.map { dep0 =>
601617
val config =
602-
if (build.scope == Scope.Main) None
603-
else Some(Configuration(build.scope.name))
618+
builds -> builds.length match {
619+
case (b, 1) if b.head.scope != Scope.Main => Some(Configuration(b.head.scope.name))
620+
case _ => None
621+
}
604622
(dep0.module.organization, dep0.module.name, dep0.version, config, dep0.minimizedExclusions)
605623
}
606624
val url = publishOptions.url.map(_.value)
@@ -629,20 +647,20 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
629647
developers = developers
630648
)
631649

632-
if (isSonatype) {
633-
if (url.isEmpty)
650+
if isSonatype then {
651+
if url.isEmpty then
634652
logger.diagnostic(
635653
"Publishing to Sonatype, but project URL is empty (set it with the '//> using publish.url' directive)."
636654
)
637-
if (license.isEmpty)
655+
if license.isEmpty then
638656
logger.diagnostic(
639657
"Publishing to Sonatype, but license is empty (set it with the '//> using publish.license' directive)."
640658
)
641-
if (scm.isEmpty)
659+
if scm.isEmpty then
642660
logger.diagnostic(
643661
"Publishing to Sonatype, but SCM details are empty (set them with the '//> using publish.scm' directive)."
644662
)
645-
if (developers.isEmpty)
663+
if developers.isEmpty then
646664
logger.diagnostic(
647665
"Publishing to Sonatype, but developer details are empty (set them with the '//> using publish.developer' directive)."
648666
)
@@ -754,10 +772,7 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
754772

755773
assert(docBuilds.isEmpty || docBuilds.length == builds.length)
756774

757-
val it = builds.iterator.zip {
758-
if (docBuilds.isEmpty) Iterator.continually(None)
759-
else docBuilds.iterator.map(Some(_))
760-
}
775+
val it = Iterator(builds -> docBuilds)
761776

762777
val publishOptions = ConfigMonoid.sum(
763778
builds.map(_.options.notForBloopOptions.publishOptions)
@@ -815,8 +830,7 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
815830
lazy val es =
816831
Executors.newSingleThreadScheduledExecutor(Util.daemonThreadFactory("publish-retry"))
817832

818-
if (publishLocal)
819-
RepoParams.ivy2Local(ivy2HomeOpt)
833+
if publishLocal then RepoParams.ivy2Local(ivy2HomeOpt)
820834
else
821835
value {
822836
publishOptions.contextual(isCi).repository match {
@@ -854,13 +868,11 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
854868
val now = Instant.now()
855869
val (fileSet0, modVersionOpt) = value {
856870
it
857-
// TODO Allow to add test JARs to the main build artifacts
858-
.filter(_._1.scope != Scope.Test)
859871
.map {
860-
case (build, docBuildOpt) =>
872+
case (builds, docBuilds) =>
861873
buildFileSet(
862-
build,
863-
docBuildOpt,
874+
builds,
875+
docBuilds,
864876
workingDir,
865877
now,
866878
isIvy2LocalLike = repoParams.isIvy2LocalLike,
@@ -1170,17 +1182,13 @@ object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
11701182
}
11711183
else url
11721184
}
1173-
if (dummy)
1174-
println("\n \ud83d\udc40 You could have checked results at")
1175-
else
1176-
println("\n \ud83d\udc40 Check results at")
1185+
if dummy then println("\n \ud83d\udc40 You could have checked results at")
1186+
else println("\n \ud83d\udc40 Check results at")
11771187
println(s" $path")
11781188
for (targetRepo <- repoParams.targetRepoOpt if !isSnapshot0) {
11791189
val url = targetRepo.stripSuffix("/") + relPath
1180-
if (dummy)
1181-
println("before they would have landed at")
1182-
else
1183-
println("before they land at")
1190+
if dummy then println("before they would have landed at")
1191+
else println("before they land at")
11841192
println(s" $url")
11851193
}
11861194
}

modules/cli/src/main/scala/scala/cli/commands/publish/PublishLocal.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ object PublishLocal extends ScalaCommand[PublishLocalOptions] {
8686
isCi = options.publishParams.isCi,
8787
() => ConfigDb.empty, // shouldn't be used, no need of repo credentials here
8888
options.mainClass,
89-
dummy = options.sharedPublish.dummy
89+
dummy = options.sharedPublish.dummy,
90+
buildTests = options.sharedPublish.scope.test
9091
)
9192
}
9293
}

modules/cli/src/main/scala/scala/cli/commands/publish/SharedPublishOptions.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package scala.cli.commands.publish
33
import caseapp.*
44

55
import scala.build.compiler.{ScalaCompilerMaker, SimpleScalaCompilerMaker}
6-
import scala.cli.commands.shared.HelpGroup
6+
import scala.cli.commands.shared.{HelpGroup, ScopeOptions}
77
import scala.cli.commands.tags
88

99
// format: off
@@ -85,7 +85,10 @@ final case class SharedPublishOptions(
8585
@Group(HelpGroup.Publishing.toString)
8686
@HelpMessage("Proceed as if publishing, but do not upload / write artifacts to the remote repository")
8787
@Tag(tags.implementation)
88-
dummy: Boolean = false
88+
dummy: Boolean = false,
89+
90+
@Recurse
91+
scope: ScopeOptions = ScopeOptions()
8992
){
9093
// format: on
9194

modules/cli/src/main/scala/scala/cli/packaging/Library.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ object Library {
6565
} manifest.getMainAttributes.put(JarAttributes.Name.MAIN_CLASS, mainClass)
6666

6767
var zos: ZipOutputStream = null
68-
val contentDirs = builds.map(b => contentDirOverride.getOrElse(b.output))
68+
val contentDirs = builds.map(b => contentDirOverride.getOrElse(b.output)).distinct
6969

7070
try {
7171
zos = new JarOutputStream(outputStream, manifest)

0 commit comments

Comments
 (0)