This page describes files, sequences of files, and file filters. The base type used is java.io.File, but several methods are augmented through implicits:
sbt 0.10+ uses
java.io.File
to represent a file instead of the custom sbt.Path class that was in
sbt 0.7 and earlier. sbt defines the alias File for java.io.File so
that an extra import is not necessary. The file method is an alias for
the single-argument File constructor to simplify constructing a new
file from a String:
val source: File = file("/home/user/code/A.scala")
Additionally, sbt augments File with a / method, which is an alias for
the two-argument File constructor for building up a path:
def readme(base: File): File = base / "README"
Relative files should only be used when defining the base directory of a
Project, where they will be resolved properly.
val root = Project("root", file("."))
Elsewhere, files should be absolute or be built up from an absolute base
File. The baseDirectory setting defines the base directory of the
build or project depending on the scope.
For example, the following setting sets the unmanaged library directory to be the “custom_lib” directory in a project’s base directory:
unmanagedBase := baseDirectory.value /"custom_lib"
Or, more concisely:
unmanagedBase := baseDirectory.value /"custom_lib"
This setting sets the location of the shell history to be in the base directory of the build, irrespective of the project the setting is defined in:
historyPath := Some( (baseDirectory in ThisBuild).value / ".history"),
A PathFinder computes a Seq[File] on demand. It is a way to build a
sequence of files. There are several methods that augment File and
Seq[File] to construct a PathFinder. Ultimately, call get on the
resulting PathFinder to evaluate it and get back a Seq[File].
The ** method accepts a java.io.FileFilter and selects all files
matching that filter.
def scalaSources(base: File): PathFinder = (base / "src") ** "*.scala"
This selects all files that end in .scala that are in src or a
descendent directory. The list of files is not actually evaluated until
get is called:
def scalaSources(base: File): Seq[File] = {
val finder: PathFinder = (base / "src") ** "*.scala"
finder.get
}
If the filesystem changes, a second call to get on the same
PathFinder object will reflect the changes. That is, the get method
reconstructs the list of files each time. Also, get only returns
Files that existed at the time it was called.
Selecting files that are immediate children of a subdirectory is done
with a single *:
def scalaSources(base: File): PathFinder = (base / "src") * "*.scala"
This selects all files that end in .scala that are in the src
directory.
If a selector, such as /, **, or *, is used on a path that does
not represent a directory, the path list will be empty:
def emptyFinder(base: File) = (base / "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 below for more information.
Another operation is concatenation of PathFinders:
def multiPath(base: File): PathFinder =
(base / "src" / "main") +++
(base / "lib") +++
(base / "target" / "classes")
When evaluated using get, this will return src/main/, lib/, and
target/classes/. The concatenated finder supports all standard
methods. For example,
def jars(base: File): PathFinder =
(base / "lib" +++ base / "target") * "*.jar"
selects all jars directly in the “lib” and “target” directories.
A common problem is excluding version control directories. This can be accomplished as follows:
def sources(base: File) =
( (base / "src") ** "*.scala") --- ( (base / "src") ** ".svn" ** "*.scala")
The first selector selects all Scala sources and the second selects all
sources that are a descendent of a .svn directory. The --- method
removes all files returned by the second selector from the sequence of
files returned by the first selector.
There is a filter method that accepts a predicate of type
File => Boolean and is non-strict:
// selects all directories under "src"
def srcDirs(base: File) = ( (base / "src") ** "*") filter { _.isDirectory }
// selects archives (.zip or .jar) that are selected by 'somePathFinder'
def archivesOnly(base: PathFinder) = base filter ClasspathUtilities.isArchive
PathFinder.empty is a PathFinder that returns the empty sequence
when get is called:
assert( PathFinder.empty.get == Seq[File]() )
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.
absString gets the absolute paths of each component and separates
them by the platform’s path separator.
getPaths produces a Seq[String] containing the absolute paths of
each component
The packaging and file copying methods in sbt expect values of type
Seq[(File,String)] and Seq[(File,File)], respectively. These are
mappings from the input file to its (String) path in the jar or its
(File) destination. This approach replaces the relative path approach
(using the ## method) from earlier versions of sbt.
Mappings are discussed in detail on the Mapping-Files page.
The argument to * and ** is of type
java.io.FileFilter.
sbt provides combinators for constructing FileFilters.
First, a String may be implicitly converted to a FileFilter. The
resulting filter selects files with a name matching the string, with a
* in the string interpreted as a wildcard. For example, the following
selects all Scala sources with the word “Test” in them:
def testSrcs(base: File): PathFinder = (base / "src") * "*Test*.scala"
There are some useful combinators added to FileFilter. The || method
declares alternative FileFilters. The following example selects all
Java or Scala source files under “src”:
def sources(base: File): PathFinder = (base / "src") ** ("*.scala" || "*.java")
The -- method excludes a files matching a second filter from the files
matched by the first:
def imageResources(base: File): PathFinder =
(base/"src"/"main"/"resources") * ("*.png" -- "logo.png")
This will get right.png and left.png, but not logo.png, for
example.