%% -*- erlang -*-
{erl_opts, [
    debug_info,
    warnings_as_errors,
    {warn_format, 1},
    warn_export_vars,
    warn_obsolete_guard,
    warn_unused_import
]}.

{deps, []}.

%% The NIF is optional: it closes a time-of-check-to-time-of-use window in WASI
%% path resolution, and the runtime falls back to a pure Erlang resolver when it
%% is absent. The script always exits 0 so a missing C compiler cannot fail the
%% build.
{pre_hooks, [{compile, "./scripts/build-nif.sh"}]}.

{profiles, [
    {test, [
        {deps, [{proper, "1.4.0"}]},
        {erl_opts, [debug_info, nowarn_export_all, {d, 'TEST'}]},
        {extra_src_dirs, [{"test/support", [{recursive, true}]},
                          {"examples", [{recursive, true}]}]}
    ]}
]}.

{dialyzer, [
    {warnings, [unknown, no_return, error_handling]},
    %% `compiler' for `cerl' and `compile:forms/2', which `wasm_core' generates
    %% Core Erlang through. Without it every call into them is an unknown
    %% function and the `unknown' warning class fires on all of them.
    {plt_extra_apps, [crypto, compiler]}
]}.

{xref_checks, [
    undefined_function_calls,
    locals_not_used,
    deprecated_function_calls
]}.

{ct_opts, [{sys_config, "test/sys.config"}]}.

%% Documentation is built with ex_doc, which reads the EEP-59 `-moduledoc' and
%% `-doc' attributes rather than edoc comments. The hand-written pages live in
%% `docs/' and the generated site is a build artefact, so it goes under
%% `_build/' with everything else that is generated.
{project_plugins, [{rebar3_lint, "5.0.4"}, rebar3_ex_doc]}.

%% Tell rebar3_hex which provider builds the docs it publishes. Without it
%% `rebar3 hex publish' stops and asks for one.
{hex, [{doc, ex_doc}]}.

%% No `output' and no `dir': the site goes to `doc/', which is both
%% rebar3_ex_doc's default and where `rebar3_hex_build:resolve_dir/2' looks for
%% it. Naming either one means naming the other, and neither plugin knows the
%% other's key, so one of them warns about it on every docs build. `doc/' is
%% gitignored; the sources are in `docs/'.
%%
%% There was a `name' here too, and nothing read it: the rendered title comes
%% from the OTP application, so the docs say `wasm', and the package name
%% rebar3_hex publishes under comes from `pkg_name' in `wasm.app.src'.
{ex_doc, [
    {source_url, <<"https://github.com/benoitc/erlang_wasm">>},
    {main, <<"readme">>},
    {extras, [
        {'README.md', #{title => <<"erlang_wasm">>}},
        {'docs/getting-started.md', #{title => <<"Getting started">>}},
        {'docs/guests.md', #{title => <<"Producing a module">>}},
        {'docs/embedding.md', #{title => <<"Embedding">>}},
        {'docs/host-functions.md', #{title => <<"Host functions">>}},
        {'docs/wasi.md', #{title => <<"WASI">>}},
        {'docs/worker.md', #{title => <<"Workers">>}},
        {'docs/streams.md', #{title => <<"Streams">>}},
        {'docs/security.md', #{title => <<"Security">>}},
        {'docs/features.md', #{title => <<"Features and conformance">>}},
        {'docs/compiled-tier.md', #{title => <<"The compiled tier">>}},
        {'docs/architecture.md', #{title => <<"Architecture">>}},
        {'docs/adding-an-instruction.md',
         #{title => <<"Adding an instruction">>}},
        {'docs/design-notes.md', #{title => <<"Design notes">>}},
        {'CHANGELOG.md', #{title => <<"Changelog">>}}
    ]},
    {groups_for_extras, [
        {<<"Guides">>, [<<"docs/getting-started.md">>, <<"docs/guests.md">>,
                        <<"docs/embedding.md">>, <<"docs/host-functions.md">>,
                        <<"docs/wasi.md">>, <<"docs/worker.md">>,
                        <<"docs/streams.md">>,
                        <<"docs/compiled-tier.md">>]},
        {<<"Reference">>, [<<"docs/security.md">>, <<"docs/features.md">>,
                           <<"CHANGELOG.md">>]},
        %% For somebody changing the runtime rather than using it. Everything
        %% above answers "how do I run a module"; these answer "where does this
        %% live and what will it break".
        {<<"Internals">>, [<<"docs/architecture.md">>,
                           <<"docs/adding-an-instruction.md">>,
                           <<"docs/design-notes.md">>]}
    ]},
    %% Every module belongs to a group. An ungrouped one lands in an unnamed
    %% bucket at the bottom of the sidebar, which is where the compiled tier and
    %% the numeric modules used to be.
    {groups_for_modules, [
        {<<"Embedding">>, [wasm, wasi, wasm_limits, wasm_engine]},
        {<<"WASI">>, [wasi_preview1, wasi_net, wasi_sock, wasi_fs, wasi_path,
                      wasi_file_nif]},
        {<<"Runtime">>, [wasm_instance, wasm_exec, wasm_memory, wasm_table,
                         wasm_global, wasm_heap, wasm_error, wasm_keeper,
                         wasm_wait, wasm_types, wasm_module_cache]},
        {<<"Compiled tier">>, [wasm_jit, wasm_core, wasm_code_slots,
                               wasm_code_cache, wasm_jit_sup]},
        {<<"Front end">>, [wasm_decode, wasm_decode_code, wasm_decode_simd,
                           wasm_decode_gc, wasm_decode_atomic, wasm_leb128,
                           wasm_validate, wasm_validate_code,
                           wasm_validate_simd, wasm_validate_atomic,
                           wasm_wat, wasm_wat_lex, wasm_wat_sexp,
                           wasm_wat_num, wasm_wat_instr, wasm_wast]},
        {<<"Numerics">>, [wasm_num, wasm_num_float, wasm_num_trunc, wasm_simd]},
        {<<"Supervision">>, [wasm_app, wasm_sup, wasm_subsup, wasm_store]}
    ]},
    {api_reference, true}
]}.

{cover_enabled, true}.
{cover_opts, [verbose]}.

{alias, [
    {check, [xref, dialyzer, {ct, "--cover"}]},
    %% Not `{as, "bench"}': rebar3 rejects the `as' namespace inside an alias,
    %% so the documented command failed outright and the published benchmark
    %% numbers could not be reproduced. The benchmarks need the `test' profile
    %% anyway, for `test/support'.
    %% `--group=bench' because the measurements are not in either suite's
    %% `all/0': they log numbers and assert nothing, so a plain `rebar3 ct'
    %% would spend minutes reporting them as cases that passed.
    %% One `ct' per suite: `--group' names a group inside *one* suite, and
    %% rebar3 answers `multiple_suites_and_cases' for a list.
    {bench, [{ct, "--suite=test/wasm_bench_SUITE --group=bench"},
             {ct, "--suite=test/wasm_gc_bench_SUITE --group=bench"}]}
]}.

%% Elvis, tuned to decisions this project has already made and documented.
%% A new per-module ignore belongs here with a one-line reason.
{elvis, [
    {config, [
        #{
            files => ["src/**/*.erl"],
            rules => [
                %% The runtime is built on macros for opcode tables, field
                %% indices and width constants; that is the point of them.
                {elvis_style, no_macros, disable},
                %% `#inst{}' and `#mut{}' are the two records the whole
                %% runtime threads; naming a type for each would restate them.
                {elvis_style, no_spec_with_records, disable},
                %% The decoder and the validator signal by throwing and
                %% `wasm_error:capture/1' turns it into a value at the
                %% boundary. That is the documented error model.
                {elvis_style, no_throw, disable},
                %% `wasm_exec' is one dispatcher on purpose: every extra call
                %% on that path has measured as a regression. See ATTEMPTS.md.
                {elvis_style, no_god_modules, disable},
                %% Generated code is called by a name resolved at run time,
                %% which is what a code slot is.
                {elvis_style, no_invalid_dynamic_calls, disable},
                %% `if_' and `catch_' are WebAssembly opcode names with the
                %% Erlang keyword escaped, not our naming.
                {elvis_style, atom_naming_convention, disable},
                %% Numeric conversions read better as `if' ladders over ranges
                %% than as nested cases.
                {elvis_style, no_if_expression, disable},
                %% Types shared through `include/wasm.hrl' are exported from
                %% the modules that own them, not from every user.
                {elvis_style, export_used_types, disable},
                %% `invoke/6' and `indirect_out/9' are the generated-code ABI;
                %% their arity is fixed by what the caller must pass.
                {elvis_style, max_function_arity, disable},
                {elvis_style, no_common_caveats_call, disable},
                {elvis_style, param_pattern_matching, disable},
                {elvis_style, dont_repeat_yourself, disable},
                {elvis_style, no_single_clause_case, disable},
                {elvis_style, simplify_anonymous_functions, disable},
                {elvis_style, consistent_variable_naming, disable},
                {elvis_style, no_deep_nesting, disable},
                %% `wasm_wat_lex' compares against a boolean because the
                %% scanner state is one.
                {elvis_style, no_boolean_in_comparison, disable},
                {elvis_style, private_data_types, disable},
                %% The convention is 80 columns and it is kept by review.
                %% As a rule it would demand reflowing 46 working lines,
                %% mostly opcode tables and generated-code comments, and the
                %% longest of them is 179: any limit that passes today would
                %% not be a limit.
                {elvis_text_style, max_line_length, disable},
                %% One `io:format' in `wasm_wat' is a diagnostic dump behind an
                %% explicit call, not a stray debug print.
                {elvis_style, no_debug_call, disable}
            ]
        }
    ]}
]}.
