From b2afa2da8c8072d4b7ab72a5c2463779fa6dcf38 Mon Sep 17 00:00:00 2001
From: Bouke van der Bijl <i@bou.ke>
Date: Tue, 20 Dec 2022 14:18:29 +0100
Subject: [PATCH] Initial commit

---
 flake.lock           |  7 ++++
 flake.nix            | 21 ++++++++++++
 lib.nix              | 32 ++++++++++++++++++
 libraries.nix        | 24 +++++++++++++
 packages.nix         | 80 ++++++++++++++++++++++++++++++++++++++++++++
 wrap-arduino-cli.nix | 35 +++++++++++++++++++
 6 files changed, 199 insertions(+)
 create mode 100644 flake.lock
 create mode 100644 flake.nix
 create mode 100644 lib.nix
 create mode 100644 libraries.nix
 create mode 100644 packages.nix
 create mode 100644 wrap-arduino-cli.nix

diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..5999137
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,7 @@
+{
+  "nodes": {
+    "root": {}
+  },
+  "root": "root",
+  "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..9ad00c5
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,21 @@
+{
+  description = "Wrapper for arduino-cli";
+
+  outputs = { self }: {
+    mkArduinoPackageOverlay = packageIndexFile: (self: super: {
+      arduinoPackages = self.lib.recursiveUpdate (super.arduinoPackages or {}) (self.callPackage ./packages.nix {
+        packageIndex = builtins.fromJSON (builtins.readFile packageIndexFile);
+      });
+    });
+
+    mkArduinoLibraryOverlay = libraryIndexFile: (self: super: {
+      arduinoLibraries = self.lib.recursiveUpdate (super.arduinoLibraries or {}) (self.callPackage ./libraries.nix {
+        libraryIndex = builtins.fromJSON (builtins.readFile libraryIndexFile);
+      });
+    });
+
+    overlay = (self: super: {
+      wrapArduinoCLI = self.callPackage ./wrap-arduino-cli.nix { };
+    });
+  };
+}
diff --git a/lib.nix b/lib.nix
new file mode 100644
index 0000000..93e1daf
--- /dev/null
+++ b/lib.nix
@@ -0,0 +1,32 @@
+{ lib }:
+let
+  alt = a: b: if a == null then b else a;
+in
+with builtins; rec {
+  latestVersion = attrs: (builtins.head (builtins.sort (a: b: (builtins.compareVersions a.version b.version) == 1) (builtins.attrValues (builtins.mapAttrs (version: value: { inherit version value; }) attrs)))).value;
+  selectSystem = system: systems:
+    if system == "aarch64-darwin" then
+      alt (lib.findFirst ({host, ...}: (match "arm64-apple-darwin.*" host) != null) null systems) (selectSystem "x86_64-darwin" systems)
+    else if system == "x86_64-darwin" then
+      alt (lib.findFirst ({host, ...}: (match "x86_64-apple-darwin.*" host) != null) null systems) (selectSystem "i686-darwin" systems)
+    else if system == "i686-darwin" then
+      lib.findFirst ({host, ...}: (match "i[3456]86-apple-darwin.*" host) != null) null systems
+    else if system == "aarch64-linux" then
+      lib.findFirst ({host, ...}: (match "(aarch64|arm64)-linux-gnu" host) != null) null systems
+    else if system == "x86_64-linux" then
+      lib.findFirst ({host, ...}: (match "x86_64-.*linux-gnu" host) != null) null systems
+    else null;
+  convertHash = hash: let
+    m = (match "(SHA-256|SHA-1|MD5):(.*)" hash);
+    algo = elemAt m 0;
+    h = elemAt m 1;
+  in
+    if m == null then
+      throw "Unsupported hash format ${hash}"
+    else if algo == "SHA-256" then
+      { sha256 = h; }
+    else if algo == "SHA-1" then
+      { sha1 = h; }
+    else
+      { md5 = h; };
+}
diff --git a/libraries.nix b/libraries.nix
new file mode 100644
index 0000000..a619cd4
--- /dev/null
+++ b/libraries.nix
@@ -0,0 +1,24 @@
+{ fetchzip, stdenv, lib, libraryIndex, pkgsBuildHost, pkgs, arduinoPackages }:
+
+with builtins;
+let
+  inherit (pkgs.callPackage ./lib.nix {}) convertHash;
+    
+  libraries = mapAttrs (name: versions: listToAttrs (map ({version, url, checksum, ...}: {
+    name = version;
+    value = stdenv.mkDerivation {
+      pname = name;
+      inherit version;
+
+      installPhase = ''
+        mkdir -p "$out/libraries/$pname"
+        cp -R * "$out/libraries/$pname/"
+      '';
+      nativeBuildInputs = [ pkgs.unzip ];
+      src = fetchurl ({
+        url = url;
+      } // (convertHash checksum));
+    };
+  }) versions)) (groupBy ({ name, ... }: name) libraryIndex.libraries);
+in
+  libraries
diff --git a/packages.nix b/packages.nix
new file mode 100644
index 0000000..691fa76
--- /dev/null
+++ b/packages.nix
@@ -0,0 +1,80 @@
+# From tools.go in arduino-cli
+#	regexpLinuxArm   = regexp.MustCompile("arm.*-linux-gnueabihf")
+#	regexpLinuxArm64 = regexp.MustCompile("(aarch64|arm64)-linux-gnu")
+#	regexpLinux64    = regexp.MustCompile("x86_64-.*linux-gnu")
+#	regexpLinux32    = regexp.MustCompile("i[3456]86-.*linux-gnu")
+#	regexpWindows32  = regexp.MustCompile("i[3456]86-.*(mingw32|cygwin)")
+#	regexpWindows64  = regexp.MustCompile("(amd64|x86_64)-.*(mingw32|cygwin)")
+#	regexpMac64      = regexp.MustCompile("x86_64-apple-darwin.*")
+#	regexpMac32      = regexp.MustCompile("i[3456]86-apple-darwin.*")
+#	regexpMacArm64   = regexp.MustCompile("arm64-apple-darwin.*")
+#	regexpFreeBSDArm = regexp.MustCompile("arm.*-freebsd[0-9]*")
+#	regexpFreeBSD32  = regexp.MustCompile("i?[3456]86-freebsd[0-9]*")
+#	regexpFreeBSD64  = regexp.MustCompile("amd64-freebsd[0-9]*")
+
+{ fetchzip, stdenv, lib, packageIndex, pkgsBuildHost, pkgs, arduinoPackages }:
+
+with builtins;
+let
+  inherit (pkgsBuildHost.xorg) lndir;
+  inherit (pkgs.callPackage ./lib.nix {}) selectSystem convertHash;
+
+  # Tools are installed in $platform_name/tools/$name/$version
+  tools = listToAttrs (map ({ name, tools, ... }: {
+    inherit name;
+    value = let platformName = name; in mapAttrs (_: versions: listToAttrs (map ({name, version, systems, ...}: {
+      name = version;
+      value = let
+        system = selectSystem stdenv.hostPlatform.system systems;
+      in
+        if system == null then
+          throw "Unsupported platform ${stdenv.hostPlatform.system}"
+        else
+          stdenv.mkDerivation {
+            pname = "${platformName}-${name}";
+            inherit version;
+
+            dirName = "packages/${platformName}/tools/${name}/${version}";
+            installPhase = ''
+              mkdir -p "$out/$dirName"
+              cp -R * "$out/$dirName/"
+            '';
+            nativeBuildInputs = [ pkgs.unzip ];
+            src = fetchurl ({
+              url = system.url;
+            } // (convertHash system.checksum));
+          };
+    }) versions)) (groupBy ({ name, ... }: name) tools);
+  }) packageIndex.packages);
+    
+  # Platform are installed in $platform_name/hardware/$architecture/$version
+  platforms = listToAttrs (map ({ name, platforms, ... }: {
+    inherit name;
+    value = mapAttrs (architecture: versions: listToAttrs (map ({version, url, checksum, toolsDependencies ? [], ...}: {
+      name = version;
+      value = stdenv.mkDerivation {
+        pname = "${name}-${architecture}";
+        inherit version;
+        dirName = "packages/${name}/hardware/${architecture}/${version}";
+
+        toolsDependencies = map ({packager, name, version}: arduinoPackages.tools.${packager}.${name}.${version}) toolsDependencies;
+        passAsFile = [ "toolsDependencies" ];
+        installPhase = ''
+          mkdir -p "$out/$dirName"
+          cp -R * "$out/$dirName/"
+
+          for i in $(cat $toolsDependenciesPath); do
+            ${lndir}/bin/lndir -silent $i $out
+          done
+        '';
+        nativeBuildInputs = [ pkgs.unzip ];
+        src = fetchurl ({
+          url = url;
+        } // (convertHash checksum));
+      };
+    }) versions)) (groupBy ({ architecture, ... }: architecture) platforms);
+  }) packageIndex.packages);
+in
+{
+  inherit tools platforms;
+}
diff --git a/wrap-arduino-cli.nix b/wrap-arduino-cli.nix
new file mode 100644
index 0000000..bc4e469
--- /dev/null
+++ b/wrap-arduino-cli.nix
@@ -0,0 +1,35 @@
+{ lib, pkgs }:
+let
+  wrap = {
+    packages ? []
+    , libraries ? []
+  }:
+  let
+    inherit (pkgs.callPackage ./lib.nix {}) latestVersion;
+
+    builtinPackages = (map latestVersion (builtins.attrValues pkgs.arduinoPackages.tools.builtin));
+
+    libPath = pkgs.symlinkJoin {
+      name = "arduino-libraries";
+      paths = libraries;
+    };
+
+    dataPath = pkgs.symlinkJoin {
+      name = "arduino-data";
+      paths = builtinPackages ++ packages ++ [
+        # Add some dummy files to keep the CLI happy
+        (pkgs.writeTextDir "inventory.yaml" (builtins.toJSON {}))
+        (pkgs.writeTextDir "package_index.json" (builtins.toJSON {packages = [];}))
+        (pkgs.writeTextDir "library_index.json" (builtins.toJSON {libraries = [];}))
+      ];
+    };
+  in
+    pkgs.runCommand "arduino-cli-wrapped" {
+      buildInputs = [ pkgs.makeWrapper ];
+      meta.mainProgram = "arduino-cli";
+    } ''
+      makeWrapper ${pkgs.arduino-cli}/bin/arduino-cli $out/bin/arduino-cli --set ARDUINO_UPDATER_ENABLE_NOTIFICATION false --set ARDUINO_DIRECTORIES_DATA ${dataPath} --set ARDUINO_DIRECTORIES_USER ${libPath}
+    '';
+in
+  lib.makeOverridable wrap
+