Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can 'sbt' accomplish 'makefile'-like functionality

I'm a new scala/sbt user with a ton of experience in writing Makefiles. In GNU make, I could write a file like:

c.txt: a.txt b.txt
       cat a.txt b.txt > $@

The result of that is that, when I run make, c.txt is recreated if either a.txt or b.txt is newer than any existing c.txt ($@ is a mnemonic that represents the name of the current target). If there exists a c.txt which is newer than a.txt and b.txt, then no execution happens.

I have some build steps in my project that are going to be derived from non-scala/java methoodologies, and thus need some make-type dependencies. My expectation would be that 'sbt' can easily do this, since it's a build system that's like 40 years newer than 'make'. But I see nothing clearly articulating this case in the docs.

Could someone provide some examples?

like image 537
Michael Wrighton Avatar asked Jan 29 '26 23:01

Michael Wrighton


1 Answers

This should provide something like what you're asking for.

Here's a simple sbt project, consisting of two files with the following layout:

myproject/build.sbt
myproject/src/main/scala/org/foo/Hello.scala

The build file contains:

name          := "hello"
version       := "0.01"
organization  := "org.foo"
scalaVersion  := "2.12.4"

The scala source file contains:

package org.foo

object Hello extends App {
  println("Hello")
}

Among the predefined sbt tasks is "compile". Issuing the command line "sbt compile" for the first time results in the generation of various files below the "target" directory (most of which you aren't typically concerned with).

Among them are the following:

./target/scala-2.12/classes/org/foo/Hello$.class
./target/scala-2.12/classes/org/foo/Hello$delayedInit$body.class
./target/scala-2.12/classes/org/foo/Hello.class

The compile task actions in this example are somewhat similar to what you would get from the following makefile:

all: ./target/scala-2.12/classes/org/foo ./target/scala-2.12/classes/org/foo/Hello.class

./target/scala-2.12/classes/org/foo:
    mkdir -p ./target/scala-2.12/classes/org/foo

./target/scala-2.12/classes/org/foo/Hello.class:
    scalac -d ./target/scala-2.12/classes/org/foo src/main/scala/org/foo/Hello.scala

Issuing the command line "make all" would result in the same three files being generated:

./target/scala-2.12/classes/org/foo/Hello$.class
./target/scala-2.12/classes/org/foo/Hello$delayedInit$body.class
./target/scala-2.12/classes/org/foo/Hello.class

In either case, you would be able to call the main method in the Hello class with the following command line:

$ scala -cp target/scala-2.12/classes org.foo.Hello
Hello

The "sbt compile" task would also generate 35 other files below the target directory, and in addition it would generate 58 files below the "project" directory, so the comparison is simplistic.

You can list predefined tasks with the following command line:

sbt tasks

On my system (sbt 1.1.2), this prints a list with 32 entries, with descriptions.

If instead of the "compile" task we had issued the "package" task, it would have automatically triggered the "compile" task ("package" depends on "compile" by default) and in addition would have generated a jar file containing the generated .class files:

$ jar -tf target/scala-2.12/hello_2.12-0.01.jar
META-INF/MANIFEST.MF
org/
org/foo/
org/foo/Hello$.class
org/foo/Hello$delayedInit$body.class
org/foo/Hello.class

In general, a build definition accomplishes more with less verbosity than an equivalent makefile, especially if your project relies on default assumptions.

like image 80
philwalk Avatar answered Jan 31 '26 12:01

philwalk



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!