gungraun_macros/lib.rs
1//! The library of gungraun-macros
2
3#![cfg_attr(docsrs, feature(doc_cfg))]
4#![doc(test(attr(warn(unused))))]
5#![doc(test(attr(allow(unused_extern_crates))))]
6
7mod bin_bench;
8mod common;
9pub(crate) mod defaults;
10mod derive_macros;
11mod lib_bench;
12
13use proc_macro::TokenStream;
14use proc_macro_error2::proc_macro_error;
15use serde::Deserialize;
16
17#[derive(Debug, Deserialize)]
18struct CargoMetadata {
19 workspace_root: String,
20}
21
22impl CargoMetadata {
23 fn try_new() -> Option<Self> {
24 std::process::Command::new(option_env!("CARGO").unwrap_or("cargo"))
25 .args(["metadata", "--no-deps", "--format-version", "1"])
26 .output()
27 .ok()
28 .and_then(|output| serde_json::de::from_slice(&output.stdout).ok())
29 }
30}
31
32/// The `#[library_benchmark]` attribute lets you define a benchmark function which you can later
33/// use in the `library_benchmark_groups!` macro.
34///
35/// This attribute accepts the following parameters:
36/// * `config`: Accepts a `LibraryBenchmarkConfig`
37/// * `setup`: A global setup function which is applied to all following [`#[bench]`][bench] and
38/// [`#[benches]`][benches] attributes if not overwritten by a `setup` parameter of these
39/// attributes.
40/// * `teardown`: Similar to `setup` but takes a global `teardown` function.
41///
42/// A short introductory example on the usage including the `setup` parameter:
43///
44/// ```rust
45/// # use gungraun_macros::library_benchmark;
46/// # mod gungraun {
47/// # pub mod client_requests { pub mod cachegrind {
48/// # pub fn start_instrumentation() {}
49/// # pub fn stop_instrumentation() {}
50/// # }}
51/// # pub struct LibraryBenchmarkConfig {}
52/// # pub mod __internal {
53/// # pub enum InternalLibFunctionKind { None, Default(fn()) }
54/// # pub struct InternalMacroLibBench {
55/// # pub id_display: Option<&'static str>,
56/// # pub args_display: Option<&'static str>,
57/// # pub func: InternalLibFunctionKind,
58/// # pub config: Option<fn() -> InternalLibraryBenchmarkConfig>
59/// # }
60/// # pub struct InternalLibraryBenchmarkConfig {}
61/// # }
62/// # }
63/// fn my_setup(value: u64) -> String {
64/// format!("{value}")
65/// }
66///
67/// fn my_other_setup(value: u64) -> String {
68/// format!("{}", value + 10)
69/// }
70///
71/// #[library_benchmark(setup = my_setup)]
72/// #[bench::first(21)]
73/// #[benches::multiple(42, 84)]
74/// #[bench::last(args = (102), setup = my_other_setup)]
75/// fn my_bench(value: String) {
76/// println!("{value}");
77/// }
78/// # fn main() {}
79/// ```
80///
81/// The `#[library_benchmark]` attribute can be applied in two ways.
82///
83/// 1. Using the `#[library_benchmark]` attribute as a standalone without [`#[bench]`][bench] or
84/// [`#[benches]`][benches] is fine for simple function calls without parameters.
85/// 2. We mostly need to benchmark cases which would need to be setup for example with a vector, but
86/// everything we set up within the benchmark function itself would be attributed to the event
87/// counts. The second form of this attribute macro uses the [`#[bench]`][bench] and
88/// [`#[benches]`][benches] attributes to set up benchmarks with different cases. The main
89/// advantage is, that the setup costs and event counts aren't attributed to the benchmark (and
90/// opposed to the old api we don't have to deal with callgrind arguments, toggles,
91/// inline(never), ...)
92///
93/// # The `#[bench]` attribute
94///
95/// The basic structure is `#[bench::some_id(/* parameters */)]`. The part after the `::` must be an
96/// id unique within the same `#[library_benchmark]`. This attribute accepts the following
97/// parameters:
98///
99/// * __`args`__: A tuple with a list of arguments which are passed to the benchmark function. The
100/// parentheses also need to be present if there is only a single argument (`#[bench::my_id(args =
101/// (10))]`).
102/// * __`config`__: Accepts a `LibraryBenchmarkConfig`
103/// * __`setup`__: A function which takes the arguments specified in the `args` parameter and passes
104/// its return value to the benchmark function.
105/// * __`teardown`__: A function which takes the return value of the benchmark function.
106///
107/// If no other parameters besides `args` are present you can simply pass the arguments as a list of
108/// values. Instead of `#[bench::my_id(args = (10, 20))]`, you could also use the shorter
109/// `#[bench::my_id(10, 20)]`.
110///
111/// ```rust
112/// # use gungraun_macros::library_benchmark;
113/// # mod gungraun {
114/// # pub mod client_requests { pub mod cachegrind {
115/// # pub fn start_instrumentation() {}
116/// # pub fn stop_instrumentation() {}
117/// # }}
118/// # pub struct LibraryBenchmarkConfig {}
119/// # pub mod __internal {
120/// # pub enum InternalLibFunctionKind { None, Default(fn()) }
121/// # pub struct InternalMacroLibBench {
122/// # pub id_display: Option<&'static str>,
123/// # pub args_display: Option<&'static str>,
124/// # pub func: InternalLibFunctionKind,
125/// # pub config: Option<fn() -> InternalLibraryBenchmarkConfig>
126/// # }
127/// # pub struct InternalLibraryBenchmarkConfig {}
128/// # }
129/// # }
130/// // Assume this is a function in your library which you want to benchmark
131/// fn some_func(value: u64) -> u64 {
132/// 42
133/// }
134///
135/// #[library_benchmark]
136/// #[bench::some_id(42)]
137/// fn bench_some_func(value: u64) -> u64 {
138/// std::hint::black_box(some_func(value))
139/// }
140/// # fn main() {}
141/// ```
142///
143/// # The `#[benches]` attribute
144///
145/// The `#[benches]` attribute lets you define multiple benchmarks in one go. This attribute accepts
146/// the same parameters as the [`#[bench]`][bench] attribute: `args`, `config`, `setup` and
147/// `teardown` and additionally the `iter` and `file` parameter. In contrast to the `args` parameter
148/// in [`#[bench]`][bench], `args` takes an array of arguments. The id (`#[benches::id(*/ parameters
149/// */)]`) is getting suffixed with the index of the current element of the `args` array.
150///
151/// ```rust
152/// # use gungraun_macros::library_benchmark;
153/// # mod my_lib { pub fn bubble_sort(_: Vec<i32>) -> Vec<i32> { vec![] } }
154/// # mod gungraun {
155/// # pub mod client_requests { pub mod cachegrind {
156/// # pub fn start_instrumentation() {}
157/// # pub fn stop_instrumentation() {}
158/// # }}
159/// # pub struct LibraryBenchmarkConfig {}
160/// # pub mod __internal {
161/// # pub enum InternalLibFunctionKind { None, Default(fn()) }
162/// # pub struct InternalMacroLibBench {
163/// # pub id_display: Option<&'static str>,
164/// # pub args_display: Option<&'static str>,
165/// # pub func: InternalLibFunctionKind,
166/// # pub config: Option<fn() -> InternalLibraryBenchmarkConfig>
167/// # }
168/// # pub struct InternalLibraryBenchmarkConfig {}
169/// # }
170/// # }
171/// use std::hint::black_box;
172///
173/// fn setup_worst_case_array(start: i32) -> Vec<i32> {
174/// if start.is_negative() {
175/// (start..0).rev().collect()
176/// } else {
177/// (0..start).rev().collect()
178/// }
179/// }
180///
181/// #[library_benchmark]
182/// #[benches::multiple(vec![1], vec![5])]
183/// #[benches::with_setup(args = [1, 5], setup = setup_worst_case_array)]
184/// fn bench_bubble_sort_with_benches_attribute(input: Vec<i32>) -> Vec<i32> {
185/// black_box(my_lib::bubble_sort(input))
186/// }
187/// # fn main() {}
188/// ```
189///
190/// Usually the `arguments` are passed directly to the benchmarking function as it can be seen in
191/// the `#[benches::multiple(...)]` case. In `#[benches::with_setup(...)]`, the arguments are passed
192/// to the `setup` function and the return value of the `setup` function is passed as argument to
193/// the benchmark function. The above `#[library_benchmark]` is pretty much the same as
194///
195/// ```rust
196/// # use gungraun_macros::library_benchmark;
197/// # mod gungraun {
198/// # pub struct LibraryBenchmarkConfig {}
199/// # pub mod client_requests { pub mod cachegrind {
200/// # pub fn start_instrumentation() {}
201/// # pub fn stop_instrumentation() {}
202/// # }}
203/// # pub mod __internal {
204/// # pub enum InternalLibFunctionKind { None, Default(fn()) }
205/// # pub struct InternalMacroLibBench {
206/// # pub id_display: Option<&'static str>,
207/// # pub args_display: Option<&'static str>,
208/// # pub func: InternalLibFunctionKind,
209/// # pub config: Option<fn() -> InternalLibraryBenchmarkConfig>
210/// # }
211/// # pub struct InternalLibraryBenchmarkConfig {}
212/// # }
213/// # }
214/// # fn bubble_sort(_: Vec<i32>) -> Vec<i32> { vec![] }
215/// # fn setup_worst_case_array(_: i32) -> Vec<i32> { vec![] }
216/// use std::hint::black_box;
217///
218/// #[library_benchmark]
219/// #[bench::multiple_0(vec![1])]
220/// #[bench::multiple_1(vec![5])]
221/// #[bench::with_setup_0(setup_worst_case_array(1))]
222/// #[bench::with_setup_1(setup_worst_case_array(5))]
223/// fn bench_bubble_sort_with_benches_attribute(input: Vec<i32>) -> Vec<i32> {
224/// black_box(bubble_sort(input))
225/// }
226/// # fn main() {}
227/// ```
228///
229/// but a lot more concise especially if a lot of values are passed to the same `setup` function.
230///
231/// The `iter` parameter accepts an iterator expression as argument (anything that implements
232/// [`std::iter::IntoIterator`]) which creates a benchmark case per iterator element. For example
233/// the following will create three benchmark cases `0`, `1` and `2` from the `0..3` range:
234///
235/// ```rust
236/// # use gungraun_macros::library_benchmark;
237/// # mod gungraun {
238/// # pub mod client_requests { pub mod cachegrind {
239/// # pub fn start_instrumentation() {}
240/// # pub fn stop_instrumentation() {}
241/// # }}
242/// # pub struct LibraryBenchmarkConfig {}
243/// # pub mod __internal {
244/// # pub enum InternalLibFunctionKind { None, Default(fn()), Iter(fn(Option<usize>) -> usize) }
245/// # pub struct InternalMacroLibBench {
246/// # pub id_display: Option<&'static str>,
247/// # pub args_display: Option<&'static str>,
248/// # pub func: InternalLibFunctionKind,
249/// # pub config: Option<fn() -> InternalLibraryBenchmarkConfig>
250/// # }
251/// # pub struct InternalLibraryBenchmarkConfig {}
252/// # }
253/// # }
254/// # mod my_lib { pub fn u64_to_string(_: u64) -> String { "0".to_owned() } }
255/// use std::hint::black_box;
256/// #[library_benchmark]
257/// #[benches::from_iter(iter = 0..3)]
258/// fn some_bench(num: u64) -> String {
259/// black_box(my_lib::u64_to_string(num))
260/// }
261/// # fn main() {}
262/// ```
263///
264/// The `file` parameter reads the specified file line by line creating a benchmark from each line.
265/// The line is passed to the benchmark function as `String` or if the `setup` parameter is also
266/// present to the `setup` function. A small example assuming you have a file `benches/inputs`
267/// (relative paths are interpreted to the workspace root) with the following content
268///
269/// ```text
270/// 1
271/// 11
272/// 111
273/// ```
274///
275/// then
276///
277/// ```rust
278/// # use gungraun_macros::library_benchmark;
279/// # mod gungraun {
280/// # pub mod client_requests { pub mod cachegrind {
281/// # pub fn start_instrumentation() {}
282/// # pub fn stop_instrumentation() {}
283/// # }}
284/// # pub struct LibraryBenchmarkConfig {}
285/// # pub mod __internal {
286/// # pub enum InternalLibFunctionKind { None, Default(fn()) }
287/// # pub struct InternalMacroLibBench {
288/// # pub id_display: Option<&'static str>,
289/// # pub args_display: Option<&'static str>,
290/// # pub func: InternalLibFunctionKind,
291/// # pub config: Option<fn() -> InternalLibraryBenchmarkConfig>
292/// # }
293/// # pub struct InternalLibraryBenchmarkConfig {}
294/// # }
295/// # }
296/// # mod my_lib { pub fn string_to_u64(_line: String) -> Result<u64, String> { Ok(0) } }
297/// use std::hint::black_box;
298/// #[library_benchmark]
299/// #[benches::by_file(file = "gungraun-macros/fixtures/inputs")]
300/// fn some_bench(line: String) -> Result<u64, String> {
301/// black_box(my_lib::string_to_u64(line))
302/// }
303/// # fn main() {}
304/// ```
305///
306/// The above is roughly equivalent to the following but with the `args` parameter
307///
308/// ```rust,ignore
309/// # use gungraun_macros::library_benchmark;
310/// # mod gungraun {
311/// # pub struct LibraryBenchmarkConfig {}
312/// # pub mod __internal {
313/// # pub enum InternalLibFunctionKind { None, Default(fn()) }
314/// # pub struct InternalMacroLibBench {
315/// # pub id_display: Option<&'static str>,
316/// # pub args_display: Option<&'static str>,
317/// # pub func: InternalLibFunctionKind,
318/// # pub config: Option<fn() -> InternalLibraryBenchmarkConfig>
319/// # }
320/// # pub struct InternalLibraryBenchmarkConfig {}
321/// # }
322/// # }
323/// # mod my_lib { pub fn string_to_u64(_line: String) -> Result<u64, String> { Ok(0) } }
324/// use std::hint::black_box;
325/// #[library_benchmark]
326/// #[benches::by_file(args = [1.to_string(), 11.to_string(), 111.to_string()])]
327/// fn some_bench(line: String) -> Result<u64, String> {
328/// black_box(my_lib::string_to_u64(line))
329/// }
330/// # fn main() {}
331/// ```
332///
333/// # More Examples
334///
335/// The `#[library_benchmark]` attribute as a standalone
336///
337/// ```rust
338/// # use gungraun_macros::library_benchmark;
339/// # mod gungraun {
340/// # pub mod client_requests { pub mod cachegrind {
341/// # pub fn start_instrumentation() {}
342/// # pub fn stop_instrumentation() {}
343/// # }}
344/// # pub struct LibraryBenchmarkConfig {}
345/// # pub mod __internal {
346/// # pub enum InternalLibFunctionKind { None, Default(fn()) }
347/// # pub struct InternalMacroLibBench {
348/// # pub id_display: Option<&'static str>,
349/// # pub args_display: Option<&'static str>,
350/// # pub func: InternalLibFunctionKind,
351/// # pub config: Option<fn() -> InternalLibraryBenchmarkConfig>
352/// # }
353/// # pub struct InternalLibraryBenchmarkConfig {}
354/// # }
355/// # }
356/// fn some_func() -> u64 {
357/// 42
358/// }
359///
360/// #[library_benchmark]
361/// // If possible, it's best to return something from a benchmark function
362/// fn bench_my_library_function() -> u64 {
363/// // The `black_box` is needed to tell the compiler to not optimize what's inside the
364/// // black_box or else the benchmarks might return inaccurate results.
365/// std::hint::black_box(some_func())
366/// }
367/// # fn main() {
368/// # }
369/// ```
370///
371/// In the following example we pass a single argument with `Vec<i32>` type to the benchmark. All
372/// arguments are already wrapped in a black box and don't need to be put in a `black_box` again.
373///
374/// ```rust
375/// # use gungraun_macros::library_benchmark;
376/// # mod gungraun {
377/// # pub mod client_requests { pub mod cachegrind {
378/// # pub fn start_instrumentation() {}
379/// # pub fn stop_instrumentation() {}
380/// # }}
381/// # pub struct LibraryBenchmarkConfig {}
382/// # pub mod __internal {
383/// # pub enum InternalLibFunctionKind { None, Default(fn()) }
384/// # pub struct InternalMacroLibBench {
385/// # pub id_display: Option<&'static str>,
386/// # pub args_display: Option<&'static str>,
387/// # pub func: InternalLibFunctionKind,
388/// # pub config: Option<fn() -> InternalLibraryBenchmarkConfig>
389/// # }
390/// # pub struct InternalLibraryBenchmarkConfig {}
391/// # }
392/// # }
393/// // Our function we want to test
394/// fn some_func_with_array(array: Vec<i32>) -> Vec<i32> {
395/// // do something with the array and return a new array
396/// # array
397/// }
398///
399/// // This function is used to create a worst case array for our `some_func_with_array`
400/// fn setup_worst_case_array(start: i32) -> Vec<i32> {
401/// if start.is_negative() {
402/// (start..0).rev().collect()
403/// } else {
404/// (0..start).rev().collect()
405/// }
406/// }
407///
408/// // This benchmark is setting up multiple benchmark cases with the advantage that the setup
409/// // costs for creating a vector (even if it is empty) aren't attributed to the benchmark and
410/// // that the `array` is already wrapped in a black_box.
411/// #[library_benchmark]
412/// #[bench::empty(vec![])]
413/// #[bench::worst_case_6(vec![6, 5, 4, 3, 2, 1])]
414/// // Function calls are fine too
415/// #[bench::worst_case_4000(setup_worst_case_array(4000))]
416/// // The argument of the benchmark function defines the type of the argument from the `bench`
417/// // cases.
418/// fn bench_some_func_with_array(array: Vec<i32>) -> Vec<i32> {
419/// // Note `array` does not need to be put in a `black_box` because that's already done for
420/// // you.
421/// std::hint::black_box(some_func_with_array(array))
422/// }
423///
424/// // The following benchmark uses the `#[benches]` attribute to setup multiple benchmark cases
425/// // in one go
426/// #[library_benchmark]
427/// #[benches::multiple(vec![1], vec![5])]
428/// // Reroute the `args` to a `setup` function and use the setup function's return value as
429/// // input for the benchmarking function
430/// #[benches::with_setup(args = [1, 5], setup = setup_worst_case_array)]
431/// fn bench_using_the_benches_attribute(array: Vec<i32>) -> Vec<i32> {
432/// std::hint::black_box(some_func_with_array(array))
433/// }
434/// # fn main() {
435/// # }
436/// ```
437///
438/// [bench]: #the-bench-attribute
439/// [benches]: #the-benches-attribute
440#[proc_macro_attribute]
441#[proc_macro_error]
442pub fn library_benchmark(args: TokenStream, input: TokenStream) -> TokenStream {
443 match lib_bench::render(args.into(), input.into()) {
444 Ok(stream) => stream.into(),
445 Err(error) => error.to_compile_error().into(),
446 }
447}
448
449/// Used to annotate functions building the to be benchmarked `gungraun::Command`
450///
451/// This macro works almost the same way as the [`macro@crate::library_benchmark`] attribute. Please
452/// see there for the basic usage.
453///
454/// # Differences to the `#[library_benchmark]` attribute
455///
456/// Any `config` parameter takes a `BinaryBenchmarkConfig` instead of a `LibraryBenchmarkConfig`.
457/// All functions annotated with the `#[binary_benchmark]` attribute need to return an
458/// `gungraun::Command`. Also, the annotated function itself is not benchmarked. Instead, this
459/// function serves the purpose of a builder for the `Command` which is getting benchmarked.
460/// So, any code within this function is evaluated only once when all `Commands` in this benchmark
461/// file are collected and built. You can put any code in the function which is necessary to build
462/// the `Command` without attributing any event counts to the benchmark results which is why the
463/// `setup` and `teardown` parameters work differently in binary benchmarks.
464///
465/// The `setup` and `teardown` parameters of `#[binary_benchmark]`, `#[bench]` and of `#[benches]`
466/// take an expression instead of a function pointer. The expression of the `setup` (`teardown`)
467/// parameter is evaluated and executed not until before (after) the `Command` is executed (not
468/// __built__). There's a special case if `setup` or `teardown` are a function pointer like in
469/// library benchmarks. In this case the `args` from `#[bench]` or `#[benches]` are passed to the
470/// function AND `setup` or `teardown` respectively.
471///
472/// For example (Suppose your crate's binary is named `my-foo`)
473///
474/// ```rust
475/// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
476/// # use gungraun_macros::binary_benchmark;
477/// # pub mod gungraun {
478/// # use std::path::PathBuf;
479/// # #[derive(Clone)]
480/// # pub struct Command {}
481/// # impl Command {
482/// # pub fn new(_a: &str) -> Self { Self {}}
483/// # pub fn stdout(&mut self, _a: Stdio) -> &mut Self {self}
484/// # pub fn arg<T>(&mut self, _a: T) -> &mut Self where T: Into<PathBuf> {self}
485/// # pub fn build(&mut self) -> Self {self.clone()}
486/// # }
487/// # pub enum Stdio { Inherit, File(PathBuf) }
488/// # #[derive(Clone)]
489/// # pub struct Sandbox {}
490/// # impl Sandbox {
491/// # pub fn new(_a: bool) -> Self { Self {}}
492/// # pub fn fixtures(&mut self, _a: [&str; 2]) -> &mut Self { self }
493/// # }
494/// # impl From<&mut Sandbox> for Sandbox { fn from(value: &mut Sandbox) -> Self {value.clone() }}
495/// # #[derive(Default)]
496/// # pub struct BinaryBenchmarkConfig {}
497/// # impl BinaryBenchmarkConfig { pub fn sandbox<T: Into<Sandbox>>(&mut self, _a: T) -> &mut Self {self}}
498/// # impl From<&mut BinaryBenchmarkConfig> for BinaryBenchmarkConfig
499/// # { fn from(_value: &mut BinaryBenchmarkConfig) -> Self { BinaryBenchmarkConfig {}}}
500/// # pub mod __internal {
501/// # use super::*;
502/// # use crate::gungraun;
503/// # pub enum InternalBinFunctionKind { None, Default(fn() -> gungraun::Command) }
504/// # pub enum InternalBinAssistantKind { None, Default(fn()) }
505/// # pub struct InternalMacroBinBench {
506/// # pub id_display: Option<&'static str>,
507/// # pub args_display: Option<&'static str>,
508/// # pub func: InternalBinFunctionKind,
509/// # pub config: Option<fn() -> InternalBinaryBenchmarkConfig>,
510/// # pub setup: InternalBinAssistantKind,
511/// # pub teardown: InternalBinAssistantKind,
512/// # }
513/// # pub struct InternalBinaryBenchmarkConfig {}
514/// # impl From<&mut BinaryBenchmarkConfig> for InternalBinaryBenchmarkConfig
515/// # { fn from(_value: &mut BinaryBenchmarkConfig) -> Self { InternalBinaryBenchmarkConfig {}} }
516/// # }
517/// # }
518/// use gungraun::{BinaryBenchmarkConfig, Sandbox};
519/// use std::path::PathBuf;
520///
521/// // In binary benchmarks there's no need to return a value from the setup function
522/// # #[allow(unused)]
523/// fn simple_setup() {
524/// println!("Put code in here which will be run before the actual command");
525/// }
526///
527/// // It is good style to write any setup function idempotent, so it doesn't depend on the
528/// // `teardown` to have run. The `teardown` function isn't executed if the benchmark
529/// // command fails to run successfully.
530/// # #[allow(unused)]
531/// fn create_file(path: &str) {
532/// // You can for example create a file here which should be available for the `Command`
533/// std::fs::File::create(path).unwrap();
534/// }
535///
536/// # #[allow(unused)]
537/// fn teardown() {
538/// // Let's clean up this temporary file after we have used it
539/// std::fs::remove_file("file_from_setup_function.txt").unwrap();
540/// }
541///
542/// #[binary_benchmark]
543/// #[bench::just_a_fixture("benches/fixture.json")]
544/// // First big difference to library benchmarks! `my_setup` is not evaluated right away and the
545/// // return value of `simple_setup` is not used as input for the `bench_foo` function. Instead,
546/// // `simple_setup()` is executed before the execution of the `Command`.
547/// #[bench::with_other_fixture_and_setup(args = ("benches/other_fixture.txt"), setup = simple_setup())]
548/// // Here, setup is a function pointer, what tells us to route `args` to `setup` AND `bench_foo`
549/// #[bench::file_from_setup(args = ("file_from_setup_function.txt"), setup = create_file, teardown = teardown())]
550/// // Just an small example for the basic usage of the `#[benches]` attribute
551/// #[benches::multiple("benches/fix_1.txt", "benches/fix_2.txt")]
552/// // We're using a `BinaryBenchmarkConfig` in binary benchmarks to configure these benchmarks to
553/// // run in a sandbox.
554/// #[benches::multiple_with_config(
555/// args = ["benches/fix_1.txt", "benches/fix_2.txt"],
556/// config = BinaryBenchmarkConfig::default()
557/// .sandbox(Sandbox::new(true)
558/// .fixtures(["benches/fix_1.txt", "benches/fix_2.txt"])
559/// )
560/// )]
561/// // All functions annotated with `#[binary_benchmark]` need to return a `gungraun::Command`
562/// fn bench_foo(path: &str) -> gungraun::Command {
563/// let path = PathBuf::from(path);
564/// // We can put any code in here which is needed to configure the `Command`.
565/// let stdout = if path.extension().unwrap() == "txt" {
566/// gungraun::Stdio::Inherit
567/// } else {
568/// gungraun::Stdio::File(path.with_extension("out"))
569/// };
570/// // Configure the command depending on the arguments passed to this function and the code
571/// // above
572/// gungraun::Command::new(env!("CARGO_BIN_EXE_my-foo"))
573/// .stdout(stdout)
574/// .arg(path)
575/// .build()
576/// }
577/// # fn main() {
578/// # // To avoid the unused warning
579/// # let _ = (bench_foo::__BENCHES[0].func);
580/// # }
581/// ```
582#[proc_macro_attribute]
583#[proc_macro_error]
584pub fn binary_benchmark(args: TokenStream, input: TokenStream) -> TokenStream {
585 match bin_bench::render(args.into(), input.into()) {
586 Ok(stream) => stream.into(),
587 Err(error) => error.to_compile_error().into(),
588 }
589}
590
591/// For internal use only.
592///
593/// The old `macro_rules! impl_traits` was easy to overlook in the source code files and this derive
594/// macro is just a much nicer way to do the same.
595///
596/// We use this derive macro to spare us the manual implementation of
597///
598/// * `From<Outer> for Inner`
599/// * `From<&Outer> for Inner` (which clones the value)
600/// * `From<&mut Outer> for Inner` (which also just clones the value)
601///
602/// for our builder tuple structs which wrap the inner type from the gungraun-runner api. So,
603/// our builders don't need a build method, which is just cool.
604#[proc_macro_derive(IntoInner)]
605#[proc_macro_error]
606pub fn into_inner(item: TokenStream) -> TokenStream {
607 match derive_macros::render_into_inner(item.into()) {
608 Ok(stream) => stream.into(),
609 Err(error) => error.to_compile_error().into(),
610 }
611}