firebreath中使用第三方庫文件

Using libraries with CMake

CMake eases working with external libraries, see How to find libraries.

Adding includes for all platforms can be done in projects/YourPlugin/CMakeLists.txt, platform specific includes and linking should go into projects/YourPlugin/<PlatForm>/projectDef.cmake.

For example to link to the Foo library on Linux we'd add to projects/YourPlugin/X11/projectDef.cmake. First let CMake find the library in question:

find_library(FOO_LIBRARY Foo)

Then add the includes if needed:

include_directories(${FOO_INCLUDE_DIRS})

Finally link to the library:

target_link_libraries(${PROJECT_NAME} FOO_LIBRARY)

If you want to link against a library in a specific location then you don't want to use find_library. You'd be better off doing something like:

set (LIBRARY_PATH ${FireBreath_SOURCE_DIR}/path/to/library)
target_link_libraries(${PROJECT_NAME} debug "${LIBRARY_PATH}/debug/library.lib")
target_link_libraries(${PROJECT_NAME} optimized "${LIBRARY_PATH}/release/library.lib")

NOTE: target_link_libraries must come after the add_library command (or add_plugin when a firebreath helper function is used). This means at the end of the plugin project CMakeLists.txt file if it is a normal fbgen-generated project for global or at the end of projectDef.cmake for platform specific.

Using FireBreath Libraries with CMake

Place the following at the bottom of your project's PluginConfig.cmake

add_firebreath_library(Fb_libary_name)

See FB example for more.

Using Boost libraries

FireBreath provides the CMake macro add_boost_library() to aid working with compiled Boost libraries. It works with both the Boost libraries FireBreath provides and also the ones you may have installed on your own.

Use it in your PluginConfig.cmake, e.g.:

add_boost_library(filesystem)

Currently supported are: thread, filesystem, system, regex, date_time, smart_ptr, serialization, signals.

Please remember that you should only be modifying the PluginConfig.cmake file that is in your project directory (e.g. by default it would be projects/yourplugin/PluginConfig.cmake). Any time you change PluginConfig.cmake or any other cmake files you need to rerun the prep script; in some rare cases you may need to delete your build directory first.

NOTE: To use a different version of the Boost libraries see  Prep Scripts - External Boost.

Dynamic libraries with relative paths

Sometimes you don't want to require the installation of your dependencies system-wide or you need special patched versions, so you want to bundle them "privately" with your plugin. In that case, depending on the OS, you may need to fix where the loader looks for the libraries or explicitly change the paths to the linked libraries.

If you are bundling your libraries with your source then adding a line as follows to your OS specific targets is an okay approach.

target_link_libraries(${PROJECT_NAME}
    ${PLUGIN_INTERNAL_DEPS}
    "${CMAKE_CURRENT_SOURCE_DIR}/lib/private_lib.so"
    )

Mac OS X

Inspect linked libraries with otool -L MyPlugin.plugin/Contents/MacOS/MyPlugin.

You can change library install paths and rpaths with install_name_tool. For specifying relative paths there are two special special symbols:

  • @loader_path - specify paths relative to the plugin library
  • @executable_path - specify paths relative to the executable in which the plugin is loaded (i.e. the browser)

A useful approach is to pack the libraries into your plugin bundle (e.g. in Contents/Frameworks) and refer to them relative to your plugin library using @loader_path.  You'll need to set a relative path in the library itself as well as in the plugin binary.  From your MyPlugin.plugin/Contents directory, run something like the following to reference Foo.dylib from your plugin:

install_name_tool -id @loader_path/../Frameworks/Foo.dylib Frameworks/Foo.dylib
install_name_tool -change /some/path/Foo.dylib @loader_path/../Frameworks/Foo.dylib MacOS/MyPlugin

To find the path you need to pass as the first argument to the -change parameter, use otool to inspect your plugin:

otool -L MacOS/MyPlugin
 
MyPlugin:
 /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 751.29.0)
 /System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices (compatibility version 1.0.0, current version 38.0.0)
 /System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 15.0.0)
 /System/Library/Frameworks/Carbon.framework/Versions/A/Carbon (compatibility version 2.0.0, current version 152.0.0)
 /usr/lib/Foo.dylib (compatibility version 50.0.0, current version 50.0.0)
 /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.0)
 /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.9.0)
 /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 550.29.0)

From the above output, the path that you'll use as the first parameter is /usr/lib/Foo.dylib.  After changing the references, you can use otool again to verify that the path in the binary was changed.

Of course, doing this after every build is not fun, so you can automate the process of copying the lib to the Frameworks directory, updating the name in the lib, and updating the reference in the plugin with CMake.  First we'll define a few functions:

#Copy the specified lib to the plugin directory.
function(copyLibToFrameworks libPath pathToPlugin)
    ADD_CUSTOM_COMMAND(
        TARGET ${PROJECT_NAME}
        POST_BUILD
        COMMAND mkdir -p ${pathToPlugin}/Contents/Frameworks
    )
    ADD_CUSTOM_COMMAND(
        TARGET ${PROJECT_NAME}
        POST_BUILD
        COMMAND cp ${libPath} ${pathToPlugin}/Contents/Frameworks
    )
endfunction()
 
#Update the reference to the lib from the plugin.
function(updateReferencesToLib fromPath toPath targetLib)
    ADD_CUSTOM_COMMAND(
        TARGET ${PROJECT_NAME}
        POST_BUILD
        COMMAND install_name_tool -change ${fromPath} ${toPath} ${targetLib}
    )
endfunction()
 
#Update the reference inside the target lib.
function(updateReferenceInLib toPath targetLib)
    ADD_CUSTOM_COMMAND(
        TARGET ${PROJECT_NAME}
        POST_BUILD
        COMMAND install_name_tool -id ${toPath} ${targetLib}
    )
endfunction()
 
#Copy and update references for a library.
function(changeLoaderPath pathInBinary libFolder libName pathToPlugin)
    copyLibToFrameworks(${libFolder}/${libName}
        ${pathToPlugin}
    )
    updateReferenceInLib(@loader_path/../Frameworks/${libName}
        ${pathToPlugin}/Contents/Frameworks/${libName}
    )
    updateReferencesToLib(${pathInBinary}
        @loader_path/../Frameworks/${libName}
        ${pathToPlugin}/Contents/MacOS/${PROJECT_NAME}
    )
endfunction()

Now you can call those functions like so:

# Build the path to the plugin binary
get_target_property(LIBDIR ${PROJECT_NAME} LIBRARY_OUTPUT_DIRECTORY)
get_target_property(ONAME ${PROJECT_NAME} OUTPUT_NAME)
set(PBIN "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${PROJECT_NAME}.plugin")
changeLoaderPath(/usr/lib/Foo.dylib
    ${CMAKE_CURRENT_SOURCE_DIR}/Mac
    Foo.dylib
    ${PBIN}
)

This copies a library from the Mac subfolder of the project into the plugin packages Frameworks folder and updates the references.  If you have another library, just call changeLoaderPath again.  It's entirely possible that Foo.dylib may have other references that you're providing locally as well - in this case, you can just call updateReferencesToLib directly after calling changeLoaderPath to update those references automatically.  Run the prep script again, and Xcode should run all this stuff after every build, making your life a lot easier.

Using Mac Frameworks

You can include Mac Frameworks the same way you would any other library.  Use find_library to locate the framework, like in the following examples:

find_library(QUARTZCORE_FRAMEWORK QuartzCore)
find_library(COCOA_FRAMEWORK Cocoa)
find_library(CARBON_FRAMEWORK Carbon)

find_library will put the path to the framework in the variable specified as the first parameter. Add that to your target_link_libraries command:

target_link_libraries(${PLUGIN_NAME}
    ${QUARTZCORE_FRAMEWORK}
    ${CARBON_FRAMEWORK}
    ${COCOA_FRAMEWORK}
)

Linux

You can inspect linked libraries with ldd, loading libraries from relative locations can be done by changing the rpath and using $ORIGIN.

E.g. when installing your library in ~/.mozilla/plugins/npFoo.so, you could put your libraries into ~/.mozilla/plugins/foo_libs/. To fix the rpath accordingly either use linker flags like -rpath $ORIGIN/foo_libs or use a utility like chrpath. The output from ldd should reflect the updated relative paths.

TODO: How to do that directly from CMake?

There seem to be some issues with setting the rpath from CMake, see this thread.

Windows

On Windows this problem doesn't generally exist as the loader searches for libraries in the DLLs directory too.

There are some situatons however, that can cause problems.

When linking in you Dlls to the plugin, you may get an ICE38 error when building the WiX Installer. This possibly relates to self-registration (?).

This can be corrected when adding you libraries by declaring a RegistryValue entry in the Component containing Files in your wxs. However the problem still remains for the plugin Dll itself. One solution to work around this problem is to delay your Dlls as /DELAYLOAD. This does create some slight complications though.

Delay Loading Dlls

A directive as follows can be added to the end of CMakeLists.txt or Win/projectDef.cmake to enable the delay loading:

set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "${LINK_FLAGS} /DELAYLOAD:myStuff.dll")
target_link_libraries(${PROJECT_NAME} myStuff delayimp.lib)

This allows WiX to build the installer without the error.

The problem now however, is that the Dll is loaded from the standard search path and might not be found. (You'll get a big hairy exception on the first call into you Dll if this is the case).

To ensure that the library does load, you can load it yourself. That way when the delayimp helper tries to find the Dll, it just adds a reference count because it is already loaded.

Pseudo-code example follows:

HMODULE hThis = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, ...)
GetModuleFileName(hThis, ...)
PathRemoveFileSpec(...)
PathAppend(..., "myStuff.dll")
LoadLibraryEx(..., LOAD_WITH_ALTERED_SEARCH_PATH)

But, dont forget to release the module handles again after the call to ensure they get released correctly!

Statically Linking to Externally Compiled Libraries

Windows

External libraries which you might want to link to, may have been compiled with either a static multi-threaded run-time CRT (/MT[d]) or dynamic multi-threaded run-time CRT (/MD[d]).

All FireBreath projects are built with static CRT by default; however, there is an option that will make it use dynamic CRT.
Use the "-D WITH_DYNAMIC_MSVC_RUNTIME=1" switch in your prep script to make the project use the dynamic runtime (inserts the /MD[d] switch into the project).

Note also, that when using WITH_DYNAMIC_MSVC_RUNTIME=1 with an external boost build, you will have to remove the Boost_USE_STATIC_RUNTIME flag from the build script.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章