sbt/project/SiteMap.scala

93 lines
3.9 KiB
Scala
Raw Normal View History

Upgrade the build to sbt 1.0.0-M5 Some plugins remain commented out, for now. sbt-doge is no longer needed because a variant of it has been folded into sbt 1. For some reason scripted requires src/doc jars of sbt, so switch back to using `publishAll` rather than `publishLocalBinAll`. :( Also, the sys.prop change in scripted is to force log4j2 to not use a thread context classloader, and avoid the following: ERROR StatusLogger Unable to create custom ContextSelector. Falling back to default. java.lang.ClassCastException: Cannot cast org.apache.logging.log4j.core.async.AsyncLoggerContextSelector to org.apache.logging.log4j.core.selector.ContextSelector at java.lang.Class.cast(Class.java:3369) at org.apache.logging.log4j.util.LoaderUtil.newCheckedInstanceOf(LoaderUtil.java:201) at org.apache.logging.log4j.util.LoaderUtil.newCheckedInstanceOfProperty(LoaderUtil.java:226) at org.apache.logging.log4j.core.impl.Log4jContextFactory.createContextSelector(Log4jContextFactory.java:97) at org.apache.logging.log4j.core.impl.Log4jContextFactory.<init>(Log4jContextFactory.java:58) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at java.lang.Class.newInstance(Class.java:442) at org.apache.logging.log4j.LogManager.<clinit>(LogManager.java:94) at org.apache.logging.log4j.spi.ThreadContextMapFactory.createThreadContextMap(ThreadContextMapFactory.java:73) at org.apache.logging.log4j.ThreadContext.init(ThreadContext.java:223) at org.apache.logging.log4j.ThreadContext.<clinit>(ThreadContext.java:202) at org.apache.logging.log4j.core.impl.ContextDataInjectorFactory.createDefaultInjector(ContextDataInjectorFactory.java:83) at org.apache.logging.log4j.core.impl.ContextDataInjectorFactory.createInjector(ContextDataInjectorFactory.java:67) at org.apache.logging.log4j.core.lookup.ContextMapLookup.<init>(ContextMapLookup.java:34) at org.apache.logging.log4j.core.lookup.Interpolator.<init>(Interpolator.java:117) at org.apache.logging.log4j.core.config.AbstractConfiguration.<init>(AbstractConfiguration.java:125) at org.apache.logging.log4j.core.config.DefaultConfiguration.<init>(DefaultConfiguration.java:46) at org.apache.logging.log4j.core.layout.PatternLayout$Builder.build(PatternLayout.java:650) at org.apache.logging.log4j.core.layout.PatternLayout.createDefaultLayout(PatternLayout.java:487) at sbt.internal.util.ConsoleAppender.<init>(ConsoleAppender.scala:245) at sbt.internal.util.ConsoleAppender$.apply(ConsoleAppender.scala:196) at sbt.internal.util.ConsoleLogger.<init>(ConsoleAppender.scala:42) at sbt.internal.util.ConsoleLogger$.apply(ConsoleAppender.scala:34) at sbt.test.ScriptedRunner.run(ScriptedTests.scala:221) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at Scripted$.doScripted(Scripted.scala:125) at $0483e89d182e9d240274$.$anonfun$scriptedTask$5(build.sbt:301)
2017-05-03 16:52:36 +02:00
import sbt.io.Path._
import sbt._
2014-05-01 18:50:07 +02:00
object SiteMap {
// represents the configurable aspects of a sitemap entry
final case class Entry(changeFreq: String, priority: Double) {
2017-04-21 09:14:31 +02:00
assert(priority >= 0.0 && priority <= 1.0,
s"Priority must be between 0.0 and 1.0:, was $priority")
2014-05-01 18:50:07 +02:00
}
2017-04-21 09:14:31 +02:00
def generate(repoBase: File,
remoteBase: URI,
gzip: Boolean,
entry: (File, String) => Option[Entry],
log: Logger): (File, Seq[File]) = {
def relativize(files: PathFinder): Seq[(File, String)] = files pair relativeTo(repoBase)
def entries(files: PathFinder) =
relativize(files) flatMap {
case (f, path) =>
entry(f, path).toList map { e =>
entryXML(e, f, path)
}
}
def entriesXML(entries: Seq[xml.Node]): xml.Elem = {
assert(entries.size <= 50000, "A site map cannot contain more than 50,000 entries.")
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
2014-05-01 18:50:07 +02:00
{ entries }
</urlset>
2017-04-21 09:14:31 +02:00
}
2017-04-21 09:14:31 +02:00
def entryXML(e: Entry, f: File, relPath: String) =
<url>
2014-05-01 18:50:07 +02:00
<loc>{ remoteBase.resolve(relPath).toString }</loc>
<lastmod>{ lastModifiedString(f) }</lastmod>
<changefreq>{ e.changeFreq }</changefreq>
<priority>{ e.priority.toString }</priority>
</url>
2017-04-21 09:14:31 +02:00
def singleSiteMap(dir: File, files: PathFinder): Option[File] = {
val es = entries(files)
if (es.isEmpty) None
else Some(writeXMLgz(dir / "sitemap.xml", dir / "sitemap.xml.gz", gzip, entriesXML(es)))
}
def indexEntryXML(sub: File, relPath: String): xml.Elem =
<sitemap>
2014-05-01 18:50:07 +02:00
<loc>{ remoteBase.resolve(relPath).toString }</loc>
<lastmod>{ lastModifiedString(sub) }</lastmod>
</sitemap>
2017-04-21 09:14:31 +02:00
def indexEntriesXML(entries: Seq[xml.Node]): xml.Elem =
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
2014-05-01 18:50:07 +02:00
{ entries }
</sitemapindex>
2017-04-21 09:14:31 +02:00
def indexEntries(subs: Seq[File]) =
relativize(subs) map { case (f, path) => indexEntryXML(f, path) }
def siteMapIndex(dir: File, subs: Seq[File]): File = {
val xml = indexEntriesXML(indexEntries(subs))
writeXMLgz(dir / "sitemap_index.xml", dir / "sitemap_index.xml.gz", gzip, xml)
2014-05-01 18:50:07 +02:00
}
2017-04-21 09:14:31 +02:00
def isSymlink(f: File) = f.getCanonicalFile != f.getAbsoluteFile
val (symlinks, normal) = (repoBase * DirectoryFilter).get.partition(dir => isSymlink(dir))
log.debug("Detected symlinks: " + symlinks.mkString("\n\t", "\n\t", ""))
val subMaps =
singleSiteMap(repoBase, (repoBase * "*.html") +++ (symlinks ** "*.html")).toList ++
normal.flatMap(dir => singleSiteMap(dir, dir ** "*.html").toList)
val index = siteMapIndex(repoBase, subMaps)
(index, subMaps)
}
2014-05-01 18:50:07 +02:00
// generates a string suitable for a sitemap file representing the last modified time of the given File
2017-04-21 09:14:31 +02:00
private[this] def lastModifiedString(f: File): String = {
val formatter = new java.text.SimpleDateFormat("yyyy-MM-dd")
2017-12-22 01:49:59 +01:00
// TODO: replace lastModified() with sbt.io.IO.getModifiedTimeOrZero(), once the build
// has been upgraded to a version of sbt that includes that call.
2017-04-21 09:14:31 +02:00
formatter.format(new java.util.Date(f.lastModified))
}
2014-05-01 18:50:07 +02:00
// writes the provided XML node to `output` and then gzips it to `gzipped` if `gzip` is true
2017-04-21 09:14:31 +02:00
private[this] def writeXMLgz(output: File, gzipped: File, gzip: Boolean, node: xml.Node): File = {
writeXML(output, node)
if (gzip) {
IO.gzip(output, gzipped)
gzipped
} else
output
}
2014-05-01 18:50:07 +02:00
private[this] def writeXML(output: File, node: xml.Node): Unit =
write(output, new xml.PrettyPrinter(1000, 4).format(node))
private[this] def write(output: File, xmlString: String): Unit = {
2014-05-01 18:50:07 +02:00
// use \n as newline because toString uses PrettyPrinter, which hard codes line endings to be \n
IO.write(output, s"<?xml version='1.0' encoding='${IO.utf8.name}'?>\n")
IO.append(output, xmlString)
}
}