direlv.elv 5.8 KB

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