Determining OS at Runtime

  • I need to support 10.4 in my application, but it uses some Carbon APIs
    that are deprecated in 10.5 and I am using some new 10.5 APIs that
    require the 10.5 SDK.

    I am sure I have seen this before, but I have been unable to find it
    in the Archive.  How do I determine at runtime which OS version I am
    running on?

    Rich Collyer
  • On Wed, Jul 1, 2009 at 7:24 PM, iseecolors<iseecolors...> wrote:
    > I need to support 10.4 in my application, but it uses some Carbon APIs that
    > are deprecated in 10.5 and I am using some new 10.5 APIs that require the
    > 10.5 SDK.
    >
    > I am sure I have seen this before, but I have been unable to find it in the
    > Archive.  How do I determine at runtime which OS version I am running on?

    Just check for the presence of the function you want to call. In
    Xcode, set your deployment target to 10.4, so that Leopard-only
    symbols will be weak-linked. Then just check the symbol for NULL
    before calling it:

        if (SomeLeopardFunction != NULL) {
            SomeLeopardFunction();
        } else {
            TigerFunction();
        }

    sherm--

    --
    Cocoa programming in Perl: http://camelbones.sourceforge.net
  • On Jul 1, 2009, at 5:22 PM, Sherm Pendley wrote:

    > On Wed, Jul 1, 2009 at 7:24 PM,
    > iseecolors<iseecolors...> wrote:
    >> I need to support 10.4 in my application, but it uses some Carbon
    >> APIs that
    >> are deprecated in 10.5 and I am using some new 10.5 APIs that
    >> require the
    >> 10.5 SDK.
    >>
    >> I am sure I have seen this before, but I have been unable to find
    >> it in the
    >> Archive.  How do I determine at runtime which OS version I am
    >> running on?
    >
    > Just check for the presence of the function you want to call. In
    > Xcode, set your deployment target to 10.4, so that Leopard-only
    > symbols will be weak-linked. Then just check the symbol for NULL
    > before calling it:
    >
    > if (SomeLeopardFunction != NULL) {
    > SomeLeopardFunction();
    > } else {
    > TigerFunction();
    > }

    If you want to make sure that you don't include any "old" code in
    your executable when you decide to make 10.5 (for example) your base
    OS version, you could arrange your code like this:

    #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
        if (SomeLeopardFunction == NULL)
            TigerFunction();
        else
    #endif
            SomeLeopardFunction();

    ...then the compiler will take care of sorting out the details for
    you. If you use that sequence in several places, it might even be
    worthwhile to create an inline wrapper function in a header file so
    the OS-specific details are kept in one place:

    inline void SomeFunction()
    {
    #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
        if (SomeLeopardFunction == NULL)
            TigerFunction();
        else
    #endif
            SomeLeopardFunction();
    }

    steve
  • One way that I've seen used is to do this:

    BOOL isLeopardOrBetter = YES;
    Class gcClass = NSClassFromString(@"NSGarbageCollector");
    if (gcClass == nil) { isLeopardOrBetter = NO; }

    Cheers,

    Dave
  • On Jul 2, 2009, at 4:29 PM, Steve Christensen wrote:

    > On Jul 1, 2009, at 5:22 PM, Sherm Pendley wrote:
    >
    >> On Wed, Jul 1, 2009 at 7:24 PM,
    >> iseecolors<iseecolors...> wrote:
    >>> I need to support 10.4 in my application, but it uses some Carbon
    >>> APIs that
    >>> are deprecated in 10.5 and I am using some new 10.5 APIs that
    >>> require the
    >>> 10.5 SDK.
    >>>
    >>> I am sure I have seen this before, but I have been unable to find
    >>> it in the
    >>> Archive.  How do I determine at runtime which OS version I am
    >>> running on?
    >>
    >> Just check for the presence of the function you want to call. In
    >> Xcode, set your deployment target to 10.4, so that Leopard-only
    >> symbols will be weak-linked. Then just check the symbol for NULL
    >> before calling it:
    >>
    >> if (SomeLeopardFunction != NULL) {
    >> SomeLeopardFunction();
    >> } else {
    >> TigerFunction();
    >> }
    >
    > If you want to make sure that you don't include any "old" code in
    > your executable when you decide to make 10.5 (for example) your
    > base OS version, you could arrange your code like this:
    >
    > #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
    > if (SomeLeopardFunction == NULL)
    > TigerFunction();
    > else
    > #endif
    > SomeLeopardFunction();
    >
    > ...then the compiler will take care of sorting out the details for
    > you. If you use that sequence in several places, it might even be
    > worthwhile to create an inline wrapper function in a header file so
    > the OS-specific details are kept in one place:
    >
    > inline void SomeFunction()
    > {
    > #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
    > if (SomeLeopardFunction == NULL)
    > TigerFunction();
    > else
    > #endif
    > SomeLeopardFunction();
    > }

    After I sent the message, I realized that a better situation for your
    case would be to use

    #ifndef NSAppKitVersionNumber10_5
    #define NSAppKitVersionNumber10_5 949
    #endif

        if (NSAppKitVersionNumber < NSAppKitVersionNumber10_5)
            ...

    since it will only use the Tiger method on Tiger.

    steve
  • On Thu, Jul 2, 2009 at 4:33 PM, Dave DeLong<davedelong...> wrote:
    > One way that I've seen used is to do this:
    >
    > BOOL isLeopardOrBetter = YES;
    > Class gcClass = NSClassFromString(@"NSGarbageCollector");
    > if (gcClass == nil) { isLeopardOrBetter = NO; }

    Don't do that. Check for the specific feature that you want to use.
    I.e. this code is fine if you want to check for the presence of
    NSGarbageCollector, however, that's exceeding unlikely, as you already
    know whether or not your code supports GC at compile time.

    --
    Clark S. Cox III
    <clarkcox3...>
  • On 2 Jul 2009, at 16:29, Steve Christensen wrote:
    > If you want to make sure that you don't include any "old" code in
    > your executable when you decide to make 10.5 (for example) your base
    > OS version, you could arrange your code like this:
    >
    > #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
    > if (SomeLeopardFunction == NULL)
    > TigerFunction();
    > else
    > #endif
    > SomeLeopardFunction();

    Preprocessor directives take effect at compile time, not at runtime.
  • On Jul 2, 2009, at 9:56 PM, Andrew Farmer wrote:

    > On 2 Jul 2009, at 16:29, Steve Christensen wrote:
    >> If you want to make sure that you don't include any "old" code in
    >> your executable when you decide to make 10.5 (for example) your
    >> base OS version, you could arrange your code like this:
    >>
    >> #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
    >> if (SomeLeopardFunction == NULL)
    >> TigerFunction();
    >> else
    >> #endif
    >> SomeLeopardFunction();
    >
    > Preprocessor directives take effect at compile time, not at runtime.

    Yes, I'm aware of that. My point was that by writing the code in this
    fashion, when you start building your software to -require- a minimum
    of 10.5, any pre-10.5 code will be compiled out because then
    MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 so all you're
    left with is the call to SomeLeopardFunction().
  • You might also just use Gestalt:

    BOOL IsLeopardOrLater(void)
    {
    SInt32 vers;
    Gestalt(gestaltSystemVersion,&vers);
    return (vers >= 0x1050);
    }

    On Fri, Jul 3, 2009 at 6:59 AM, Steve Christensen<punster...> wrote:
    > On Jul 2, 2009, at 9:56 PM, Andrew Farmer wrote:
    >
    >> On 2 Jul 2009, at 16:29, Steve Christensen wrote:
    >>>
    >>> If you want to make sure that you don't include any "old" code in your
    >>> executable when you decide to make 10.5 (for example) your base OS version,
    >>> you could arrange your code like this:
    >>>
    >>> #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
    >>>   if (SomeLeopardFunction == NULL)
    >>>       TigerFunction();
    >>>   else
    >>> #endif
    >>>       SomeLeopardFunction();
    >>
    >> Preprocessor directives take effect at compile time, not at runtime.
    >
    > Yes, I'm aware of that. My point was that by writing the code in this
    > fashion, when you start building your software to -require- a minimum of
    > 10.5, any pre-10.5 code will be compiled out because then
    > MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 so all you're left
    > with is the call to SomeLeopardFunction().
    >

    --
    Mark Munz
    unmarked software
    http://www.unmarked.com/
  • For the case of a function, if you're deploying on 10.4 but using a
    10.5 function, that function will be weak-linked so doing a runtime
    check is both faster and more accurate in determining whether a
    function exists or not. For a Cocoa class method, you could replace
    the test with a call to -respondsToSelector: to determine if it
    exists or not. In both cases you're taking a feature-based approach
    rather than a "I know this feature showed up in this OS version"
    approach.

    And if you did actually need a IsLeopardOrLater function, it would be
    better to cache the Gestalt result once since it won't change while
    your app is running. Otherwise you're making a selector value lookup
    each time.

    BOOL IsLeopardOrLater(void)
    {
        static SInt32 sVersion = 0;

        if (sVersion == 0)
            Gestalt(gestaltSystemVersion, &sVersion);

        return (sVersion >= 0x1050);
    }

    On Jul 3, 2009, at 9:00 AM, Mark Munz wrote:

    > You might also just use Gestalt:
    >
    > BOOL IsLeopardOrLater(void)
    > {
    > SInt32 vers;
    > Gestalt(gestaltSystemVersion,&vers);
    > return (vers >= 0x1050);
    > }
    >
    >
    > On Fri, Jul 3, 2009 at 6:59 AM, Steve Christensen<punster...>
    > wrote:
    >> On Jul 2, 2009, at 9:56 PM, Andrew Farmer wrote:
    >>
    >>> On 2 Jul 2009, at 16:29, Steve Christensen wrote:
    >>>>
    >>>> If you want to make sure that you don't include any "old" code
    >>>> in your
    >>>> executable when you decide to make 10.5 (for example) your base
    >>>> OS version,
    >>>> you could arrange your code like this:
    >>>>
    >>>> #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
    >>>> if (SomeLeopardFunction == NULL)
    >>>> TigerFunction();
    >>>> else
    >>>> #endif
    >>>> SomeLeopardFunction();
    >>>
    >>> Preprocessor directives take effect at compile time, not at runtime.
    >>
    >> Yes, I'm aware of that. My point was that by writing the code in this
    >> fashion, when you start building your software to -require- a
    >> minimum of
    >> 10.5, any pre-10.5 code will be compiled out because then
    >> MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 so all
    >> you're left
    >> with is the call to SomeLeopardFunction().
  • On Jul 3, 2009, at 10:06 AM, Steve Christensen wrote:

    > For the case of a function, if you're deploying on 10.4 but using a
    > 10.5 function, that function will be weak-linked so doing a runtime
    > check is both faster and more accurate in determining whether a
    > function exists or not. For a Cocoa class method, you could replace
    > the test with a call to -respondsToSelector: to determine if it
    > exists or not. In both cases you're taking a feature-based approach
    > rather than a "I know this feature showed up in this OS version"
    > approach.

    Just as a caution, you may run across functions that don't have the
    appropriate weak attribute in the header, so you'll crash at launch
    time on 10.4 (this bit me recently with CFStringTokenizer, rdar://problem/6781636
    )  So if you're checking function pointers against NULL, check the
    header to make sure they have the correct accessibility macro.

    --
    Adam
  • On Jul 3, 2009, at 1:06 PM, Steve Christensen wrote:

    > For the case of a function, if you're deploying on 10.4 but using a
    > 10.5 function, that function will be weak-linked so doing a runtime
    > check is both faster and more accurate in determining whether a
    > function exists or not.

    Beware that there have been edge cases in the past where a non-NULL
    function test will give you a false positive with disastrous results.

    It goes like this:

    if (FunctionName != NULL) {
    FunctionName(x, y, z);
    }

    FunctionName was SPI on 10.x but (inadvertently) exported, but API on
    10.y.

    When running on 10.x, your NULL function pointer test will pass, but
    this is SPI and isn't supported for use. The signature may have
    changed, or the range of valid input values may be a subset of the
    supported ones on 10.y.

    This is not a theoretical problem, but one which has happened in the
    past.

    What is the take home lesson here? Test all branches of the code you
    write.

    Jim