with import <nixpkgs> {}; | |
let | |
path = ./mwe; | |
string = "./mwe"; | |
pathPath = path + /test/file.nix; | |
pathString = path + "/test/file.nix"; | |
pathStringPath = path + "/test" + /file.nix; | |
stringPath = string + /test/file.nix; | |
stringPathWorking = string + /nix/var/nix/db/schema; | |
stringString = string + "/test/file.nix"; | |
toPathRelative = p: ./. + p; | |
toPathAbsolute = p: /. + p; | |
isString = v: builtins.typeOf v == "string"; | |
isPath = v: builtins.typeOf v == "path"; | |
in | |
# Strings, paths and `+` | |
# ====================== | |
# | |
# a short-turned-long primer ;-) | |
# ------------------------------ | |
# | |
# Rule 1: | |
# The result of a `+` is of the same type as the first element added | |
# string + any -> string, path + any -> path. | |
# | |
# Rule 2: | |
# A path represents a path on the local filesystem. | |
# When turned into a string, it must therefore be copied to the store, and be replaced by the resulting store path. | |
# Otherwise, when generating configuration files & such, the path may not exist on the deployment machine, or not exist anymore locally. | |
# This explains why stringPath fails. Moving '/test/file.nix' to the store is doomed to fail. | |
# '/test/file.nix' obviously does not exist. | |
#assert isString stringPath; #=> error: getting attributes of path '/test/file.nix': No such file or directory | |
assert isString stringPathWorking; | |
assert isString stringString; | |
# Exception to rule 2: | |
# When a path is appended to a path, no such check occurs. The rationale | |
# being that the check will happen later on, when the path is really needed | |
# (this preserves lazyness, and avoids the error above in most cases.) | |
# | |
# This allows to write things as ${nixpkgs + /nixos/doc/manual/options-to-docbook.xsl} | |
# without having to copy the whole ${nixpkgs} to the store, and only the resulting path. | |
assert isPath pathPath; | |
assert isPath pathString; | |
assert isPath pathStringPath; | |
assert pathString == pathPath; | |
assert pathStringPath == pathPath; | |
# It should be clear by now that an expression such as | |
# | |
# ${nixpkgs + /nixos/doc/manual/options-to-docbook.xsl} | |
# | |
# will only work when `nixpkgs` is a path, because `/nixos/doc/manual/options-to-docbook.xsl`r | |
# cannot be expected to exist in any local filesystem. | |
# | |
# The strange thing is, builtins.fetchtarball returns a string, and not a | |
# path. This is valid because that string carries itself as a store path | |
# context. | |
# To fix your issue, you need to ensure that `nixpkgs` is a path. | |
# To that end, builtins.toPath fails to deliver because it turns strings into... strings, | |
# and only works with absolute paths. It could be renamed to throwIfRelative ;-). | |
assert isString (builtins.toPath "/nix/var/nix/db/schema"); | |
#assert isString (builtins.toPath "./mwe"); #=> error: string './mwe' doesn't represent an absolute path | |
# In fact, the only way to coerce to a path is to append to a path. | |
assert isPath (toPathAbsolute "/nix/var/nix/db/schema"); | |
assert isPath (toPathRelative "./mwe/result/file.nix"); | |
# As for the long-term fix, we should turn | |
# | |
# ${nixpkgs + /nixos/doc/manual/options-to-docbook.xsl} | |
# | |
# to | |
# | |
# ${nixpkgs + "/nixos/doc/manual/options-to-docbook.xsl"} | |
# | |
# Do you want to make the PR yourself ? | |
# I guess this all boils down to an unfinished work at getting rid of paths. | |
# They have mostly indistinguishable from strings nowadays, but still | |
# persist here and there. | |
# Appendix | |
# -------- | |
# | |
# There are misunderstandings in your code | |
# | |
# stableTarball = /nix/store/wq5cwrk0lf5r217676ypcp3fppky3b1w-80754f5cfd69d0caf8cff6795d3ce6d99479abde.tar.gz; | |
# | |
# This is wrong because it creates a copy of that path in the nix store. | |
# You end up with a new derivation with the exact same content and a longer name. | |
# (See the three hashes in the resulting name ?) | |
# | |
# $ nix-instantiate --expr --eval 'let x = /nix/store/wq5cwrk0lf5r217676ypcp3fppky3b1w-80754f5cfd69d0caf8cff6795d3ce6d99479abde.tar.gz; in "${x}"' | |
# "/nix/store/11dbw5s7r1zcqvq6pwz0d30z6g5b1nky-wq5cwrk0lf5r217676ypcp3fppky3b1w-80754f5cfd69d0caf8cff6795d3ce6d99479abde.tar.gz" | |
# | |
# Now, I guess you ended up there mostly out of despair :-D. | |
# To do that, you should rather use storePath | |
# | |
# $ nix-instantiate --expr --eval 'let x = builtins.storePath /nix/store/wq5cwrk0lf5r217676ypcp3fppky3b1w-80754f5cfd69d0caf8cff6795d3ce6d99479abde.tar.gz; in "${x}"' | |
# "/nix/store/wq5cwrk0lf5r217676ypcp3fppky3b1w-80754f5cfd69d0caf8cff6795d3ce6d99479abde.tar.gz" | |
# | |
# but... like builtins.fetchtarball, it returns a string, and not a path, so | |
# in your particular case, it was the "right" way to go. | |
{ | |
# If you want to inspect the values defined here, use nix-instantiate. | |
# | |
# $ nix-instantiate --eval <this-file>.nix --json --strict | yq . | |
# { | |
# "path": "/nix/store/mr3bwp0537dsi662hzacww621z4avzhp-mwe", | |
# "pathPath": "/nix/store/vylng2dfxdy6m3dwasscpgvjj50yxjqa-file.nix", | |
# "pathString": "/nix/store/vylng2dfxdy6m3dwasscpgvjj50yxjqa-file.nix", | |
# "pathStringPath": "/nix/store/vylng2dfxdy6m3dwasscpgvjj50yxjqa-file.nix", | |
# "string": "./mwe", | |
# "stringPathWorking": "./mwe/nix/store/yr1nmfgr6ifs1lgqyafcizwxm2rp8y9k-schema", | |
# "stringString": "./mwe/test/file.nix" | |
# } | |
# | |
inherit string path pathPath pathString pathStringPath /*stringPath*/ stringPathWorking stringString; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
Thank you so much for this write-up!