Mark Needham

Thoughts on Software Development

Archive for the ‘sbt’ tag

Sbt: Rolling with continuous/incremental compilation and Jetty

without comments

As I mentioned in an earlier post we’re using SBT on our project and one of it’s cool features is that it will listen to the source directory and then automatically recompile the code when it detects file changes.

We’ve also installed the sbt-jetty-embed plugin which allows us to create a war which has Jetty embedded so that we can keep our application containerless.

That plugin adds an action called ‘jetty’ to sbt so we (foolishly in hindsight) thought that we would be able to launch the application in triggered execution mode by making use of a ~ in front of that:

$ ./sbt
> ~jetty

Unfortunately that doesn’t do any continuous compilation which left us quite confused until we realised that RTFM might be a good idea…

What we actually needed to launch the application locally and edit code/see changes as if it’s written in a dynamic language was the following:

$ ./sbt
> jetty-run
> ~ prepare-webapp

Written by Mark Needham

June 10th, 2011 at 12:16 am

Posted in Build,Scala

Tagged with

Sbt: Zipping files without their directory structure

with one comment

We’re using SBT on our project and Pat and I have been trying to work out how to zip together some artifacts so that they’re all available from the top level of the zip file i.e. we don’t want to copy the directory structure where the files come from.

I’ve been playing around with this in the Scala REPL which we can launch with our project’s dependencies loaded with the following command:

./sbt console-project

Our original attempt to zip together the artifacts looked like this:

FileUtilities.zip(List(("ops" / "deploy")), "dist.zip", true, log)

But unfortunately that keeps the directory structure which isn’t what we want!

mneedham@markneedham.home ~/Projects/core$ unzip -l dist.zip 
Archive:  dist.zip
  Length     Date   Time    Name
 --------    ----   ----    ----
        0  06-04-11 17:52   ops/
        0  06-04-11 17:52   ops/deploy/
     2534  06-03-11 17:47   ops/deploy/start-server.sh
 --------                   -------
     2534                   3 files

Pat figured out that what we needed to do was make use of the ## function after our path so our code would read like this:

FileUtilities.zip(List(("ops" / "deploy") ##), "dist.zip", true, log)

Et voila:

mneedham@markneedham.home ~/Projects/core$ unzip -l dist.zip 
Archive:  dist.zip
  Length     Date   Time    Name
 --------    ----   ----    ----
     2534  06-03-11 17:47   start-server.sh
 --------                   -------
     2534                   1 file

The ## function is defined like so and converts a path object into a BaseDirectory:

28
override def ## : Path = new BaseDirectory(this)

The code in FileUtilities that generates an entry for each file in the zip file looks like this:

164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
               def makeFileEntry(path: Path) =
                {   
                        val relativePath = path.relativePathString("/")
                        log.debug("\tAdding " + path + " as " + relativePath + " ...")
 
                        val e = createEntry(relativePath)
                        e setTime path.lastModified
                        e   
                } 
 
                def addFileEntry(path: Path)
                {   
                        val file = path.asFile
                        if(file.exists)
                        {   
                                output putNextEntry makeFileEntry(path)
                                transferAndClose(new FileInputStream(file), output, log)
                                output.closeEntry()
                        }   
                        else
                                log.warn("\tFile " + file + " does not exist.")
                }

Line 179 is where the meta data is defined for the archive and it makes use of “relativePathString” which has been overriden by BaseDirectory to return “”:

83
84
85
86
87
88
89
90
91
private final class BaseDirectory(private[sbt] val path: Path) extends Path
{
        override def ## : Path = this
        override def toString = path.toString
        def asFile = path.asFile
        def relativePathString(separator: String) = ""
        def projectRelativePathString(separator: String) = path.projectRelativePathString(separator)
        private[sbt] def prependTo(s: String) = "." + sep + s 
}

Line 176 returns the file in its original location so it can still be copied into the archive.

The problem with using an identifier like ## is that it’s very difficult to Google so you end up trawling the source code for its uses or hoping that you can find the explanation for its use in the documentation!

Written by Mark Needham

June 4th, 2011 at 5:24 pm

Posted in Build,Scala

Tagged with ,