direlv.elv 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. use path
  2. use builtin
  3. use str
  4. fn get-env {|name &default=$nil|
  5. if (has-env $name) {
  6. builtin:get-env $name
  7. } else {
  8. put $default
  9. }
  10. }
  11. fn get-hash {|@a|
  12. var dir = $pwd
  13. if (> (count $a) 0) {
  14. set dir = $a[0]
  15. }
  16. str:trim-right (echo $dir | sha256sum) "- "
  17. }
  18. fn state-type {|@a|
  19. var dir = $pwd
  20. if (> (count $a) 0) {
  21. set dir = $a[0]
  22. }
  23. if (path:is-regular $dir"/flake.nix") {
  24. put "nix"
  25. } elif (path:is-regular $dir"/shell.nix") {
  26. put "nix"
  27. } elif (path:is-regular $dir"/default.nix") {
  28. put "nix"
  29. } else {
  30. put $nil
  31. }
  32. }
  33. var state-dir = (get-env XDG_STATE_HOME &default=~/.local/state)/direlv
  34. var dir-stack = []
  35. var ignore = [
  36. AR
  37. AS
  38. CC
  39. CONFIG_SHELL
  40. CXX
  41. HOME
  42. HOST_PATH
  43. LD
  44. NM
  45. OBJCOPY
  46. OBJDUMP
  47. OLDPWD
  48. RANLIB
  49. READELF
  50. SHELL
  51. SIZE
  52. SOURCE_DATE_EPOCH
  53. STRINGS
  54. STRIP
  55. TEMP
  56. TEMPDIR
  57. TERM
  58. TMP
  59. TMPDIR
  60. TZ
  61. buildInputs
  62. buildPhase
  63. builder
  64. cmakeFlags
  65. configureFlags
  66. depsBuildBuild
  67. depsBuildBuildPropagated
  68. depsBuildTarget
  69. depsBuildTargetPropagated
  70. depsHostHost
  71. depsHostHostPropagated
  72. depsTargetTarget
  73. depsTargetTargetPropagated
  74. doCheck
  75. doInstallCheck
  76. dontAddDisableDepTrack
  77. mesonFlags
  78. name
  79. nativeBuildInputs
  80. out
  81. outputs
  82. patches
  83. phases
  84. propagatedBuildInputs
  85. propagatedNativeBuildInputs
  86. shell
  87. shellHook
  88. src
  89. stdenv
  90. strictDeps
  91. system
  92. ]
  93. var extend = [
  94. PATH
  95. XDG_DATA_DIRS
  96. ]
  97. fn diff-env {|env|
  98. each {|row|
  99. var key value = (all $row)
  100. put [$key (get-env $key)]
  101. } $env
  102. }
  103. fn apply-env {|env|
  104. each {|row|
  105. var key value = (all $row)
  106. if (eq $value $nil) {
  107. put -$key
  108. unset-env $key
  109. } else {
  110. put +$key
  111. set-env $key $value
  112. }
  113. } $env
  114. }
  115. fn env-from-nix-flake {|state-file|
  116. var data = (from-json < $state-file)
  117. keys $data[variables] | each {|key|
  118. var v = $data[variables][$key]
  119. if (eq $v[type] "exported") {
  120. if (str:has-prefix $key "NIX_") {
  121. continue
  122. } elif (has-value $ignore $key) {
  123. continue
  124. } elif (has-value $extend $key) {
  125. var existing = (get-env $key)
  126. var extended = $v[value]
  127. if (not-eq $existing $nil) {
  128. set extended = $extended":"$existing
  129. }
  130. put [$key $extended]
  131. } else {
  132. put [$key $v[value]]
  133. }
  134. }
  135. }
  136. }
  137. fn push-dir {|type state-file path|
  138. var env-diff = $nil
  139. if (not-eq $state-file $nil) {
  140. var env = []
  141. if (eq $type "nix") {
  142. set @env = (env-from-nix-flake $state-file)
  143. }
  144. set @env-diff = (diff-env $env)
  145. edit:notify "Entered direlv "$path
  146. edit:notify (print (apply-env $env))
  147. }
  148. var entry = [&dir=$path &env-diff=$env-diff]
  149. set dir-stack = [ $entry $@dir-stack ]
  150. }
  151. fn pop-dir {
  152. var last = $dir-stack[0]
  153. if (not-eq $last[env-diff] $nil) {
  154. edit:notify "Exited direlv "$last[dir]
  155. edit:notify (print (apply-env $last[env-diff]))
  156. }
  157. set dir-stack = $dir-stack[1..]
  158. }
  159. fn current-dir {
  160. if (> (count $dir-stack) 0) {
  161. put $dir-stack[0][dir]
  162. } else {
  163. put $nil
  164. }
  165. }
  166. edit:add-var direlv~ {|@a|
  167. if (eq (count $a) 0) {
  168. echo "direlv <cache|clear>"
  169. return
  170. } elif (eq $a[0] "cache") {
  171. var type = (state-type)
  172. if (eq $type $nil) {
  173. fail "No flake.nix or shell.nix found"
  174. }
  175. mkdir -p $state-dir
  176. var hash = (get-hash)
  177. var state-file = $state-dir/$hash'-'$type
  178. if (eq $type "nix") {
  179. var args = [
  180. "--print-build-logs"
  181. "--profile"
  182. $state-file
  183. "--command"
  184. "true"
  185. ]
  186. if (path:is-regular "flake.nix") {
  187. # noop
  188. } elif (path:is-regular "shell.nix") {
  189. set args = [ "--file" "shell.nix" $@args ]
  190. } elif (path:is-regular "default.nix") {
  191. set args = [ "--file" "default.nix" $@args ]
  192. }
  193. e:nix develop $@args
  194. }
  195. # if we're already in a direlv, unload first
  196. var dir = (current-dir)
  197. if (eq $dir $pwd) {
  198. pop-dir
  199. }
  200. push-dir $type $state-file $pwd
  201. } elif (eq $a[0] "clear") {
  202. var dir = (current-dir)
  203. if (not-eq $dir $nil) {
  204. var hash = (get-hash $dir)
  205. find $state-dir -name $hash'*' -delete
  206. pop-dir
  207. }
  208. }
  209. }
  210. fn on-chdir {|dir|
  211. while (> (count $dir-stack) 0) {
  212. if (eq $pwd $dir-stack[0][dir]) {
  213. return
  214. } elif (str:has-prefix $pwd $dir-stack[0][dir]) {
  215. break
  216. } else {
  217. pop-dir
  218. }
  219. }
  220. var last = "/"
  221. if (> (count $dir-stack) 0) {
  222. set last = $dir-stack[0][dir]
  223. }
  224. var current = $pwd
  225. var candidates = []
  226. while (and (str:has-prefix $current $last) (not-eq $current $last)) {
  227. set candidates = [ $current $@candidates ]
  228. if (eq $current "/") {
  229. # put this at the end so it can run against `/` once
  230. break
  231. }
  232. set current = (path:dir $current)
  233. }
  234. for candidate $candidates {
  235. var type = (state-type $candidate)
  236. if (not-eq $type $nil) {
  237. var hash = (get-hash $candidate)
  238. var state-file = $state-dir/$hash'-'$type
  239. if (path:is-regular $state-file &follow-symlink=$true) {
  240. push-dir $type $state-file $candidate
  241. } else {
  242. push-dir $type $nil $candidate
  243. }
  244. } else {
  245. push-dir "none" $nil $candidate
  246. }
  247. }
  248. }
  249. set after-chdir = [$on-chdir~]