Introduction
Image "from" Jurassic Park © Universal
What is this?
Nedryglot contains a set of Nedryland base extensions with opinionated defaults for a set of programming languages. One core principle however, is that Nedryglot is only additive, i.e. the source package is also usable without any involvement of Nedryglot.
Currently supported languages are Python, Rust and Terraform.
How to use it?
Nedryglot comes in form of two base extensions. One for all supported
languages and one for enabling protobuf support for all languages. To
use in your nedryland project, first obtain the sources (using Niv,
fetchTarball
etc.) and then either include the base extension by
path in the baseExtensions
argument to mkProject
:
mkProject {
# ...
baseExtensions = [
(nedryglotPath + ./languages.nix)
# other extensions...
(nedryglotPath + ./protobuf.nix)
];
}
Alternatively, you can import Nedryglot first, and then include it in
baseExtensions
.
let
nedryglot = import ./path/to/nedryglot/ { };
in
mkProject {
# ...
baseExtensions = [
nedryglot.languages
# other extensions...
nedryglot.protobuf
];
}
After doing this, base.languages
will contain attributes for the
supported languages. Note that protobuf support is optional and to
disable it you can simply omit it from baseExtensions
. If you want
protobuf it should be placed after all extension that defines
languages.
All languages comes with functions to create components for specific purposes,
such as mkLibrary or mkService. These functions takes an attribute set as
argument and creates a target on the component for that purpose, the default
target for each language is the name of the language. Attributes will appear on that
target, to add attributes to the resulting component, use overrideAttrs
.
{ base }:
(base.languages.rust.mkLibrary {
name = "example";
src = ./.;
randomNumber = 4;
}).overrideAttrs (_: {
anotherNumbers = "three";
})
$nix eval -f default.nix example.rust.randomNumber --raw
>4
$nix eval -f default.nix example.anotherNumber --raw
>three
Rust components
mkLibrary mkService mkClient mkComponent
mkService
andmkClient
builds binaries.mkLibrary
creates an unpacked rust package (unpacked .crate file) since rust builds everything from source, which can be depended on by other components.mkComponent
is made for building extensions where you can create your own Nedryland component types by passing in thenedrylandType
string.
Specifying Crate Dependencies
In order to be deterministic crates are downloaded and verified ahead of building, and
these crates need to be made available to the build environment. This is done through
buildInputs
and propagatedBuildInputs
. Crates can be be defined using
base.languages.rust.fetchCrate {name = "some-crate-name"; version = "x.y.z"; sha256 = "sha"; deps = [crate]}
where sha256 is the same as you will find on
crates index and deps is a list of these
kind of derivations.
To ease writing of multiple such expressions there's a CLI tool
gen-crate-expression
both in each rust shell and as an app in Nedryglot's
flake. Use gen-crate-expression --help
for usage information.
A set of crates has also been generated and ships with nedryglot under
base.languages.rust.crates
. These are roughly equivalent to the crates available in
Rust Playground (that is, the 100 most downloaded crates on
crates.io). This default crate set can be
overridden by making an extension. This extension overrides the crate set with a set of
one crate:
{ base }:
{
languages.rust = base.languages.rust.overrideAttr {
crates = {
gimli = base.languages.rust.fetchCrate {
name="gimli";
version="0.27.3";
sha256="b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"; deps=[];
};
};
};
}
Specifying Other Dependencies
Specifying dependencies in a Rust component can be done in two ways. First option is to
assign any of the standard mkDerivation
arguments a list with the needed inputs.
# in the component nix file
{ base, lib, hello }:
base.languages.rust.mkClient {
name = "krabba";
src = ./.;
# always depend on hello from the build platform
buildInputs = [ hello ]
}
In the above example, note that we are always depending on the hello
package from the
build platform (see below). If we are not cross-compiling, this is
fine.
To ease cross compilation, a concept called splicing
is used and to take
advantage of that, the same arguments can also be assigned a function with a single
argument pkgs
. This function must return a list with the desired dependencies. pkgs
will be the correct package set for the current platform, making it possible to use the
same dependency specification for multiple platforms.
# in the component nix file
{ base, lib }:
base.languages.rust.mkClient {
name = "krabba";
src = ./.;
buildInputs = pkgs:
# depend on hello for all platforms
[ pkgs.hello ]
# depend on pthreads only for windows
++ lib.optional (pkgs.stdenv.hostPlatform.isWindows) pkgs.windows.pthreads
# and something only for wasi
++ lib.optional (pkgs.stdenv.hostPlatform.isWasi) pkgs.someWasiDependency
}
Cross Compilation
Nedryglot supports adding cross build targets to the rust language in addition
to the provided default target. The default target is always called rust
irrespective of platform. To add more targets either override base.languages.rust
and send in more crossTargets
, which by default is an empty set.
About Platforms
The platform concept is borrowed from tools like GNU autotools and might require some explanation since it is central to cross-compilation and not entirely intuitive.
Build Platform
This is the platform we are currently building on.
Host Platform
This is the platform we are building for. I.e. the platform that the software will run on. If we are not cross compiling, build platform and host platform are always the same.
Target Platform
This only makes sense if the software is compiler-like. For example, a compiler built on Linux for Linux will have Linux as build platform and host platform. However, even though it runs on Linux, it might produce binaries for Windows. I.e. Windows is the target platform.
mkCrossTarget
Nedryglot exposes mkCrossTarget
through base.languages.rust.mkCrossTarget
. This
function allows you to create your own custom cross target with the correct
arguments. It furthermore allows adding attributes to all targets using that target
definition.
# ...
myCrossTarget = base.languages.rust.mkCrossTarget {
name = "🐟-Ƽ";
pkgs = pkgsCross.fiscFive;
# attrs requires a function with one argument,
# containing the attributes from the package using
# the cross target
attrs = pkgAttrs: {
# the target requires the special
# build input "blubb" to produce
# valid builds
buildInputs =
base.languages.rust.combineInputs
(pkgAttrs.buildInputs or [ ])
(pkgs: [ pkgs.blubb ])
};
};
Note also the use of combineInputs
to combine input declarations, irrespective of if
they are functions or lists.
To add cross targets per component:
# in the component nix file.
{ base, pkgsCross }:
(base.languages.rust.override {
crossTargets = {
windows = base.languages.rust.mkCrossTarget {
name = "Windows";
pkgs = pkgsCross.mingwW64;
};
};
}).mkClient {
...
}
To add a cross target for every rust component in the project
# project.nix
nedryland.mkProject {
name = "windows-target-example";
baseExtensions = [ ./rust-target-extension.nix ];
}
The cross targets are exposed as <component>.<target>
which would in the above example
be <component>.windows
. As stated above, the default target is always called rust
.
# rust-target-extension.nix
{ base, pkgsCross }:
{
languages.rust = base.languages.rust.override {
crossTargets = {
windows = base.languages.rust.mkCrossTarget {
name = "Windows";
pkgs = pkgsCross.mingwW64;
};
};
};
}
In the above example, the project-wide meaning of base.languages.rust
is changed to
include cross-builds for Windows. However, it is also possible to create different
variations of the language by assigning to a different attribute,
e.g. base.languages.rustWithWindows
.
When a cross compilation target has been added project-wide, it is still possible to opt out of using it by overriding locally in a component. For example, to only build for the build platform:
# in the component nix file
{ base }:
(base.languages.rust.override {
crossTargets = { };
}).mkClient {
...
}
This will cause the above added windows
target to be removed and this component will
only build for the build platform, accessible on the component target rust
.
Overriding the Default Target
A component might only make sense to build for a cross target. To change the meaning of
the default target (rust
), override the attribute rust
in the cross target set, like
this:
# in the component nix file
{ base }:
(base.languages.rust.override {
crossTargets = {
rust = base.languages.rust.mkCrossTarget {
name = "Windows";
pkgs = pkgsCross.mingwW64;
};
}).mkClient {
...
}
This will cause rust
to correspond to a Windows build of the component. It is also
possible to refer to an existing cross target through crossTargets
# in the component nix file
{ base }:
(base.languages.rust.override {
crossTargets = {
rust = base.languages.rust.crossTargets.windows;
}).mkClient {
...
}
This will yield the same result as the previous example if windows
is already a defined target.
Adding Cross Targets
Using override
on the rust language means that any cross target definitions sent in will
replace the existing ones (with the exception of rust
which is added if it does not
exist in crossTargets
). To add a new cross target without replacing the existing ones,
use overrideCrossTargets
:
# rust-target-extension.nix
{ base, pkgsCross }:
{
languages.rust = base.languages.rust.overrideCrossTargets (prevTargets: {
windows = base.languages.rust.mkCrossTarget {
name = "Windows";
pkgs = pkgsCross.mingwW64;
};
});
}
This will add the windows
target without removing any of the pre-existing targets. Note
that overrideCrossTargets
also expects a function for the first argument, which gives
access to the set of crossTargets
that existed before the call to
overrideCrossTargets
. This makes it easier to create new targets based on existing ones.
Overriding the Rust Version
base.languages.rust
exposes the function mkRustToolset
to change the
build/lint tools. This function takes a number of required arguments to override
the specific tools. The non-overriden tools are the following:
nedryland.mkProject
{
baseExtensions = [
# Nedryglot itself needs to be imported first to have something to override.
nedryglot
# Then we define our override.
({ base }: {
languages.rust = base.languages.rust.override (base.languages.rust.mkRustToolset {
rustc = nixpkgs.rustPlatform.rustc;
cargo = nixpkgs.rustPlaform.cargo;
clippy = nixpkgs.clippy;
rust-analyzer = nixpkgs.rust-analyzer;
rustfmt = nixpkgs.rustfmt;
});
})
];
}
Individual components can of course override the rust version. This can be useful when needing new features from nightly, without forcing the entire Nedryland project to use nightly. This example shows
how to make a service with a different rust version, assuming the variable nightly
has been created with attributes:
{ base }:
(base.languages.rust.override (base.languages.rust.mkRustToolset {
rustc = nightly.rustc;
cargo = nightly.cargo;
clippy = nightly.clippy;
rust-analyzer = nightly.rust-analyzer;
rustfmt = nightly.rustfmt;
})).mkService {
name = "example-service";
src = ./.;
}
See the override example for how to use the oxalica overlay.
Python components
Specifying dependencies
shellInputs
, checkInputs
, buildInputs
, nativeBuildInputs
and
propagatedBuildInputs
attributes can specified as either a list or a
function. In the case of a function the function will be called with
the python package set for the current interpreter.
Alternatively the whole set passed to mkLibrary, mkClient, mkComponent and mkService can be a function that gets intersected with the set of python packages for the current interpreter.
buildInputs
buildInputs
for python components are dependencies are going to be built and
linked. For runtime dependencies use propagatedBuildInputs
.
propagatedBuildInputs
Often when making python components you want to put your python dependencies
into propagatedBuildInputs. This is because it maps to install_requires in
setup.py/setup.cfg
. If you put myOwnPythonPackageDependency
as a
buildInput
you would be able to test and run the library locally in the shell
but if another package depended on example-lib
the other package would get
errors because myOwnPythonPackageDependency
would be missing.
{ pkgs, base, myOwnPythonPackageDependency }:
base.languages.python.mkLibrary rec {
name = "example-lib";
version = "1.0.0";
src = ./.;
propagatedBuildInputs = [ myOwnPythonPackageDependency ];
nativeBuildInputs = pythonPkgs: [ pythonPkgs.setuptools ];
}
Outputs
If the component is setting format
to one of setuptools
, pyproject
or
flit
it will build a wheel and the derivation will become a multi output
derivation with wheel
(the wheel) and out
(install of the wheel). The
wheel
output of the python
target is also added to the component as a
target. If base.languages.python
is overridden to be a set of python versions,
one output is generated corresponding to each version, and each version have a
wheel
output. The wheel on the component corresponds to the python
target
(the default version).
Checks and Config
The installCheckPhase
(which is what python uses instead of checkPhase
) runs
a function called standardTests
(can be turned off by setting
doStandardTests = false;
), this function runs pytest with black, coverage,
flake8, mypy, isort and pylint. These tools are wrapped with a config that is
generated by merging a default config from Nedryglot with a config for that tool
in the component1. To inspect the resulting config, use the
show-generated-config
shell command.
Python in Nedryglot exposes a set of hooks, one of which is check
which adds
all of the above, except adding it to the installCheckPhase
. This can be used
to add python linting to other types of components.
⚠️ Note that Nedryglot does not support pylint config in setup.cfg.
In case you want to use ruff instead of black, isort, pylint, and
flake8, you can set installCheckPhase
to ruffStandardTests
instead.
Formatting
By default the formatter will follow the selected check variant (ruff
or black+isort). However, this can be overridden by setting the
formatter
attribute to ruff
or standard
.
Api docs
Nedryglot will output python API docs, by default using Sphinx, the sphinx theme can be set with the config attribute:
[docs.python]
# Currently only supported theme, other than default.
sphinx-theme = "rtd"
# Built-in extensions to activate, `napoleon` is enabled by default.
sphinx-extensions = [ "graphviz" ]
Nedryglot also supports generating docs using pdoc by setting the config
[docs.python]
generator = "pdoc"
Api docs config with docsConfig attribute
Per component docs configuration can be done by settings docsConfig for the python component.
autodocMockImports for sphinx
To avoid errors with autodoc and missing python modules that might not be available when generating the documentation we can set autodocMockImports for the python component as:
base.languages.python.mkLibrary rec {
...
docsConfig = {
autodocMockImports = [
"python_module1"
"python_module2"
];
};
...
}
Overriding Python Version
By default python components in Nedryglot have the target python
,
this corresponds to the default python version (defaults to python3 in
the package set). Nedryglot can also build multiple versions on the
same component, and when depending on components it selects the target
of the same python version as itself. To override the python version
use base.languages.python.override
with an attribute set containing
python
(the default) and any other python versions in
pythonVersions
. These python versions are assumed to look like
pythonXX
in nixpkgs.
nedryland.mkProject
{
baseExtensions = [
# Nedryglot itself needs to be imported first to have something to override.
nedryglot
# Then we define our override.
({ base, pkgs }: {
languages.python = base.languages.python.override {
# Set the default python version.
python = pkgs.python38;
pythonVersions = {
# Add python38, python39 and python310 targets
inherit (pkgs) python38 python39 python310;
};
};
})
];
}
Changing the default checkphase
base.languages.python.override {
# Change the default tests and formatter to use ruff
defaultCheckPhase = "ruffStandardTests";
};
The above example changes the default tests and formatter to use ruff.
C/C++
Declaring Components
Components using C or C++ are grouped under base.languages.c
and use the same toolset.
Use mkClient
, mkService
or mkLibrary
to create components of the
corresponding type. Available tools for the development shell include clangd,
valgrind and doxygen. Doxygen comes wrapped with a theme and some default settings.
Specifying Dependencies & Cross Compilation
A minimal component setup can look like this:
{ languages }:
languages.c.mkLibrary {
name = "example";
version = "4.4.4";
src = ./.;
}
However, when dependencies are needed it is recommended to define the component attributes as a function with those dependencies as an input:
{ languages }:
languages.mkLibrary ({ harfbuzz }: {
name = "example";
version = "4.5.3";
src = ./.;
buildInputs = [ harfbuzz ];
})
When using a function it will be called with packages for the target platform, making cross compilation work correctly.
When depending on other Nedryland components they will be resolved to the correct platform as well, but only when used in any of the variations of the buildInputs attributes.
{ languages }:
languages.mkLibrary ({ harfbuzz, componentA }: {
name = "example";
version = "5.0.0";
src = ./.;
buildInputs = [ harfbuzz componentA ];
randomAttr = [ componentA ];
})
In this example componentA will be resolved to the correct platform in
buildInputs, but in randomAttr it will still be the whole component. To manually
resolve a target, the targetName
string can be depended on:
{ languages }:
languages.mkLibrary ({ harfbuzz, componentA, targetName }: {
name = "example";
version = "5.0.0";
src = ./.;
buildInputs = [ harfbuzz componentA ];
randomAttr = [ componentA.${targetName} ];
})
Documentation
C/C++ components comes with a Doxygen with some defaults (project name and
version set to the component's name and version for example), these defaults can
be overriden by the Doxyfile in the component's directory. To view the resulting
config use doxygen --print-generated-config
. This doxygen also comes with
doxygen-awesome-css for a
more modern styling.
Overriding platforms
In the c language the notion of a platform is the same as the notion of a platform in nix with the addition of toolset. In other words linux+clang and linux+gcc are different platforms.
Platforms in the c language controls which targets components generate
when using the mk*
functions.
For example if there is a platform definition for windows
a
component created with for example mkLibrary
will have a windows
target.
Build Systems
Since C and C++ have many different build systems without any clear standard, nedryglot makes no assumptions about which one is used.
Adding support for a custom build system would look something like the factory example.
Examples
Adding a platform definition
base.languages.c.overridePlatforms
(_platforms: {
# This will be accessible as <component>.riscv
riscv = base.languages.c.mkPlatform {
name = "RISC-V";
pkgs = pkgsCross.riscv64;
platformOverrides = attrs {
# This will ensure all derivations created with this platform will
# have the hello example as dependency.
buildInputs = attrs.buildInputs or [] ++ [ pkgsCross.riscv64.hello ];
};
};
linux-clang = base.languages.c.mkPlatform {
name = "Linux-Clang";
inherit pkgs;
stdenv = pkgs.clangStdenv;
# This will be accessible as <component>.clang
output = "clang";
};
})
}
Changing an existing platform definition
Changes the target name by adding the prefix mega.
base.languages.c.overridePlatforms
(builtins.mapAttrs (
platformName: platform:
platform.override {
output = "mega-${platformName}";
}
))
Changing the platforms factory function (mkDerivation
)
In this example we change the default doxygenOutputDir
. The
derivation is modified to add the prefix "sune" to all derivation
names, also adds an extra buildInput.
base.languages.c.overridePlatforms
(builtins.mapAttrs (
_platformName: platform:
platform.overrideFactory {
overrideAttrs = attrs: {
# All derivations will have the name sune
name = "sune-${attrs.name}";
buildInputs = [ platform.pkgs.tbb ] ++ attrs.buildInputs or [];
};
doxygenOutputDir = "special/docs/folder";
}
))
Terraform
Support for building and deploying terraform projects.
Tests
During checkPhase
terraform fmt
and terraform validate
will be
run.
Shell commands
Provides a terraform command that runs terraform which can optionally
disable apply in case deployment is done in CI. The default is to
disable apply
in the shell. If you want apply enabled pass
enableApplyInShell = true
when creating your component.terraform.tfstate
Deployment
Deployment uses a custom script that wraps terraform apply and
plan. Additionally you can give the script a path to the terraform
sources, however everything will be set up automatically for you on
the target deployment.terraform
.
This means you can give the generated deploy script arguments such as
plan
or apply
.
Overriding the terraform version
By default it will use the terraform package from whatever nixpkgs
version you are using.
nedryland.mkProject
{
baseExtensions = [
# Nedryglot itself needs to be imported first to have something to override.
nedryglot
# Then we define our override.
({ base, pkgs }: {
languages.terraform = base.languages.terraform.override {
terraform = pkgs.terraform_0_13;
};
})
];
}
Protobuf
Nedryglot supports generating protobuf types for any language that has
a fromProtobuf
function in their language set. Of the languages that
Nedryglot supports, protobuf is enabled for python and rust.
Declaring a protobuf component
The languages
argument for mkModule
decides which languages to
generate protobuf code for.
{ base }:
base.languages.protobuf.mkModule {
name = "example";
version = "1.0.0";
src = ./.;
languages = [ base.languages.python base.languages.rust ];
}
The created module has a property for each language which contains a component that in turn contains a library with the protobuf code for that language. This component can be used as an input to other targets.
{ base , protocols }:
base.languages.rust.mkClient {
name = "pruttocols";
src = ./.;
buildInputs = [ protocols ];
}
Protobuf dependencies
When declaring a protobuf module it is possible to depend on another
protobuf module by using the protoInputs
argument to mkModule. This
can be any component created with base.languages.protobuf.mkModule
.
{ base, baseProtocols }:
base.languages.protobuf.mkModule {
name = "ext";
version = "1.0.0";
src = ./.;
languages = [ base.languages.python base.languages.rust ];
protoInputs = [ baseProtocols ];
}
Protobuf services
When supplying mkModule
with includeServices = true
grpc code will
additionally be generated for the component.
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
Added
- Python now generates a
run
command and cli entry point for clients and services.
Changed
- Python now uses
pyproject
instead ofsetuptools
by default. Template for targetSetup has been updated accordingly. It also conforms toruff
s checks and formatting by default.
Fixed
- Python's config merger now handles mypy overrides targeting multiple packages.
- C/C++ format commands used
[]
instead of{}
in the fd glob expression so it matched single chars.
[5.0.0] - 2024-10-15
Added
input
parameter tomkPlatform
for C. This controls what target gets used for dependent components.
Removed
targetName
no longer gets sent to themk*
functions for C.
[4.1.5] - 2024-09-13
Fixed
- Overriding the factory of a C platform caused the 'override' function to disappear from the resulting set.
output
now works as expected on C platforms. Previously, setting output to a different value thanname
created a broken configuration.
[4.1.4] - 2024-06-03
Fixed
- Python check runner not finding config when not in root of component.
- Fix some outside tools not getting check configs because TMP is not always set.
[4.1.3] - 2024-03-22
Fixed
- Flake8 not getting configuration.
[4.1.2] - 2024-03-22
Fixed
- Sphinx documentation can now access the nativeBuildInputs of the source package.
- Python coverage tool not getting proper configuration.
[4.1.1] - 2024-03-21
Fixed
- Terraform deploy now sends sigint to terraform on ctrl+C, giving it a chance to shut down a little more gracefully.
[4.1.0] - 2024-02-16
Added
- Naming checks for the default ruff configuration.
Fixed
- Missing shebang in python check tools. This caused errors depending on which exec function you used.
[4.0.0] - 2024-02-14
Added
- Support for overriding python linters config.
Changed
- Ruff default configuration. Now picks upp issues similar to to the alternative (isort, black, flake etc).
[3.1.0] - 2024-02-08
Added
- Ruff support for python components. Currently have to opt in by
setting
defaultCheckPhase = "ruffStandardTests";
onbase.languages.python
. This will change the global default for the python language. To set it for a single component setinstallCheckPhase
tostandardTests
orruffStandardTests
or any combination of the two. To individually set the formatter set theformatter
on the component to either "standard" (black + isort) or to "ruff" (ruff 🐕). If formatter is not set it will be detected from the value ofinstallCheckPhase
.
[3.0.0] - 2024-02-06
Added
gen-default-crates
command to generate the list of default crates shipped with Nedryglot.- Support for nixpkgs versions up to 23.11
Changed
- Now uses Nedryland version 10.0.0
[2.0.1] - 2023-12-05
Fixed
- C does not expect
doc
andman
output if doxygen is disabled. - C platform override now lets the user override all attributes of the derivation, not
just the attributes sent in. For example,
lintPhase
was hardcoded inmkDerivation
for all C platforms.
[2.0.0] - 2023-11-07
Added
build
shell command for python.
Fixed
- Terraform deploy will now exit with non-zero exit code if deploy failed.
- Give pylint a $HOME so it won't complain when writing cache and stats.
Changed
- Nedryglot now requires version 9.0.0 of nedryland or higher.
- Python linting tools now combines their configs during runtime instead of build time.
- Rust components now only uses vendored dependencies. Dependencies
can be vendored with the
fetchCrate
function. Refer to the docs for more information.
[1.2.2] - 2023-06-9
Fixed
- Config merge script including non mypy settings for mypy.
[1.2.1] - 2023-06-1
Fixed
- Config merge script not considering if certain fields did not exist.
[1.2.0] - 2023-05-31
Added
- Add support for supplying python check tools with arguments (black, isort, pylint, flake8, mypy, pytest)
Fixed
- mypy overrides not working in for example pyproject.toml.
[1.1.0] - 2023-04-21
Added
- docsConfig added for python components.
- C/C++ as a supported language. The language currently makes no assumptions on build system. See the documentation for more information.
Fixed
- Fixed black nix store check.
- Make sure python check tool runners has a shell shebang.
[1.0.3] - 2023-02-06
Fixed
- Disabled black on aarch64-darwin since it is broken in nixpkgs 22.05.
- Hide check command if checks are not available.
Changed
- The merged/generated configs for python's linters do not check the filesystem during nix eval.
Added
- Tests for nixpkgs 22.11
[1.0.2] - 2023-01-17
Fixed
- Python checks were accidentally left off, they are now on by default.
[1.0.1] - 2023-01-13
Fixed
- Python version check to use the correct attribute and check for non-Nedryland components.
[1.0.0] - 2023-01-11
Added
- Broke out languages from Nedryland into Nedryglot with notable changes:
- All languages can be overridden.
- Added shell setup for terraform language.
- New and updated documentation.
- New examples.
- Python language now supports several versions of python. Creates one target per version.
- Rust language now uses rust from nixpkgs by default.
- Languages no longer uses the generic
package
attribute. There shouldn't be any blessed targets or assumptions.