1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
use crate::{Bindgen, Compile, LanguageMethods, Runner, Verify};
use anyhow::{Context, Result, bail};
use clap::Parser;
use std::env;
use std::path::Path;
use std::process::Command;
#[derive(Default, Debug, Clone, Parser)]
pub struct CustomOpts {
/// Specifies how to compile programs not natively known to this executable.
///
/// For example `--custom foo=my-foo-script.sh` will register that files
/// with the extension `foo` (e.g. `test.foo`) will be compiled with
/// `my-foo-script.sh` by this program.
///
/// The script specified will be invoked with its first argument as one of
/// three values:
///
/// * `prepare` - this is used to perform any one-time setup for an entire
/// test run, such as downloading artifacts. This has the `PREP_DIR`
/// environment variable set.
///
/// * `bindgen` - this is used to perform bindings generation for the
/// program at-hand. This has the `WIT`, and `BINDINGS_DIR` env
/// vars set.
///
/// * `compile` - this is used to perform an actual compilation which
/// creates a component. This has the `SOURCE`, `KIND`, `PREP_DIR`,
/// `BINDINGS_DIR`, `ARTIFACTS_DIR`, and `OUTPUT` env vars set.
///
/// * `verify` - this is used to verify that generated bindings are valid,
/// but does not create an actual component necessarily. This has the
/// `WIT`, `BINDINGS_DIR`, and `ARTIFACTS_DIR` env vars set.
///
/// Environment variables are used to communicate various bits and pieces of
/// data to scripts. Environment variables used are:
///
/// * `PREP_DIR` - the output of the `prepare` step and also available
/// during the `compile` step. Used for once-per-test-run storage.
///
/// * `WIT` - path to a `*.wit` file during the `bindgen` step.
///
/// * `BINDINGS_DIR` - the output directory of `bindgen` and also inputs
/// to `compile`.
///
/// * `SOURCE` - the source file being compiled as part of `compile`.
///
/// * `KIND` - either `runner` or `test` as part of the `compile` step.
///
/// * `ARTIFACTS_DIR` - temporary directory which contains `BINDINGS_DIR`
/// where temporary artifacts can be stored. Part of the `compile` step.
///
/// * `OUTPUT` - where to place the final output component.
#[arg(long , value_name = "EXT=PATH", value_parser = parse_custom)]
pub custom: Vec<(String, String)>,
}
fn parse_custom(s: &str) -> Result<(String, String)> {
let mut parts = s.splitn(2, '=');
Ok((
parts.next().unwrap().to_string(),
parts
.next()
.context("must be of the form `a=b`")?
.to_string(),
))
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Language {
extension: String,
script: String,
}
impl Language {
pub fn lookup(runner: &Runner, language: &str) -> Result<Language> {
for (ext, script) in runner.opts.custom.custom.iter() {
if ext == language {
return Ok(Language {
extension: ext.to_string(),
script: script.to_string(),
});
}
}
bail!(
"file extension `{language}` is unknown, but you can pass \
a script with `--custom {language}=my-script.sh` to get it working"
)
}
}
impl LanguageMethods for Language {
fn display(&self) -> &str {
&self.extension
}
fn comment_prefix_for_test_config(&self) -> Option<&str> {
None
}
fn should_fail_verify(
&self,
_name: &str,
_config: &crate::config::WitConfig,
_args: &[String],
) -> bool {
false
}
fn generate_bindings(&self, runner: &Runner, bindgen: &Bindgen, dir: &Path) -> Result<()> {
runner.run_command(
Command::new(&self.script)
.arg("bindgen")
.env("WIT", &bindgen.wit_path)
.env("BINDINGS_DIR", dir),
)
}
fn prepare(&self, runner: &mut Runner) -> Result<()> {
let dir = env::current_dir()?
.join(&runner.opts.artifacts)
.join(&self.extension);
runner.run_command(
Command::new(&self.script)
.arg("prepare")
.env("PREP_DIR", &dir),
)
}
fn compile(&self, runner: &Runner, compile: &Compile<'_>) -> Result<()> {
let dir = env::current_dir()?
.join(&runner.opts.artifacts)
.join(&self.extension);
runner.run_command(
Command::new(&self.script)
.arg("compile")
.env("SOURCE", &compile.component.path)
.env("KIND", compile.component.kind.to_string())
.env("PREP_DIR", &dir)
.env("BINDINGS_DIR", &compile.bindings_dir)
.env("ARTIFACTS_DIR", &compile.artifacts_dir)
.env("OUTPUT", &compile.output),
)
}
fn verify(&self, runner: &Runner, verify: &Verify<'_>) -> Result<()> {
runner.run_command(
Command::new(&self.script)
.arg("verify")
.env("WIT", verify.wit_test)
.env("BINDINGS_DIR", &verify.bindings_dir)
.env("ARTIFACTS_DIR", &verify.artifacts_dir),
)
}
}