Recent

Author Topic: OSX Framework - How to Use/Where to Look  (Read 477 times)

kevin.black

  • Full Member
  • ***
  • Posts: 122
OSX Framework - How to Use/Where to Look
« on: June 10, 2019, 04:01:29 am »
macOS Mojave on a Parallels VM on a 2015 MBP Retina

Caveat: Not a doctor, I am a babe in the woods when it comes to macOS, so these questions might seem simplistic, I've researched and cannot find anything that answers my questions.

I have a DYLIB that is to be used with a Swift application. The Swift programmer wants the DYLIB(s) to be in the app bundle under Frameworks. He builds his DYLIBs with the output folder as follows:

    @executable_path/../Frameworks/DYLIBName.dylib

Which supposedly puts the dylib into the mac bundle under Contents/Frameworks, but I am confused as to how the dylib knows which application to put itself. I know you can use Install_name_Tool as in:

   install_name_tool -id @executable_path/../Frameworks/DYLIBName.dylib DYLIBName.dylib

But that isn't doing it automatically from the DYLIB build.

First things First: Just having a Framework referenced is eluding me. I have the DYLIB referenced in my code as:

Code: Pascal  [Select]
  1. const
  2.   ...
  3.   ...
  4.   {$IFDEF DARWIN}
  5.   DYLIBName = 'DYLIBName.dylib';
  6.   {$linkframework DYLIBName.dylib}
  7.   {$ENDIF DARWIN}

And, of course, the linker cannot find the DYLIB/Framework which is in the same folder as the application.  Note I have copied the DYLIB to /System/Library/Frameworks, but it still didn't find it there.

QUESTION #1:
The obvious question, then, where is the linker looking for the DYLIB using the $linkframework directive (and how do I tell the linker where to look)?

QUESTION #2:
So basically how do I get application bundle/Contents/Frameworks as part of the search path. I've looked at the available Macros and there doesn't seem to be anything that is the equivalent of @executable_path/../Frameworks/. 

How do I get the linker to reference the DYLIB in the app bundle (under Contents/Frameworks) ?

And as an aside, is there any Lazarus/FPC documentation WRT $linkframework and how it's supposed to work? The only reference I could find was this:

Quote
1.2.45 $LINKFRAMEWORK : Link to a framework

The {$LINKFRAMEWORK name} will link to a framework named name. This switch is available only on the Darwin platform.

Which is sort of the microsoft help solution, 100% accurate and totally useless :(


Again, apologies if a bit simplistic, but this is very new to me.

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2264
    • havefunsoft.com
Re: OSX Framework - How to Use/Where to Look
« Reply #1 on: June 10, 2019, 04:37:34 am »
1) Framework names should be specified without any extension. (Thus no need for ".dylib")
2) Frameworks are physically directories (with .framework) extension. + some special structure inside. As well as a .dylib would also be inside of a framework directory.
3) you might find SEARCH rules, if you run "man ld" from your Terminal:
Code: [Select]
  Search paths
     ld maintains a list of directories to search for a library or framework to use.  The default library search
     path is /usr/lib then /usr/local/lib.  The -L option will add a new library search path.  The default frame-
     work search path is /Library/Frameworks then /System/Library/Frameworks.  (Note: previously, /Net-
     work/Library/Frameworks was at the end of the default path.  If you need that functionality, you need to
     explicitly add -F/Network/Library/Frameworks).  The -F option will add a new framework search path.  The -Z
     option will remove the standard search paths.  The -syslibroot option will prepend a prefix to all search
     paths.
4) since you're using .dylib, you should be able to:
4.1) place it under bundle/Contents/Frameworks
4.2) simply use {$linklib} (instead of {$Linkframework} ) in your pascal code.
« Last Edit: June 10, 2019, 04:43:56 am by skalogryz »
Patron Cocoa Widgetset development https://www.patreon.com/skalogryz

kevin.black

  • Full Member
  • ***
  • Posts: 122
Re: OSX Framework - How to Use/Where to Look
« Reply #2 on: June 10, 2019, 07:10:32 am »
@ skalogryz,

Thank you, that makes it somewhat clearer.

Quote
4) since you're using .dylib, you should be able to:
4.1) place it under bundle/Contents/Frameworks
4.2) simply use {$linklib} (instead of {$Linkframework} ) in your pascal code.
That seems to work, but I'm not sure if it's because the DYLIBs are present outside the bundle and it just happens to find them, I'll need to do some 'different' testing.

Quote
2) Frameworks are physically directories (with .framework) extension. + some special structure inside. As well as a .dylib would also be inside of a framework directory.
I wasn't aware that there were anything additional about framework folders. Since I am working with a Swift developer I am trying to do what he 'says' he needs. He has suggested putting the DYLIBs in the /Contents/Frameworks folder which I think is (potentially) wrong. I have created a /Contents/Frameworks/FrameworkName.framework folder and put the DYLIBs in there.

Theoretically I should now be able to do a:

Code: Pascal  [Select]
  1. {$linkframework FrameworkName}

But I have an issue, how do I (in the project options) set the path to the AppBundle/Contents/Frameworks so it can find the framework. There is nothing in the man page that refers to that (other than -F will add a search path, but how to get the app bundle as the start of the search (ie. it would be good if I could use  "@executable_path/../Frameworks/FrameworkName" in the search path?

Thanks again...

DonAlfredo

  • Hero Member
  • *****
  • Posts: 1121
Re: OSX Framework - How to Use/Where to Look
« Reply #3 on: June 10, 2019, 08:22:10 am »
You could try to add the below into the custom options of your project:
-k"-rpath @executable_path/../Frameworks"

kevin.black

  • Full Member
  • ***
  • Posts: 122
Re: OSX Framework - How to Use/Where to Look
« Reply #4 on: June 11, 2019, 01:22:01 am »
@DonAlfredo,

SEE EDIT BELOW

SEE EDIT EDIT BELOW

You could try to add the below into the custom options of your project:
-k"-rpath @executable_path/../Frameworks"
Thank you, I tried that and still get Framework not found, so this is what I have:
  • In the App bundle a folder Contents/Frameworks
  • In the Frameworks folder (in the app bundle) a folder called MYFrameWork.framework
  • In that folder, the two DYLIBs
  • In the custom options the -k"-rpath etc
And the code below:
Code: Pascal  [Select]
  1. const
  2.   ...
  3.   ...
  4.   libOne = 'libOne.dylib';
  5.   libTwo = 'libTwo.dylib';
  6.   {$linkframework MYFramework.framework}
And I get the error:

Quote
Messages, Warnings: 17, Hints: 78
...
ld: framework not found MYFramework.framework
An error occurred while linking
MYApp.lpr(26,0) Error: Error while linking
MYApp.lpr(26,0) Verbose: There were 1 errors compiling module, stopping
Verbose: Compilation aborted

So the -k option appears not to work. clearly there must be some way to do this?

EDIT:
After some stuffing around @DonAlfredo's suggestion DID INDEED WORK. In my main application I simply did a $linklib (rather than a $linklibrary). I moved the DYLIBs to a different folder than the main application (to make sure that the app wasn't just finding the DYLIBs in its own folder). I put the DYLIBs in the app bundle in Contents/Frameworks. I used install_name_tool to set the DYLIB location and 0tool -D to confirm that was so.

I then ran the application on it's own and it found and executed functions in the DYLIB.

The install_name_tool was done manually. If there were a way to do this automatically (as you build the DYLIB) then that would save some stuffing around, but hey, it works.

EDIT EDIT

It may well be that this did NOT work. I found a copy of the DYLIBs (which I thought I had removed) in /usr/local/lib and when I removed them, the app will not link.

Putting the -r ".... command in the Options>Pass Options to Linker causes an error at link time. Putting it in  the Custom Options works (well, no error), but I am now getting linker errors as in ld: library not found. According to @DonAlfredo and to this post of 2015 what I am now doing should work:

https://forum.lazarus.freepascal.org/index.php?topic=27435.0

The DYLIBs themselves have been setup correctly (As far as I can tell) using the install_name_tool

Code: Pascal  [Select]
  1. Lindeman:x86_64-darwin kevin$ otool -D libMYDYLIB.dylib
  2. libMYDYLIB.dylib:
  3. @executable_path/../Frameworks/libMYDYLIB.dylib
This is the full list of custom options:
Code: Pascal  [Select]
  1. -k'-rpath @executable_path/../Frameworks'
  2. -dBorland
  3. -dVer150
  4. -dDelphi7
  5. -dCompiler6_Up
  6. -dPUREPASCAL
If I put the DYLIBs in /usr/local/lib all is well, so obviously the -k'-rpath... is simply not working, ie. the linker is not looking in the app bundle. Is there (other than what I have already done) some other way to force the linker to check the app bundle (I'm guessing not because the -k'-rpath... option is designed to do just that?
« Last Edit: June 12, 2019, 01:50:54 am by kevin.black »

kevin.black

  • Full Member
  • ***
  • Posts: 122
Re: OSX Framework - How to Use/Where to Look
« Reply #5 on: June 13, 2019, 05:06:51 am »
So here's how I think it works:
  • Build the DYLIBS
  • use the install_name_too1 to setup the @executable_path...
  • Confirm with otool -D DYLIBNAME
  • Put the DYLIBS in a place the linker expects to find them (/usr/local/lib say)
  • Build your test application (with -k"-rpath @executable_path... as a compiler option)
  • Copy the DYLIBs into the app bundle (in this case Contents/Frameworks)
  • To ensure the app is getting the DYLIBs from the App bundle rename those outside with a $ at the end of the name
  • Do an otool -L on the App
  • You should see the DYLIBS referenced at @executable_path...
  • Run the app
So the otool -L APPNAME should return something like this (as it did in my case), You can see the two DYLIBs that I wanted in the App bundle:
Code: Pascal  [Select]
  1.         /System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 23.0.0)
  2.         @executable_path/../Frameworks/libEmpyreanCrypto.dylib (compatibility version 1.0.0, current version 1.0.0)     <<<======
  3.         @executable_path/../Frameworks/libDbXServerAPI.dylib (compatibility version 0.0.0, current version 0.0.0)       <<<======
  4.         /usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)
  5.         /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.200.5)

If all goes well it should run OK and access the functions in the DYLIBs that are in the App bundle.

Kevin