Can I include a framework in another framework? - objective-c

I'm writing a framework (called Lighthouse.framework) that, in turn, uses code from another framework (RegexKit.framework, to be precise). I have copied RegexKit.framework into my own framework, so that it has a structure like the following:
Lighthouse.framework/
Versions/
A/
Frameworks/
RegexKit.framework
Lighthouse
However, when I try to run an application that uses Lighthouse.framework (my framework), I get the following error:
dyld: Library not loaded: #executable_path/../Frameworks/RegexKit.framework/Versions/A/RegexKit
Referenced from: /Users/mdippery/Developer/Projects/Current/lighthouse/build/Debug/Lighthouse.framework/Versions/A/Lighthouse
Reason: image not found
Obviously, the loader isn't finding RegexKit.
Here're the paths the loader expects to load, courtesy otool:
build/Debug/Lighthouse.framework/Versions/A/Lighthouse:
/Users/mdippery/Library/Frameworks/Lighthouse.framework/Versions/A/Lighthouse (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 12.0.0)
#executable_path/../Frameworks/RegexKit.framework/Versions/A/RegexKit (compatibility version 0.4.0, current version 0.6.0)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 111.1.4)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 227.0.0)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 476.19.0)
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 677.26.0)
Can I include a framework in another framework? Is this the proper way to do it? How can I resolve my error?

The easiest way is to use #rpath. Your configuration should look like:
Set RegExKit.framework's Installation Directory to #rpath
Set Lighthouse.frameworks's Installation Directory to #rpath
Set Lighthouse.framework's Runpath Search Paths to #loader_path/Frameworks
Ensure that RegExKit.framework is copied into Lighthouse.framework's Framework subfolder (use a custom build phase for this)
Finally, any applications linking to Lighthouse.framework should set Runpath Search Paths to #loader_path/../Frameworks

Yes, you can.
However, you need the included framework to "know" what its installed location will be at the time that it is built; otherwise, dyld won't be able to find it at run time, as you saw.
The relevant settings in XCode, if I recall correctly, are "Installation Directory" and "Framework Install Name". The latter probably won't matter for your usage, but you're going to need the former to be something along the lines of: #executable_path/../Frameworks/Lighthouse.framework/Versions/A/Frameworks/RegexKit.framework/Versions/A/

I discovered a fix for this problem. I incorporated some ideas from sbooth's answer, but the fix was simpler. I ran this script:
install_name_tool -change #executable_path/../Frameworks/RegexKit.framework/Versions/A/RegexKit #loader_path/Frameworks/RegexKit.framework/Versions/A/RegexKit "${TARGET_BUILD_DIR}/${PRODUCT_NAME}.framework/Versions/A/${PRODUCT_NAME}"
as a Run Build Script phase.
Note that, for the general case, you have to change #executable_path/../ to #loader_path/, and all is well.

Related

Upgrading cmake in Yocto

I'm using Yocto and struggling to update cmake. The poky version that we are using provides cmake version 3.3.1, but one of the packages that I'm building requires cmake version 3.5 or greater.
Looking at the poky git repo, the latest provides version 3.8.2. I figured that the easiest way to upgrade cmake was to include the recipes for cmake from the current poky master branch within my own meta repo, in order to override the lesser version. So I copied the cmake directory, and expected the build to continue along...
Unfortunately, the cmake recipes that I copied in aren't working. The recipes won't even load, as they throw an error, saying "docker must contain prefix as its prefix." Printing the output of the prefix and docdir variables to the console, I see that docdir is set to "${datadir}/doc" -- the datadir variable is not expanded.
In summary, the questions that I have are:
What is the best way to upgrade cmake? Is the best way to copy in the updated recipes into my own meta repo?
Why isn't the datadir variable being expanded, and how can I fix that?
What is probably happening is that the recipe for cmake 3.8.2 (2.4) is not backward compatible with the poky version from 3.3.1 (Version 1.9). The reference is here. I guess some refactoring and important milestones happened meanwhile
The easiest way is to upgrade the whole poky folder in your ecosystem, hoping it is not breaking your other recipes.

Warning:the '-d' option with a directory destination is ignored because '-module' is specified

When I upgrade my Project Kotlin version from 1.0.5 to 1.1.1, it produce the warning
Warning:the '-d' option with a directory destination is ignored because '-module' is specified during compilation. What is this warning for? What do I need to fix?
This is a Kotlin bug fixed in version 1.1.2.

Detect current CMake version using CMake

I am working on a project that uses CMake. The top CMakeLists.txt file contains the following line:
cmake_minimum_required(VERSION 3.7.2) # Kittens will die if you switch to an earlier version of CMake. We suggest using CMake 3.8.0.
I want to force all developers to switch to CMake 3.8.0, but for some reasons, not all developers have administration rights and are not able to switch from 3.7.2 to 3.8.0 immediately. Actually, we do not need any new features of version 3.8.0, but our policy is to use always the newest and greatest tools to prevent "porting up" problems in the future - for instance switching fast from Qt4 to Qt5 was a good decission in the past - I know switching always to the newest libraries and tools has also some drawbacks as discussed here, but we want to do it this way.
Because of this, instead of forcing everyone to use version 3.8.0, I'd like to output a warning message if CMake 3.7.2 is used. Somehow like this:
# not working - just pseudocode
if(CMAKE_VERSION == "3.7.2")
message("Please consider to switch to CMake 3.8.0")
endif()
I tried to read the VERSION variable, but this does not work. Does anyone now how this check can be achieved?
There exist a few variables for that, described here:
CMAKE_MAJOR_VERSION
major version number for CMake, e.g. the "2" in CMake 2.4.3
CMAKE_MINOR_VERSION
minor version number for CMake, e.g. the "4" in CMake 2.4.3
CMAKE_PATCH_VERSION
patch version number for CMake, e.g. the "3" in CMake 2.4.3
Also, the variable CMAKE_VERSION contains the string for the version.
In your case, you would, for instance, use the following:
if(${CMAKE_VERSION} VERSION_LESS "3.8.0")
message("Please consider to switch to CMake 3.8.0")
endif()
Other comparison operators are VERSION_EQUAL and VERSION_GREATER.

Compiling againt another version of Eigen using CMake

Am using ubuntu 16 which seems automatically linking against Eigen version 3.2.92 located at /usr/include/Eigen3. I would like to link against version 3.2.0. Thus my questions is
How could I get Eigen version 3.2.0? It is not clear from Eigen website
What I did so far is just copying /usr/include/Eigen3 from an ubuntu 14 machine, since the latter automatically comes with version 3.2.0
How to link against it using CMake?
Tried
SET (EIGEN3_INCLUDE_DIR "/home/usr/mylib/eigen/eigen3/Eigen") but without success.
For info, am using ROS (Kinetic) catkin. It happens that catkin somehow forces the development packages to linking/compiling against packages installed by default (/usr/include/..)
Other versions of Eigen are available on the website or better, from the hg repo.
How is the EIGEN3_INCLUDE_DIR used in your cmake file? For example, in one of my projects, we have set(EIGEN_INCLUDE_DIR ${SOURCE_DIR}/Common). Note that it doesn't have the "3" (it's just a variable name) and that it doesn't include the last "/Eigen" in the path.
First of all, Eigen is a header-only library, so you are not linking against it :-) Instead you want to use specific header files.
For your own packages, you can use include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR}, assuming you set EIGEN3_INCLUDE_DIR correspondingly to the version you want to use. Beware that if that version differs too much (e.g. 2.x vs 3.x) with versions used by interfaces (e.g. tf library?), this may cause some issues if datatypes changed. You also need to make sure that no other directive overwrites that - best to check the parameters to g++ for that.

Xcode 5 how to Linking Dynamic Libraries from App Bundle

I want to distribute some libraries in to my OS X application bundle, last two days i am working on this however couldn't make it. until now what i did.
with using install name tool i have fixed library paths. additionally in time i have tried #loader_path/../Libraries and #executable_path/../Libraries as well.
otool -L libMagickWand-6.Q16.2.dylib
#rpath/../Libraries/libMagickWand-6.Q16.2.dylib (compatibility version 3.0.0, current version 3.0.0)
#rpath/../Libraries/libMagickCore-6.Q16.2.dylib (compatibility version 3.0.0, current version 3.0.0)
#rpath/../Libraries/libfreetype.6.dylib (compatibility version 18.0.0, current version 18.2.0)
#rpath/../Libraries/libbz2.1.0.dylib (compatibility version 1.0.0, current version 1.0.5)
#rpath/../Libraries/libz.1.2.5.dylib (compatibility version 1.0.0, current version 1.2.5)
#rpath/../Libraries/libltdl.7.dylib (compatibility version 11.0.0, current version 11.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)
in project targets / Build Phases / Add New Build Phase / Add Copy Files build Phase and copied all dynamic libraries to my app bundle.
that worked well, I can see the libraries are in the app bundle.
then adding #rpath/../Libraries/ to Build Settings / Runpath Search Paths
but still getting error message..
ld: library not found for -lMagickWand-6.Q16.2 clang: error: linker
command failed with exit code 1 (use -v to see invocation)
if i add direct path lets say libraries are located in /User/username/libs/ to Library Search Paths in build settings it works.
am i missing something?
Content/Libraries is not a standard directory within an app bundle; use Contents/Frameworks instead (.dylibs are allowed in that directory just the same as .frameworks).
Set the Install Name of each library to #rpath/libWhatever.dylib and set the Runpath Search Path of the executable (in Contents/MacOS) to #loader_path/../Frameworks.
For library interdependencies then Runpath Search Path will need to be simply #loader_path so dependent libraries can be loaded from the same directory.
EDIT: People might find the copy_dylibs.py script in this repo useful for copying third-party .dylibs into the App Bundle. It recursively hunts for libraries that need copying and corrects the Install Name of the libraries as well as code-signing them.