1
0
mirror of https://github.com/janet-lang/janet synced 2025-11-17 15:57:12 +00:00

Compare commits

..

47 Commits

Author SHA1 Message Date
Calvin Rose
7e0c692d4e Remove test-install target from sr.ht 2025-11-15 08:39:47 -06:00
Calvin Rose
732fe0ad03 Try new openbsd. 2025-11-15 08:33:41 -06:00
Calvin Rose
0c8622c803 posix_chroot warning 2025-11-15 08:15:26 -06:00
Calvin Rose
94f2494f8d MSVC warning 2025-11-15 08:14:28 -06:00
Calvin Rose
0f9ecc2da5 Prepare for 1.40.0 release 2025-11-15 07:31:23 -06:00
Calvin Rose
83f5da3b8f Merge pull request #1662 from McSinyx/dobytes-err-ctx
Include context in dobytes' error string
2025-11-09 09:20:23 -06:00
Calvin Rose
9b9f2a1713 Merge pull request #1660 from pyrmont/bugfix.confirm-bundle-files
Clarify error message for missing bundle script during bundle installation
2025-11-09 09:06:06 -06:00
Calvin Rose
8df4d47ede Merge pull request #1663 from McSinyx/dobytes-err-keep
Keep dobytes' error string
2025-11-09 08:40:07 -06:00
Nguyễn Gia Phong
1c372fbf32 Keep dobytes' error string
The reason for failure would be more useful
than the most recently evaluated value.
2025-11-05 17:00:04 +09:00
Nguyễn Gia Phong
8ace580498 Include context in dobytes' error string 2025-11-05 16:53:19 +09:00
Michael Camilleri
8241d9cbb4 Revert support for bundles without an info file 2025-10-27 09:32:38 +09:00
Michael Camilleri
6bd02bb5b6 Confirm necessary files during bundle installation 2025-10-26 06:53:27 +09:00
Calvin Rose
2a3308005d Merge pull request #1659 from ianthehenry/try-macro-hygiene
fix (try) macro hygiene
2025-10-25 12:15:12 -05:00
Ian Henry
0c34033b72 add some tests for the (try) macro 2025-10-24 21:56:07 -07:00
Ian Henry
f1ec0cc48b fix try macro hygiene
Allow re-using the same symbol for the fiber and error. This allows you to
write code like (try (print "hi") ([_ _] (print "oh no"))), fixing a regression
introduced in #1605.
2025-10-24 21:33:15 -07:00
Calvin Rose
98265f0637 Merge pull request #1658 from sogaiu/tweak-has-bin-script 2025-10-17 15:44:50 -05:00
sogaiu
1018cb9cca Support :has-exe as well
This is a companion change to spork's issue

As implemented, :has-been-script didn't quite
mean what was intended.  :has-exe should
be an improvement.

To prevent breakage, both :has-bin-script and
:has-exe should be supported for a while in
both spork and janet.  Eventually
:has-bin-script can be retired.
2025-10-17 22:36:45 +09:00
Calvin Rose
2204209133 Merge pull request #1656 from jpaquim/build-system-consistency
Minor adjustments to Makefile
2025-10-15 07:51:12 -05:00
jpaquim
95abff31d9 Fix file mention in CONTRIBUTING.md 2025-10-11 17:48:23 +01:00
jpaquim
a776466423 Add $(RUN) for valgrind/callgrind, add dedicated callgrind-test target 2025-10-11 17:45:44 +01:00
jpaquim
511c1f4b0a Refactor with image-only variable 2025-10-11 16:32:24 +01:00
jpaquim
c29195596e Adapt generated file comments to build type 2025-10-11 16:29:14 +01:00
jpaquim
56027227fb Add missing phony targets and documentation for install-{jpm,spork}-git 2025-10-11 16:11:05 +01:00
jpaquim
c057e14b20 Fix dependencies and variable names in Makefile 2025-10-11 16:02:02 +01:00
Calvin Rose
db7f741dad Extend environment variable behavior to NO_COLOR and JANET_PROFILE
Env vars set to empty strings should behave the same as unset variables.
2025-10-04 13:12:29 -05:00
Calvin Rose
c901edbfb9 Interpret an empty JANET_PATH as unset
This is a common idiom with environment variables, where
a variable set to the empty string should behave the same as
an unset variable.
2025-10-04 10:43:55 -05:00
Calvin Rose
8fd1672963 Merge pull request #1652 from sogaiu/fix-bundle-install-breakage 2025-09-29 09:53:47 -05:00
sogaiu
9b99fc44b9 Revert some bundle/install changes 2025-09-29 23:45:53 +09:00
Michael Camilleri
f393531335 Add bundle/add-manpage 2025-09-28 19:27:53 -05:00
Michael Camilleri
6b8e5249ca Change :dependencies hook to :postdeps 2025-09-28 19:27:53 -05:00
Michael Camilleri
6a96b615f0 Tidy up bundle/ docstrings 2025-09-28 19:27:53 -05:00
Calvin Rose
8ec465d308 Merge pull request #1651 from neuschaefer/a64be
Recognise aarch64_be as big-endian
2025-09-27 14:29:47 -05:00
J. Neuschäfer
07bfd34c2f Recognise aarch64_be as big-endian 2025-09-26 02:07:09 +00:00
Calvin Rose
5f7878c00f Merge pull request #1649 from jsks/kvs-docstring 2025-09-23 06:42:51 -05:00
Joshua Krusell
aaf8ac2217 Tweak kvs docstring 2025-09-23 11:28:06 +02:00
Calvin Rose
73b1cf547e Update CHANGELOG 2025-09-21 10:26:49 -05:00
Calvin Rose
ed2ae562c6 Merge pull request #1647 from sogaiu/tweak-flycheck-related-docstrings 2025-09-21 00:53:51 -05:00
sogaiu
dd59d84b51 Tweak some flycheck-related docstrings 2025-09-21 14:09:35 +09:00
Calvin Rose
06873fbf0b Update CHANGELOG.md 2025-09-20 14:30:10 -05:00
Calvin Rose
1ff26d702a Refactor flycheck to allow customization. Address #1638
Bindings can define their own flycheckers in a simple fashion.
2025-09-20 10:32:16 -05:00
Calvin Rose
4da568254a Manually declare chroot on macos. 2025-09-19 21:17:02 -05:00
Calvin Rose
357f1f94ca Add os/posix-chroot
Gated in a similar manner to os/posix-fork.
2025-09-19 16:32:45 -05:00
Calvin Rose
015e49c806 Merge pull request #1645 from pyrmont/bugfix.avoid-apc-use
Use `SetEvent` rather than `QueueUserAPC` on Windows
2025-09-19 16:31:13 -05:00
Michael Camilleri
6b06ab5f9c Remove unused function on Windows 2025-09-17 15:51:53 +09:00
Michael Camilleri
fe6c6e15a6 Add workaround to timer resolution issue on Windows 2025-09-17 15:48:37 +09:00
Michael Camilleri
b4eb52ca45 Start worker thread in suspended state on Windows 2025-09-16 11:57:40 +09:00
Michael Camilleri
aca5428846 Use SetEvent rather than QueueUserAPC on Windows 2025-09-16 10:24:24 +09:00
19 changed files with 309 additions and 156 deletions

View File

@@ -1,4 +1,4 @@
image: openbsd/7.6
image: openbsd/7.7
sources:
- https://git.sr.ht/~bakpakin/janet
packages:
@@ -10,7 +10,6 @@ tasks:
gmake
gmake test
doas gmake install
gmake test-install
doas gmake uninstall
- meson_min: |
cd janet

View File

@@ -1,7 +1,11 @@
# Changelog
All notable changes to this project will be documented in this file.
## Unreleased - ???
## 1.40.0 - 2025-11-15
- Add `os/posix-chroot`
- Fix `ev/deadline` with interrupt race condition bug on Windows.
- Improve `flycheck` by allowing functions and macros to define their own flycheck behavior via the metadata `:flycheck`.
- Add `*flychecking*` dynamic binding to check if inside flycheck evalutation
- Add `gcperthread` callback for abstract types. This lets threaded abstracts have a finalizer that is called per thread, as well as a global finalizer.
- Add `JANET_DO_ERROR_*` flags to describe the return value of `janet_dobytes` and `janet_dostring`.

View File

@@ -61,7 +61,7 @@ ensure a consistent code style for C.
## Janet style
All janet code in the project should be formatted similar to the code in core.janet.
All janet code in the project should be formatted similar to the code in src/boot/boot.janet.
The auto formatting from janet.vim will work well.
## Typo Fixing and One-Line changes

View File

@@ -261,6 +261,7 @@ $(JANET_STATIC_LIBRARY): $(JANET_TARGET_OBJECTS)
# Testing assumes HOSTCC=CC
TEST_SCRIPTS=$(wildcard test/suite*.janet)
EXAMPLE_SCRIPTS=$(wildcard examples/*.janet)
repl: $(JANET_TARGET)
$(RUN) ./$(JANET_TARGET)
@@ -268,21 +269,26 @@ repl: $(JANET_TARGET)
debug: $(JANET_TARGET)
$(DEBUGGER) ./$(JANET_TARGET)
VALGRIND_COMMAND=valgrind --leak-check=full --quiet
VALGRIND_COMMAND=$(RUN) valgrind --leak-check=full --quiet
CALLGRIND_COMMAND=$(RUN) valgrind --tool=callgrind
valgrind: $(JANET_TARGET)
$(VALGRIND_COMMAND) ./$(JANET_TARGET)
test: $(JANET_TARGET) $(TEST_PROGRAMS)
test: $(JANET_TARGET) $(TEST_SCRIPTS) $(EXAMPLE_SCRIPTS)
for f in test/suite*.janet; do $(RUN) ./$(JANET_TARGET) "$$f" || exit; done
for f in examples/*.janet; do $(RUN) ./$(JANET_TARGET) -k "$$f"; done
valtest: $(JANET_TARGET) $(TEST_PROGRAMS)
valtest: $(JANET_TARGET) $(TEST_SCRIPTS) $(EXAMPLE_SCRIPTS)
for f in test/suite*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
for f in examples/*.janet; do ./$(JANET_TARGET) -k "$$f"; done
for f in examples/*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) -k "$$f"; done
callgrind: $(JANET_TARGET)
for f in test/suite*.janet; do valgrind --tool=callgrind ./$(JANET_TARGET) "$$f" || exit; done
$(CALLGRIND_COMMAND) ./$(JANET_TARGET)
calltest: $(JANET_TARGET) $(TEST_SCRIPTS) $(EXAMPLE_SCRIPTS)
for f in test/suite*.janet; do $(CALLGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
for f in examples/*.janet; do $(CALLGRIND_COMMAND) ./$(JANET_TARGET) -k "$$f"; done
########################
##### Distribution #####
@@ -413,9 +419,6 @@ clean:
-rm -rf build vgcore.* callgrind.*
-rm -rf test/install/build test/install/modpath
test-install:
echo "JPM has been removed from default install."
help:
@echo
@echo 'Janet: A Dynamic Language & Bytecode VM'
@@ -427,7 +430,8 @@ help:
@echo ' make test Test a built Janet'
@echo ' make valgrind Assess Janet with Valgrind'
@echo ' make callgrind Assess Janet with Valgrind, using Callgrind'
@echo ' make valtest Run the test suite with Valgrind to check for memory leaks'
@echo ' make valtest Run the test suite and examples with Valgrind to check for memory leaks'
@echo ' make calltest Run the test suite and examples with Callgrind'
@echo ' make dist Create a distribution tarball'
@echo ' make docs Generate documentation'
@echo ' make debug Run janet with GDB or LLDB'
@@ -437,6 +441,9 @@ help:
@echo " make format Format Janet's own source files"
@echo ' make grammar Generate a TextMate language grammar'
@echo
@echo ' make install-jpm-git Install jpm into the current filesystem'
@echo ' make install-spork-git Install spork into the current filesystem'
@echo
.PHONY: clean install repl debug valgrind test \
valtest dist uninstall docs grammar format help compile-commands
.PHONY: clean install install-jpm-git install-spork-git repl debug valgrind test \
valtest callgrind callgrind-test dist uninstall docs grammar format help compile-commands

View File

@@ -0,0 +1,3 @@
@{
:name "sample-bad-bundle1"
}

View File

@@ -0,0 +1,3 @@
@{
:name "sample-bad-bundle2"
}

View File

@@ -7,7 +7,7 @@
###
###
(def defn :macro
(def defn :macro :flycheck
```
(defn name & more)
@@ -43,7 +43,7 @@
# Build return value
~(def ,name ,;modifiers (fn ,name ,;(tuple/slice more start)))))
(defn defmacro :macro
(defn defmacro :macro :flycheck
"Define a macro."
[name & more]
(setdyn name @{}) # override old macro definitions in the case of a recursive macro
@@ -57,12 +57,12 @@
[f & args]
(f ;args))
(defmacro defmacro-
(defmacro defmacro- :flycheck
"Define a private macro that will not be exported."
[name & more]
(apply defn name :macro :private more))
(defmacro defn-
(defmacro defn- :flycheck
"Define a private function that will not be exported."
[name & more]
(apply defn name :private more))
@@ -144,7 +144,7 @@
(defmacro /= "Shorthand for (set x (/ x n))." [x & ns] ~(set ,x (,/ ,x ,;ns)))
(defmacro %= "Shorthand for (set x (% x n))." [x & ns] ~(set ,x (,% ,x ,;ns)))
(defmacro assert
(defmacro assert :flycheck # should top level assert flycheck?
"Throw an error if x is not truthy. Will not evaluate `err` if x is truthy."
[x &opt err]
(def v (gensym))
@@ -154,7 +154,7 @@
,v
(,error ,(if err err (string/format "assert failure in %j" x))))))
(defmacro defdyn
(defmacro defdyn :flycheck
``Define an alias for a keyword that is used as a dynamic binding. The
alias is a normal, lexically scoped binding that can be used instead of
a keyword to prevent typos. `defdyn` does not set dynamic bindings or otherwise
@@ -171,6 +171,9 @@
(defdyn *macro-form*
"Inside a macro, is bound to the source form that invoked the macro")
(defdyn *flychecking*
"Check if the current form is being evaluated inside `flycheck`. Will be `true` while flychecking.")
(defdyn *lint-error*
"The current lint error level. The error level is the lint level at which compilation will exit with an error and not continue.")
@@ -345,12 +348,15 @@
[body catch]
(assert (and (not (empty? catch)) (indexed? (catch 0))) "the first element of `catch` must be a tuple or array")
(let [[err fib] (catch 0)
r (or err (gensym))
f (or fib (gensym))]
r (gensym)
f (gensym)]
~(let [,f (,fiber/new (fn :try [] ,body) :ie)
,r (,resume ,f)]
(if (,= (,fiber/status ,f) :error)
(do ,;(tuple/slice catch 1))
(do
,(if err ~(def ,err ,r))
,(if fib ~(def ,fib ,f))
,;(tuple/slice catch 1))
,r))))
(defmacro with-syms
@@ -1800,8 +1806,8 @@
(flatten-into @[] xs))
(defn kvs
``Takes a table or struct and returns and array of key value pairs
like `@[k v k v ...]`. Returns a new array.``
``Takes a table or struct and returns a new array of key value pairs
like `@[k v k v ...]`.``
[dict]
(def ret @[])
(loop [k :keys dict] (array/push ret k (in dict k)))
@@ -2354,7 +2360,7 @@
(set macexvar macex)
(defmacro varfn
(defmacro varfn :flycheck
``Create a function that can be rebound. `varfn` has the same signature
as `defn`, but defines functions in the environment as vars. If a var `name`
already exists in the environment, it is rebound to the new function. Returns
@@ -3945,7 +3951,7 @@
[& forms]
(def state (gensym))
(def loaded (gensym))
~((fn []
~((fn :delay []
(var ,state nil)
(var ,loaded nil)
(fn []
@@ -3977,7 +3983,7 @@
:lazy lazy
:map-symbols map-symbols}))
(defmacro ffi/defbind-alias
(defmacro ffi/defbind-alias :flycheck
"Generate bindings for native functions in a convenient manner.
Similar to defbind but allows for the janet function name to be
different than the FFI function."
@@ -3988,6 +3994,8 @@
(def formal-args (map 0 arg-pairs))
(def type-args (map 1 arg-pairs))
(def computed-type-args (eval ~[,;type-args]))
(if (dyn *flychecking*)
(break ~(defn ,alias ,;meta [,;formal-args] nil)))
(def {:native lib
:lazy lazy
:native-lazy llib
@@ -4003,7 +4011,7 @@
~(defn ,alias ,;meta [,;formal-args]
(,ffi/call ,(make-ptr) ,(make-sig) ,;formal-args))))
(defmacro ffi/defbind
(defmacro ffi/defbind :flycheck
"Generate bindings for native functions in a convenient manner."
[name ret-type & body]
~(ffi/defbind-alias ,name ,name ,ret-type ,;body)))
@@ -4014,6 +4022,51 @@
###
###
(def- flycheck-specials @{})
(defn- flycheck-evaluator
``
An evaluator function that is passed to `run-context` that lints
(flychecks) code for `flycheck`. This means code will be parsed,
compiled, and have macros expanded, but the code will not be
evaluated.
``
[thunk source env where]
(when (and (tuple? source) (= (tuple/type source) :parens))
(def head (source 0))
(def entry (get env head {}))
(def fc (get flycheck-specials head (get entry :flycheck)))
(cond
# Sometimes safe form
(function? fc)
(fc thunk source env where)
# Always safe form
fc
(thunk))))
(defn flycheck
```
Check a file for errors without running the file. Found errors
will be printed to stderr in the usual format. Top level functions
and macros that have the metadata `:flycheck` will also be evaluated
during flychecking. For full control, the `:flycheck` metadata can
also be a function that takes 4 arguments - `thunk`, `source`, `env`,
and `where`, the same as the `:evaluator` argument to `run-context`.
Other arguments to `flycheck` are the same as `dofile`. Returns nil.
```
[path &keys kwargs]
(def mc @{})
(def new-env (make-env (get kwargs :env)))
(put new-env *flychecking* true)
(put new-env *module-cache* @{})
(put new-env *module-loading* @{})
(put new-env *module-make-env* (fn :make-flycheck-env [&] (make-env new-env)))
(try
(dofile path :evaluator flycheck-evaluator ;(kvs kwargs) :env new-env)
([e f]
(debug/stacktrace f e "")))
nil)
(defn- no-side-effects
`Check if form may have side effects. If returns true, then the src
must not have side effects, such as calling a C function.`
@@ -4029,59 +4082,29 @@
(all no-side-effects (values src)))
true))
(defn- is-safe-def [x] (no-side-effects (last x)))
(defn- is-safe-def [thunk source env where]
(if (no-side-effects (last source))
(thunk)))
(def- safe-forms {'defn true 'varfn true 'defn- true 'defmacro true 'defmacro- true
'def is-safe-def 'var is-safe-def 'def- is-safe-def 'var- is-safe-def
'defglobal is-safe-def 'varglobal is-safe-def 'defdyn true})
(def- importers {'import true 'import* true 'dofile true 'require true})
(defn- use-2 [evaluator args]
(each a args (import* (string a) :prefix "" :evaluator evaluator)))
(defn- flycheck-evaluator
``An evaluator function that is passed to `run-context` that lints (flychecks) code.
This means code will parsed and compiled, macros executed, but the code will not be run.
Used by `flycheck`.``
(defn- flycheck-importer
[thunk source env where]
(when (tuple? source)
(def head (source 0))
(def safe-check
(or
(safe-forms head)
(if (symbol? head)
(if (string/has-prefix? "define-" head) is-safe-def))))
(cond
# Sometimes safe form
(function? safe-check)
(if (safe-check source) (thunk))
# Always safe form
safe-check
(thunk)
# Use
(= 'use head)
(use-2 flycheck-evaluator (tuple/slice source 1))
# Import-like form
(importers head)
(let [[l c] (tuple/sourcemap source)
newtup (tuple/setmap (tuple ;source :evaluator flycheck-evaluator) l c)]
((compile newtup env where))))))
(let [[l c] (tuple/sourcemap source)
newtup (tuple/setmap (tuple ;source :evaluator flycheck-evaluator) l c)]
((compile newtup env where))))
(defn flycheck
``Check a file for errors without running the file. Found errors will be printed to stderr
in the usual format. Macros will still be executed, however, so
arbitrary execution is possible. Other arguments are the same as `dofile`. `path` can also be
a file value such as stdin. Returns nil.``
[path &keys kwargs]
(def old-modcache (table/clone module/cache))
(table/clear module/cache)
(try
(dofile path :evaluator flycheck-evaluator ;(kvs kwargs))
([e f]
(debug/stacktrace f e "")))
(table/clear module/cache)
(merge-into module/cache old-modcache)
nil)
(defn- flycheck-use
[thunk source env where]
(each a (drop 1 source) (import* (string a) :prefix "" :evaluator flycheck-evaluator)))
# Add metadata to defs and import macros for flychecking
(each sym ['def 'var]
(put flycheck-specials sym is-safe-def))
(each sym ['def- 'var- 'defglobal 'varglobal]
(put (dyn sym) :flycheck is-safe-def))
(each sym ['import 'import* 'dofile 'require]
(put (dyn sym) :flycheck flycheck-importer))
(each sym ['use]
(put (dyn sym) :flycheck flycheck-use))
###
###
@@ -4174,7 +4197,7 @@
(spit manifest-name b))
(defn bundle/manifest
"Get the manifest for a give installed bundle"
"Get the manifest for a given installed bundle."
[bundle-name]
(def name (get-manifest-filename bundle-name))
(assertf (fexists name) "no bundle %v found" bundle-name)
@@ -4199,7 +4222,9 @@
(put new-env *syspath* fixed-syspath)
(with-env new-env
(put new-env :bundle-dir (bundle-dir bundle-name)) # get the syspath right
(require (string "@syspath/bundle/" bundle-name)))))
(try
(require (string "@syspath/bundle/" bundle-name))
([_] (error "bundle must contain bundle.janet or bundle/init.janet"))))))
(defn- do-hook
[module bundle-name hook & args]
@@ -4236,7 +4261,9 @@
nil)
(defn bundle/uninstall
"Remove a bundle from the current syspath"
``Remove a bundle from the current syspath. There is 1 hook called during
uninstallation (uninstall). A user can register a hook by defining a
function with the same name in the bundle script.``
[bundle-name]
(def breakage @{})
(each b (bundle/list)
@@ -4272,8 +4299,8 @@
order)
(defn bundle/prune
"Remove all orphaned bundles from the syspath. An orphaned bundle is a bundle that is
marked for :auto-remove and is not depended on by any other bundle."
``Remove all orphaned bundles from the current syspath. An orphaned bundle is a
bundle that is marked for :auto-remove and is not depended on by any other bundle.``
[]
(def topo (bundle/topolist))
(def rtopo (reverse topo))
@@ -4302,33 +4329,44 @@
(not (not (os/stat (bundle-dir bundle-name) :mode))))
(defn bundle/install
"Install a bundle from the local filesystem. The name of the bundle will be inferred from the bundle, or passed as a parameter :name in `config`."
``Install a bundle from the local filesystem. The name of the bundle is
the value mapped to :name in either `config` or the info file. There are
5 hooks called during installation (postdeps, clean, build, install and
check). A user can register a hook by defining a function with the same name
in the bundle script.``
[path &keys config]
(def path (bundle-rpath path))
(def s (sep))
# Detect bundle name
(def infofile-src1 (string path s "bundle" s "info.jdn"))
(def infofile-src2 (string path s "info.jdn"))
(def infofile-src (cond (fexists infofile-src1) infofile-src1
(fexists infofile-src2) infofile-src2))
(def infofile-src (cond
(fexists infofile-src1) infofile-src1
(fexists infofile-src2) infofile-src2))
(def info (-?> infofile-src slurp parse))
(def bundle-name (get config :name (get info :name)))
(assertf bundle-name "unable to infer bundle name for %v, use :name argument" path)
(assertf bundle-name
"unable to infer bundle name for %v, use :name argument or add :name to info file" path)
(assertf (not (string/check-set "\\/" bundle-name))
"bundle name %v cannot contain path separators" bundle-name)
(assert (next bundle-name) "cannot use empty bundle-name")
(assertf (not (fexists (get-manifest-filename bundle-name)))
"bundle %v is already installed" bundle-name)
# Check bscript
(def bscript-src1 (string path s "bundle" s "init.janet"))
(def bscript-src2 (string path s "bundle.janet"))
(def bscript-src (cond
(fexists bscript-src1) bscript-src1
(fexists bscript-src2) bscript-src2))
# Setup installed paths
(prime-bundle-paths)
(os/mkdir (bundle-dir bundle-name))
# Copy infofile
(def infofile-dest (bundle-file bundle-name "info.jdn"))
(when infofile-src (copyfile infofile-src infofile-dest))
# Copy aliased initfile
(def initfile-alias (string path s "bundle.janet"))
(def initfile-dest (bundle-file bundle-name "init.janet"))
(when (fexists initfile-alias) (copyfile initfile-alias initfile-dest))
# Copy aliased infofile
(when (fexists infofile-src2)
(copyfile infofile-src2 (bundle-file bundle-name "info.jdn")))
# Copy aliased bscript
(when (fexists bscript-src2)
(copyfile bscript-src2 (bundle-file bundle-name "init.janet")))
# Copy some files into the new location unconditionally
(def implicit-sources (string path s "bundle"))
(when (= :directory (os/stat implicit-sources :mode))
@@ -4337,21 +4375,21 @@
(merge-into man config)
(sync-manifest man)
(edefer (do (print "installation error, uninstalling") (bundle/uninstall bundle-name))
(when (os/stat infofile-dest :mode)
(def info (-> infofile-dest slurp parse))
(when info
(def deps (seq [d :in (get info :dependencies @[])]
(string (if (dictionary? d) (get d :name) d))))
(string (if (dictionary? d) (get d :name) d))))
(def missing (filter (complement bundle/installed?) deps))
(when (next missing)
(error (string "missing dependencies " (string/join missing ", "))))
(put man :dependencies deps)
(put man :info info))
(def module (get-bundle-module bundle-name))
(def clean (get config :clean))
(def check (get config :check))
(def module (get-bundle-module bundle-name))
(def all-hooks (seq [[k v] :pairs module :when (symbol? k) :unless (get v :private)] (keyword k)))
(put man :hooks all-hooks)
(do-hook module bundle-name :dependencies man)
(do-hook module bundle-name :dependencies man) # deprecated, use :postdeps
(do-hook module bundle-name :postdeps man)
(when clean
(do-hook module bundle-name :clean man))
(do-hook module bundle-name :build man)
@@ -4361,15 +4399,21 @@
(when check
(do-hook module bundle-name :check man)))
(print "installed " bundle-name)
(when (get man :has-bin-script)
(when (or (get man :has-exe)
# remove eventually
(get man :has-bin-script))
(def binpath (string (dyn *syspath*) s "bin"))
(eprintf "executable scripts have been installed to %s" binpath))
(eprintf "executable files have been installed to %s" binpath))
(when (get man :has-man)
(def manpath (string (dyn *syspath*) s "man"))
(eprintf "man pages have been installed to %s" manpath))
bundle-name)
(defn- bundle/pack
"Take an installed bundle and create a bundle source directory that can be used to
reinstall the bundle on a compatible system. This is used to create backups for installed
bundles without rebuilding, or make a prebuilt bundle for other systems."
``Take an installed bundle and create a bundle source directory that can be
used to reinstall the bundle on a compatible system. This is used to create
backups for installed bundles without rebuilding, or make a prebuilt bundle
for other systems.``
[bundle-name dest-dir &opt is-backup]
(var i 0)
(def man (bundle/manifest bundle-name))
@@ -4399,9 +4443,9 @@
dest-dir)
(defn bundle/replace
"Reinstall an existing bundle from a new directory. Similar to bundle/reinstall,
but installs the replacement bundle from any directory. This is necesarry to replace a package without
breaking any dependencies."
``Reinstall an existing bundle from a new directory. Similar to
bundle/reinstall, but installs the replacement bundle from any directory.
This is necessary to replace a package without breaking any dependencies.``
[bundle-name path &keys new-config]
(def manifest (bundle/manifest bundle-name))
(def config (get manifest :config @{}))
@@ -4428,7 +4472,7 @@
bundle-name)
(defn bundle/add-directory
"Add a directory during the install process relative to `(dyn *syspath*)`"
"Add a directory during an install relative to `(dyn *syspath*)`."
[manifest dest &opt chmod-mode]
(def files (get-files manifest))
(def s (sep))
@@ -4456,7 +4500,7 @@
ret)
(defn bundle/add-file
"Add files during an install relative to `(dyn *syspath*)`"
"Add a file during an install relative to `(dyn *syspath*)`."
[manifest src &opt dest chmod-mode]
(default dest src)
(def files (get-files manifest))
@@ -4473,9 +4517,9 @@
absdest)
(defn bundle/add
"Add files and directories during a bundle install relative to `(dyn *syspath*)`.
Added files and directories will be recorded in the bundle manifest such that they are properly tracked
and removed during an upgrade or uninstall."
``Add a file or directory during an install relative to `(dyn *syspath*)`.
Added files and directories will be recorded in the bundle manifest such
that they are properly tracked and removed during an upgrade or uninstall.``
[manifest src &opt dest chmod-mode]
(default dest src)
(def s (sep))
@@ -4490,20 +4534,31 @@
(errorf "bad path %s - file is a %s" src mode)))
(defn bundle/add-bin
``
Shorthand for adding scripts during an install. Scripts will be installed to
`(string (dyn *syspath*) "/bin")` by default and will be set to be executable.
``
[manifest src &opt dest chmod-mode]
``Add a file to the "bin" subdirectory of the current syspath. By default,
files will be set to be executable.``
[manifest src &opt filename chmod-mode]
(def s (sep))
(default dest (last (string/split s src)))
(default filename (last (string/split s src)))
(default chmod-mode 8r755)
(os/mkdir (string (dyn *syspath*) s "bin"))
(put manifest :has-bin-script true)
(bundle/add-file manifest src (string "bin" s dest) chmod-mode))
(put manifest :has-exe true)
(put manifest :has-bin-script true) # remove eventually
(bundle/add-file manifest src (string "bin" s filename) chmod-mode))
(defn bundle/add-manpage
``Add a file to the man subdirectory of the current syspath. Files are
copied inside a directory `mansec`. By default, `mansec` is "man1".``
[manifest src &opt mansec]
(def s (sep))
(default mansec "man1")
(def filename (last (string/split s src)))
(os/mkdir (string (dyn *syspath*) s "man"))
(os/mkdir (string (dyn *syspath*) s "man" s mansec))
(put manifest :has-man true)
(bundle/add-file manifest src (string "man" s mansec s filename)))
(defn bundle/update-all
"Reinstall all bundles"
"Reinstall all bundles."
[&keys configs]
(each bundle (bundle/topolist)
(bundle/reinstall bundle ;(kvs configs)))))
@@ -4515,7 +4570,10 @@
###
# conditional compilation for reduced os
(def- getenv-alias (if-let [entry (in root-env 'os/getenv)] (entry :value) (fn [&])))
(def- getenv-raw (if-let [entry (in root-env 'os/getenv)] (entry :value) (fn [&])))
(defn- getenv-alias [env-var &opt dflt]
(def x (getenv-raw env-var dflt))
(if (= x "") nil x)) # empty string is coerced to nil
(defn- run-main
[env subargs arg]
@@ -4890,14 +4948,15 @@
"src/core/wrap.c"])
# Print janet.c to stdout
(print "/* Amalgamated build - DO NOT EDIT */")
(def image-only (has-value? boot/args "image-only"))
(print "/* " (if image-only "Image-only" "Amalgamated") " build - DO NOT EDIT */")
(print "/* Generated from janet version " janet/version "-" janet/build " */")
(print "#define JANET_BUILD \"" janet/build "\"")
(print ```#define JANET_AMALG```)
(defn do-one-file
[fname]
(unless (has-value? boot/args "image-only")
(unless image-only
(print "\n/* " fname " */")
(print "#line 0 \"" fname "\"\n")
(def source (slurp fname))

View File

@@ -88,7 +88,7 @@ void *janet_abstract_threaded(const JanetAbstractType *atype, size_t size) {
#ifdef JANET_WINDOWS
size_t janet_os_mutex_size(void) {
return sizeof(SRWLOCK);
return sizeof(CRITICAL_SECTION);
}
size_t janet_os_rwlock_size(void) {
@@ -96,20 +96,20 @@ size_t janet_os_rwlock_size(void) {
}
void janet_os_mutex_init(JanetOSMutex *mutex) {
InitializeSRWLock((PSRWLOCK) mutex);
InitializeCriticalSection((CRITICAL_SECTION *) mutex);
}
void janet_os_mutex_deinit(JanetOSMutex *mutex) {
/* no op? */
(void) mutex;
DeleteCriticalSection((CRITICAL_SECTION *) mutex);
}
void janet_os_mutex_lock(JanetOSMutex *mutex) {
AcquireSRWLockExclusive((PSRWLOCK) mutex);
EnterCriticalSection((CRITICAL_SECTION *) mutex);
}
void janet_os_mutex_unlock(JanetOSMutex *mutex) {
ReleaseSRWLockExclusive((PSRWLOCK) mutex);
/* error handling? May want to keep counter */
LeaveCriticalSection((CRITICAL_SECTION *) mutex);
}
void janet_os_rwlock_init(JanetOSRWLock *rwlock) {

View File

@@ -746,6 +746,7 @@ typedef struct SandboxOption {
static const SandboxOption sandbox_options[] = {
{"all", JANET_SANDBOX_ALL},
{"chroot", JANET_SANDBOX_CHROOT},
{"env", JANET_SANDBOX_ENV},
{"ffi", JANET_SANDBOX_FFI},
{"ffi-define", JANET_SANDBOX_FFI_DEFINE},
@@ -771,6 +772,7 @@ JANET_CORE_FN(janet_core_sandbox,
"Disable feature sets to prevent the interpreter from using certain system resources. "
"Once a feature is disabled, there is no way to re-enable it. Capabilities can be:\n\n"
"* :all - disallow all (except IO to stdout, stderr, and stdin)\n"
"* :chroot - disallow calling `os/posix-chroot`\n"
"* :env - disallow reading and write env variables\n"
"* :ffi - disallow FFI (recommended if disabling anything else)\n"
"* :ffi-define - disallow loading new FFI modules and binding new functions\n"

View File

@@ -83,7 +83,7 @@ struct JanetChannel {
int closed;
int is_threaded;
#ifdef JANET_WINDOWS
SRWLOCK lock;
CRITICAL_SECTION lock;
#else
pthread_mutex_t lock;
#endif
@@ -117,6 +117,9 @@ typedef struct {
double sec;
JanetVM *vm;
JanetFiber *fiber;
#ifdef JANET_WINDOWS
HANDLE cancel_event;
#endif
} JanetThreadedTimeout;
#define JANET_MAX_Q_CAPACITY 0x7FFFFFF
@@ -604,12 +607,7 @@ void janet_ev_init_common(void) {
#endif
}
#ifdef JANET_WINDOWS
static VOID CALLBACK janet_timeout_stop(ULONG_PTR ptr) {
UNREFERENCED_PARAMETER(ptr);
ExitThread(0);
}
#elif JANET_ANDROID
#if JANET_ANDROID
static void janet_timeout_stop(int sig_num) {
if (sig_num == SIGUSR1) {
pthread_exit(0);
@@ -620,10 +618,14 @@ static void janet_timeout_stop(int sig_num) {
static void handle_timeout_worker(JanetTimeout to, int cancel) {
if (!to.has_worker) return;
#ifdef JANET_WINDOWS
(void) cancel;
QueueUserAPC(janet_timeout_stop, to.worker, 0);
if (cancel && to.worker_event) {
SetEvent(to.worker_event);
}
WaitForSingleObject(to.worker, INFINITE);
CloseHandle(to.worker);
if (to.worker_event) {
CloseHandle(to.worker_event);
}
#else
#ifdef JANET_ANDROID
if (cancel) janet_assert(!pthread_kill(to.worker, SIGUSR1), "pthread_kill");
@@ -693,10 +695,20 @@ static void janet_timeout_cb(JanetEVGenericMessage msg) {
static DWORD WINAPI janet_timeout_body(LPVOID ptr) {
JanetThreadedTimeout tto = *(JanetThreadedTimeout *)ptr;
janet_free(ptr);
SleepEx((DWORD)(tto.sec * 1000), TRUE);
janet_interpreter_interrupt(tto.vm);
JanetEVGenericMessage msg = {0};
janet_ev_post_event(tto.vm, janet_timeout_cb, msg);
JanetTimestamp wait_begin = ts_now();
DWORD duration = (DWORD)round(tto.sec * 1000);
DWORD res = WAIT_TIMEOUT;
JanetTimestamp wait_end = ts_now();
for (DWORD i = 1; res == WAIT_TIMEOUT && (wait_end - wait_begin) < duration; i++) {
res = WaitForSingleObject(tto.cancel_event, (duration + i));
wait_end = ts_now();
}
/* only send interrupt message if result is WAIT_TIMEOUT */
if (res == WAIT_TIMEOUT) {
janet_interpreter_interrupt(tto.vm);
JanetEVGenericMessage msg = {0};
janet_ev_post_event(tto.vm, janet_timeout_cb, msg);
}
return 0;
}
#else
@@ -3270,7 +3282,13 @@ JANET_CORE_FN(cfun_ev_deadline,
tto->vm = &janet_vm;
tto->fiber = tocheck;
#ifdef JANET_WINDOWS
HANDLE worker = CreateThread(NULL, 0, janet_timeout_body, tto, 0, NULL);
HANDLE cancel_event = CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == cancel_event) {
janet_free(tto);
janet_panic("failed to create cancel event");
}
tto->cancel_event = cancel_event;
HANDLE worker = CreateThread(NULL, 0, janet_timeout_body, tto, CREATE_SUSPENDED, NULL);
if (NULL == worker) {
janet_free(tto);
janet_panic("failed to create thread");
@@ -3285,6 +3303,10 @@ JANET_CORE_FN(cfun_ev_deadline,
#endif
to.has_worker = 1;
to.worker = worker;
#ifdef JANET_WINDOWS
to.worker_event = cancel_event;
ResumeThread(worker);
#endif
} else {
to.has_worker = 0;
}

View File

@@ -67,6 +67,7 @@
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#include <AvailabilityMacros.h>
int chroot(const char *dirname);
#else
extern char **environ;
#endif
@@ -1541,6 +1542,28 @@ JANET_CORE_FN(os_posix_fork,
#endif
}
JANET_CORE_FN(os_posix_chroot,
"(os/posix-chroot dirname)",
"Call `chroot` to change the root directory to `dirname`. "
"Not supported on all systems (POSIX only).") {
janet_sandbox_assert(JANET_SANDBOX_CHROOT);
janet_fixarity(argc, 1);
#ifdef JANET_WINDOWS
(void) argv;
janet_panic("not supported on Windows");
#else
const char *root = janet_getcstring(argv, 0);
int result;
do {
result = chroot(root);
} while (result == -1 && errno == EINTR);
if (result == -1) {
janet_panic(janet_strerror(errno));
}
return janet_wrap_nil();
#endif
}
#ifdef JANET_EV
/* Runs in a separate thread */
static JanetEVGenericMessage os_shell_subr(JanetEVGenericMessage args) {
@@ -2849,6 +2872,7 @@ void janet_lib_os(JanetTable *env) {
JANET_CORE_REG("os/touch", os_touch),
JANET_CORE_REG("os/realpath", os_realpath),
JANET_CORE_REG("os/cd", os_cd),
JANET_CORE_REG("os/posix-chroot", os_posix_chroot),
#ifndef JANET_NO_UMASK
JANET_CORE_REG("os/umask", os_umask),
#endif

View File

@@ -60,7 +60,6 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
done = 1;
}
} else {
ret = janet_wrap_string(cres.error);
int32_t line = (int32_t) parser->line;
int32_t col = (int32_t) parser->column;
if ((cres.error_mapping.line > 0) &&
@@ -68,13 +67,17 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
line = cres.error_mapping.line;
col = cres.error_mapping.column;
}
JanetString ctx = janet_formatc("%s:%d:%d: compile error",
sourcePath, line, col);
JanetString errstr = janet_formatc("%s: %s",
(const char *)ctx,
(const char *)cres.error);
ret = janet_wrap_string(errstr);
if (cres.macrofiber) {
janet_eprintf("%s:%d:%d: compile error", sourcePath,
line, col);
janet_eprintf("%s", (const char *)ctx);
janet_stacktrace_ext(cres.macrofiber, ret, "");
} else {
janet_eprintf("%s:%d:%d: compile error: %s\n", sourcePath,
line, col, (const char *)cres.error);
janet_eprintf("%s\n", (const char *)errstr);
}
errflags |= JANET_DO_ERROR_COMPILE;
done = 1;
@@ -89,12 +92,14 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
done = 1;
break;
case JANET_PARSE_ERROR: {
const char *e = janet_parser_error(parser);
errflags |= JANET_DO_ERROR_PARSE;
ret = janet_cstringv(e);
int32_t line = (int32_t) parser->line;
int32_t col = (int32_t) parser->column;
janet_eprintf("%s:%d:%d: parse error: %s\n", sourcePath, line, col, e);
JanetString errstr = janet_formatc("%s:%d:%d: parse error: %s",
sourcePath, line, col,
janet_parser_error(parser));
ret = janet_wrap_string(errstr);
janet_eprintf("%s\n", (const char *)errstr);
done = 1;
break;
}
@@ -122,7 +127,8 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
janet_loop();
if (fiber) {
janet_gcunroot(janet_wrap_fiber(fiber));
ret = fiber->last_value;
if (!errflags)
ret = fiber->last_value;
}
}
#endif

View File

@@ -68,6 +68,7 @@ typedef struct {
int has_worker;
#ifdef JANET_WINDOWS
HANDLE worker;
HANDLE worker_event;
#else
pthread_t worker;
#endif

View File

@@ -147,6 +147,7 @@ extern "C" {
|| defined(__s390x__) /* S390 64-bit */ \
|| defined(__s390__) /* S390 32-bit */ \
|| defined(__ARMEB__) /* ARM big endian */ \
|| defined(__AARCH64EB__) /* ARM64 big endian */ \
|| ((defined(__CC_ARM) || defined(__ARMCC__)) /* ARM RealView compiler */ \
&& defined(__BIG_ENDIAN))
#define JANET_BIG_ENDIAN 1
@@ -1899,6 +1900,7 @@ JANET_API void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *pr
#define JANET_SANDBOX_FFI_USE 2048
#define JANET_SANDBOX_FFI_JIT 4096
#define JANET_SANDBOX_SIGNAL 8192
#define JANET_SANDBOX_CHROOT 16384
#define JANET_SANDBOX_FFI (JANET_SANDBOX_FFI_DEFINE | JANET_SANDBOX_FFI_USE | JANET_SANDBOX_FFI_JIT)
#define JANET_SANDBOX_FS (JANET_SANDBOX_FS_WRITE | JANET_SANDBOX_FS_READ | JANET_SANDBOX_FS_TEMP)
#define JANET_SANDBOX_NET (JANET_SANDBOX_NET_CONNECT | JANET_SANDBOX_NET_LISTEN)

View File

@@ -50,6 +50,11 @@
(def errsym (keyword (gensym)))
~(assert (= ,errsym (try (do ,;forms) ([_] ,errsym))) ,msg))
(defmacro assert-error-value
[msg errval & forms]
(def e (gensym))
~(assert (= ,errval (try (do ,;forms) ([,e] ,e))) ,msg))
(defn check-compile-error
[form]
(def result (compile form))

View File

@@ -1023,4 +1023,11 @@
(assert (deep-not= @{:key1 "value1" [@"key2"] @"value2"}
@{:key1 "value1" [@"key2"] @"value2"}) "deep= mutable keys")
# different try overloads
(assert (= (try (error :error) ([] :caught)) :caught))
(assert (= (try (error :error) ([e] e)) :error))
(assert (= (try (error :error) ([e fib] [e (fiber? fib)])) [:error true]))
# regression test for #1659
(assert (= (try (error :error) ([_ _] :caught)) :caught))
(end-suite)

View File

@@ -117,8 +117,17 @@
(assert (= 0 (length (bundle/list))) "bundles are listed correctly 7")
(assert (= 0 (length (bundle/topolist))) "bundles are listed correctly 8")
# Try installing a bundle that is missing bundle script
(assert-error-value "bundle missing bundle script"
"bundle must contain bundle.janet or bundle/init.janet"
(bundle/install "./examples/sample-bad-bundle1"))
(assert (= 0 (length (bundle/list))) "check failure 0")
(assert (= 0 (length (bundle/topolist))) "check failure 1")
# Try installing a bundle that fails check
(assert-error "bad test" (bundle/install "./examples/sample-bad-bundle" :check true))
(assert-error-value "bundle check hook fails"
"Check failed!"
(bundle/install "./examples/sample-bad-bundle2" :check true))
(assert (= 0 (length (bundle/list))) "check failure 0")
(assert (= 0 (length (bundle/topolist))) "check failure 1")