How to stop xsbt from reloading webapp on resources change - sbt

We are using sbt with xsbt-web-plugin to develop our liftweb app. In our project build we have several subprojects and we use dependencies of a Project to share some stuff between all the subprojects.
object ProjectBuild extends Build {
//...
lazy val standalone = Project(
id = "standalone",
base = file("standalone"),
settings = Seq(...),
dependencies = Seq(core) // please notice this
)
lazy val core = Project(
id = "core",
base = file("core"),
settings = Seq(...)
}
// ...
}
To ease the development we use 'project standalone' '~;container:start; container:reload /' command automatically recompile changed files.
We decided to serve some common assets from shared core project as well. This works fine with lift. But what we faced when added our files to core/src/main/resources/toserve folder, is that any change to any javascript or css file causes application to restart jetty. This is annoying since such reload takes lots of resources.
So I started investigating on how to prevent this, even found someone mentioning watchSources sbt task that scans for changed files.
But adding this code as a watchSources modification (event that println prints all the files) does not prevent from reloading webapp each time I change assets located in core resources folder.
lazy val core = Project(
id = "core",
base = file("core"),
settings = Seq(
// ...
// here I added some tuning of watchSources
watchSources ~= { (ws: Seq[File]) => ws filterNot { path =>
println(path.getAbsolutePath)
path.getAbsolutePath.endsWith(".js")
} }
)
I also tried adding excludeFilter to unmanagedSorces, unmanagedResorces but with no luck.
I'm not an sbt expert and such modification of settings looks more like a magic for me (rather then a usual code). Also such tuning seem to be uncovered by documentation =(
Can anyone please help me to prevent sbt from reloading webapp on each asset file change?
Thanks a lot!

You're on the right track by using watchSources, but you'll also need to exclude the resources directory itself:
watchSources ~= { (ws: Seq[File]) =>
ws filterNot { path =>
path.getName.endsWith(".js") || path.getName == ("resources")
}
}

Can you switch from using "resources" folder to using "webapp" folder? That will also free you from restarts. Here's a demo project for Lift (that uses "webapp"):
https://github.com/lift/lift_26_sbt/
For example, the "basic" template:
https://github.com/lift/lift_26_sbt/tree/master/scala_211/lift_basic/src/main/webapp

Related

How to invoke Annotation Processor from Gradle plugin

I am currently working on a Gradle custon plugin that should analyse my root project for specific configs in every subproject and then generate some kotlin source code in the build dir. I can't figure out a way to invoke my annotation processor from my gradle plugin which has a custom task for this matter.
Any ideas how to achieve this? Any resource/tutorial/documentation is also highly welcomed.
Thanks in advance and be safe.
After a long time of googling and mostly trying and failing, I finally figured out the solution to my question. Here is my task configuration.
Basically we have to provide the annotation processor's classpath as a project configuration. In my case I added this block to the project's build.gradle
allprojects {
configurations {
myProcessor //pick any name!!!
}
}
and then as a dependency in app build.gradle
dependencies {
myProcessor "PATH_TO_MY_PROCESSOR_JAR" //or maven dependency if it's uploaded to maven central
}
tasks.register(
"myTaskName",
JavaCompile::class.java
) {
compiler ->
with(compiler.options) {
isFork = true
isIncremental = true
}
with(compiler) {
group = shuttle.plugin.ShuttlePlugin.TASK_GROUP
destinationDir = outputDir
classpath = variant.getCompileClasspath(null)
options.annotationProcessorPath = configurations.getByName("myProcessor") //this is the missing piece!!
source = files(projectDir.resolve("src/main/java")).asFileTree
}
}
However, this task will only compile Java classes Only and not kotlin.
Any Idea how to fix this behaviour knowing that my plugin targets only android apps so I don't have direct access to kotlinCompile gradle default task?

Is it possible to disable publish without disabling publishLocal in sbt?

I have an sbt project where docker:publishLocal will create a docker image on my machine for testing, and docker:publish will publish the image to a repository and also publish jar files from the build to a repository.
If my project is a snapshot, I would like to disable publishing to the repositories, while still being able to build the local image.
ThisBuild / publishArtifact := ! isSnapshot.value
does the right thing for the publish command, but it also disables publishLocal.
I want to write something like
if (isSnapshot.value) {
publish := { }
}
but that gives me an error that I do not understand at all:
[info] Loading project definition from /Users/dev/project
/Users/dev/build.sbt:1: error: type mismatch;
found : Unit
required: sbt.internal.DslEntry
if (isSnapshot.value) {
^
Past experience dictates that redefining publish to conditionally call the original version won't work, as
publish := {
if (!isSnapshot.value) publish.value
}
gives warnings that the task is always evaluated.
Is there a way to do this?
The problem with this code is that it evaluates publish.value regardless of the if structure. I recommend reading the documentation on task dependencies. If you want to "delay" the evaluation of a task in one of the if branches, you need to use dynamic task definition:
publish := Def.taskDyn {
if (isSnapshot.value)
Def.task {} // doing nothing
else
Def.task { publish.value } // could be written as just publish
}.value
But apart from fixing your code, you should be aware that there is a special setting for the functionality you want, it's called skip:
publish/skip := isSnapshot.value
Another thing to notice, is the scoping. If you want to override docker:publish, which is the same as Docker/publish in the new syntax, you should add this Docker/ scope prefix to every mention of publish in the code above.

Problems with libraries in premake

I have experienced certain problems when using libraries in premake4 scripts.
1) When creating a shared library (.dll) on Windows 10 using a premake4 script, it creates the dll fine, but it also creates a static library of small size (2K).
In my case, I was creating a shared library named MathLib.dll using a premake4 script. It did that correctly, but it also created a file named libMathLib.a of size 2K. (It may be empty.)
I don't see why there was a need for the Makefile generated by premake4 to create libMathLib.a, when in fact the objective was to create a .dll file. I think this may be a premake4 bug and I have raised it on the premake4 Issue tracker on github.
The premake4 lua script is as follows:
-- Dir : Files > C > SW > Applications > Samples >
-- premakeSamples > premake-sharedlib-create
--#!lua
-- A solution contains projects,
-- and defines the available configurations
solution "MathLib"
configurations { "Debug", "Release" }
-- A project defines one build target
project "MathLib"
kind "SharedLib"
language "C++"
files { "**.h", "**.cpp" }
includedirs {"../../../ProgramLibraries/Headers/"}
-- Create target library in Files > C > SW >
-- Applications > ProgramLibraries
targetdir "../../../ProgramLibraries/"
configuration "Debug"
defines { "DEBUG" }
flags { "Symbols" }
configuration "Release"
defines { "NDEBUG" }
flags { "Optimize" }
-- Register the "runmakefile" action.
newaction
{
trigger = "runmakefile",
description = "run the generated makefile to create the executable using the default ('debug' config)",
execute = function()
os.execute("make")
end
}
-- Register the "runmakefilerelease" action.
newaction
{
trigger = "runmakefilerelease",
description = "run the generated makefile to create the executable using the 'release' config)",
execute = function()
os.execute("make config=release")
end
}
2) The above problem is more serious than it sounds. Supposing I had already created a genuine static library named libMathLib.a in the Libraries dir, using a separate premake4 script. Subsequently, if I also create a shared library named MathLib.dll in the same directory as the static library, a dummy static library (possibly empty) would be created and replace the earlier genuine static library.
3) -- EDIT -- : I had reported this point (use of a static library) as a problem, but it has started working now. I don't know the reason, but the only difference, as far as I am aware, is that I had shut down and restarted my PC (and therefore my MSYS session on Windows 10). Therefore I am deleting this point.
How can I solve the above 2 problems?
That's the import library. You can turn it off with Premake's NoImportLib flag.
flags { "NoImportLib" }

How to publish an artifact with pom-packaging in SBT?

I have a multi-project build in SBT where some projects should aggregate dependencies and contain no code. So then clients could depend on these projects as a single dependency instead of directly depending on all of their aggregated dependencies. With Maven, this is a common pattern, e.g. when using Spring Boot.
In SBT, I figured I can suppress the generation of the empty artifacts by adding this setting to these projects:
packagedArtifacts := Classpaths.packaged(Seq(makePom)).value
However, the makePom task writes <packaging>jar</packaging> in the generated POM. But now that there is no JAR anymore, this should read <packaging>pom</packaging> instead.
How can I do this?
This question is a bit old, but I just came across the same issue and found a solution. The original answer does point to the right page where this info can be found, but here is an example. It uses the pomPostProcess setting to transform the generated POM right before it is written to disk. Essentially, we loop over all the XML nodes, looking for the element we care about and then rewrite it.
import scala.xml.{Node => XmlNode, NodeSeq => XmlNodeSeq, _}
import scala.xml.transform._
pomPostProcess := { node: XmlNode =>
val rule = new RewriteRule {
override def transform(n: XmlNode): XmlNodeSeq = n match {
case e: Elem if e != null && e.label == "packaging" =>
<packaging>pom</packaging>
case _ => n
}
}
new RuleTransformer(rule).transform(node).head
},
Maybe you could modify the result pom as described here: Modifying the generated POM
You can disable publishing the default artifacts of JAR, sources, and docs, then opt in explicitly to publishing the POM. sbt produces and publishes a POM only, with <packaging>pom</packaging>.
// This project has no sources, I want <packaging>pom</pom> with dependencies
lazy val bundle = project
.dependsOn(moduleA, moduleB)
.settings(
publishArtifact := false, // Disable jar, sources, docs
publishArtifact in makePom := true,
)
lazy val moduleA = project
lazy val moduleB = project
lazy val moduleC = project
Run sbt bundle/publishM2 to verify the POM in ~/.m2/repository.
I dare say this is almost intuitive, a rare moment of pleasant surprise with sbt 😅
I confirmed this with current sbt 1.3.9, and 1.0.1, the oldest launcher I happen to have installed on my machine.
The Artifacts page in the reference docs may be helpful, perhaps this trick should be added there.

How to create a basic project setup using sbt-native-packager

I have a project setup working with SBT to the point of creating sub-project artifacts.
I have been searching for a way to create a JAR file that contains sub-project JAR files along with some meta information. Based on suggestions, I looked at sbt-native-packager and it seems to have the capabilities I need.
I am wondering if someone would be willing to help me along this path by providing tips on creating a skeleton package specification for the plugin.
I think my configuration is pretty simple.
What I want to end up with is a JAR file with the following contents:
/manifest.xml
module.xml
modules/sub-project-one.jar
sub-project-two.jar
sub-project-three.jar
Both the manifest.xml and module.xml files will be generated from project information. The name of the resulting JAR file will be based on the name of the root project, it's version and a suffix "nkp.jar" (e.g. overlay-1.0.1.nkp.jar).
Thanks in advance for any help getting me going with this.
-- Randy
Here's the basics of what you want:
val createManifestXml = taskKey[File]("Creates the packaged maniest.xml")
val createModuleXml = taskKey[File]("Creates the module.xml file.")
// TODO - Define createManifestXml + createModuleXml
mappings in Universal ++= {
Map(createManifestXml.value -> "manifest.xml"
module.xml -> "module.xml")
}
mappings in Universal ++= {
val moduleJars =
Seq((packageBin in subProjectOne).value,
(packageBin in subProjectTwo).value,
(packageBin in subProjectThree).value)
moduleJars map { jar =>
jar -> s"module/${jar.getName}"
}
}
This will insure that the tgz/txz/zip can be generated. You can then either use the "generic" universal -> msi and universal -> rpm/deb mappings or create that mapping by hand if you desire.
Hope that helps!

Resources