direlv.elv 4.3 KB

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