├── .gitignore ├── LICENSE ├── README.md └── cenv.scm /.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | *.import.scm 3 | *.types 4 | *.link 5 | *.o 6 | *.build.sh 7 | *.install.sh 8 | *~ 9 | *.swp 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Jim Ursetto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cenv 2 | 3 | Chicken 5 virtual environments. 4 | 5 | ## Description 6 | 7 | A single `csi` script, `cenv.scm`, which creates a self-contained Chicken 5 environment in a specified directory. It remembers which Chicken you used to create it, and it keeps its own repository of eggs, which extends that base Chicken. 8 | 9 | When the environment is active, `csi` and `csc` and compiled programs use this repository, falling back to the base repository if an extension does not exist. `chicken-install` installs eggs into this new repository. `chicken-uninstall` is limited to the new repository, and `chicken-status` shows both. Generally, everything behaves as you expect. 10 | 11 | `cenv` could be useful if: 12 | 13 | - You are a regular user using the system Chicken and you have no permissions to, or don't want to, install eggs systemwide. 14 | - You want to test or switch between different versions of eggs, without creating an entire Chicken installation to house them. 15 | 16 | ## Usage 17 | 18 | Initialize an environment by running `cenv.scm` with `csi`, using the `init` verb. The full path of the Chicken you use is permanently stored in the new environment (even if you don't call it with a full path). 19 | 20 | csi -s /path/to/cenv.scm init 21 | ~/mychicken/csi -s /path/to/cenv.scm init 22 | /path/to/cenv.scm init 23 | 24 | Enter the environment by sourcing `center` (from bash), then running commands. 25 | 26 | source myenv/bin/center 27 | chicken-install ... 28 | 29 | With `cexec` you can run a one-off command in the environment. It will use `/bin/sh`, so you can run it from a weird shell, such as fish. 30 | 31 | ./myenv/bin/cexec chicken-install ... 32 | 33 | At the moment, there is no `cexit` to leave a `center`ed environment. You should start a subshell first if you need the ability to exit, or use `cexec`. 34 | 35 | ## Example -- installing an egg 36 | 37 | Nothing up my sleeve, `base64` is not installed: 38 | 39 | $ csi -R base64 -p '(base64-encode "hello")' 40 | Error: (import) during expansion of (import ...) - cannot import 41 | from undefined module: base64 42 | 43 | Init environment in `~/tmp/myenv`: 44 | 45 | $ cd ~/tmp 46 | $ csi -s ~/scheme/cenv/cenv.scm init myenv 47 | Using CHICKEN 5.0.0 in /Users/jim/local/chicken/5.0.0 48 | Initializing repository in myenv 49 | New repository path: 50 | (/Users/jim/tmp/myenv/lib /Users/jim/local/chicken/5.0.0/lib/chicken/9) 51 | 52 | Enter the new environment, and install `base64`: 53 | 54 | $ bash # Start a subshell 55 | $ source myenv/bin/center 56 | $ chicken-install base64 57 | building srfi-14 58 | installing srfi-14 59 | building srfi-13 60 | installing srfi-14 61 | building base64 62 | installing base64 63 | 64 | $ tree myenv/lib 65 | myenv/lib 66 | ├── base64.egg-info 67 | ├── base64.import.so* 68 | ├── base64.link 69 | ├── base64.o 70 | ├── base64.so* 71 | ├── srfi-13.egg-info 72 | ├── srfi-13.import.so* 73 | ├── srfi-13.link 74 | ├── srfi-13.o 75 | ├── srfi-13.so* 76 | ├── srfi-13.types 77 | ├── srfi-14.egg-info 78 | ├── srfi-14.import.so* 79 | ├── srfi-14.link 80 | ├── srfi-14.o 81 | ├── srfi-14.so* 82 | └── srfi-14.types 83 | 84 | Run `base64` (still in the new environment): 85 | 86 | $ csi -R base64 -p '(base64-encode "hello")' 87 | aGVsbG8= 88 | 89 | Exit the environment (subshell); `base64` no longer works: 90 | 91 | $ exit 92 | $ csi -R base64 -p '(base64-encode "hello")' 93 | Error: (import) during expansion of (import ...) - cannot import 94 | from undefined module: base64 95 | 96 | Note that the `base64` dependencies `srfi-13` and `srfi-14` were installed in your repository as well. If you had them installed in your base Chicken, they would not be installed unless chicken-install deemed it necessary, for example to satisfy a version requirement. 97 | 98 | ## Example -- two Chickens 99 | 100 | Initialize 2 environments, using 2 different versions of Chicken 5. `cexec` and `center` use the Chicken you initialized the environment with. 101 | 102 | $ ~/local/chicken/5.0.0/bin/csi -s ~/scheme/cenv/cenv.scm init c500 103 | Using CHICKEN 5.0.0 in /Users/jim/local/chicken/5.0.0 104 | Initializing repository in c500 105 | New repository path: 106 | (/Users/jim/tmp/c500/lib /Users/jim/local/chicken/5.0.0/lib/chicken/9) 107 | 108 | $ ~/local/chicken/5.0.1/bin/csi -s ~/scheme/cenv/cenv.scm init c501 109 | Using CHICKEN 5.0.1 in /Users/jim/local/chicken/5.0.1 110 | Initializing repository in c501 111 | New repository path: 112 | (/Users/jim/tmp/c501/lib /Users/jim/local/chicken/5.0.1/lib/chicken/10) 113 | 114 | Run each version of chicken-install using `cexec` and check its version. 115 | 116 | $ ./c500/bin/cexec chicken-install -version 117 | 5.0.0 118 | 119 | $ ./c501/bin/cexec chicken-install -version 120 | 5.0.1 121 | 122 | Alternatively you could use `center`: 123 | 124 | $ chicken-install -version # default chicken in my PATH 125 | 4.12.0 126 | 127 | $ source c501/bin/center 128 | $ chicken-install -version 129 | 5.0.1 130 | 131 | ## Technical 132 | 133 | Behind the scenes, `cenv` is just manipulating the environment variables `CHICKEN_INSTALL_PREFIX`, `CHICKEN_INSTALL_REPOSITORY`, `CHICKEN_REPOSITORY_PATH` and `PATH`. 134 | 135 | ## Caveats and TODOs 136 | 137 | - `cenv` extends the base Chicken's environment; the new environment is not completely isolated. 138 | - It would be possible to isolate the environment more by copying only a list of known base modules from the base repository into the new repository at initialization time, and omitting the base repository from the repo path. 139 | - Egg shared data may not be found inside a cenv, as (lacking support from the core) every egg hacks together a method of locating its shared data. Special action is taken in `center` to set the chicken-doc repository location correctly. If we turned `cenv` into a library, eggs could use it to standardize finding this data, and we could eliminate special casing. 140 | -------------------------------------------------------------------------------- /cenv.scm: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env csi -script 2 | 3 | (import (chicken file) 4 | (chicken file posix) 5 | (chicken irregex) 6 | (chicken pathname) 7 | (chicken platform) 8 | (chicken process) 9 | (chicken process-context)) 10 | 11 | ;; kinda silly 12 | (define (parse-args #!optional (args (command-line-arguments))) 13 | (when (null? args) 14 | (usage)) 15 | (let ((verb (car args)) 16 | (rest (cdr args))) 17 | `((command . ,(executable-pathname)) 18 | (verb . ,(string->symbol verb)) 19 | (args . ,rest)))) 20 | 21 | (define (usage) 22 | (print "usage: " (program-name) " init ") 23 | (exit 1)) 24 | 25 | (define (dispatch) 26 | (let ((args (parse-args))) 27 | (let ((verb (alist-ref 'verb args))) 28 | (case verb 29 | ((init) 30 | (let ((args (alist-ref 'args args))) 31 | (when (null? args) 32 | (usage)) 33 | (cenv-init-repository (car args)))) 34 | (else 35 | (usage)))))) 36 | 37 | (define (template str alist) 38 | (irregex-replace/all "{{([A-Za-z0-9_-]+)}}" 39 | str 40 | (lambda (m) 41 | (let ((key (string->symbol (irregex-match-substring m 1)))) 42 | (or (alist-ref key alist) 43 | (error 'template "missing value for key" key)))))) 44 | 45 | ;; realpath is available as C_realpath but only if we compile. 46 | (define (realpath x) 47 | (normalize-pathname 48 | (if (absolute-pathname? x) 49 | x 50 | (make-pathname (current-directory) x)))) 51 | (define (last x) 52 | (if (pair? (cdr x)) 53 | (last (cdr x)) 54 | (car x))) 55 | 56 | ;; We precompute our env dir, the chicken prefix, and the chicken system repo; so we 57 | ;; cannot relocate, but it makes things simpler. 58 | ;; We may want to ignore any CHICKEN_REPOSITORY_PATH set in the calling environment; 59 | ;; we currently honor it, which is of debatable utility. 60 | (define center-data #< #t) 124 | (map (cut string-append dirname <>) 125 | '("/bin" "/lib" "/share"))) 126 | (create-center dirname) 127 | (create-cexec dirname) 128 | (test-cenv dirname)) 129 | 130 | (dispatch) 131 | --------------------------------------------------------------------------------