Skip to content

Commit f35464c

Browse files
Add --doc, --sources, and --transitive options to publishLocal command (forwardport #4697) (#3512)
Fixes #2487 Forward port of #4697 I'd like to use that locally, but keeping this PR as a draft until tests can be added, later on. (Bin compat might need to be taken into account too.)
1 parent 3063b48 commit f35464c

File tree

5 files changed

+257
-32
lines changed

5 files changed

+257
-32
lines changed

example/javalib/publishing/2-publish-module/build.mill

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,12 @@ object foo extends JavaModule with PublishModule {
2727
Publishing Artifact(com.lihaoyi,foo,0.0.1) to ivy repo...
2828

2929
*/
30+
31+
// `publishLocal` accepts options like `--doc=false` and `--sources=false`,
32+
// to disable publishing javadoc JARs and source JARs, which are generated and
33+
// published by default. This can be helpful if you're not interested in javadoc JARs,
34+
// and javadoc generation fails, and you would rather address those errors later for example.
35+
36+
// `publishLocal` also accepts `--transitive=true`, to also publish locally the
37+
// transitive dependencies of the module being published. This ensures the module
38+
// can be resolved from the local repository, with no missing dependencies.

example/kotlinlib/publishing/2-publish-module/build.mill

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,12 @@ object foo extends KotlinModule with PublishModule {
3232
Publishing Artifact(com.lihaoyi,foo,0.0.1) to ivy repo...
3333

3434
*/
35+
36+
// `publishLocal` accepts options like `--doc=false` and `--sources=false`,
37+
// to disable publishing javadoc JARs and source JARs, which are generated and
38+
// published by default. This can be helpful if you're not interested in javadoc JARs,
39+
// and javadoc generation fails, and you would rather address those errors later for example.
40+
41+
// `publishLocal` also accepts `--transitive=true`, to also publish locally the
42+
// transitive dependencies of the module being published. This ensures the module
43+
// can be resolved from the local repository, with no missing dependencies.

example/scalalib/publishing/2-publish-module/build.mill

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,14 @@ Publishing Artifact(com.lihaoyi,foo_2.13,0.0.1) to ivy repo...
3838
// is useful as a lightweight way of testing out the published artifacts, without
3939
// the setup overhead and long latencies of publishing artifacts globally accessible
4040
// to anyone in the world.
41+
42+
// `publishLocal` accepts options like `--doc=false` and `--sources=false`,
43+
// to disable publishing javadoc JARs and source JARs, which are generated and
44+
// published by default. This can be helpful if you're not interested in javadoc JARs,
45+
// and javadoc generation fails or takes too much time. When using Scala 2, disabling
46+
// javadoc generation can bring large speedups, given it entails compiling your code
47+
// a second time.
48+
49+
// `publishLocal` also accepts `--transitive=true`, to also publish locally the
50+
// transitive dependencies of the module being published. This ensures the module
51+
// can be resolved from the local repository, with no missing dependencies.

scalalib/src/mill/scalalib/PublishModule.scala

Lines changed: 73 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -307,9 +307,7 @@ trait PublishModule extends JavaModule { outer =>
307307
pomPackagingType match {
308308
case PackagingType.Pom => Task.Anon(Seq())
309309
case _ => Task.Anon(Seq(
310-
(jar(), PublishInfo.jar),
311-
(sourceJar(), PublishInfo.sourcesJar),
312-
(docJar(), PublishInfo.docJar)
310+
(jar(), PublishInfo.jar)
313311
))
314312
}
315313
}
@@ -336,34 +334,83 @@ trait PublishModule extends JavaModule { outer =>
336334
* Publish artifacts to a local ivy repository.
337335
* @param localIvyRepo The local ivy repository.
338336
* If not defined, the default resolution is used (probably `$HOME/.ivy2/local`).
337+
* @param sources whether to generate and publish a sources JAR
338+
* @param doc whether to generate and publish a javadoc JAR
339+
* @param transitive if true, also publish locally the transitive module dependencies of this module
340+
* (this includes the runtime transitive module dependencies, but not the compile-only ones)
339341
*/
340-
def publishLocal(localIvyRepo: String = null): define.Command[Unit] = Task.Command {
341-
publishLocalTask(Task.Anon {
342-
Option(localIvyRepo).map(os.Path(_, Task.workspace))
343-
})()
342+
def publishLocal(
343+
localIvyRepo: String = null,
344+
sources: Boolean = true,
345+
doc: Boolean = true,
346+
transitive: Boolean = false
347+
): define.Command[Unit] = Task.Command {
348+
publishLocalTask(
349+
Task.Anon {
350+
Option(localIvyRepo).map(os.Path(_, Task.workspace))
351+
},
352+
sources,
353+
doc,
354+
transitive
355+
)()
344356
Result.Success(())
345357
}
346358

359+
// bin-compat shim
360+
def publishLocal(
361+
localIvyRepo: String
362+
): define.Command[Unit] =
363+
publishLocal(localIvyRepo, sources = true, doc = true, transitive = false)
364+
347365
/**
348366
* Publish artifacts the local ivy repository.
349367
*/
350368
def publishLocalCached: T[Seq[PathRef]] = Task {
351-
publishLocalTask(Task.Anon(None))().map(p => PathRef(p).withRevalidateOnce)
369+
val res = publishLocalTask(
370+
Task.Anon(None),
371+
sources = true,
372+
doc = true,
373+
transitive = false
374+
)()
375+
res.map(p => PathRef(p).withRevalidateOnce)
352376
}
353377

354-
private def publishLocalTask(localIvyRepo: Task[Option[os.Path]]): Task[Seq[Path]] = Task.Anon {
355-
val publisher = localIvyRepo() match {
356-
case None => LocalIvyPublisher
357-
case Some(path) => new LocalIvyPublisher(path)
378+
private def publishLocalTask(
379+
localIvyRepo: Task[Option[os.Path]],
380+
sources: Boolean,
381+
doc: Boolean,
382+
transitive: Boolean
383+
): Task[Seq[Path]] =
384+
if (transitive) {
385+
val publishTransitiveModuleDeps = (transitiveModuleDeps ++ transitiveRunModuleDeps).collect {
386+
case p: PublishModule => p
387+
}
388+
Target.traverse(publishTransitiveModuleDeps.distinct) { publishMod =>
389+
publishMod.publishLocalTask(localIvyRepo, sources, doc, transitive = false)
390+
}.map(_.flatten)
391+
} else {
392+
val sourcesJarOpt =
393+
if (sources) Task.Anon(Some(PublishInfo.sourcesJar(sourceJar())))
394+
else Task.Anon(None)
395+
val docJarOpt =
396+
if (doc) Task.Anon(Some(PublishInfo.docJar(docJar())))
397+
else Task.Anon(None)
398+
399+
Task.Anon {
400+
val publisher = localIvyRepo() match {
401+
case None => LocalIvyPublisher
402+
case Some(path) => new LocalIvyPublisher(path)
403+
}
404+
val publishInfos =
405+
defaultPublishInfos() ++ sourcesJarOpt().toSeq ++ docJarOpt().toSeq ++ extraPublish()
406+
publisher.publishLocal(
407+
pom = pom().path,
408+
ivy = Right(ivy().path),
409+
artifact = artifactMetadata(),
410+
publishInfos = publishInfos
411+
)
412+
}
358413
}
359-
val publishInfos = defaultPublishInfos() ++ extraPublish()
360-
publisher.publishLocal(
361-
pom = pom().path,
362-
ivy = Right(ivy().path),
363-
artifact = artifactMetadata(),
364-
publishInfos = publishInfos
365-
)
366-
}
367414

368415
/**
369416
* Publish artifacts to a local Maven repository.
@@ -396,7 +443,12 @@ trait PublishModule extends JavaModule { outer =>
396443

397444
private def publishM2LocalTask(m2RepoPath: Task[os.Path]): Task[Seq[PathRef]] = Task.Anon {
398445
val path = m2RepoPath()
399-
val publishInfos = defaultPublishInfos() ++ extraPublish()
446+
val publishInfos = defaultPublishInfos() ++
447+
Seq(
448+
PublishInfo.sourcesJar(sourceJar()),
449+
PublishInfo.docJar(docJar())
450+
) ++
451+
extraPublish()
400452

401453
new LocalM2Publisher(path)
402454
.publish(pom().path, artifactMetadata(), publishInfos)

scalalib/test/src/mill/scalalib/PublishModuleTests.scala

Lines changed: 155 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -82,18 +82,20 @@ object PublishModuleTests extends TestSuite {
8282
lazy val millDiscover = Discover[this.type]
8383
}
8484

85-
trait TestPublishModule extends PublishModule {
86-
def publishVersion = "0.1.0-SNAPSHOT"
87-
def pomSettings = PomSettings(
88-
organization = "com.lihaoyi.pubmodtests",
89-
description = "test thing",
90-
url = "https://github.com/com-lihaoyi/mill",
91-
licenses = Seq(License.Common.Apache2),
92-
versionControl = VersionControl.github("com-lihaoyi", "mill"),
93-
developers = Nil
94-
)
95-
}
9685
object compileAndRuntimeStuff extends TestBaseModule {
86+
def organization = "com.lihaoyi.pubmodtests"
87+
def version = "0.1.0-SNAPSHOT"
88+
trait TestPublishModule extends PublishModule {
89+
def publishVersion = version
90+
def pomSettings = PomSettings(
91+
organization = organization,
92+
description = "test thing",
93+
url = "https://github.com/com-lihaoyi/mill",
94+
licenses = Seq(License.Common.Apache2),
95+
versionControl = VersionControl.github("com-lihaoyi", "mill"),
96+
developers = Nil
97+
)
98+
}
9799
object main extends JavaModule with TestPublishModule {
98100
def ivyDeps = Seq(
99101
ivy"org.slf4j:slf4j-api:2.0.15"
@@ -343,6 +345,148 @@ object PublishModuleTests extends TestSuite {
343345
runtimeClassPathCheck(ivy2RuntimeTransitiveRunCp)
344346
runtimeClassPathCheck(m2RuntimeTransitiveRunCp)
345347
}
348+
349+
test("docSourcesArgs") - UnitTester(compileAndRuntimeStuff, null).scoped { eval =>
350+
val ivy2Repo = eval.evaluator.workspace / "ivy2Local"
351+
val moduleName = "main"
352+
val subDir =
353+
os.sub / compileAndRuntimeStuff.organization / moduleName / compileAndRuntimeStuff.version
354+
def repoHasIvyXml(): Boolean =
355+
os.isFile(ivy2Repo / subDir / "ivys/ivy.xml")
356+
def repoHasJar(): Boolean =
357+
os.isFile(ivy2Repo / subDir / "jars" / s"$moduleName.jar")
358+
def repoHasSourcesJar(): Boolean =
359+
os.isFile(ivy2Repo / subDir / "srcs" / s"$moduleName-sources.jar")
360+
def repoHasDocJar(): Boolean =
361+
os.isFile(ivy2Repo / subDir / "docs" / s"$moduleName-javadoc.jar")
362+
def clearRepo(): Unit =
363+
os.remove.all(ivy2Repo)
364+
365+
eval(compileAndRuntimeStuff.main.publishLocal(ivy2Repo.toString)).right.get
366+
assert(repoHasIvyXml())
367+
assert(repoHasJar())
368+
assert(repoHasSourcesJar())
369+
assert(repoHasDocJar())
370+
371+
clearRepo()
372+
373+
eval(compileAndRuntimeStuff.main.publishLocal(ivy2Repo.toString, doc = false)).right.get
374+
assert(repoHasIvyXml())
375+
assert(repoHasJar())
376+
assert(repoHasSourcesJar())
377+
assert(!repoHasDocJar())
378+
379+
clearRepo()
380+
381+
eval(compileAndRuntimeStuff.main.publishLocal(
382+
ivy2Repo.toString,
383+
doc = false,
384+
sources = false
385+
)).right.get
386+
assert(repoHasIvyXml())
387+
assert(repoHasJar())
388+
assert(!repoHasSourcesJar())
389+
assert(!repoHasDocJar())
390+
}
391+
392+
test("transitive") - UnitTester(compileAndRuntimeStuff, null).scoped { eval =>
393+
val ivy2Repo = eval.evaluator.workspace / "ivy2Local"
394+
val mainModuleName = "main"
395+
val transitiveModuleName = "transitive"
396+
val runtimeTransitiveModuleName = "runtimeTransitive"
397+
def subDir(moduleName: String) =
398+
os.sub / compileAndRuntimeStuff.organization / moduleName / compileAndRuntimeStuff.version
399+
def repoHasIvyXml(moduleName: String): Boolean =
400+
os.isFile(ivy2Repo / subDir(moduleName) / "ivys/ivy.xml")
401+
def repoHasJar(moduleName: String): Boolean =
402+
os.isFile(ivy2Repo / subDir(moduleName) / "jars" / s"$moduleName.jar")
403+
def repoHasSourcesJar(moduleName: String): Boolean =
404+
os.isFile(ivy2Repo / subDir(moduleName) / "srcs" / s"$moduleName-sources.jar")
405+
def repoHasDocJar(moduleName: String): Boolean =
406+
os.isFile(ivy2Repo / subDir(moduleName) / "docs" / s"$moduleName-javadoc.jar")
407+
def clearRepo(): Unit =
408+
os.remove.all(ivy2Repo)
409+
410+
eval(compileAndRuntimeStuff.transitive.publishLocal(ivy2Repo.toString)).right.get
411+
assert(!repoHasIvyXml(mainModuleName))
412+
assert(!repoHasJar(mainModuleName))
413+
assert(!repoHasSourcesJar(mainModuleName))
414+
assert(!repoHasDocJar(mainModuleName))
415+
assert(repoHasIvyXml(transitiveModuleName))
416+
assert(repoHasJar(transitiveModuleName))
417+
assert(repoHasSourcesJar(transitiveModuleName))
418+
assert(repoHasDocJar(transitiveModuleName))
419+
420+
clearRepo()
421+
422+
eval(compileAndRuntimeStuff.transitive.publishLocal(
423+
ivy2Repo.toString,
424+
transitive = true
425+
)).right.get
426+
assert(repoHasIvyXml(mainModuleName))
427+
assert(repoHasJar(mainModuleName))
428+
assert(repoHasSourcesJar(mainModuleName))
429+
assert(repoHasDocJar(mainModuleName))
430+
assert(repoHasIvyXml(transitiveModuleName))
431+
assert(repoHasJar(transitiveModuleName))
432+
assert(repoHasSourcesJar(transitiveModuleName))
433+
assert(repoHasDocJar(transitiveModuleName))
434+
435+
clearRepo()
436+
437+
eval(compileAndRuntimeStuff.transitive.publishLocal(ivy2Repo.toString, doc = false)).right.get
438+
assert(!repoHasIvyXml(mainModuleName))
439+
assert(!repoHasJar(mainModuleName))
440+
assert(!repoHasSourcesJar(mainModuleName))
441+
assert(!repoHasDocJar(mainModuleName))
442+
assert(repoHasIvyXml(transitiveModuleName))
443+
assert(repoHasJar(transitiveModuleName))
444+
assert(repoHasSourcesJar(transitiveModuleName))
445+
assert(!repoHasDocJar(transitiveModuleName))
446+
447+
clearRepo()
448+
449+
eval(compileAndRuntimeStuff.transitive.publishLocal(
450+
ivy2Repo.toString,
451+
doc = false,
452+
transitive = true
453+
)).right.get
454+
assert(repoHasIvyXml(mainModuleName))
455+
assert(repoHasJar(mainModuleName))
456+
assert(repoHasSourcesJar(mainModuleName))
457+
assert(!repoHasDocJar(mainModuleName))
458+
assert(repoHasIvyXml(transitiveModuleName))
459+
assert(repoHasJar(transitiveModuleName))
460+
assert(repoHasSourcesJar(transitiveModuleName))
461+
assert(!repoHasDocJar(transitiveModuleName))
462+
463+
clearRepo()
464+
465+
eval(compileAndRuntimeStuff.runtimeTransitive.publishLocal(ivy2Repo.toString)).right.get
466+
assert(!repoHasIvyXml(mainModuleName))
467+
assert(!repoHasJar(mainModuleName))
468+
assert(!repoHasSourcesJar(mainModuleName))
469+
assert(!repoHasDocJar(mainModuleName))
470+
assert(repoHasIvyXml(runtimeTransitiveModuleName))
471+
assert(repoHasJar(runtimeTransitiveModuleName))
472+
assert(repoHasSourcesJar(runtimeTransitiveModuleName))
473+
assert(repoHasDocJar(runtimeTransitiveModuleName))
474+
475+
clearRepo()
476+
477+
eval(compileAndRuntimeStuff.runtimeTransitive.publishLocal(
478+
ivy2Repo.toString,
479+
transitive = true
480+
)).right.get
481+
assert(repoHasIvyXml(mainModuleName))
482+
assert(repoHasJar(mainModuleName))
483+
assert(repoHasSourcesJar(mainModuleName))
484+
assert(repoHasDocJar(mainModuleName))
485+
assert(repoHasIvyXml(runtimeTransitiveModuleName))
486+
assert(repoHasJar(runtimeTransitiveModuleName))
487+
assert(repoHasSourcesJar(runtimeTransitiveModuleName))
488+
assert(repoHasDocJar(runtimeTransitiveModuleName))
489+
}
346490
}
347491

348492
}

0 commit comments

Comments
 (0)