direlv.elv 5.3 KB

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