Skip to content

Commit

Permalink
move contributing.md and add script_modules.md
Browse files Browse the repository at this point in the history
  • Loading branch information
annacrombie committed Dec 17, 2023
1 parent 11157dd commit 5861d12
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ originally based on `boson`, though has since been almost completely rewritten.

[muon]: https://muon.build
[samurai]: https://github.com/michaelforney/samurai
[contributing guide]: https://git.sr.ht/~lattis/muon/tree/master/item/CONTRIBUTING.md
[contributing guide]: https://git.sr.ht/~lattis/muon/tree/master/item/doc/contributing.md
[mailing list]: https://lists.sr.ht/~lattis/muon/
[issue tracker]: https://todo.sr.ht/~lattis/muon/
[#muon on libera.chat]: ircs://irc.libera.chat/#muon
Expand Down
File renamed without changes.
206 changes: 206 additions & 0 deletions doc/script_modules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
<!--
SPDX-FileCopyrightText: Stone Tickle <[email protected]>
SPDX-License-Identifier: GPL-3.0-only
-->

# Script Modules

With muon, you can write modules in an extended form of the meson dsl. This is
for a number of reasons:

- Many meson modules are effectively reusable chunks of meson code (calls to
dependency, custom\_target, etc.), so it is not necessary to access things
that a native module has access to like the interpreter state.
- Lowering the bar for module contributions (this is important because they
often involve specific tools I have no experience with/interest in).
- Potential to some day share module code with other meson implementations.

That last point is a bit pie in the sky, but at least this makes it technically
possible from muon's point of view and proves that it has value.

## Writing a new script module

All script modules live at `src/script/modules/<module_name>.meson`. After
adding a new module, you must modify `src/script/meson.build` to register it.
muon will embed the module source text as a char[] in the built executable, and
when the module is import()ed that source will be interpreted.

## Module structure

A script module consists of functions that perform the various module
operations, with a return statement at the bottom of the file that exports those
functions.

```
func func1()
endfunc
func func2()
endfunc
return {
'func1': func1,
'func2': func2,
}
```

The final `return` at module scope is what "exports" the functions to module
consumers. The returned object must be of type `dict[func]`.

## Functions

Function definition is started with the `func` keyword followed by an
identifier, and a list of arguments enclosed in parenthesis. If the function
returns a value it must specify the type of that value by adding `-> <type>`
after the argument list.

Positional and keyword arguments are supported in the argument list. Positional
arguments must come first. Keyword arguments are specified by adding a colon
(:) to the end of the argument followed by an optional default value.
All arguments must also specify their type by following the argument name with a
type specifier.

Examples:

```
func a() -> int
return 1
endfunc
func b(arg1 int) -> int
return arg1
endfunc
func b(arg1 int, kw str:, kw_with_default str: 'default')
message(arg1)
message(kw)
message(kw_with_default)
endfunc
```

## Scope

Scope generally works like other interpreted languages.

Modules have their own scope and cannot access any external variables (save
builtins such as meson, build\_machine, etc.).

Functions also get their own scope, but can see variables that have been
declared in parent scopes prior to the function definition. Function
definitions also capture their scope.

Example:

```
func a() -> func
i = 0
func b() -> int
i += 1
return i
endfunc
return b
endfunc
counter = a()
counter() #=> 1
counter() #=> 2
new_counter = a()
new_counter() #=> 1
new_counter() #=> 2
counter() #=> 3
```

## Type specifiers

Type specifiers take the form of the ones found in the meson docs. The
following types are allowed:

- `void`
- `compiler`
- `dep`
- `meson`
- `str`
- `int`
- `list`
- `dict`
- `bool`
- `file`
- `build_tgt`
- `subproject`
- `build_machine`
- `feature`
- `external_program`
- `python_installation`
- `runresult`
- `cfg_data`
- `custom_tgt`
- `test`
- `module`
- `install_tgt`
- `env`
- `inc`
- `option`
- `disabler`
- `generator`
- `generated_list`
- `alias_tgt`
- `both_libs`
- `typeinfo`
- `func`
- `source_set`
- `source_configuration`

In addition, a value that can take one of many types can be specified by writing
multiple types separated with a `|`.

```
int|str
dict|list|str
```

There are a few preset combinations of types:

- `any` - all of the above types except `void`
- `exe` - `str|file|external_program|python_installation|build_tgt|custom_tgt`

`dict` and `list` should also be followed by a sub-type enclosed in square
brackets.

```
dict[str]
dict[str|int]
```

Finally, you can wrap the entire type in `glob[<type>]` or `listify[<type>]` to
get the special argument handling detailed in doc/contributing.md (search for
`TYPE_TAG_{GLOB,LISTIFY}`.

## Additional built-in functions

Various additional builtin functions are avaliable:

- `serial_load(path str) -> any` - load a serialized object from a file
- `serial_dump(path str, obj any)` - serialize and save an object to a file
- `is_void(arg any) -> bool` - check if arg is the value void. This can be used
to check if a kwarg with no default value has been set.
- `typeof(arg any) -> str` - returns the type of an object.
- `list.delete(index int)` - delete the element from list at the specefied index
- `dict.delete(key str)` - delete key from dict

The `fs` module also has additonal functions:

- `copy(src str|file, dest str)` - copy a file from src to dest
- `write(dest str|file, data str)` - write `data` to `dest`
- `cwd() -> str` - returns muon's current working directory
- `mkdir(path str)` - make the directory at `path`
- `is_basename(path str) -> bool` - true if str contains no path separators
- `is_subpath(base str, sub str) -> bool` - true if `sub` is a subpath of `base`
- `add_suffix(path str, suff str) -> str` - add the suffix `suff` to `path`
- `make_absolute(path str) -> str` - prepend cwd to `path` if path is relative
- `relative_to(base str, sub str) -> str` - return path `sub` relative to `base`
- `without_ext(path str) -> str` - return path with extension removed
to it, otherwise return path
- `executable(path str) -> str` - if path has no path separators, prepend './'

0 comments on commit 5861d12

Please sign in to comment.