vortex_cuda_macros/lib.rs
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4//! Proc macros for CUDA conditional compilation.
5//!
6//! These macros simplify gating code based on CUDA availability.
7//!
8//! # Usage
9//!
10//! ```ignore
11//! use vortex_cuda_macros::{cuda_available, cuda_not_available, cuda_test};
12//!
13//! // Only compiled when CUDA is available
14//! #[cuda_available]
15//! fn cuda_only_function() { /* ... */ }
16//!
17//! // Only compiled when CUDA is NOT available
18//! #[cuda_not_available]
19//! fn fallback_function() { /* ... */ }
20//!
21//! // Ignore tests when CUDA is not available
22//! #[crate::test]
23//! async fn my_test() {
24//! ...
25//! }
26//! ```
27
28use std::process::Command;
29use std::sync::LazyLock;
30
31use proc_macro::TokenStream;
32use quote::quote;
33use syn::parse_macro_input;
34
35/// Cached result of nvcc availability check.
36static NVCC_AVAILABLE: LazyLock<bool> = LazyLock::new(|| {
37 Command::new("nvcc")
38 .arg("--version")
39 .output()
40 .is_ok_and(|o| o.status.success())
41});
42
43/// Conditionally compiles the annotated item only when CUDA is available.
44#[proc_macro_attribute]
45pub fn cuda_available(_attr: TokenStream, item: TokenStream) -> TokenStream {
46 if *NVCC_AVAILABLE {
47 item
48 } else {
49 TokenStream::new()
50 }
51}
52
53/// Conditionally compiles the annotated item only when CUDA is not available.
54#[proc_macro_attribute]
55pub fn cuda_not_available(_attr: TokenStream, item: TokenStream) -> TokenStream {
56 if *NVCC_AVAILABLE {
57 TokenStream::new()
58 } else {
59 item
60 }
61}
62
63/// Test attribute to ignore tests if CUDA isn't available. Supports both sync and async tests (using tokio).
64///
65/// Must be named `test` to work with frameworks like `rstest`.
66#[proc_macro_attribute]
67pub fn test(_attr: TokenStream, item: TokenStream) -> TokenStream {
68 let item = parse_macro_input!(item as syn::ItemFn);
69 if *NVCC_AVAILABLE {
70 if item.sig.asyncness.is_some() {
71 quote! {
72 #[tokio::test]
73 #item
74 }
75 } else {
76 quote! {
77 #[test]
78 #item
79 }
80 }
81 .into()
82 } else {
83 if item.sig.asyncness.is_some() {
84 quote! {
85 #[tokio::test]
86 #[ignore]
87 #item
88 }
89 } else {
90 quote! {
91 #[test]
92 #[ignore]
93 #item
94 }
95 }
96 .into()
97 }
98}