Repository: casey/just (rust)

Issue:
Feature request: add attribute `openbsd`
Trying to compile a recent version of [optimism](https://github.com/ethereum-optimism/optimism/blob/op-node/v1.10.0/CONTRIBUTING.md#development-quick-start) on OpenBSD/adJ 7.6beta1 I found that it uses `just`.

On OpenBSD/adJ 7.6beta1, it was straightforward to compile `just` and run its tests except for two tests that depend on attributes for the supported platforms (`linux`, `windows`, `macos`):
```sh
 % uname -a
OpenBSD selah.pasosdeJesus.org 7.6 APRENDIENDODEJESUS.MP#1 amd64
% rustc --version
rustc 1.81.0 (eeb90cda1 2024-09-04) (built from a source tarball)
% git clone git@github.com:casey/just.git/
...
% cargo build
...
% cargo install --path .
...
% RUST_BACKTRACE=1 LANG=POSIX cargo test
...

failures:                                                                                                                                   
                                                                                                                                                                
---- attributes::multiple_attributes_one_line stdout ----                                                                                                       
Bad stderr: Diff < left / right > :                                                                                                                             
<error: Justfile contains no recipes.                                                                                                                           
>exit 1                                                                                                                                                         
                                                                                                                                                                
                                                                               
thread 'attributes::multiple_attributes_one_line' panicked at tests/attributes.rs:59:6:                                                                         
Output mismatch.                                                               
stack backtrace:                                                               
   0: rust_begin_unwind                                                        
   1: core::panicking::panic_fmt                                               
   2: integration::test::Test::run                                             
             at ./tests/test.rs:293:7                                          
   3: integration::attributes::multiple_attributes_one_line                  
             at ./tests/attributes.rs:48:3                                     
   4: integration::attributes::multiple_attributes_one_line::{{closure}}                                                                                        
             at ./tests/attributes.rs:47:34                                                                                                                     
   5: core::ops::function::FnOnce::call_once                                    
             at /usr/obj/ports/rust-1.81.0/rustc-1.81.0-src/library/core/src/ops/function.rs:250:5                                                              
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
                                                                                                                                                                
---- os_attributes::os stdout ----                                                                                                                              
thread 'os_attributes::os' panicked at tests/os_attributes.rs:59:7:                                                                                             
unexpected os family                                                                                                                                            
stack backtrace:                                                                                                                                                
   0: rust_begin_unwind                                                                                                                                         
   1: core::panicking::panic_fmt                                               
   2: integration::os_attributes::os                                                                                                                            
             at ./tests/os_attributes.rs:59:7                                  
   3: integration::os_attributes::os::{{closure}}                              
             at ./tests/os_attributes.rs:35:8                                  
   4: core::ops::function::FnOnce::call_once                                   
             at /usr/obj/ports/rust-1.81.0/rustc-1.81.0-src/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
                                                                               
                                                                               
failures:                                                                                                                                                       
    attributes::multiple_attributes_one_line                                                                                                                    
    os_attributes::os                                                           
                                                                                                                                                                
test result: FAILED. 850 passed; 2 failed; 7 ignored; 0 measured; 0 filtered out; finished in 20.05s
                                                                                                                                                                
error: test failed, to rerun pass `--test integration`
```

Since Rust supports OpenBSD:  <https://doc.rust-lang.org/rustc/platform-support/openbsd.html>, IMHO it makes sense to add initial support for OpenBSD in `just` by adding an attribute `openbsd` to enable recipes on OpenBSD.




Pull request description:
Add initial support for OpenBSD with attribute openbsd. Closes #2496
I tested this change on OpenBSD 7.6beta1 and on Linux Ubuntu 24.04 and it worked in both.

Patch:
diff --git a/README.md b/README.md
index 27e3307a..97689207 100644
--- a/README.md
+++ b/README.md
@@ -48,9 +48,9 @@ Yay, all your tests passed!
   [`make`'s complexity and idiosyncrasies](#what-are-the-idiosyncrasies-of-make-that-just-avoids).
   No need for `.PHONY` recipes!
 
-- Linux, MacOS, and Windows are supported with no additional dependencies.
-  (Although if your system doesn't have an `sh`, you'll need to
-  [choose a different shell](#shell).)
+- Linux, MacOS, Windows, and other reasonable unices are supported with no
+  additional dependencies. (Although if your system doesn't have an `sh`,
+  you'll need to [choose a different shell](#shell).)
 
 - Errors are specific and informative, and syntax errors are reported along
   with their source context.
@@ -1986,6 +1986,7 @@ change their behavior.
 | `[no-cd]`<sup>1.9.0</sup> | recipe | Don't change directory before executing recipe. |
 | `[no-exit-message]`<sup>1.7.0</sup> | recipe | Don't print an error message if recipe fails. |
 | `[no-quiet]`<sup>1.23.0</sup> | recipe | Override globally quiet recipes and always echo out the recipe. |
+| `[openbsd]`<sup>master</sup> | recipe | Enable recipe on OpenBSD. |
 | `[positional-arguments]`<sup>1.29.0</sup> | recipe | Turn on [positional arguments](#positional-arguments) for this recipe. |
 | `[private]`<sup>1.10.0</sup> | alias, recipe | Make recipe, alias, or variable private. See [Private Recipes](#private-recipes). |
 | `[script]`<sup>1.33.0</sup> | recipe | Execute recipe as script. See [script recipes](#script-recipes) for more details. |
diff --git a/crates-io-readme.md b/crates-io-readme.md
index c05ac0a6..63c900f2 100644
--- a/crates-io-readme.md
+++ b/crates-io-readme.md
@@ -1,6 +1,7 @@
 `just` is a handy way to save and run project-specific commands.
 
-Commands are stored in a file called `justfile` or `Justfile` with syntax inspired by `make`:
+Commands are stored in a file called `justfile` or `Justfile` with syntax
+inspired by `make`:
 
 ```make
 build:
@@ -15,8 +16,9 @@ test TEST: build
     ./test --test {{TEST}}
 ```
 
-`just` produces detailed error messages and avoids `make`'s idiosyncrasies, so debugging a justfile is easier and less surprising than debugging a makefile.
+`just` produces detailed error messages and avoids `make`'s idiosyncrasies, so
+debugging a justfile is easier and less surprising than debugging a makefile.
 
-It works on Linux, MacOS, and Windows.
+It works on all operating systems supported by Rust.
 
 Read more on [GitHub](https://github.com/casey/just).
diff --git a/src/attribute.rs b/src/attribute.rs
index 39a0a800..4ec813f0 100644
--- a/src/attribute.rs
+++ b/src/attribute.rs
@@ -18,6 +18,7 @@ pub(crate) enum Attribute<'src> {
   NoCd,
   NoExitMessage,
   NoQuiet,
+  Openbsd,
   PositionalArguments,
   Private,
   Script(Option<Interpreter<'src>>),
@@ -36,6 +37,7 @@ impl AttributeDiscriminant {
       | Self::NoCd
       | Self::NoExitMessage
       | Self::NoQuiet
+      | Self::Openbsd
       | Self::PositionalArguments
       | Self::Private
       | Self::Unix
@@ -83,6 +85,7 @@ impl<'src> Attribute<'src> {
       AttributeDiscriminant::NoCd => Self::NoCd,
       AttributeDiscriminant::NoExitMessage => Self::NoExitMessage,
       AttributeDiscriminant::NoQuiet => Self::NoQuiet,
+      AttributeDiscriminant::Openbsd => Self::Openbsd,
       AttributeDiscriminant::PositionalArguments => Self::PositionalArguments,
       AttributeDiscriminant::Private => Self::Private,
       AttributeDiscriminant::Script => Self::Script({
@@ -131,6 +134,7 @@ impl Display for Attribute<'_> {
       | Self::NoCd
       | Self::NoExitMessage
       | Self::NoQuiet
+      | Self::Openbsd
       | Self::PositionalArguments
       | Self::Private
       | Self::Script(None)
diff --git a/src/recipe.rs b/src/recipe.rs
index 81fbc7ac..ac7fdd65 100644
--- a/src/recipe.rs
+++ b/src/recipe.rs
@@ -113,17 +113,19 @@ impl<'src, D> Recipe<'src, D> {
   }
 
   pub(crate) fn enabled(&self) -> bool {
-    let windows = self.attributes.contains(&Attribute::Windows);
     let linux = self.attributes.contains(&Attribute::Linux);
     let macos = self.attributes.contains(&Attribute::Macos);
+    let openbsd = self.attributes.contains(&Attribute::Openbsd);
     let unix = self.attributes.contains(&Attribute::Unix);
+    let windows = self.attributes.contains(&Attribute::Windows);
 
-    (!windows && !linux && !macos && !unix)
-      || (cfg!(target_os = "windows") && windows)
+    (!windows && !linux && !macos && !openbsd && !unix)
       || (cfg!(target_os = "linux") && (linux || unix))
       || (cfg!(target_os = "macos") && (macos || unix))
-      || (cfg!(windows) && windows)
+      || (cfg!(target_os = "openbsd") && (openbsd || unix))
+      || (cfg!(target_os = "windows") && windows)
       || (cfg!(unix) && unix)
+      || (cfg!(windows) && windows)
   }
 
   fn print_exit_message(&self) -> bool {


Test patch:
diff --git a/tests/attributes.rs b/tests/attributes.rs
index 9f022025..80393f1a 100644
--- a/tests/attributes.rs
+++ b/tests/attributes.rs
@@ -6,9 +6,10 @@ fn all() {
     .justfile(
       "
       [macos]
-      [windows]
       [linux]
+      [openbsd]
       [unix]
+      [windows]
       [no-exit-message]
       foo:
         exit 1
@@ -48,7 +49,7 @@ fn multiple_attributes_one_line() {
   Test::new()
     .justfile(
       "
-      [macos, windows,linux]
+      [macos,windows,linux,openbsd]
       [no-exit-message]
       foo:
         exit 1
@@ -64,7 +65,7 @@ fn multiple_attributes_one_line_error_message() {
   Test::new()
     .justfile(
       "
-      [macos, windows linux]
+      [macos,windows linux,openbsd]
       [no-exit-message]
       foo:
         exit 1
@@ -73,10 +74,10 @@ fn multiple_attributes_one_line_error_message() {
     .stderr(
       "
         error: Expected ']', ':', ',', or '(', but found identifier
-         ——▶ justfile:1:17
+         ——▶ justfile:1:16
           │
-        1 │ [macos, windows linux]
-          │                 ^^^^^
+        1 │ [macos,windows linux,openbsd]
+          │                ^^^^^
           ",
     )
     .status(1)
@@ -88,7 +89,7 @@ fn multiple_attributes_one_line_duplicate_check() {
   Test::new()
     .justfile(
       "
-      [macos, windows, linux]
+      [macos, windows, linux, openbsd]
       [linux]
       foo:
         exit 1
diff --git a/tests/os_attributes.rs b/tests/os_attributes.rs
index 81729850..de740580 100644
--- a/tests/os_attributes.rs
+++ b/tests/os_attributes.rs
@@ -47,6 +47,10 @@ fn os() {
       [linux]
       foo:
         echo quxx
+
+      [openbsd]
+      foo:
+        echo bob
     ",
     )
     .stdout(if cfg!(target_os = "macos") {
@@ -55,6 +59,8 @@ fn os() {
       "baz\n"
     } else if cfg!(target_os = "linux") {
       "quxx\n"
+    } else if cfg!(target_os = "openbsd") {
+      "bob\n"
     } else {
       panic!("unexpected os family")
     })
@@ -64,6 +70,8 @@ fn os() {
       "echo baz\n"
     } else if cfg!(target_os = "linux") {
       "echo quxx\n"
+    } else if cfg!(target_os = "openbsd") {
+      "echo bob\n"
     } else {
       panic!("unexpected os family")
     })
@@ -75,10 +83,11 @@ fn all() {
   Test::new()
     .justfile(
       "
-      [macos]
-      [windows]
       [linux]
+      [macos]
+      [openbsd]
       [unix]
+      [windows]
       foo:
         echo bar
     ",