1
votes

We have a Cocoa app that makes use of the R framework. Our intention is NOT to ship R with the app, as users will likely have their own version installed locally. The question we have is how to properly weak link against a framework that has multiple versions.

We're weak linking against it:

  • Framework set to optional in linked binaries
  • Added as -weak_framework in build settings > other linker flags
  • We do the usual "class exists" detection in code before attempting to use R.

Here's what happens:

  • If the same version of R we linked against is installed, everything works fine.
  • If R is not installed, everything is fine. We detect the absence and it gracefully fails.
  • Our trouble is when we link against a version (let's say 3.1) and the user has a different version (let's say 3.2). Then we get the error can't resolve symbol (some sybmol) in (my app) because dependent dylib #1 could not be loaded.

I guess this makes sense as we built against 3.1 and it's not there - 3.2 is. I'm just unclear on how to approach properly weak linking against an external library like this and allowing it to work with a different version. Or I'm completely missing the boat on how to weak link entirely. Which is entirely possible.

Any guidance very much appreciated.

Thank you

1

1 Answers

1
votes

@eww and I have continued investigating this, and here's what we were able to find. When the framework and application are being compiled, they are referencing a specific version of R.framework. We were able to see this by running:

otool -L /Applications/StatTag.app/Contents/Frameworks/RCocoa.framework/RCocoa 
/Applications/StatTag.app/Contents/Frameworks/RCocoa.framework/RCocoa:
  @rpath/RCocoa.framework/Versions/A/RCocoa (compatibility version 1.0.0, current version 1.0.0)
  **/Library/Frameworks/R.framework/Versions/3.3/Resources/lib/libR.dylib (compatibility version 3.3.0, current version 3.3.2)**
  /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 1252.0.0)
  /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
  /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
  /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1253.0.0)

After some searching, this post came up which clued us in on how to change the R.framework reference to be "Current" instead of the specific version

install_name_tool -change /Library/Frameworks/R.framework/Versions/3.3/Resources/lib/libR.dylib /Library/Frameworks/R.framework/Versions/Current/Resources/lib/libR.dylib /Applications/StatTag.app/Contents/Frameworks/RCocoa.framework/RCocoa

We can see now that it's referencing the Current version folder:

otool -L /Applications/StatTag.app/Contents/Frameworks/RCocoa.framework/RCocoa 
/Applications/StatTag.app/Contents/Frameworks/RCocoa.framework/RCocoa:
  @rpath/RCocoa.framework/Versions/A/RCocoa (compatibility version 1.0.0, current version 1.0.0)
  /Library/Frameworks/R.framework/Versions/Current/Resources/lib/libR.dylib (compatibility version 3.3.0, current version 3.3.2)
  /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 1252.0.0)
  /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
  /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
  /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1253.0.0)

What tripped us up a little more is when this gets built into our final application, there are other local frameworks referencing RCocoa and R, which meant we had to run install_name_tool multiple times. Happy to report things are now working.