Compiler Qt 4 avec Visual C++ 2005

Aurélien Regat-Barrel

Cet article a pour but de faciliter la compilation Qt/Windows Open Source Edition avec Microsoft Visual C++ 2005. Il présente aussi de manière détaillée le processus de compilation et le principe de configuration de cette bibliothèque.

Ceci est un ancien article qui n'est plus d'actualité car désormais Microsoft Visual C++ 2005 Express est officiellement supporté par Qt/Windows Open Source Edition depuis la version 4.3.2. Les informations contenues ne sont donc plus d'actualité, et sont conservées en ligne à titre informatif. Si vous recherchez un moyen simple de compiler le code source de Qt, vous pouvez utiliser l'outil gratuit CuteBuilder, dont cet article a inspiré la réalisation.

- Titre : Compiler Qt 4 avec Visual C++ 2005

- Auteur : Aurélien Regat-Barrel

- Parution : 1 mars 2006

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2012 Aurélien Regat-Barrel. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Première compilation de Qt

- III. Configurer la compilation

- III-A. Principe

- III-B. Support des bases de données : exemple avec ODBC et PostgreSQL

- IV. Détails sur le processus de compilation et le fonctionnement du patch

- V. Personnaliser la compilation

- VI. Personnalisation avancée de la compilation

- VI-A. Compilation d'une version release sur mesure

- VI-B. Modification des options de compilation de Visual C++

- VI-C. Modification de qmake

- VI-D. Recompilation des bibliothèques tierces

- VII. Réalisation d'un paquetage prêt à l'emploi

- VIII. Liens utiles

I - Introduction

L'année 2005 est marquée de deux bonnes nouvelles en matière de développement sous Windows : Qt est disponible en version GPL sous Windows depuis la version 4 (Qt/Windows Open Source Edition) et Microsoft distribue gratuitement Visual C++ 2005 en version limitée mais qui reste à mon avis largement au-dessus des autres IDE C++ gratuits.

Microsoft et Trolltech ont donc fait preuve de générosité, mais cela ne nous dispense pas pour autant d'un minimum d'efforts avant de pouvoir utiliser conjointement les deux produits. Car Trolltech a volontairement limité le support de Qt/Windows Open Source Edition au compilateur MingW (port de GCC sous Windows), ce qui complique son utilisation avec Visual C++. Mais avec quelques connaissances et un peu de malice, cela reste possible. Cet article tente d'expliquer en détail comment procéder.

Les lecteurs pressés peuvent se rendre directement en fin d'article où ils trouveront une archive contenant Qt 4.1.1 prête à être utilisée avec Visual C++ 2005.

Les versions des logiciels utilisés lors de la rédaction de cet article sont VC++ 8.0 (Express Edition de novembre 2005), Qt 4.1.1 et PostgreSQL 8.1, le tout sous Windows XP SP2.

II - Première compilation de Qt

Avant toute chose, il faut bien sûr commencer par installer Visual C++ et Qt. Pour VC++, je vous renvoie vers l'article Prise en main de Visual C++ Express Edition. Veillez à bien avoir installé le Platform SDK (PSDK).
Pour Qt, récupérez la dernière version disponible de Qt/Windows Open Source Edition (au moment où j'écris ces lignes, il s'agit de la version 4.1.1 : ftp://ftp.trolltech.com/qt/source/qt-win-opensource-src-4.1.1.zip [ftp://ftp.trolltech.com/qt/source/qt-win-opensource-src-4.1.1.zip]), et décompressez l'archive dans un répertoire donné. Pour cet article, j'utilise C:\qt4 comme emplacement racine.

Normalement, la compilation est effectuée en deux temps :

- configurer les options de Qt au moyen de configure qui va générer un makefile pour nmake, l'outil de construction Microsoft de type make ;

- lancer la compilation à proprement parler avec nmake.

Pour générer les makefiles pour nmake, configure utilise qmake, l'outil de construction propre à Qt cette fois-ci. qmake est une sorte de métamake, c'est-à-dire un programme qui génère des makefiles pour d'autres make. À partir d'un unique fichier projet, qmake est capable de créer indifféremment un makefile pour le GNU make, le nmake de Microsoft, le bmake de Borland, mais aussi des fichiers projets pour Visual C++ depuis sa version 6 incluse !

Mais comme évoqué lors de l'introduction, la version GPL n'est prévue pour être compilée qu'avec MingW. Si on lance configure, celui-ci nous le rappelle :

This is the Qt/Windows Open Source Edition.
The Qt/Windows Open Source Edition only supports the MinGW compiler.

Cette limitation porte essentiellement sur le processus de compilation et non sur les sources de la bibliothèque en elle-même. Il manque ainsi les fichiers de configuration pour les différentes versions de Visual C++.

Heureusement, il existe un patch ajoutant le support de ce compilateur (et d'autres…), qui se trouve ici : Qt/Win Free - Unofficial patches for Qt4. Il devrait normalement y avoir une version correspondant à la version de Qt que vous utilisez. En ce qui me concerne (Qt 4.1), j'ai téléchargé le fichier racs4qt41p3.zip. Une fois récupéré, il faut le décompresser dans le même répertoire que Qt, soit C:\qt4 dans mon cas.

Le répertoire C:\qt4 contient donc à sa racine l'ensemble du code source de Qt ainsi que les fichiers du patch. Il n'y a plus qu'à installer ce dernier. En lisant le fichier d'aide, on apprend qu'il suffit d'exécuter installpatch41.bat.

Avant de démarrer la compilation, il reste une dernière chose à effectuer : préparer l'environnement, c'est-à-dire faire en sorte que l'ensemble des outils (nmake…) et bibliothèques (VC++, PSDK…) de compilation soient accessibles. En ce qui concerne les outils et bibliothèques fournis avec VC++ 2005, tout est automatiquement configuré par un fichier de commande spécial vsvars32.bat créé lors de l'installation. Il se trouve normalement dans C:\Program Files\Microsoft Visual Studio 8\Common7\Tools. La variable d'environnement %VS80COMNTOOLS% devrait désigner cet emplacement. Il suffit donc de taper dans la console (les guillemets sont importants) :

"%VS80COMNTOOLS%vsvars32.bat"

pour rendre les outils de VC++ accessibles.
De même, le Platform SDK dispose d'un fichier de commande créé à l'installation qui se charge de configurer l'environnement de compilation pour le rendre accessible :

"%programfiles%\Microsoft Platform SDK\SetEnv.cmd"

Il ne reste plus qu'à démarrer la compilation. Le fichier d'aide du patch indique qu'il ne faut pas passer par configure mais par qconfigure.bat fourni avec le patch, en lui passant en premier argument le nom du compilateur à utiliser, soit msvc2005 dans notre cas :

qconfigure.bat msvc2005

Le processus démarre alors (vous pouvez aller boire un café…). Une fois terminé, vous devriez obtenir dans les répertoires C:\qt4\bin et C:\qt4\lib les divers outils et modules Qt compilés. Vous pouvez en particulier lancer C:\qt4\bin\qtdemo.exe pour avoir droit à une présentation interactive à la PowerPoint du framework.

Prévoyez 1,5 Go d'espace disque libre pour une compilation complète (bibliothèque Qt + outils + tutoriel + exemples). Sur un processeur de type Pentium IV 3 GHz, elle peut nécessiter plus d'une heure en release en fonction des options d'optimisation.

III - Configurer la compilation

III-A - Principe

La compilation est paramétrable via configure au moyen de diverses options. Exécutez

configure.exe -h

pour en avoir le détail.

Dans notre cas, nous devons passer par le fichier qconfigure.bat, en lui donnant msvc2005 comme premier paramètre. Il est possible de lui en donner d'autres en suivant afin qu'il les transmette à configure.

Mais attention : il s'agit d'un fichier .bat basique qui n'accepte que neuf arguments, dont le premier est réservé. On est donc limité à huit paramètres, les suivants sont ignorés, sans aucun avertissement !

Ceci est embêtant, car on peut facilement arriver à dépasser ce nombre. Théoriquement, configure supporte le paramètre -loadconfig qui permet de spécifier un fichier contenant une liste de paramètres, mais la version livrée avec Qt 4.1 ne semble pas le supporter. Il en est de même pour d'autres paramètres (saveconfig, redo, prefix…). La version Windows de configure n'est en effet pas totalement finalisée (cela devrait évoluer à partir de Qt 4.2).

Une solution est d'éditer le fichier qconfigure.bat pour ajouter lors de l'appel à configure des paramètres supplémentaires. Mais ce fichier qconfigure.bat possède un autre inconvénient : il relance systématiquement la compilation de toute la bibliothèque, y compris celle des outils, des exemples, des tutoriels, etc. Ce qui est très long, et inutile si on l'a déjà fait. C'est pourquoi je me suis lancé dans la réalisation de mon propre fichier .bat, que je présente un peu plus loin.

III-B - Support des bases de données : exemple avec ODBC et PostgreSQL

Il peut aussi être nécessaire d'installer d'autres bibliothèques nécessaires à la compilation de Qt, en particulier si vous comptez utiliser des bases de données. Par exemple, si vous souhaitez pouvoir utiliser PostgreSQL depuis Qt, deux options s'offrent à vous :

- passer par ODBC : en compilant Qt avec le support d'ODBC, vous pourrez utiliser n'importe quelle base de données disposant d'un pilote ODBC sans rien avoir à faire de spécial avec Qt. ODBC s'occupe d'offrir une interface uniformisée que Qt exploite. Il suffit alors d'installer le pilote du SGBD et de configurer une source ODBC (avec odbcad32.exe présent dans le répertoire system32) ;

- interroger directement le SGBD au moyen de sa bibliothèque de connexion spécialisée : la plupart des SGBD disposent d'une API spécifique permettant de s'interfacer directement avec eux. Par exemple, Qt est capable de dialoguer de manière directe avec PostgreSQL au moyen de sa bibliothèque libpq (voir les liens utiles en fin d'article). Cette solution est plus performante, mais il faut correctement installer et configurer cette bibliothèque avant d'envisager de compiler Qt.

En plus de PostgreSQL, Qt 4.1 sait aussi directement s'interfacer avec MySQL, IBM DB2, Borland InterBase, Oracle, SQLite et Sybase. Pour plus d'informations à ce sujet, consultez la documentation de QSqlDatabase ainsi que celle du module QtSql.

Dans le cas de PostgreSQL, l'installation du driver ODBC (cercle du haut dans l'image ci-dessous) et/ou de la bibliothèque libpq (cercle du bas) se fait/font depuis l'installeur :

Installation du driver ODBC pour PostgreSQL et de sa bibliothèque libpq

- Si vous activez le support d'ODBC dans Qt, les fichiers bibliothèque nécessaires pour la compilation font partie du Platform SDK, et il n'y a donc rien de plus à configurer, car cela a été effectué juste avant. Il faut en revanche s'assurer d'avoir installé le SDK ODBC qui fait partie du MDAC SDK et non du Core SDK (voir Démarrer avec Visual C++ 2005 Express - Installation du Platform SDK).

- Si vous activez le support direct de PostgreSQL (ou autre), il faut respectivement ajouter à l'INCLUDE pathet au LIB path l'emplacement des fichiers .h et .lib de la bibliothèque concernée. Dans le cas de la libpq de PostgreSQL 8.1, l'emplacement par défaut de ces fichiers est rendu accessible ainsi :

set INCLUDE=%programfiles%\PostgreSQL\8.1\include;%INCLUDE%
set LIB=%programfiles%\PostgreSQL\8.1\lib\ms;%LIB%

IV - Détails sur le processus de compilation et le fonctionnement du patch

Le rôle de configure est, ô surprise, de configurer la compilation de Qt. Concrètement, cela consiste en répercuter à plusieurs niveaux la signification des paramètres reçus en ligne de commande :

- au niveau des makefiles qui serviront à nmake (générés par qmake) ;

- donc au niveau de qmake (via son appel en ligne de commande ainsi que de son fichier cache (.qmake.cache, voir Configuring qmake's Environment - Cache File) ;

- au niveau du code source de la bibliothèque, via le fichier qtconfig.h contenant une liste de #define appropriés.

configure génère aussi le fichier qtconfig.cpp et se charge de recompiler qmakeavant de l'appeler, sauf si on lui a demandé de ne pas le faire (option -no-qmake).

Dans la version Open Source de Qt, configure n'accepte de fonctionner que si QMAKESPEC est défini à win32-g++, c'est-à-dire que Qt est configuré pour être compilé avec MingW.

Pour le faire fonctionner, il faut donc définir QMAKESPEC à win32-g++ au moyen du paramètre -platform win32-g++ (ou encore en créant une variable d'environnement nommée QMAKESPEC initialisée à win32-g++). Mais cela a pour conséquence que la compilation va être paramétrée pour MingWau moyen du contenu de C :\qt4\mkspecs\win32-g++\. Autant dire qu'elle échouera rapidement dans notre cas.

Pour permettre la compilation avec Visual C++ 2005, le patch crée (entre autres) le répertoire C:\qt4\mkspecs\win32-msvc2005\ qui contient ce qu'il faut pour que qmake parvienne à utiliser VC++ 2005 comme compilateur. On pourrait remplacer le contenu d win32-g++ par celui de win32-msvc2005 afin de gruger configure, mais il y a plus fin. Analysons le fonctionnement du patch et de son fichier qconfigure.bat.

Dans notre cas, il se résume ainsi :

- lancer configure avec l'option -platform win32-g++ dans le but de le faire fonctionner ;

- compiler qmake grâce au makefile spécialement créé par le patch (C:\qmake\Makefile.win32-msvc2005) ;

- définir QMAKESPEC à win32-msvc2005 afin de pouvoir compiler avec Visual C++ 2005 ;

- lancer qmake pour qu'il génère les makefiles de nmake ;

- lancer nmake pour qu'il compile les makefiles générés précédemment.

En plus de -platform win32-g++, qconfigure.bat transmet systématiquement à votre insu les paramètres -no-qmake et -dont-process dont l'utilité est expliquée en suivant, ainsi que les paramètres -qt-zlib -qt-libmng -qt-libpng -qt-libjpeg dont je n'ai pas pris le temps d'étudier le but étant donné qu'ils me conviennent.

configure est ainsi exécuté de façon minimale afin qu'il se limite effectivement à la stricte configuration de Qt, c'est-à-dire sans compiler qmake (via l'option -no-qmake], car elle échouerait et sans générer les makefiles (-dont-process) puisque qmake n'est pas disponible .configure se limite donc essentiellement à créer les fichiers qtconfig.h et qtconfig.cpp qui conviennent, ainsi qu'à configurer qmake via .qmake.cache.

Une fois ceci effectué, on en a terminé avec la configuration. Reste à créer les makefiles pour nmake via qmake, mais ce dernier n'existe toujours pas. Il faut donc d'abord le compiler, avec VC++ et non MingW. Le patch installe pour cela le makefile qu'il faut, dans C:\qt4\qmake\Makefile.win32-msvc2005. Il suffit de le transmettre à nmake :

cd qmake
nmake -f Makefile.win32-msvc2005
cd ..

pour obtenir au bout de quelques minutes le précieux qmake.

Il ne reste plus alors qu'à définir correctement QMAKESPECet à lancer qmake pour obtenir les makefiles tant attendus.

SET QMAKESPEC=win32-msvc2005
bin\qmake

qmake va alors aller lire le fichier cache .qmake.cache dans son répertoire parent (qui est aussi le répertoire courant). Puis comme aucun fichier projet ne lui est donné en argument, il va regarder dans le répertoire courant (C:\qt4) s'il en trouve un. Et justement il y a un : projects.pro, qui constitue donc le point de départ de la compilation de Qt.

Si l'on ouvre ce fichier, on trouve un passage intéressant à son début :

isEmpty(QT_PROJECTS) {
#  QT_PROJECTS = qmake
QT_PROJECTS += src
!cross_compile:QT_PROJECTS += tools
else:QT_PROJECTS += tools/qtestlib
QT_PROJECTS += demos examples
}
SUBDIRS += $$QT_PROJECTS

Ainsi, si QT_PROJECTS n'est pas défini, les sous-répertoires traités par qmakesont src, tools, demos et examples. Donc, en définissant QT_PROJECTS à src avant l'appel de qmake, on peut restreindre la compilation uniquement au code source de la bibliothèque, ce qui est bien moins long.

Note : ceci ne semble pas documenté, donc sans garantie. Une solution tout aussi simple est de donner en paramètre à qmake le fichier projet à traiter, src/src.pro, ou encore de lancer nmake avec en paramètre le nom de la règle à construire.

V - Personnaliser la compilation

En ce qui me concerne, j'ai l'habitude de travailler ainsi avec les bibliothèques :

- sous forme de dll en version debug pour le développement ;

- sous forme de bibliothèque statique en version release, afin de pouvoir créer un exécutable final autonome (sans aucune dll à livrer avec).

Dans le cas de Qt, en debug comme en release, je souhaite bénéficier :

- des exceptions (-exceptions) ;

- du RTTI (-rtti) ;

- de la STL (-stl).

Ceux susceptibles de varier d'une compilation de Qt à l'autre sont :

- -debug : compiler une version pour le débogage ;

- -release : compiler une version optimisée pour la diffusion ;

- -shared : compiler sous forme de dll ;

- -static : compiler sous forme de bibliothèque statique ;

- -qt-sql-odbc : compiler avec le support d'ODBC ;

- -qt-sql-psql : compiler avec le support natif de PostgreSQL ;

- -no-style-motif : pas de support du style Motif ;

- -no-style-cde : pas de support du style CDE ;

- -no-style-plastique : pas de support du style Plastique ;

- -no-accessibility : pas de support de l'accessibilité (voir Accessibility Classes).

Idéalement, ces derniers paramètres devraient être passés selon les besoins en ligne de commande, et les autres devraient être automatiquement actifs. C'est pourquoi je les ai inclus directement dans mon propre fichier .bat (voir les explications relatives à qconfigure.bat un peu plus haut). Ce fameux fichier .bat sur mesure, le voici :

compileqt.bat

@echo off

REM Parties de la bibliothèque à compiler
if not defined QT_PROJECTS set QT_PROJECTS=src

REM chemin vers le fichier environnement du Platform SDK
set PSDK_SETENV_PATH="%programfiles%\Microsoft Platform SDK\SetEnv.cmd"

REM Tester si nmake est dans le PATH
for %%i in (nmake.exe) do if not "%%~$PATH :i"=="" goto ready_to_compile

REM nmake n'est pas dans le PATH : executer vsvars32.bat

if not defined VS80COMNTOOLS goto vcpp8_not_found
if not exist %PSDK_SETENV_PATH% goto psdk_not_found

call "%VS80COMNTOOLS%vsvars32.bat"
call %PSDK_SETENV_PATH%

:ready_to_compile

REM configurer Qt
configure.exe  -qt-zlib -qt-libmng -qt-libpng -qt-libjpeg -no-qmake -dont-process^
-platform win32-g++  -exceptions -rtti -stl %*
if not ERRORLEVEL 0 goto end

REM Tester si qmake existe
if exist bin\qmake.exe goto start_qmake

REM Sinon, compiler qmake
cd qmake
nmake -f Makefile.win32-msvc2005
cd ..
if not ERRORLEVEL 0 goto end

:start_qmake
set QMAKESPEC=win32-msvc2005
bin\qmake "QT_PROJECTS=%QT_PROJECTS%"
if not ERRORLEVEL 0 goto end

REM compiler avec VC++
nmake

goto end

REM gestion des erreurs

:vcpp8_not_found
echo Visual C++ 2005 ne semble pas installe :
echo La variable d'environnement VS80COMNTOOLS n'existe pas
goto end

:psdk_not_found
echo Le Platform SDK ne semble pas installe:
echo Le fichier %PSDK_SETENV_PATH% n'existe pas

:end

Ce fichier ne compile pas qmake s'il l'a déjà été, et limite par défaut la compilation aux sources de Qt. Il est aussi configuré pour aller chercher vsvars32.bat et le PSDK dans leurs emplacements par défaut définis respectivement par la variable d'environnement VS80COMNTOOLS (qui devrait déjà exister) et la variable PSDK_SETENV_PATH créée au tout début du fichier. Vérifiez que l'emplacement désigné par ces deux variables est correct (un message d'erreur devrait s'afficher si ce n'est pas le cas).
Si la variable d'environnement QT_PROJECTS n'est pas définie, elle est initialisée à src afin de restreindre par défaut la compilation au code source de la bibliothèque uniquement. Les autres valeurs possibles sont tools, demos et examples. Les valeurs choisies sont transmises en argument à qmake lors de son appel.

Pour compiler la version dll debug, il faut l'appeler ainsi :

compileqt -debug -shared

Et pour compiler toute la bibliothèque en version release statique :

set QT_PROJECTS=src tools demos examples
compileqt -release -static

Personnellement, pour la compilation en release, je désactive certaines fonctionnalités afin d'accélérer la compilation et de réduire la taille des exécutables produits :

set QT_PROJECTS=src tools
compileqt -release -static -no-style-motif -no-style-cde -no-style-plastique -no-accessibility

VI - Personnalisation avancée de la compilation

VI-A - Compilation d'une version release sur mesure

Normalement, vous devriez être parvenu à compiler une version release statique de Qt, idéale pour compiler un exécutable final autonome. Enfin presque. Car à y regarder de plus près, si on obtient bien un exécutable qui ne dépend d'aucune dll Qt en release, ce même exécutable reste lié dynamiquement à la bibliothèque C/C++ standard, la CRT. Visual C++ permet en effet de se lier soit statiquement à la CRT, soit dynamiquement. Dans le dernier cas, l'exécutable obtenu est dépendant des dll msvcr80.dll et msvcp80.dll en release, et msvcr80d.dll et msvcp80d.dll en debug.

Vous l'aurez compris, en debug comme en release, Qt est compilé pour utiliser la version dynamique de la CRT, c'est-à-dire au moyen de l'option -MD-MT au lieu de en release. Les programmes obtenus doivent ainsi être distribués accompagnés des dll nécessaires. Mais c'est encore pire dans notre cas, car ces dll ne peuvent pas être simplement copiées dans le même répertoire que votre programme, mais doivent être installées de manière particulière (en tant que shared ou private side-by-side assemblies ce qui nécessite de créer un installeur de type Windows Installer). Consultez Visual C++ Libraries as Shared Side-by-Side Assemblies et Redistributing Visual C++ Files) pour plus de détails.

Pour ne pas s'encombrer d'une telle procédure, pouvoir compiler Qt avec l'option -MT se révèle indispensable.

VI-B - Modification des options de compilation de Visual C++

Cette option est renseignée au niveau du fichier de configuration qmake.conf qui existe en autant de versions qu'il y a de compilateurs supportés. Dans notre cas, il a été créé par le patch, dans le répertoire dédié mkspecs (voir la documentation sur QMAKESPEC) , c'est-à-dire précisément C:\qt4\mkspecs\win32-msvc2005\ en ce qui nous concerne.
Ce fichier de configuration définit la variable QMAKE_CFLAGS_RELEASE, qui est utilisée par qmake pour préciser les options de compilation en release au compilateur utilisé, soit Visual C++ 2005 ici.QMAKE_CFLAGS_RELEASE est définie ainsi :

QMAKE_CFLAGS_RELEASE    = -O2 -MD

Il suffit donc de modifier -MD en -MT pour pouvoir en théorie compiler avec une utilisation statique de la CRT. Tant qu'on y est, on peut aussi spécifier des options supplémentaires, en particulier -GL qui active l'optimisation de l'ensemble du programme. Cette option a un impact sur l'édition de liens (qui est plus longue) et sur le format des fichiers .lib générés (lire la documentation ainsi que l'article Les bienfaits de l'option Whole Program Optimisation de Visual C++ .Net qui traite du sujet).

La nouvelle ligne devient donc :

QMAKE_CFLAGS_RELEASE    = -O2 -MT -GL

Normalement, il ne reste plus qu'à tout recompiler pour avoir le résultat attendu.

VI-C - Modification de qmake

Sauf que la compilation échoue rapidement au niveau de C:\qt4\src\tools\moc\ avec un obscur message d'erreur :

link /NOLOGO /SUBSYSTEM:CONSOLE /incremental:no /OUT :"..\..\..\bin\moc.e
xe" @C:\DOCUME~1\Aurelien\LOCALS~1\Temp\nm672.tmp
moc.obj : MSIL .netmodule or module compiled with /GL found; restarting link wit
h /LTCG; add /LTCG to the link command line to improve linker performance
Generating code
Finished generating code
mt.exe /manifest ..\..\..\bin\moc.exe.manifest /outputresource :..\..\..\
bin\moc.exe;#2
Microsoft (R) Manifest Tool version 5.2.3790.2014
Copyright (c) Microsoft Corporation 2005.
All rights reserved.

..\..\..\bin\moc.exe.manifest : general error c1010070: Failed to load and parse
the manifest. Le fichier spicifii est introuvable.
NMAKE : fatal error U1077: '"C:\Program Files\Microsoft Visual Studio 8\VC\BIN\m
t.exe"' : return code '0x1f'
Stop.
NMAKE : fatal error U1077: '"C:\Program Files\Microsoft Visual Studio 8\VC\BIN\n
make.EXE"' : return code '0x2'
Stop.
NMAKE : fatal error U1077 : 'cd' : return code '0x2'
Stop.
NMAKE : fatal error U1077 : 'cd' : return code '0x2'
Stop.

Pour comprendre, il faut savoir qu'une des nouveautés de Visual C++ 2005 concerne la gestion des manifest, des petits fichiers XML destinés entre autres à gérer les multiples versions d'une même dll (voir Understanding Manifest Generation for C/C++ Programs).

Vous avez déjà été confronté aux manifest si vous avez voulu bénéficier des styles de Windows XP dans vos programmes. Jusque-là, pour le commun des programmeurs, il s'agissait de leur seule utilité. Depuis Visual C++ 2005, tout programme utilisant (entre autres) la CRT de manière dynamique (donc en tant que dll) doit posséder un manifest adapté. C'est pourquoi les dll précédentes ne peuvent pas être simplement copiées dans le même répertoire que l'application qui les utilise.

Créer le bon manifest est une tâche fastidieuse, mais heureusement rendue transparente par Visual C++ qui se charge de générer le manifest qui convient (voir Manifest Generation at the Command Line). Ce manifest est généré par le linker sous forme de fichier séparé en même temps qu'il crée l'exécutable (ou la dll). Conformément au principe des manifest, il lui donne le même nom que l'exécutable (ou que la dll) avec l'extension .manifest en plus (par exemple, VC++ génère pour moc.exe le fichier moc.exe.manifest). Et il faut bien évidemment redistribuer ce fichier séparé en même temps que son exécutable (ou sa dll).

Avoir ce fichier supplémentaire à livrer avec son programme est contraignant. C'est pourquoi il est aussi possible d'embarquer le manifest au sein même de l'exécutable (ou de la dll), dans ses ressources. C'est précisément le rôle d'un nouvel outil apparu avec Visual C++ 2005 : mt.exe. Ce petit programme se charge en quelque sorte de fusionner un fichier manifest donné avec son fichier exécutable (ou autre) associé (voir How to: Embed a Manifest Inside a C/C++ Application).

Et c'est précisément ce petit programme qui échoue dans notre cas, lors de la compilation de moc.exe. Pourquoi ? Tout simplement parce que le fichier manifest qu'on lui demande d'insérer dans moc.exe (qui vient d'être construit avec succès) n'existe pas. Et pour cause : comme nous avons compilé moc.exe avec l'option -MT il n'y en a pas besoin, et Visual C++ ne l'a donc pas créé.

La faute est donc au makefile fourni à nmake. Ce dernier contient en effet les instructions suivantes en ce qui concerne la construction de moc.exe :

C:\qt4\src\tools\moc\Makefile.Release

$(DESTDIR_TARGET) :  $(OBJECTS) $(DESTDIR_TARGET).manifest
$(LINK) $(LFLAGS) /OUT:"$(DESTDIR_TARGET)" @<<
$(OBJECTS) $(LIBS)
<<
mt.exe /manifest $(DESTDIR_TARGET).manifest /outputresource :$(DESTDIR_TARGET);#2

nmake va ainsi d'abord effectuer l'édition de liens pour créer $(DESTDIR_TARGET) qui correspond dans notre cas à C:\qt4\bin\moc.exe, puis va appeler mt.exe en lui demandant d'embarquer le fichier C:\qt4\bin\moc.exe.manifest dans la cible qui vient d'être construite. Or, dans notre cas, comme ce fichier n'existe pas, mt.exe ne doit pas être invoqué.

Plutôt que de se référer à la présence ou non de l'option -MT, il est plus simple de réutiliser le principe présenté dans How to: Embed a Manifest Inside a C/C++ Application, à savoir d'appeler mt.exe seulement si le fichier manifest existe.
Si l'on remplace le passage précédent par :

C:\qt4\src\tools\moc\Makefile.Release

$(DESTDIR_TARGET) :  $(OBJECTS) $(DESTDIR_TARGET).manifest
$(LINK) $(LFLAGS) /OUT:"$(DESTDIR_TARGET)" @<<
$(OBJECTS) $(LIBS)
<<
@if exist $(DESTDIR_TARGET).manifest mt.exe /manifest $(DESTDIR_TARGET).manifest /outputresource:$(DESTDIR_TARGET);#2

et que l'on relance la compilation (en tapant simplement nmake), la compilation de moc.exe réussit sans erreur.

Nous avons donc la solution, le problème concerne sa mise en œuvre, car il faut effectuer cette modification au niveau de tous les makefiles générés par qmake. Il est donc préférable de corriger cela directement au niveau de qmake, ce qui implique de modifier son code source. Ceci est permis par sa licence GPL, à condition de documenter les changements apportés.

Une rapide investigation dans le répertoire C:\qt4\qmake\ fait apparaître que la ligne fautive se trouve dans le fichier dédié à la génération des makefiles pour nmake, soit le fichier C:\qt4\qmake\generators\win32\msvc_nmake.cpp.

Ce fichier ne fait pas partie de Qt/Windows Open Source Edition mais est créé par le patch, ce qui ne modifie rien en ce qui concerne la licence GPL.

En modifiant la ligne 112 :

project->variables() [ "QMAKE_POST_LINK" ] +=
"mt.exe /manifest $(DESTDIR_TARGET).manifest /outputresource:$(DESTDIR_TARGET);#2";

ainsi :

project->variables() [ "QMAKE_POST_LINK" ] +=
"@if exist $(DESTDIR_TARGET).manifest "
"mt.exe /manifest $(DESTDIR_TARGET).manifest /outputresource:$(DESTDIR_TARGET);#2";

qmake générera désormais des makefiles compatibles avec l'option -MT.

Il ne reste plus qu'à tout recompiler, à commencer par qmake. Et pour cela le patch nous a fourni un makefile spécial, qu'il convient aussi de modifier tant qu'à faire afin que qmake utilise les mêmes options -MT et -GL.

Il faut donc éditer le fichier C:\qt4\qmake\Makefile.win32-msvc2005 afin de remplacer :

CFLAGS        = -nologo -Zm200 -GS -wd4996 -O2 -MD -W3 $(DEFINES)
CXXFLAGS      = -nologo -Zm200 -GS -wd4996 -O2 -MD -GR -EHsc -W3 $(DEFINES)

par :

CFLAGS        = -nologo -Zm200 -GS -wd4996 -O2 -MT -GL -W3 $(DEFINES)
CXXFLAGS      = -nologo -Zm200 -GS -wd4996 -O2 -MT -GL -GR -EHsc -W3 $(DEFINES)

et de rajouter devant la ligne

mt.exe /manifest $(DESTDIR_TARGET).manifest /outputresource :$(DESTDIR_TARGET);#2

le test

@if exist $(DESTDIR_TARGET).manifest

Une fois tout ceci effectué, la compilation en -release -static peut être effectuée selon les procédures décrites précédemment.

Le principe d'optimisation de l'option -GL (voir l'article de Gilles Vollant) fait que la phase de compilation est beaucoup plus rapide, au détriment de l'édition de liens qui est bien plus longue et gourmande en mémoire RAM (prévoyez 250 Mo). Ne vous inquiétez donc pas de voir la compilation se figer pendant plusieurs minutes.
Notez aussi que la compilation de qmake est indépendante du reste, et n'est pas soumise aux options passées à configure. Autrement dit, dans notre cas, comme nous avons modifié Makefile.win32-msvc2005, elle sera toujours effectuée en release -MT -GL.

Vous pouvez télécharger les versions complètes modifiées de msvc_nmake.cpp, qmake.conf, et Makefile.win32-msvc2005, ou bien utiliser le patch que je vous propose en fin d'article.

VI-D - Recompilation des bibliothèques tierces

Après tous ces laborieux efforts, on obtient bien de jolis exécutables autonomes. Sauf si l'on utilise le module QtSql. Si l'on a compilé le support d'ODBC, on est lié à la dll odbc32.dll, ce qui n'est pas grave, car il s'agit d'une dll système présente sous tous les Windows (au même titre que kernel32.dll etc.).
Par contre, mauvaise nouvelle, si on a compilé avec le support d'une bibliothèque tierce telle que libpq dans mon cas, on peut être dépendant d'une dll (libpq.dll). Cette dll a été installée avec PostgreSQL, et sa version statique n'est pas fournie. Autrement dit, elle doit être distribuée avec votre application.

Pour s'en passer, il faut veiller à linker avec la version statique si vous la possédez, ou bien dans le cas de PostgreSQL recompiler la bibliothèque libpq afin de l'obtenir. Je ne peux que vous encourager à le faire, surtout que dans mon cas la dll installée par défaut (vraisemblablement compilée avec Visual C++ 6) provoquait des plantages incompréhensibles dans le module QtSql, plantages qui ont disparu après avoir recompilé la bibliothèque avec Visual C++ 2005.

Je ne détaillerai pas la recompilation de la libpq avec Visual C++ dans cet article. Reportez-vous aux liens donnés sur la documentation de PostgreSQL en fin d'article.

VII - Réalisation d'un paquetage prêt à l'emploi

Pour bien terminer cet article, il serait appréciable de pouvoir faire cohabiter à moindres frais les versions compilées en debug et release de la bibliothèque. Souvenez-vous des explications du début sur le rôle de configure au niveau du code source de la bibliothèque : générer le fichier qconfig.h qui convient. Mélanger deux compilations différentes de la bibliothèque (une en debug et l'autre en release) revient donc à fusionner les deux fichiers qconfig.h produits (dans C:\qt4\src\corelib\global\).
Il suffit pour cela d'isoler le contenu spécifique aux versions debug et release au moyen d'un test sur la macro _DEBUG, comme dans l'exemple suivant :

#ifdef _DEBUG

// partie spécifique à la version debug
#ifndef QT_DLL
#define QT_DLL
#endif

#else

// partie spécifique à la version release

#endif

// partie commune aux versions debug et release

De cette manière, il est possible de mixer deux compilations différentes de Qt. C'est ce que j'ai fait dans le but de créer une archive prête à l'emploi. Celle-ci contient :

- le code source de Qt 4.1.1 patché ;

- l'ensemble de la bibliothèque (code source, outils, exemples…) compilé en -release -static -qt-sql-odbc ;

- les modules uniquement compilés en -debug -shared -qt-sql-odbc ;

- la version fusionnée de C:\qt4\src\corelib\global\qconfig.h ainsi que les fichiers qconfig.h et arch\qatomic.h créés dans les répertoires C:\include\Qt\ et C:\include\QtCore\ (ils sont identiques en debug et en release) ;

- les fichiers générés par moc dans le répertoire /tmp/moc/debug_shared lors de la compilation en debug des répertoires corelib, gui, netword, XML, opengl, qt3support, sql et svg situés dans C:\qt4\src\. La présence de ces fichiers évite d'obtenir des erreurs lors du traçage de l'exécution du code source depuis le débogueur en debug ;

- le contenu du répertoire C:\lib\ de chacune des deux compilations ;

- les répertoires C:\bin\, C:\examples\ et C:\demos\ de la version release (sans les fichiers générés par la compilation).

La version debug inclut aussi les fichiers de débogage .pdb qui permettent de naviguer dans le code source de la bibliothèque lors d'une session de débogage.

L'ensemble est compressé dans une archive zip de 250 Mo qu'il convient de décompresser dans le répertoire C:\qt4\ afin que Qt Assistant et Qt Demo localisent les fichiers dont ils ont besoin (car cet emplacement est codé en dur lors de leur compilation). À noter que certains exemples de Qt Demo ne fonctionnent pas, car ils nécessitent une compilation de Qt sous forme de dll (pour les plugins notamment) ou avec le support d'une base de données particulière (SQLite…).

- Télécharger Qt 4.1.1 compilé en debug et en release pour Visual C++ 2005 (250 Mo)

- Télécharger le patch permettant une compilation en release static -MT

- Télécharger le fichier bat pour la compilation de Qt avec Visual C++ 2005

VIII - Liens utiles

L'URL de la documentation de PostgreSQL évolue constamment à cause du numéro de version qui est incrémenté fréquemment, ce qui fait que les liens qui vont suivre seront certainement invalides. Notez donc bien le numéro des chapitres qui sont le Chapitre 28. libpq - Bibliothèque C en ce qui concerne la libpq et le Chapitre 15. Installation sur Windows du seul client en ce qui concerne sa compilation avec Visual C++.
Vous pouvez aussi vous tourner vers la documentation originale en anglais dont l'URL semble plus stable : Chapter 28. libpq - C Library et Chapter 15. Client-Only Installation on Windows.

Si vous n'avez jamais utilisé Visual C++, l'article Démarrer avec Visual C++ 2005 Express peut vous aider à débuter.
Concernant le fonctionnement de l'option -GL, vous pouvez lire l'article de Gilles Vollant consacré à ce sujet : Les bienfaits de l'option Whole Program Optimisation de Visual C++ .Net.

Si vous souhaitez aussi utiliser Qt 4 avec Dev-C++, je vous renvoie vers l'article de Nicolas Joseph : Installer Qt4 sous Windows.