Saturday 19 July 2014

Enable WebKit Support for Static Qt

Here is an outline of how to compile static Qt with WebKit support.

Caveat

  1. It is only an outline.
  2. Licensing issues arise from static linking are not dealt with.
  3. The instructions are OS and toolchain specific.
  4. The patches provided here were not tested. They were extracted from a larger set which was tested but contains unrelated enhancements.
  5. The static libraries generated for QtWebKit are huge. Linking against them is time-consuming.
  6. QDeclarativeWebView doesn't run well under static builds. It uses a QML extension which requires DLL support.
  7. QtDesigner supports QWebView as a plugin, which is DLL-based. Under static builds, the support isn't there.

Environment

  • OS: Windows 7 Enterprise x64.
  • Compiler: Visual Studio .NET 2012.
  • GNU patch: available in the Cygwin project. You can also try a standalone version from the Gnuwin32 project.
  • Qt source: version 4.8.6.

Procedure

  1. Unpack the Qt source to %QRSRC%, the source directory.
  2. Apply this patch (taken from here) to solve a compilation problem with Visual Studio 2012 .NET.
  3. Download and apply these patches as well.
    1. 110-reenable-static-webkit.patch
    2. 120-fix-webkit-static-build.patch
    3. 130-resolve-qtscript-jscore-name-clash-part1.patch
    4. 140-resolve-qtscript-jscore-name-clash-part2.patch
  4. Recompile configure.exe (for configuring Qt on Windows):

    rd /s /q "%QTBUILD%"
    mkdir "%QTBUILD%"
    cd /d "%QTBUILD%"
    "%QTSRC%\configure.exe" -opensource -confirm-license -platform win32-msvc2012 -arch windows -release -static -fast
    cd /d "%QTBUILD%\tools\configure"
    nmake

    Here %QTBUILD% is the build directory. A new copy of configure.exe will be created in %QTSRC%, replacing the old one. You don't need to nmake install.

  5. With the new configure.exe, configure and compile Qt with static WebKit support. For example,

    rd /s /q "%QTBUILD%"
    mkdir "%QTBUILD%"
    cd /d "%QTBUILD%"
    "%QTSRC%\configure.exe" -opensource -confirm-license -platform win32-msvc2012 -arch windows -prefix %QTPREFIX% -debug-and-release -static -webkit
    nmake
    nnmke install

    Here %QTPREFIX% is the installation directory for Qt.

What do these patches change?

First Patch

Configure.exe explicitly disables WebKit support for static builds of Qt. This patch removes the restriction.

Second Patch

Under shared builds, QtWebKit is built in the following way.

  1. Up to two copies (debug and release) of static JavaScriptCore are built. They are both named jscore.lib and are placed in different build directories.
  2. Up to two copies (debug and release) of static WebCore are built. They are both named webcore.lib and placed in different build directories.
  3. The build process makes an adjustment in order to build static libraries under shared configurations: it adds staticlib to the CONFIG variable.
  4. Up to two sets (debug and release) of object files from the WebKit project are created.
  5. Then, the debug and release QtWebKit DLLs are created by linking the corresponding versions of jscore.lib, webcore.lib and WebKit object files.
  6. Afterwards, these static libraries and object files are discarded, in a sense. They stay in their build directories and are not installed to %QTPREFIX%\lib. Furthermore, the static libraries are excluded from the link dependencies of QtWebKit.

For static builds, this patch changes the build procedure:

  1. Under static builds it is not necessary to add staticlib to CONFIG.
  2. The debug and release versions of static JavaScriptCore are renamed to JavaScriptCored.lib and JavaScriptCore.lib.
  3. The debug and release versions of static WebCore are renamed to WebCored.lib and WebCore.lib.
  4. They are installed to %QTPREFIX%\lib.
  5. They are included in the link dependencies of QtWebKit.

Third Patch

For static builds, JavaScriptCore[d].lib and QtScript[d].lib contain object files compiled from the JavaScriptCore source (there are two copies of them). Each contains its own instance of JITStubs.obj, compiled from a copy of JITStubs.cpp. Both instances expose the same function names, and for applications that link to both libraries, the linker isn't happy and complains that some symbols are multiply defined.

It turns out that everything in both copies of JavaScriptCore source are placed in the JSC namespace. Therefore, JavaScriptCore symbols are name-mangled with this namespace name, but there is a twist for QtScript. When QtScript is compiled, JSC is redefined to be QTJSC via macro substitutions. Effectively the symbols are in the QTJSC namespace, therefore link both libraries shouldn't cause errors.

There is another twist. Both namespaces contain functions that are declared as extern "C". That means they are C functions, and namespace (thus name-mangling) has no effects, even if the functions are in a namespace. The plain C function names are exposed twice, once in JavaScriptCore[d].lib and QtScript[d].lib, causing the linker to be unhappy. We solve the problem by mangling these offending function names manually. Once choice is to prefix them with JSC. For example, func becomes JSC_func. For each offending function name, there are three places to change, in both copies of JavaScriptCore source:

  1. The headers that declares it (both copies of JITStubs.h).
  2. The sources that defines it (both copies of JitStubs.cpp).
  3. Places that call these functions.

We must also cater for QtScript. During Qtscript compilation, JSC is redefined as QTJSC. Therefore JSC_func must somehow become QTJSC_func, otherwise we end up with two JSC_func's and the problem isn't fixed at all. We will do that with a macro CONCAT(x,y). It concatenates two strings with an underscore character in-between. Instead of changing the name to JSC_func, we change it to CONCAT(JSC,func). This macro accepts one level of indirection: if JSC is defined as QTJSC, CONCAT(JSC,func) is properly expanded to QTJSC_func. Therefore, the new function names (in the form of CONCAT(JSC,xxxx)) will be expanded differently for JavaScriptCore and QtScript, and multiple definitions are removed.

After we change headers and sources, we must modify places that call the affected functions. Where are these places? We let the compiler tell us. After these functions are renamed, the calls become invalid because they call non-existent functions. When we compile, we get compiler errors which will tell us where some of the calls are. Fix them, recompile, and if we still get compiler errors, there are more calls to fix. Rinse and repeat until there are no errors.

This patch contains all modifications made to copies of JITStubs.h, JITStubs.cpp and the callers.

Fourth patch

It fixes a problem very similar to the one solved by the third patch. This time, the offending objects are two copies of Assertions.obj. We modify the copies of Assertions.h and Assertion.cpp and also the calls to troublesome functions. A minor difference is that, we prefix function names with WTF instead of JSC because these functions belong to WTF, the Web Template Framework. Notice QtScript redefines WTF to be QTWTF during compilation, so the new function names won't clash.

Another minor difference is that, the offending functions are not placed in a namespace. This doesn't affect our plan at all (think about it).

This patch contains all modifications made to copies of Assertions.h, Assertions.cpp and the callers.

2 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. can you update this patch for qtwebkit in qt 5.4 wich is the last qt has qtwebkit

    ReplyDelete