Paths  

Examples

The examples are written in the context of a Project definition and assume the following directory structure:

project/
    .svn/
    src/
        .svn/
        main/
            .svn/
            resources/
                .svn/
                LICENSE
                logo.png
                left.png
                right.png
            scala/
                .svn/
                sbt/
                    .svn/
                    Project.scala
                    Analyzer.scala
    lib/
        ivy.jar
    target/
        sbt.jar
        classes/
            sbt/
                Project.class
                Analyzer.class

Simple Paths

val p = "src" / "main" / "scala" / "sbt" / "Analyzer.scala"

Here, "src" is converted to a Path relative to the project directory by an implicit conversion defined in Project. Alternatively, the implicit conversion may be called directly:

val p = path("src") / "main" / "scala" / "sbt" / "Analyzer.scala"

Either back or forward slashes may be used (they are equivalent):

val p = "src" \ "main" \ "scala" \ "sbt" \ "Analyzer.scala"

Typical usage in sbt is to build up paths:

    def sourcePath = path(sourceDirectoryName)
    def mainSourcePath = sourcePath / mainDirectoryName
    def mainScalaSourcePath = mainSourcePath / scalaDirectoryName

The argument to \ may not contain slashes. If you want to make a relative path that includes slashes, use Path.fromString:

    def aPath: Path = "some" / "path"
    def relPath: Path = Path.fromString(aPath, "a/sub/path")

To convert a java.io.File to a Path, use Path.fromFile. To convert a Path to a File, use asFile. For example:

    def aPath: Path = "some" / "path"
    def aFile: File = aPath.asFile

    def tmpFile: File = new File(System.getProperty(java.io.tmpdir))
    def pathFromFile: Path = Path.fromFile(tmpFile)

Path Finders

val scalaSources: PathFinder = "src" ** "*.scala"

This selects all files that end in .scala that are in src or a descendent directory. The list of paths is not actually evaluated until get is called:

val sourceSet: Set[Path] = scalaSources.get

If the filesystem changes, a second call to get on the same PathFinder object will reflect the changes. That is, the get method reevaluates the list of paths each time. get only returns Paths that existed at the time it was called.

Selecting files that are immediate children of a subdirectory is done with a single *:

val scalaSources = mainScalaSourcePath / "sbt" * "*.scala"

If a selector is used on a path that does not represent a directory, the path list will be empty:

val emptyFinder = "lib" / "ivy.jar" * "not_possible"

The argument to the child and descendent selectors * and ** is actually a NameFilter. An implicit is used to convert a String to a NameFilter that interprets * to represent zero or more characters of any value. See the Name Filters section for more information.

Another operation is concatenation of PathFinders:

val multiPath = ("src" / "main") +++ "lib" +++ ("target" / "classes")

The concatenated finder supports all standard operators. For example,

val jars = ("lib" +++ "target") * "*.jar"

selects ivy.jar and sbt.jar.

There is a filter method that is non-strict:

val srcDirs = ("src" ** "*") filter { _.isDirectory }
val archivesOnly = somePathFinder filter ClasspathUtilities.isArchive

Path.emptyPathFinder is a PathFinder that returns the empty set when get is called. To construct a PathFinder from Iterable[Path], use Path.lazyPathFinder. This method is call-by-name, so its argument will be re-evaluated on each call to get.

Convert a PathFinder to a String using one of the following methods:

  • toString is for debugging. It puts the absolute path of each component on its own line.
  • relativeString gets the relative paths of each component and separates them by the platform's path separator.
  • absString gets the absolute paths of each component and separates them by the platform's path separator.

Base Paths

In some situations, it is useful to define the directory a path is relative to. For example, the package action in sbt packages compiled classes and all files under resources. The full path name should not be used in the jar, however. This is where the ## operator comes in. The paths for this situation would look like:

val allClasses = ("target" / "classes" ##) ** "*.class"
val allResources = ("src" / "main" / "resources" ##) ** "*"
val toPackage = allClasses +++ allResources

Ignoring the .svn directories, the paths in the jar would then look like:

LICENSE
logo.png
sbt/
    Project.class
    Analyzer.class

Note that the ## operator is also defined for PathFinders.

Excluding Paths

A common problem is excluding version control directories. This can be accomplished as follows:

def sources = ("src" ** "*.scala") --- ("src" ** ".svn" ** "*.scala")

The first selector selects all Scala sources and the second removes all sources that are a descendent of a .svn directory. This pattern is common enough that there is a method descendentsExcept that does the same thing as the above:

def sources = "src".descendentsExcept("*.scala", ".svn")

Two methods are available for use in a project definition to consistently exclude paths. They are descendents and defaultExcludes. defaultExcludes provides a filter that descendents uses to exclude paths from its results. The definitions of descendents and defaultExcludes are:

def descendents(parent: PathFinder, include: FileFilter) = parent.descendentsExcept(include, defaultExcludes)
def defaultExcludes: FileFilter = (".*"  - ".") || HiddenFileFilter

You could use this for the above examples like:

def sources = descendents("src", "*.scala")

If you want to change the default exclusions, you can do so by overriding defaultExcludes. For example,

override def defaultExcludes = super.defaultExcludes || "*~"

Name and File Filters

There are some useful operations on NameFilters. The | operator declares alternative NameFilters:

def sources = "src" ** ("*.scala" | "*.java")

The - operator excludes a set of names:

def imageResources = "src"/"main"/"resources" * ("*.png" - "logo.png")

This will get right.png and left.png. - is also available as a prefix operator.

NameFilter can be used for filtering Strings in general using its accept method directly. Additionally NameFilter is a subclass of java.io.FileFilter by way of sbt.FileFilter, which is a thin wrapper around java.io.FileFilter that provides operators for combining filters. Infix operators for sbt.FileFilter are doubled to distinguish them from the operators for NameFilter.

For example:

def filterA: sbt.FileFilter = ...
def filterB: sbt.FileFilter = ...

def filterC = filterA -- filterB
def filterD = filterA || filterB
def filterE = filterA && filterB