Created
March 28, 2019 20:52
-
-
Save layus/e2024dea364ff0dc7112d8cffe388cdf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
Thank you so much for this write-up!