Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to call @available? #111

Open
jbis9051 opened this issue Apr 6, 2022 · 8 comments
Open

How to call @available? #111

jbis9051 opened this issue Apr 6, 2022 · 8 comments

Comments

@jbis9051
Copy link

jbis9051 commented Apr 6, 2022

if (@available(macOS 10.15, *)){
   /// whatever
}

How would I do this in rust?

@simlay
Copy link

simlay commented Apr 6, 2022

Your question is a little off topic from this repo but I don't know of much better place.

The Swift @available attribute and the objective-c API_AVAILABLE aren't quite the same in rust. The objective-c macro I think is evaluated at compile time. I'm not sure how the @available attribute works in swift.

I think the best way to emulate this type of thing is to use the sysinfo crate's os_long_version with something like...

let s = System::new();
if(s.os_version().starts_with("11.0")) { // for macOS
    // whatever
}

The implementation for long_os_version shows a few of the options.

You may also use the #[cfg(target_os("macos"))] attribute for this type of thing but it doesn't give you OS version granularity.

@jbis9051
Copy link
Author

jbis9051 commented Apr 7, 2022

@simlay Thank you for your reply. I agree, it's a bit off topic but wasn't sure where to ask.

I could be wrong, but I believe @available in ObjC is runtime. https://stackoverflow.com/a/47334301/7886229

The sysinfo crate would be a last resort. I'd like to tap into the same thing @available uses.

@simlay
Copy link

simlay commented Apr 7, 2022

Hmm. The docks from clang: https://clang.llvm.org/docs/LanguageExtensions.html#objective-c-available

Before LLVM 5.0, when calling a function that exists only in the OS that’s newer than the target OS (as determined by the minimum deployment version), programmers had to carefully check if the function exists at runtime, using null checks for weakly-linked C functions, +class for Objective-C classes, and -respondsToSelector: or +instancesRespondToSelector: for Objective-C methods. If such a check was missed, the program would compile fine, run fine on newer systems, but crash on older systems.

This implies that it may actually do the check for you at runtime. I'm not sure how to send that to the objective-c runtime. @madsmtm has been doing some interesting work on objc2 and may have some thoughts.

@jbis9051
Copy link
Author

jbis9051 commented Apr 7, 2022

However, https://godbolt.org/z/s8Gzx3hME, this seems to indicate that it's compile time if I am reading the assembly right

@madsmtm
Copy link

madsmtm commented Jun 11, 2022

@available is a compile-time directive, it conditionally compiles the code depending on the deployment target (can be set using the MACOSX_DEPLOYMENT_TARGET / IPHONEOS_DEPLOYMENT_TARGET / ... environment variables).

I am working on a crate to help do this in Rust, as it would be beneficial for a few things only available in newer versions, namely certain optimizations, linking to frameworks and some C symbols.

However, it'll probably take a while before it is done, so I would recommend the sysinfo crate for now.

@jbis9051
Copy link
Author

jbis9051 commented Jun 11, 2022

@madsmtm I'd like to be able to do runtime checks as well.

Something like from https://nshipster.com/available/:

@available(iOS 13.0, *)
final class CustomCompositionalLayout: UICollectionViewCompositionalLayout {  }

func createLayout() -> UICollectionViewLayout {
    if #available(iOS 13, *) { // runtime check!
        return CustomCompositionalLayout()
    } else {
        return UICollectionViewFlowLayout()
    }
}

I'm not familiar with Swift (or low level stuff) enough to know how exactly that works.

Is it dynamic linking or something? Is that possible now in Rust? If not, would the crate you are making be able to do runtime checks as well?

@madsmtm
Copy link

madsmtm commented Jun 11, 2022

Actually it's a little more complex than I thought: Objective-C's @available / Swift's #available acts as both a compile-time and a run-time check!

As an example, if you have the following Objective-C code:

if (@available(macOS 10.10, *)) {
    printf("Hello, macOS > 10.10!\n");
}
if (@available(macOS 10.14, *)) {
    printf("Hello, macOS > 10.14!\n");
}

And tell clang that the deployment version is 10.12, it can remove the first @available check at compile-time, but has insert a runtime check for the second @available. Note that removing the first branch at compile-time is an optimization - it could choose not to!

Check out a tweaked version of your godbolt example, where we set -mmacos-version-min=10.12 (equivalent to setting environment variable MACOSX_DEPLOYMENT_TARGET=10.12).

@madsmtm
Copy link

madsmtm commented Jun 11, 2022

But again, using the sysinfo crate you can essentially do the exact same runtime checks (albeit less efficiently).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants