diff --git a/.gitignore b/.gitignore index f03ce8a..e22a2fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ .devenv .direnv +.idea/ + +result diff --git a/err.go b/err.go index 0c218d2..0aff5c8 100644 --- a/err.go +++ b/err.go @@ -1,17 +1,18 @@ package gonix -// #cgo pkg-config: nix-expr-c +// #cgo pkg-config: nix-expr-c nix-main-c // #include // #include // #include // #include +// #include import "C" import ( "errors" "fmt" ) -func nixError(err C.int, ctx *Context) error { +func nixError(err C.nix_err, ctx *Context) error { switch err { case C.NIX_OK: return nil diff --git a/external.go b/external.go index 2e2bf58..1ff9e2b 100644 --- a/external.go +++ b/external.go @@ -61,7 +61,7 @@ type ExternalValueProvider interface { CoerceToString(addContext func(string) error, copyMore, copyToStore bool) (string, error) // Equal is called for a comparison of two external values. If `other` is not an external - // value, Equal isn't called and instead `false` is always returned. + // cvalue, Equal isn't called and instead `false` is always returned. Equal(other ExternalValueProvider) bool // PrintValueAsJSON is called when the value is converted to JSON. The result must @@ -72,7 +72,7 @@ type ExternalValueProvider interface { // PrintValueAsXML(strict, location, copyToStore bool) string } -// ExternalValue is a wrapper around a go value passed into nix. +// ExternalValue is a wrapper around a go cvalue passed into nix. type ExternalValue struct { cev *C.ExternalValue } diff --git a/external_test.go b/external_test.go index a205ab5..7b9bbe6 100644 --- a/external_test.go +++ b/external_test.go @@ -28,7 +28,7 @@ func (v *simpleExternal) Equal(other gonix.ExternalValueProvider) bool { } func (v *simpleExternal) PrintValueAsJSON(strict, copyToStore bool) string { - return `{ "value" : "simpleexternal" }` + return `{ "cvalue" : "simpleexternal" }` } func ExampleState_NewExternalValue() { @@ -91,5 +91,5 @@ func ExampleExternalValueProvider_PrintValueAsJSON() { } fmt.Println(res) - // Output: {"value":"simpleexternal"} + // Output: {"cvalue":"simpleexternal"} } diff --git a/flake.lock b/flake.lock index 99036b5..d107766 100644 --- a/flake.lock +++ b/flake.lock @@ -2,30 +2,32 @@ "nodes": { "cachix": { "inputs": { - "devenv": "devenv_2", + "devenv": [ + "devenv" + ], "flake-compat": [ + "devenv" + ], + "git-hooks": [ "devenv", - "flake-compat" + "git-hooks" ], "nixpkgs": [ "devenv", "nixpkgs" - ], - "pre-commit-hooks": [ - "devenv", - "pre-commit-hooks" ] }, "locked": { - "lastModified": 1712055811, - "narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=", + "lastModified": 1748883665, + "narHash": "sha256-R0W7uAg+BLoHjMRMQ8+oiSbTq8nkGz5RDpQ+ZfxxP3A=", "owner": "cachix", "repo": "cachix", - "rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30", + "rev": "f707778d902af4d62d8dd92c269f8e70de09acbe", "type": "github" }, "original": { "owner": "cachix", + "ref": "latest", "repo": "cachix", "type": "github" } @@ -33,52 +35,21 @@ "devenv": { "inputs": { "cachix": "cachix", - "flake-compat": "flake-compat_2", - "nix": "nix_2", - "nixpkgs": "nixpkgs_2", - "pre-commit-hooks": "pre-commit-hooks" - }, - "locked": { - "lastModified": 1716847473, - "narHash": "sha256-Sn3PpgNAmV9pLr8pefvVAiHzBebq23nFf5QeHqMfVGk=", - "owner": "cachix", - "repo": "devenv", - "rev": "60ed3dba92ed027bb886611b53cf9b9d1e7f149e", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "devenv", - "type": "github" - } - }, - "devenv_2": { - "inputs": { - "flake-compat": [ - "devenv", - "cachix", - "flake-compat" - ], + "flake-compat": "flake-compat", + "git-hooks": "git-hooks", "nix": "nix", - "nixpkgs": "nixpkgs", - "poetry2nix": "poetry2nix", - "pre-commit-hooks": [ - "devenv", - "cachix", - "pre-commit-hooks" - ] + "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1708704632, - "narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=", + "lastModified": 1751820494, + "narHash": "sha256-y8IGLpeJ/IvAiBaS17hkSC21WsCw2f4GHxg3GxvMTro=", "owner": "cachix", "repo": "devenv", - "rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196", + "rev": "c5f01223c737cd749f994b772ae84445acd45a9d", "type": "github" }, "original": { "owner": "cachix", - "ref": "python-rewrite", "repo": "devenv", "type": "github" } @@ -86,43 +57,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-compat_2": { - "flake": false, - "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-compat_3": { - "flake": false, - "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "lastModified": 1747046372, + "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", "owner": "edolstra", "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", "type": "github" }, "original": { @@ -134,16 +73,17 @@ "flake-parts": { "inputs": { "nixpkgs-lib": [ + "devenv", "nix", "nixpkgs" ] }, "locked": { - "lastModified": 1712014858, - "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", + "lastModified": 1733312601, + "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", + "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", "type": "github" }, "original": { @@ -152,54 +92,28 @@ "type": "github" } }, - "flake-utils": { + "git-hooks": { "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1689068808, - "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_2": { - "inputs": { - "systems": "systems_2" - }, - "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", - "type": "github" + "flake-compat": [ + "devenv", + "flake-compat" + ], + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "nixpkgs" + ] }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_3": { "locked": { - "lastModified": 1667395993, - "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "lastModified": 1749636823, + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "623c56286de5a3193aa38891a6991b28f9bab056", "type": "github" }, "original": { - "owner": "numtide", - "repo": "flake-utils", + "owner": "cachix", + "repo": "git-hooks.nix", "type": "github" } }, @@ -207,13 +121,12 @@ "inputs": { "nixpkgs": [ "devenv", - "pre-commit-hooks", + "git-hooks", "nixpkgs" ] }, "locked": { "lastModified": 1709087332, - "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", "owner": "hercules-ci", "repo": "gitignore.nix", "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", @@ -225,209 +138,63 @@ "type": "github" } }, - "libgit2": { - "flake": false, - "locked": { - "lastModified": 1697646580, - "narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=", - "owner": "libgit2", - "repo": "libgit2", - "rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5", - "type": "github" - }, - "original": { - "owner": "libgit2", - "repo": "libgit2", - "type": "github" - } - }, "nix": { - "inputs": { - "flake-compat": "flake-compat", - "nixpkgs": [ - "devenv", - "cachix", - "devenv", - "nixpkgs" - ], - "nixpkgs-regression": "nixpkgs-regression" - }, - "locked": { - "lastModified": 1712911606, - "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", - "owner": "domenkozar", - "repo": "nix", - "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", - "type": "github" - }, - "original": { - "owner": "domenkozar", - "ref": "devenv-2.21", - "repo": "nix", - "type": "github" - } - }, - "nix-github-actions": { - "inputs": { - "nixpkgs": [ - "devenv", - "cachix", - "devenv", - "poetry2nix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1688870561, - "narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=", - "owner": "nix-community", - "repo": "nix-github-actions", - "rev": "165b1650b753316aa7f1787f3005a8d2da0f5301", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nix-github-actions", - "type": "github" - } - }, - "nix_2": { "inputs": { "flake-compat": [ "devenv", "flake-compat" ], - "nixpkgs": [ + "flake-parts": "flake-parts", + "git-hooks-nix": [ "devenv", - "nixpkgs" + "git-hooks" ], - "nixpkgs-regression": "nixpkgs-regression_2" - }, - "locked": { - "lastModified": 1712911606, - "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", - "owner": "domenkozar", - "repo": "nix", - "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", - "type": "github" - }, - "original": { - "owner": "domenkozar", - "ref": "devenv-2.21", - "repo": "nix", - "type": "github" - } - }, - "nix_3": { - "inputs": { - "flake-compat": "flake-compat_3", - "flake-parts": "flake-parts", - "libgit2": "libgit2", - "nixpkgs": "nixpkgs_3", - "nixpkgs-regression": "nixpkgs-regression_3", - "pre-commit-hooks": "pre-commit-hooks_2" + "nixpkgs": "nixpkgs", + "nixpkgs-23-11": [ + "devenv" + ], + "nixpkgs-regression": [ + "devenv" + ] }, "locked": { - "lastModified": 1716910256, - "narHash": "sha256-U1noU+xZOxC2Tcnlw4ks3i1jTrCNoxBCtXbl2dMnZPo=", - "owner": "nixos", + "lastModified": 1750955511, + "narHash": "sha256-IDB/oh/P63ZTdhgSkey2LZHzeNhCdoKk+4j7AaPe1SE=", + "owner": "cachix", "repo": "nix", - "rev": "1e2b26734b4da101247678aec405c9dcfdc33f98", + "rev": "afa41b08df4f67b8d77a8034b037ac28c71c77df", "type": "github" }, "original": { - "owner": "nixos", + "owner": "cachix", + "ref": "devenv-2.30", "repo": "nix", "type": "github" } }, "nixpkgs": { "locked": { - "lastModified": 1692808169, - "narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "9201b5ff357e781bf014d0330d18555695df7ba8", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-regression": { - "locked": { - "lastModified": 1643052045, - "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", - "type": "github" - }, - "original": { - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", - "type": "github" - } - }, - "nixpkgs-regression_2": { - "locked": { - "lastModified": 1643052045, - "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", - "type": "github" - }, - "original": { - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", - "type": "github" - } - }, - "nixpkgs-regression_3": { - "locked": { - "lastModified": 1643052045, - "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "lastModified": 1747179050, + "narHash": "sha256-qhFMmDkeJX9KJwr5H32f1r7Prs7XbQWtO0h3V0a0rFY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "rev": "adaa24fbf46737f3f1b5497bf64bae750f82942e", "type": "github" }, "original": { "owner": "NixOS", - "repo": "nixpkgs", - "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", - "type": "github" - } - }, - "nixpkgs-stable": { - "locked": { - "lastModified": 1710695816, - "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "614b4613980a522ba49f0d194531beddbb7220d3", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-23.11", + "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } }, "nixpkgs_2": { "locked": { - "lastModified": 1713361204, - "narHash": "sha256-TA6EDunWTkc5FvDCqU3W2T3SFn0gRZqh6D/hJnM02MM=", + "lastModified": 1746807397, + "narHash": "sha256-zU2z0jlkJGWLhdNr/8AJSxqK8XD0IlQgHp3VZcP56Aw=", "owner": "cachix", "repo": "devenv-nixpkgs", - "rev": "285676e87ad9f0ca23d8714a6ab61e7e027020c6", + "rev": "c5208b594838ea8e6cca5997fbf784b7cca1ca90", "type": "github" }, "original": { @@ -439,114 +206,25 @@ }, "nixpkgs_3": { "locked": { - "lastModified": 1709083642, - "narHash": "sha256-7kkJQd4rZ+vFrzWu8sTRtta5D1kBG0LSRYAfhtmMlSo=", - "owner": "NixOS", + "lastModified": 1756217674, + "narHash": "sha256-TH1SfSP523QI7kcPiNtMAEuwZR3Jdz0MCDXPs7TS8uo=", + "owner": "nixos", "repo": "nixpkgs", - "rev": "b550fe4b4776908ac2a861124307045f8e717c8e", + "rev": "4e7667a90c167f7a81d906e5a75cba4ad8bee620", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "release-23.11", + "owner": "nixos", + "ref": "nixos-25.05", "repo": "nixpkgs", "type": "github" } }, - "poetry2nix": { - "inputs": { - "flake-utils": "flake-utils", - "nix-github-actions": "nix-github-actions", - "nixpkgs": [ - "devenv", - "cachix", - "devenv", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1692876271, - "narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=", - "owner": "nix-community", - "repo": "poetry2nix", - "rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "poetry2nix", - "type": "github" - } - }, - "pre-commit-hooks": { - "inputs": { - "flake-compat": [ - "devenv", - "flake-compat" - ], - "flake-utils": "flake-utils_2", - "gitignore": "gitignore", - "nixpkgs": [ - "devenv", - "nixpkgs" - ], - "nixpkgs-stable": "nixpkgs-stable" - }, - "locked": { - "lastModified": 1713775815, - "narHash": "sha256-Wu9cdYTnGQQwtT20QQMg7jzkANKQjwBD9iccfGKkfls=", - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "2ac4dcbf55ed43f3be0bae15e181f08a57af24a4", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, - "pre-commit-hooks_2": { - "inputs": { - "flake-compat": [ - "nix" - ], - "flake-utils": "flake-utils_3", - "gitignore": [ - "nix" - ], - "nixpkgs": [ - "nix", - "nixpkgs" - ], - "nixpkgs-stable": [ - "nix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1712897695, - "narHash": "sha256-nMirxrGteNAl9sWiOhoN5tIHyjBbVi5e2tgZUgZlK3Y=", - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "40e6053ecb65fcbf12863338a6dcefb3f55f1bf8", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, "root": { "inputs": { "devenv": "devenv", - "nix": "nix_3", - "nixpkgs": [ - "nix", - "nixpkgs" - ], - "systems": "systems_3" + "nixpkgs": "nixpkgs_3", + "systems": "systems" } }, "systems": { @@ -563,36 +241,6 @@ "repo": "default", "type": "github" } - }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_3": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index c78cdbe..2304695 100644 --- a/flake.nix +++ b/flake.nix @@ -1,8 +1,6 @@ { inputs = { - nix.url = "github:nixos/nix"; - nixpkgs.follows = "nix/nixpkgs"; - # nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; systems.url = "github:nix-systems/default"; devenv.url = "github:cachix/devenv"; }; @@ -13,44 +11,40 @@ }; outputs = - { - self, - nixpkgs, - nix, - devenv, - systems, - ... + { self + , nixpkgs + , devenv + , systems + , ... }@inputs: let forEachSystem = nixpkgs.lib.genAttrs (import systems); in { - devShells = forEachSystem ( - system: + + packages = forEachSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; - nixPkg = nix.packages.${system}.nix; + nixPkg = pkgs.nixVersions.nix_2_30; in { - default = devenv.lib.mkShell { - inherit inputs pkgs; - - modules = [ - { - env.hardeningDisable = [ "fortify" ]; - languages.nix.enable = true; - languages.go.enable = true; - languages.c.enable = true; + default = pkgs.buildGoModule { + name = "gonix"; + src = ./.; + vendorHash = null; + env.CGO_ENABLED = 1; + nativeBuildInputs = [ pkgs.pkg-config ]; + buildInputs = [ nixPkg ]; + # this is not actually required for the build, but + # for tests that require `import ` + NIX_PATH = "nixpkgs=${nixpkgs}"; - packages = [ - nixPkg.dev - pkgs.pkg-config - pkgs.delve - ]; - } - ]; + # gonix tests require instantiating a store, which cannot be done inside a nix build + # (that would be recursive nix) so when building inside nix, we disable the tests + # + # you can still run tests with nix develop -c go test ./... -v + doCheck = false; }; - } - ); + }); }; } diff --git a/gonix.go b/gonix.go index 4df3758..fa3b7ca 100644 --- a/gonix.go +++ b/gonix.go @@ -3,11 +3,13 @@ // nix-daemon. package gonix -// #cgo pkg-config: nix-expr-c +// #cgo pkg-config: nix-expr-c nix-util-c nix-store-c nix-main-c // #include +// #include // #include // #include // #include +// #include /* typedef const char cchar_t; void nixGetCallbackString_cgo(cchar_t * start, unsigned int n, char ** user_data); @@ -39,33 +41,39 @@ func Version() string { } // GetSetting returns the value of a setting. +// +// Warning: since gonix started binding against Nix 2.30+, this always returns NIX_ERR_KEY func GetSetting(ctx *Context, name string) (string, error) { var str *string = new(string) strh := cgo.NewHandle(str) defer strh.Delete() - cerr := C.nix_setting_get(ctx.ccontext, C.CString(name), (*[0]byte)(C.nixGetCallbackString_cgo), unsafe.Pointer(strh)) + + nameCStr := C.CString(name) + defer C.free(unsafe.Pointer(nameCStr)) + + cerr := C.nix_setting_get(ctx.ccontext, nameCStr, (*[0]byte)(C.nixGetCallbackString_cgo), nil) if cerr != C.NIX_OK { return "", nixError(cerr, ctx) } return *str, nil } -// SetSetting sets the setting to the passed value. This value affects all the -// calls done to nix API within the lifetime of the executable (irregardless of +// SetSetting sets the setting to the passed cvalue. this value affects all the +// calls done to nix API within the lifetime of the executable (regardless of // the context passed). +// +// Warning: since gonix started binding against Nix 2.30+, this always returns NIX_ERR_KEY func SetSetting(ctx *Context, name, value string) error { - cerr := C.nix_setting_set(ctx.ccontext, C.CString(name), C.CString(value)) + nameStr := C.CString(name) + defer C.free(unsafe.Pointer(nameStr)) + + valueStr := C.CString(value) + defer C.free(unsafe.Pointer(valueStr)) + + cerr := C.nix_setting_set(ctx.ccontext, nameStr, valueStr) return nixError(cerr, ctx) } const maxBufferSize = 1024 * 10 // type nixStringCallback func(start *char[0] , n int, data *void) - -// InitPlugins loads the plugins specified in Nix's plugin-files setting. -// -// This function should be called once (if needed), after all the required settings were set. -func InitPlugins(ctx *Context) error { - cerr := C.nix_init_plugins(ctx.ccontext) - return nixError(cerr, ctx) -} diff --git a/gonix_test.go b/gonix_test.go index 4a6cc2f..347b40e 100644 --- a/gonix_test.go +++ b/gonix_test.go @@ -2,6 +2,7 @@ package gonix_test import ( "fmt" + "testing" "github.com/farcaller/gonix" ) @@ -27,7 +28,8 @@ func Example() { fmt.Println(strVal) // Output: {"answer":42} } -func ExampleGetSetting() { +func TestExampleGetSetting(t *testing.T) { + t.Skipf("Since Nix 2.30+, this test always fails. It is not clear if it is a bug on the Nix C API, a change in behaviour in the Nix C API, or a misconfig on the test's side") ctx := gonix.NewContext() val, err := gonix.GetSetting(ctx, "trace-verbose") if err != nil { diff --git a/primop.go b/primop.go index 2a61a56..d02cf67 100644 --- a/primop.go +++ b/primop.go @@ -1,13 +1,15 @@ package gonix -// #cgo pkg-config: nix-expr-c +// #cgo pkg-config: nix-expr-c nix-main-c // #include // #include +// #include // #include // #include +// #include /* -void nixPrimOp_cgo(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) { - void nixPrimOp(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret); +void nixPrimOp_cgo(void * user_data, nix_c_context * context, EvalState * state, nix_value ** args, nix_value * ret) { + void nixPrimOp(void * user_data, nix_c_context * context, EvalState * state, nix_value ** args, nix_value * ret); nixPrimOp(user_data, context, state, args, ret); } void finalizePrimOp_cgo(void * obj, void * cd) { @@ -29,7 +31,7 @@ type PrimOp struct { cprimop *C.PrimOp } -type PrimOpFunc func(ctx *Context, state *State, args ...*Value) *Value +type PrimOpFunc func(ctx *Context, state *State, args []*Value, ret *Value) error type primOpHandle struct { numArgs int @@ -49,13 +51,14 @@ func NewPrimOp(ctx *Context, name string, args []string, doc string, fun PrimOpF } argNames := (**C.char)(unsafe.Pointer(&argNamesPtrs[0])) - h := unsafe.Pointer(cgo.NewHandle(primOpHandle{len(args), fun})) + handle := cgo.NewHandle(primOpHandle{len(args), fun}) + handlePtr := &handle - cprimop := C.nix_alloc_primop(ctx.ccontext, (*[0]byte)(C.nixPrimOp_cgo), C.int(len(args)), C.CString(name), argNames, C.CString(doc), h) + cprimop := C.nix_alloc_primop(ctx.ccontext, (*[0]byte)(C.nixPrimOp_cgo), C.int(len(args)), C.CString(name), argNames, C.CString(doc), unsafe.Pointer(handlePtr)) if cprimop == nil { return nil, NewContext().lastError() } - C.nix_gc_register_finalizer(unsafe.Pointer(cprimop), unsafe.Pointer(h), (*[0]byte)(C.finalizePrimOp_cgo)) + C.nix_gc_register_finalizer(unsafe.Pointer(cprimop), unsafe.Pointer(handlePtr), (*[0]byte)(C.finalizePrimOp_cgo)) po := &PrimOp{cprimop} runtime.SetFinalizer(po, func(v *PrimOp) { diff --git a/primop_callback.go b/primop_callback.go index dfb2b93..aa93dce 100644 --- a/primop_callback.go +++ b/primop_callback.go @@ -1,9 +1,10 @@ package gonix -// #cgo pkg-config: nix-expr-c +// #cgo pkg-config: nix-expr-c nix-main-c // #include // #include // #include +// #include import "C" import ( @@ -19,31 +20,40 @@ func finalizePrimOp(obj, cd unsafe.Pointer) { } //export nixPrimOp -func nixPrimOp(funh unsafe.Pointer, cctx *C.nix_c_context, cstate *C.EvalState, cargs unsafe.Pointer, cret unsafe.Pointer) { - h := cgo.Handle(funh) +func nixPrimOp(funh unsafe.Pointer, cctx *C.nix_c_context, cstate *C.EvalState, cargs unsafe.Pointer, cret *C.nix_value) { + h := (*cgo.Handle)(funh) poh := h.Value().(primOpHandle) ctx := &Context{cctx} state := &State{nil, ctx, cstate} - args := make([]*Value, poh.numArgs) + doError := func(err error) { + C.nix_set_err_msg(cctx, C.NIX_ERR_UNKNOWN, C.CString(err.Error())) + } + + args := make([]*Value, 0, poh.numArgs) for idx := 0; idx < poh.numArgs; idx++ { - cargPtr := (*unsafe.Pointer)(unsafe.Pointer(uintptr(cargs) + uintptr(uintptr(idx)*unsafe.Sizeof(cargs)))) - carg := *cargPtr + cargPtr := (**C.nix_value)(unsafe.Pointer(uintptr(cargs) + uintptr(uintptr(idx)*unsafe.Sizeof(cret)))) - val, err := wrapValue(state, unsafe.Pointer(carg)) + val, err := wrapValue(state, *cargPtr) if err != nil { - panic(fmt.Errorf("failed to wrap value during a primop call: %v", err)) + doError(fmt.Errorf("failed to wrap cvalue during a primop call: %v", err)) } err = val.Force() if err != nil { - panic(fmt.Errorf("failed to force value during a primop call: %v", err)) + doError(fmt.Errorf("failed to force cvalue during a primop call: %v", err)) } - args[idx] = val + args = append(args, val) + } + + retValue, err := wrapValue(state, cret) + if err != nil { + doError(fmt.Errorf("failed to wrap cvalue during a primop call: %v", err)) + return } - ret := poh.fun(ctx, state, args...) - if ret != nil { - C.nix_copy_value(cctx, unsafe.Pointer(cret), ret.cvalue) + err = poh.fun(ctx, state, args, retValue) + if err != nil { + doError(fmt.Errorf("error during Go callback: %v", err)) } } diff --git a/primop_test.go b/primop_test.go index a246d1b..8520aba 100644 --- a/primop_test.go +++ b/primop_test.go @@ -2,35 +2,79 @@ package gonix_test import ( "fmt" + "os" + "strings" + "testing" "github.com/farcaller/gonix" ) func ExampleRegisterGlobalPrimOp() { ctx := gonix.NewContext() - store, _ := gonix.NewStore(ctx, "dummy", nil) + store, err := gonix.NewStore(ctx, "dummy", nil) + if err != nil { + panic(fmt.Errorf("failed to create a store: %v", err)) + } - gonix.RegisterGlobalPrimOp( + err = gonix.RegisterGlobalPrimOp( ctx, "summer", []string{"arg0", "arg1", "arg2"}, "docs", - func(ctx *gonix.Context, state *gonix.State, args ...*gonix.Value) *gonix.Value { + func(ctx *gonix.Context, state *gonix.State, args []*gonix.Value, ret *gonix.Value) error { var sum int64 = 0 for _, val := range args { i, _ := val.GetInt() sum += i } - r, _ := state.NewInt(sum) - return r + return ret.SetInt(sum) }) + if err != nil { + panic(fmt.Errorf("failed to register: %v", err)) + } + + state := store.NewState([]string{os.Getenv("NIX_PATH")}) + + res, err := state.EvalExpr(`builtins.summer 1 2 3`, ".") + if err != nil { + panic(fmt.Errorf("failed to eval: %v", err)) + } + + i, err := res.GetInt() + if err != nil { + panic(fmt.Errorf("failed to convert the value to int: %v", err)) + } + if i != 6 { + panic(fmt.Errorf("expected 6, got %d", i)) + } +} + +func TestRegisterGlobalPrimOpWithError(t *testing.T) { + ctx := gonix.NewContext() + store, _ := gonix.NewStore(ctx, "dummy", nil) + + err := gonix.RegisterGlobalPrimOp( + ctx, + "goError", + []string{"arg0", "arg1", "arg2"}, + "docs", + func(ctx *gonix.Context, state *gonix.State, args []*gonix.Value, ret *gonix.Value) error { + return fmt.Errorf("oops! An error - args were: %v", args) + }) + if err != nil { + t.Fatalf("failed to register: %v", err) + } state := store.NewState(nil) - res, _ := state.EvalExpr(`builtins.summer 1 2 3`, ".") + res, err := state.EvalExpr(`builtins.goError 1 2 3`, ".") + if err == nil { + t.Fatalf("expected an error, got %v", res) + } - fmt.Println(res) - // Output: 6 + if !strings.Contains(err.Error(), "oops! An error - args were:") { + t.Fatalf("expected an error with the args, got %v", err) + } } func ExampleState_NewPrimOp() { @@ -43,9 +87,9 @@ func ExampleState_NewPrimOp() { "helloworlder", []string{"target"}, "docs", - func(ctx *gonix.Context, state *gonix.State, args ...*gonix.Value) *gonix.Value { - v, _ := state.NewString(fmt.Sprintf("hello, %s", args[0])) - return v + func(ctx *gonix.Context, state *gonix.State, args []*gonix.Value, ret *gonix.Value) error { + err := ret.SetString(fmt.Sprintf("hello, %s", args[0])) + return err }) vop, _ := state.NewPrimOp(op) diff --git a/state.go b/state.go index 3cb62b4..c0936d2 100644 --- a/state.go +++ b/state.go @@ -1,8 +1,11 @@ package gonix -// #cgo pkg-config: nix-expr-c +// #cgo pkg-config: nix-expr-c nix-main-c // #include +// #include // #include +// #include +// #include import "C" import ( @@ -28,6 +31,9 @@ func (s *Store) NewState(searchPath []string) *State { var cSearchPath **C.char if len(searchPath) > 0 { cSearchPath = (**C.char)(unsafe.Pointer(&searchPathPtrs[0])) + } else { + var cNull *C.char + cSearchPath = &cNull } cstate := C.nix_state_create(s.context().ccontext, cSearchPath, s.cstore) @@ -53,7 +59,11 @@ func (s *State) EvalExpr(expr, path string) (*Value, error) { return nil, err } cexpr := C.CString(expr) + defer C.free(unsafe.Pointer(cexpr)) + cpath := C.CString(path) + defer C.free(unsafe.Pointer(cpath)) + cerr := C.nix_expr_eval_from_string(s.context().ccontext, s.cstate, cexpr, cpath, ret.cvalue) err = nixError(cerr, s.context()) if err != nil { @@ -69,7 +79,7 @@ func (s *State) Call(fun, argument *Value) (*Value, error) { return nil, err } - var carg unsafe.Pointer + var carg *C.nix_value if argument != nil { carg = argument.cvalue } diff --git a/state_test.go b/state_test.go index 5f019d0..9edf097 100644 --- a/state_test.go +++ b/state_test.go @@ -14,7 +14,7 @@ func ExampleState_Call() { } state := store.NewState(nil) - f, err := state.EvalExpr("value: value * 2", ".") + f, err := state.EvalExpr("cvalue: cvalue * 2", ".") if err != nil { panic(fmt.Errorf("failed to eval a function: %v", err)) } @@ -96,7 +96,7 @@ func ExampleState_NewList() { newInt(11), }) if err != nil { - panic(fmt.Errorf("failed to get the string value: %v", err)) + panic(fmt.Errorf("failed to get the string cvalue: %v", err)) } fmt.Println(l) diff --git a/value.go b/value.go index 1dd25ad..94deb52 100644 --- a/value.go +++ b/value.go @@ -1,9 +1,11 @@ package gonix -// #cgo pkg-config: nix-expr-c +// #cgo pkg-config: nix-expr-c nix-main-c // #include +// #include // #include // #include +// #include /* typedef const char cchar_t; void nixGetCallbackString_cgo(cchar_t * start, unsigned int n, char ** user_data); @@ -37,12 +39,12 @@ const ( // Value is a wrapper around a nix value. type Value struct { state *State - cvalue unsafe.Pointer + cvalue *C.nix_value } -func wrapValue(state *State, cvalue unsafe.Pointer) (*Value, error) { - runtime.SetFinalizer(&cvalue, func(v *unsafe.Pointer) { - C.nix_gc_decref(state.context().ccontext, *v) +func wrapValue(state *State, cvalue *C.nix_value) (*Value, error) { + runtime.SetFinalizer(&cvalue, func(v **C.nix_value) { + C.nix_gc_decref(state.context().ccontext, unsafe.Pointer(*v)) }) return &Value{state, cvalue}, nil } @@ -54,7 +56,7 @@ func wrapValue(state *State, cvalue unsafe.Pointer) (*Value, error) { func newValue(state *State) (*Value, error) { cvalue := C.nix_alloc_value(state.context().ccontext, state.cstate) if cvalue == nil { - return nil, NewContext().lastError() + return nil, state.context().lastError() } return wrapValue(state, cvalue) } @@ -63,7 +65,7 @@ func (v *Value) context() *Context { return v.state.ctx } -// Force forces the evaluation of a Nix value. +// Force forces the evaluation of a nix value. func (v *Value) Force() error { cerr := C.nix_value_force(v.context().ccontext, v.state.cstate, v.cvalue) return nixError(cerr, v.context()) @@ -82,7 +84,7 @@ func (v *Value) Type() ValueType { return (ValueType)(t) } -func (v Value) String() string { +func (v *Value) String() string { var val, err any switch v.Type() { case NixTypeThunk: diff --git a/value_darwin.go b/value_darwin.go index 01a19ce..1328791 100644 --- a/value_darwin.go +++ b/value_darwin.go @@ -3,6 +3,7 @@ package gonix // #cgo pkg-config: nix-expr-c +// #include // #include import "C" diff --git a/value_linux.go b/value_linux.go index a2aeb7b..60a452a 100644 --- a/value_linux.go +++ b/value_linux.go @@ -3,6 +3,7 @@ package gonix // #cgo pkg-config: nix-expr-c +// #include // #include import "C"