Introduction

JP 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 and mkClient 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 the nedrylandType 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.

1

⚠️ 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 of setuptools by default. Template for targetSetup has been updated accordingly. It also conforms to ruffs 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 to mkPlatform for C. This controls what target gets used for dependent components.

Removed

  • targetName no longer gets sent to the mk* 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 than name 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"; on base.languages.python. This will change the global default for the python language. To set it for a single component set installCheckPhase to standardTests or ruffStandardTests or any combination of the two. To individually set the formatter set the formatter on the component to either "standard" (black + isort) or to "ruff" (ruff 🐕). If formatter is not set it will be detected from the value of installCheckPhase.

[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 and man 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 in mkDerivation 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.