In antiquity, installing sbt was sufficient to compile, test, and run most Scala projects. sbt would fetch all your JVM dependencies, and you didn't have other dependencies. There was a Scala CLR target, but it was abandoned.
Years passed, and Scala.js was born, and we were back to crossing platforms. We suddenly needed a Node installation to run our tests. More libraries are supporting Scala Native. Static site generators outside our JVM bubble have come and gone.
"Faugh," you say. "The JVM is good enough for me". Fine, but your libraries target Java 8 for broad appeal and your applications target Java 17 because it's 9 bigger, and you have to juggle multiple JVMs.
Wouldn't it be nice to have one-stop shopping for your development environment again?
Enter the Nix package manager. We can use nix develop to get a full, build enviroment for modern, cross-platform Scala.
For now, you'll need at least Nix 2.4 and to enable the Nix command.
$ java -version
bash: java: command not found
$ sbt -version
bash: sbt: command not found
$ node --version
bash: node: command not foundAll my apes gone.
The one-liner nix develop github:typelevel/typelevel-nix#library gets us a full environment:
$ nix develop github:typelevel/typelevel-nix#library
🔨 Welcome to typelevel-lib-shell
[general commands]
menu - prints this menu
sbt - A build tool for Scala, Java and more
scala-cli - Command-line tool to interact with the Scala language
[versions]
Java - 8.0.292
Node - 16.13.1
$ sbt -version
sbt version in this project: 1.6.1
sbt script version: 1.6.1
$ exitAlternatively, nix develop github:typelevel/typelevel-nix#application gives us the same, with a more contemporary JDK:
$ nix develop github:typelevel/typelevel-nix#application
🔨 Welcome to typelevel-app-shell
[general commands]
menu - prints this menu
sbt - A build tool for Scala, Java and more
scala-cli - Command-line tool to interact with the Scala language
[versions]
Java - 17.0.1
$ sbt -version
sbt version in this project: 1.6.1
sbt script version: 1.6.1
$ exitThe typelevelShell module can be imported into your devshell.mkShell configuration:
{
inputs = {
typelevel-nix.url = "github:typelevel/typelevel-nix";
nixpkgs.follows = "typelevel-nix/nixpkgs";
flake-utils.follows = "typelevel-nix/flake-utils";
};
outputs = { self, nixpkgs, flake-utils, typelevel-nix }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [ typelevel-nix.overlays.default ];
};
in
{
devShell = pkgs.devshell.mkShell {
imports = [ typelevel-nix.typelevelShell ];
name = "my-project-shell";
typelevelShell = {
jdk.package = pkgs.jdk8;
sbtMicrosites = {
enable = true;
siteDir = ./site;
};
};
};
}
);
}if you don't currently have a flake.nix you can create one quickly with the following command.
# For a library (defaults to jdk8)
nix flake init -t github:typelevel/typelevel-nix#library
# For an application service (defaults to jdk23)
nix flake init -t github:typelevel/typelevel-nix#applicationDon't forget to tweak the devShell.name property and customize to taste.
Extra configuration in typelevelShell:
typelevelShell.jdk.package: the JDK package to use forsbtand the$JAVA_HOMEenvironment. Defaults topkgs.jdk17. If you're writing a library, you probably wantpkgs.jdk8.typelevelShell.native.enable: provide a clang environment for scala-native. Defaults totruein the library shell, andfalseelsewhere.typelevelShell.native.libraries: a list of split-output libraries to include (dev output) and link (lib output). Defaults to[ pkgs.zlib ].typelevelShell.nodejs.enable: provide NodeJS. Defaults totruein the library shell, andfalseelsewhere.typelevelShell.nodejs.package: the package to use for NodeJS. Defaults topkgs.nodejs.typelevelShell.sbtMicrosites.enable: enables Jekyll support for sbt-microsites. Defaults tofalse.typelevelShell.sbtMicrosites.siteDir: directory with yourGemfile,Gemfile.lock, andgemset.nix. Run bundix to create a gemset.nix, and every time you upgrade Jekyll.
Not yet, but flake.lock makes it reproducible.
Absolutely! The shell.nix provides a flakes-compatible shell that works with nix-shell. It selects the application shell by default, but you can be specific about it. E.g.
$ nix-shell --argstr shell library
To use it remotely, copy the content of the shell.nix in your project and point src to this repository instead. E.g.
{
src = fetchTarball {
url = "https://github.com/typelevel/typelevel-nix/archive/main.tar.gz";
sha256 = "0000000000000000000000000000000000000000000000000000"; # replace hash
};
};Yes. You should be able to put a flake.nix so you can share with your colleagues or open source collaborators, and even provide a direnv so your $SHELL and $EDITOR do the right thing. Examples forthcoming.
typelevelShell = {
jdk.package = builtins.getAttr "jdk${pkgs.lib.fileContents ./.java-version}" pkgs;
}