Monday, 15 December 2014

Getting an Offline Copy of Windows Driver Kit (WDK)

Microsoft provides a web installer for WDK but not an ISO image. If you want an offline copy, you have to execute it to download everything. The problems are:

  1. If the online computer is sufficiently locked down, you might not be able to execute it.
  2. Even if you are allowed to, it insists on installing .NET 4.5 if it is missing. This is bad because downloading shouldn't require installation of anything.
  3. Even if you let it install .NET 4.5, it can't unless it has administrative privileges.

Instead of executing the installer, we can extract download information from it and download accordingly.

Procedure

  1. Download the web installers from a online computer from here. There are actually two files: wdksetup.exe and wdktestsetup.exe. The first installer installs WDK proper and the second installs WDK Test Pack. Download both.
  2. If the online computer is somewhat locked down, take the downloads to another computer that you have more control of, and perform your work there.
  3. Use 7-zip to extract wdksetup.exe as if it were an archive.
  4. Check that you have these files:

  5. Open the file u12 with a text editor. (It contains XML data.)
  6. Locate the line that contains the downloadroot tag:
    <downloadroot>http://go.microsoft.com/fwlink/?LinkID=392876</downloadroot>.

    This URL redirects to a top level remote directory that contains our download files.

  7. We need to figure out exactly what this directory is. On the online computer, open a web browser, type this address in the address bar, and press enter.

    Ignore the 404 error which isn't important. We want the redirection result, which is reflected in the address bar:

    http://download.microsoft.com/download/0/8/C/08C7497F-8551-4054-97DE-60C0E510D97A/wdk

    Let's call this URL $T.

  8. Go back to the computer you have been using (see step 2). Close the file u12.
  9. Open the file 0.
  10. To make viewing easier, insert a carriage return after every close tag. The following isn't exactly correct but will do: if you use Notepad++, try 'find-and-replace'. First, set the search mode to 'Regular expression'. Next, the 'Find what' field should be > and the 'Replace with' field should be >\n. Then, click 'Replace All'.

  11. Notice the lines that begin with the tag <Payload id="..." />. Basically they describe what you need to download. Other lines aren't important.
  12. Skipping those lines with attributes Packaging="embedded". They describe files that aren't downloaded but are extracted from the web installer itself. Notice the SourcePath attributes. Compare with the screenshot in step 4. Does it ring a bell?
  13. Take note of the lines with attributes Packaging="external". For each line, you must
    • record the FilePath attribute. It specifies where a file should be downloaded to, relative to the location of the web installer. If a file with matching file size and hash isn't there, the installer will download into that place. Put it differently, it expresses directory layout for placing downloads.
    • record the download URL. There are two cases.
      • If DownloadUrl attribute is present, used as the download URL.
      • If absent, the download URL is derived from the SourcePath attribute. First, convert all backslashes in SourcePath to forward slashes, then append the result to $T (step 7) to give the download URL. For example, if SourcePath is

        Installers\1c33d17316f25da4cbe42ca09b018509.cab

        then the download URL would be:

        http://download.microsoft.com/download/0/8/C/08C7497F-8551-4054-97DE-60C0E510D97A/wdk/Installers/c33d17316f25da4cbe42ca09b018509.cab.

    To avoid confusion, when recording FilePath attributes, convert all backslashes to forward slashes.

  14. Put the download URLs and FilePath attributes into two separate files, url.txt and filepath.txt.

    url.txt would look like:

    http://download.microsoft.com/download/0/8/C/08C7497F-8551-4054-97DE-60C0E510D97A/wdk/Installers/1c33d17316f25da4cbe42ca09b018509.cab http://download.microsoft.com/download/0/8/C/08C7497F-8551-4054-97DE-60C0E510D97A/wdk/Installers/b6923fe117b94abbb1703c09cf619759.cab
    .
    .
    .

    whereas filepath.txt would look like:

    Installers/1c33d17316f25da4cbe42ca09b018509.cab Installers/b6923fe117b94abbb1703c09cf619759.cab
    .
    .
    .

  15. You now have url.txt and filepath.txt. Use the former for downloading and latter for moving the downloads to a proper directory layout. Typically, you use some kind of scripts for that purpose, e.g. with Cygwin bash script or Powershell scripts.
  16. Once you have downloaded everything into right place, put wdksetup.exe to the top level download directory. Now you have an offline copy of WDK.
  17. Repeat steps 3 to 16 differently:
    • In step 3, open wdktestsetup.exe instead.
    • In steps 5 to 8, open u11 instead.
    • In step 14, save the download URLs to a different file; e.g. urltest.txt.
    • In step 15, save the file paths to a different file; e.g. filepathtest.txt.
    • In step 16, put wdktestsetup.exe to the top level download directory instead.
  18. You now have an offline copy of WDK Test Pack.

Extra Remarks

  1. Although you can carry out steps 11 to 15 by hand, you'd better use some kind of scripting; e.g. Cygwin shell script or Powershell script.
  2. We separate the download information into download URLs and directory layouts, and place them into different files. The reason is that each has a different purpose.
  3. If during download you get 404 errors, it is OK: there could be lines in u12 and u11 that are not used by the web installers and have no corresponding files on the server. Try doing a full install from the resulting download trees. If the installers report success, then these 404 errors are harmless.

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.