Showing posts with label OS X. Show all posts
Showing posts with label OS X. Show all posts

Thursday, January 6, 2011

CMake - Install - Linux and OS X

As described in previous posts deploying a program that uses an embedded copy of Firebird requires shipping some database server files. For Windows this can be done by the software that creates the installation package.

For Linux and OS X it is possible to use the CMake INSTALL command. This command creates an INSTALL target in your development tool: buiding that target will copy files to the folder specified by the CMAKE_INSTALL_PREFIX variable.
This can be used to copy the compiled program, the Firebird runtime and other files as needed to the install folder. Then the install folder will contain a ready to run copy of the program, with all the needed files.

First use a variable to store the path of the files used by the Firebird embedded server. I keep them in a folder that has the same structure as the deployed files. Here is an example:

  # look for the folder containing the firebird embedded files to ship with the program
  find_path( FB_EMBEDDED_PATH firebird.conf ${PROJECT_SOURCE_DIR}/firebird_runtime ${PROJECT_SOURCE_DIR}/../../firebird_embedded_2.0.4_runtime )
  message( STATUS "Embedded firebird files path: " ${FB_EMBEDDED_PATH} )

At the end of CMakeLists.txt add code like this:

install( TARGETS vvv DESTINATION . )

if( APPLE )
  # copy the Firebird runtime
  install( DIRECTORY ${FB_EMBEDDED_PATH}/ DESTINATION vvv.app/Contents/MacOS USE_SOURCE_PERMISSIONS )
  install( FILES ${FB_EMBEDDED_PATH}/firebird/firebird.msg DESTINATION vvv.app/Contents/MacOS/firebird/bin/firebird )
  # fix filename references in the runtime
  set( FIXUP_COMMAND ${PROJECT_SOURCE_DIR}/MACOSX_fixup_bundle.sh " " ${CMAKE_INSTALL_PREFIX}/vvv.app )
  install( CODE "execute_process( COMMAND ${FIXUP_COMMAND} )" )
endif( APPLE )

if( UNIX AND NOT APPLE )
  # copy the Firebird runtime
  install( DIRECTORY ${FB_EMBEDDED_PATH}/ DESTINATION . USE_SOURCE_PERMISSIONS )
  # copy other files used only for the Linux version
  install( FILES ${PROJECT_SOURCE_DIR}/linux_specific/readme.txt
                 ${PROJECT_SOURCE_DIR}/linux_specific/License.txt
                 DESTINATION . )
  install( FILES ${PROJECT_SOURCE_DIR}/linux_specific/vvv-start.sh
                 DESTINATION . PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE
                                           GROUP_READ WORLD_READ WORLD_EXECUTE )
endif( UNIX AND NOT APPLE )

if( UNIX )
  # set the installation path of the executable file and the resources (in OS X they are inside the bundle )
  if( APPLE )
    set( EXECUTABLE_INTALL_PATH vvv.app/Contents/MacOS )
    set( RESOURCES_INTALL_PATH vvv.app/Contents/Resources )
  else( APPLE )
    set( EXECUTABLE_INTALL_PATH . )
    set( RESOURCES_INTALL_PATH . )
  endif( APPLE )



  # copy other files to the installation path
  install( FILES ${PROJECT_SOURCE_DIR}/vvv-struct-update.fdb
                 ${PROJECT_SOURCE_DIR}/VVV.fbk
                 ${PROJECT_SOURCE_DIR}/help/en/vvv.htb
                 DESTINATION ${EXECUTABLE_INTALL_PATH} )


endif( UNIX )

Under OS X CMake will execute a script named MACOSX_fixup_bundle.sh to patch the dylibs as described here. CMake has some support to automatically fix things like these: I struggled for some time but I was not able to understand how it works. Documentation is scarce to I gave up and I decided to directly run a script.
Here is the script used by CMake:

#!/bin/bash

# this script will fix the components of the Firebird runtime to make it run from any location

# the script receives the path of the bundle to fix
BUNDLEPATH=$*

# the following line contains the name of the executable file that will be patched
# it is the only line that should be changed when copying this file to another project
EXECFILE=${BUNDLEPATH}/Contents/MacOS/vvv

LIBPATH=${BUNDLEPATH}/Contents/MacOS/firebird
LIBBINPATH=${BUNDLEPATH}/Contents/MacOS/firebird/bin
# path of library files relative to the main executable
NEWLIBPATH="@executable_path/firebird"
# path of library files relative to other library files
NEWLIBPATH_FOR_LIBS="@loader_path"
# path of library files relative to executables in the "firebird/bin" folder
NEWLIBPATH_FROM_BIN="@loader_path/.."
OLDLIBPATH="/Library/Frameworks/Firebird.framework/Versions/A/Libraries"
OLDLIBFBEMBEDFILENAME="/Library/Frameworks/Firebird.framework/Versions/A/Firebird"

# change the references in the files contained in the "firebird" folder
for TARGET in libfbembed.dylib libicudata.dylib libicui18n.dylib libicuuc.dylib ; do
  LIBFILE=${LIBPATH}/${TARGET}
  OLDTARGETID=${OLDLIBPATH}/${TARGET}
  NEWTARGETID=${NEWLIBPATH}/${TARGET}
  NEWTARGETID_FOR_LIBS=${NEWLIBPATH_FOR_LIBS}/${TARGET}
  install_name_tool -id ${NEWTARGETID_FOR_LIBS} ${LIBFILE}
  install_name_tool -change ${OLDTARGETID} ${NEWTARGETID} ${EXECFILE}
  for POSSIBLECALLERNAME in libfbembed.dylib libicudata.dylib libicui18n.dylib libicuuc.dylib ; do
    POSSIBLECALLERFILE=${LIBPATH}/${POSSIBLECALLERNAME}
    install_name_tool -change ${OLDTARGETID} ${NEWTARGETID_FOR_LIBS} ${POSSIBLECALLERFILE}
  done
done

# change the references in the files contained in the "firebird/bin" folder
for TARGET in gbak isql ; do
  FILE=${LIBBINPATH}/${TARGET}
  for POSSIBLECALLEDNAME in libfbembed.dylib libicudata.dylib libicui18n.dylib libicuuc.dylib ; do
    OLDTARGETID=${OLDLIBPATH}/${POSSIBLECALLEDNAME}
    NEWTARGETID=${NEWLIBPATH_FROM_BIN}/${POSSIBLECALLEDNAME}
    install_name_tool -change ${OLDTARGETID} ${NEWTARGETID} ${FILE}
  done
  # change the reference to libfbembed into the program, that contains a reference to a different name (the framework name)
  NEWTARGETID=${NEWLIBPATH_FROM_BIN}/libfbembed.dylib
  install_name_tool -change ${OLDLIBFBEMBEDFILENAME} ${NEWTARGETID} ${FILE}
done

# change the reference to libfbembed into the caller program, that contains a reference to a different name (the framework name)
NEWTARGETID=${NEWLIBPATH}/libfbembed.dylib
install_name_tool -change ${OLDLIBFBEMBEDFILENAME} ${NEWTARGETID} ${EXECFILE}



You will need to edit the EXECFILE definition (near the file top) to change the name from "vvv" to your program's name.

Monday, December 20, 2010

CMake - Build an OS X Bundle

OS X applications are structured as bundles. A bundle is a folder with a predefined structure that contains the program and other files.
It is possible to create an application bundle with CMake using specific commands.

First we must specify the files containing the application icons. For example:

#--------------------------------------------------------------------------------
# For Apple set the icns file containing icons
if(APPLE)
  # icon files to copy in the bundle
  set( OSX_ICON_FILES ${CMAKE_CURRENT_SOURCE_DIR}/graphics/vvv.icns ${CMAKE_CURRENT_SOURCE_DIR}/graphics/vvv-document.icns )
  # set where in the bundle to put the icns files
  set_source_files_properties( ${OSX_ICON_FILES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
  # include the icns files in the target
  set( SRCS ${SRCS} ${OSX_ICON_FILES} )
ENDIF(APPLE)


add_executable( vvv WIN32 MACOSX_BUNDLE ${SRCS} )

This example defines two icons, one for the application and one for the application documents. The SRCS variable already contains the list of source files and this code adds the icons to the files list.
The ADD_EXECUTABLE command in the last line tells CMake to create an OS X bundle.

The following code will tell CMake where to find the Info.plist that will be copied into the bundle:

if(APPLE)
  # configure CMake to use a custom Info.plist
  set_target_properties( vvv PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${PROJECT_SOURCE_DIR}/vvv-Info.plist )
ENDIF(APPLE)

Using the code above CMake will be able to create an OS X application bundle.

Monday, June 7, 2010

Firebird embedded - OS X

Deploying an embedded Firebird database in an OS X bundle is similar to deploying it for Linux, but there are more problems to solve. This might not be the best solution, but it works so it can be at least a good starting point.

First I copied the files that I needed from the Firebird Framework folders to another folder. For example, let's call it /Users/fulvio/firebird_runtime.
Then I renamed the Firebird file to libfbembed.dylib: this is probably not necessary but I feel better using the same filename as in other platforms.

Files structure

I create the application as a bundle. The executable file is stored in the folder Contents/MacOS inside the bundle. In this same folder i also store:

firebird.conf
firebird (a folder)

..firebird.msg

..libfbembed.dylib
..libicudata.dylib
..libicui18n.dylib
..libicuuc.dylib
..security2.fdb
..bin (a folder)
....fb_lock_mgr..
....gbak
....isql
....firebird (a folder)
......firebird.msg
..intl (a folder)
....fbintl.conf
....fbintl.dylib

I used dots instead of spaces to indent because the blog editor keeps ignoring leading spaces.
Indented files are stored it the folder above them so, for example, gbak is stored in the bundle as Contents/MacOS/firebird/bin/gbak.

I added some "copy files" build phase in XCode to copy the Firebird files from /Users/fulvio/firebird_runtime to the bundle.

Linking

I link the program against /Users/fulvio/firebird_runtime/libfbembed.dylib adding the following text to Other linker flags:

-L/Users/fulvio/firebird_runtime

and adding the Firebird .dylib files to the "Link Binary With Libraries" build phase in XCode.

Environment variables

The next step is to set the FIREBIRD environment variable to the executable folder.
The command line is "export FIREBIRD=."
The same result can be obtained adding

<key>LSEnvironment</key>
<dict>
<key>FIREBIRD</key>
<string>.</string>
</dict>

to the Info.plist file contained in the bundle.

firebird.conf

Open the "firebird.conf" file and look for #RootDirectory. The leading pound sign means that the line is commented. Change it to:

RootDirectory = ./firebird

note that the line is not commented any more.

Patching

The bundle still doesn't work because the program looks for the libraries in the original framework's location, not in the bundle.
.dylib files contain their original full path and the original full path of all the used libraries, and executable files do the same. You can verify this running the otool -L and otool -D commands.
I solved this problem using the install_name_tool command that changes the file reference contained in libraries and executables. It only works if the new paths are not longer than the old ones, but this is not a problem for us. I use a "Run Script" build phase in XCode to execute the following script:

#!/bin/bash
EXECFILE=${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}
LIBPATH=${BUILT_PRODUCTS_DIR}/${EXECUTABLE_FOLDER_PATH}/firebird
LIBBINPATH=${BUILT_PRODUCTS_DIR}/${EXECUTABLE_FOLDER_PATH}/firebird/bin
# path of library files relative to the main executable
NEWLIBPATH="@executable_path/firebird"
# path of library files relative to other library files
NEWLIBPATH_FOR_LIBS="@loader_path"
# path of library files relative to executables in the "firebird/bin" folder
NEWLIBPATH_FROM_BIN="@loader_path/.."
OLDLIBPATH="/Library/Frameworks/Firebird.framework/Versions/A/Libraries"
OLDLIBFBEMBEDFILENAME="/Library/Frameworks/Firebird.framework/Versions/A/Firebird"

# change the references in the files contained in the "firebird" folder
for TARGET in libfbembed.dylib libicudata.dylib libicui18n.dylib libicuuc.dylib ; do
LIBFILE=${LIBPATH}/${TARGET}
OLDTARGETID=${OLDLIBPATH}/${TARGET}
NEWTARGETID=${NEWLIBPATH}/${TARGET}
NEWTARGETID_FOR_LIBS=${NEWLIBPATH_FOR_LIBS}/${TARGET}
install_name_tool -id ${NEWTARGETID_FOR_LIBS} ${LIBFILE}
install_name_tool -change ${OLDTARGETID} ${NEWTARGETID} ${EXECFILE}
for POSSIBLECALLERNAME in libfbembed.dylib libicudata.dylib libicui18n.dylib libicuuc.dylib ; do
POSSIBLECALLERFILE=${LIBPATH}/${POSSIBLECALLERNAME}
install_name_tool -change ${OLDTARGETID} ${NEWTARGETID_FOR_LIBS} ${POSSIBLECALLERFILE}
done
done

# change the references in the files contained in the "firebird/bin" folder
for TARGET in gbak isql ; do
FILE=${LIBBINPATH}/${TARGET}
for POSSIBLECALLEDNAME in libfbembed.dylib libicudata.dylib libicui18n.dylib libicuuc.dylib ; do
OLDTARGETID=${OLDLIBPATH}/${POSSIBLECALLEDNAME}
NEWTARGETID=${NEWLIBPATH_FROM_BIN}/${POSSIBLECALLEDNAME}
install_name_tool -change ${OLDTARGETID} ${NEWTARGETID} ${FILE}
done
# change the reference to libfbembed into the program, that contains a reference to a different name (the framework name)
NEWTARGETID=${NEWLIBPATH_FROM_BIN}/libfbembed.dylib
install_name_tool -change ${OLDLIBFBEMBEDFILENAME} ${NEWTARGETID} ${FILE}
done

# change the reference to libfbembed into the caller program, that contains a reference to a different name (the framework name)
NEWTARGETID=${NEWLIBPATH}/libfbembed.dylib
install_name_tool -change ${OLDLIBFBEMBEDFILENAME} ${NEWTARGETID} ${EXECFILE}

I learned something about .dlybs patching from

http://ynniv.com/blog/2006/02/deploying-app-that-use-dylibs-on-mac.html

but this link does not seem to work any more.