From 6668f5becfb8fcb6d10e42495c6ea5cdba2d15c5 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Sat, 22 Dec 2012 13:12:02 -0800 Subject: [PATCH] Implement a more direct headersclean check Test each include file directly, instead of doing a large #include. This verifies that each header is compilable on its own. One big advantage of doing it via a special compiler in qmake is that we skip pre-compiled headers, which has hidden build errors in the past. This solution is implemented by making syncqt produce a second list of headers. This list is the same as the list of headers in the source code to be installed, minus the headers that declare themselves to be unclean, via the pragma: #pragma qt_sync_skip_header_check This mechanism is applied only for public libraries (skipping QtPlatformSupport, an internal_module). This test is enabled only for -developer-builds of Qt because it increases the compilation time. On QtTest: the library only links to QtCore, but it has two headers that provide inline-only functionality by including QtGui and QtWidgets headers (namely, qtest_gui.h and qtest_widget.h). If those two modules aren't getting compiled due to -no-gui or -no-widgets to configure, we need to remove the respective headers from the list of headers to be checked. If they are being built, then we need to make QtTest's build wait for the headers to be generated and that happens when qmake is first run inside the src/gui and src/widgets directories. Change-Id: I57d64bd697a92367c8464c073a42e4d142a9a15f Reviewed-by: Oswald Buddenhagen --- bin/syncqt.pl | 13 +++- configure | 20 +++++- mkspecs/features/data/dummy.cpp | 2 + mkspecs/features/qt_module.prf | 10 +-- mkspecs/features/qt_module_headers.prf | 86 ++++++++++++++++++++++++++ src/src.pro | 4 +- src/testlib/testlib.pro | 5 ++ tools/configure/configureapp.cpp | 10 ++- 8 files changed, 138 insertions(+), 12 deletions(-) create mode 100644 mkspecs/features/data/dummy.cpp diff --git a/bin/syncqt.pl b/bin/syncqt.pl index 3bee0175fff..3b3e127e86e 100755 --- a/bin/syncqt.pl +++ b/bin/syncqt.pl @@ -190,8 +190,9 @@ sub shouldMasterInclude { } ###################################################################### -# Syntax: classNames(iheader) +# Syntax: classNames(iheader, clean) # Params: iheader, string, filename to parse for classname "symlinks" +# (out) clean, boolean, will be set to false if the header isn't clean # # Purpose: Scans through iheader to find all classnames that should be # synced into library's include structure. @@ -199,7 +200,8 @@ sub shouldMasterInclude { ###################################################################### sub classNames { my @ret; - my ($iheader) = @_; + my ($iheader, $clean) = @_; + $$clean = 1; my $ihdrbase = basename($iheader); my $classname = $classnames{$ihdrbase}; @@ -212,6 +214,7 @@ sub classNames { chomp $line; chop $line if ($line =~ /\r$/); if($line =~ /^\#/) { + $$clean = 0 if ($line =~ m/^#pragma qt_sync_skip_header_check/); return @ret if($line =~ m/^#pragma qt_sync_stop_processing/); push(@ret, $1) if($line =~ m/^#pragma qt_class\(([^)]*)\)[\r\n]*$/); $line = 0; @@ -828,6 +831,7 @@ foreach my $lib (@modules_to_sync) { my $pri_install_pfiles = ""; my $pri_install_qpafiles = ""; my $pri_injections = ""; + my $pri_clean_files = ""; my $libcapitals = uc($lib); my $master_contents = @@ -929,9 +933,10 @@ foreach my $lib (@modules_to_sync) { } } + my $clean_header; my $iheader = $subdir . "/" . $header; $iheader =~ s/^\Q$basedir\E/$out_basedir/ if ($shadow); - my @classes = $public_header && (!$minimal && $is_qt) ? classNames($iheader) : (); + my @classes = $public_header && (!$minimal && $is_qt) ? classNames($iheader, \$clean_header) : (); if($showonly) { print "$header [$lib]\n"; foreach(@classes) { @@ -980,6 +985,7 @@ foreach my $lib (@modules_to_sync) { $injection .= ":$class"; } $pri_install_files.= "$pri_install_iheader ";; + $pri_clean_files .= "$pri_install_iheader " if ($clean_header); } elsif ($qpa_header) { $pri_install_qpafiles.= "$pri_install_iheader ";; @@ -1120,6 +1126,7 @@ foreach my $lib (@modules_to_sync) { $headers_pri_contents .= "SYNCQT.HEADER_CLASSES = $pri_install_classes\n"; $headers_pri_contents .= "SYNCQT.PRIVATE_HEADER_FILES = $pri_install_pfiles\n"; $headers_pri_contents .= "SYNCQT.QPA_HEADER_FILES = $pri_install_qpafiles\n"; + $headers_pri_contents .= "SYNCQT.CLEAN_HEADER_FILES = $pri_clean_files\n"; $headers_pri_contents .= "SYNCQT.INJECTIONS = $pri_injections\n"; my $headers_pri_file = "$out_basedir/include/$lib/headers.pri"; writeFile($headers_pri_file, $headers_pri_contents, $lib, "headers.pri file"); diff --git a/configure b/configure index 4addf22896d..03421dfc4c4 100755 --- a/configure +++ b/configure @@ -762,6 +762,7 @@ QPA_PLATFORM_GUARD=yes CFG_CXX11=auto CFG_DIRECTWRITE=no CFG_WERROR=auto +CFG_HEADERSCLEAN=auto CFG_QREAL=double OPT_MAC_SDK= COMMERCIAL_USER=ask @@ -2240,6 +2241,13 @@ while [ "$#" -gt 0 ]; do UNKNOWN_OPT=yes fi ;; + headersclean) + if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then + CFG_HEADERSCLEAN="$VAL" + else + UNKNOWN_OPT=yes + fi + ;; xkb-config-root) CFG_XKB_CONFIG_ROOT="$VAL" ;; @@ -6712,8 +6720,16 @@ if [ "$CFG_DEV" = "yes" ]; then if [ "$CFG_WERROR" != "no" ]; then QMAKE_CONFIG="$QMAKE_CONFIG warnings_are_errors" fi -elif [ "$CFG_WERROR" = "yes" ]; then - QMAKE_CONFIG="$QMAKE_CONFIG warnings_are_errors" + if [ "$CFG_HEADERSCLEAN" != "no" ]; then + QMAKE_CONFIG="$QMAKE_CONFIG headersclean" + fi +else + if [ "$CFG_WERROR" = "yes" ]; then + QMAKE_CONFIG="$QMAKE_CONFIG warnings_are_errors" + fi + if [ "$CFG_HEADERSCLEAN" = "yes" ]; then + QMAKE_CONFIG="$QMAKE_CONFIG headersclean" + fi fi cat >>"$QTCONFIG.tmp" < and violate the standards. + hcleanFLAGS = -WX -W3 -wd4180 -wd4458 + hcleanCOMMAND = $$QMAKE_CXX -c $(CXXFLAGS) $$hcleanFLAGS $(INCPATH) $$hcleanDEFS -FI${QMAKE_FILE_IN} -Fo${QMAKE_FILE_OUT} \ + $$[QT_INSTALL_DATA/src]/mkspecs/features/data/dummy.cpp + } + + !isEmpty(hcleanCOMMAND):if(!contains(QT_CONFIG, debug_and_release)|CONFIG(release, debug|release)) { + header_check.dependency_type = TYPE_C + header_check.CONFIG += no_link + header_check.output = ${QMAKE_VAR_OBJECTS_DIR}header_${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)} + header_check.input = SYNCQT.CLEAN_HEADER_FILES + header_check.variable_out = PRE_TARGETDEPS + header_check.name = headercheck ${QMAKE_FILE_IN} + header_check.commands = $$hcleanCOMMAND + silent:header_check.commands = @echo compiling[header] ${QMAKE_FILE_IN} && $$hcleanCOMMAND + QMAKE_EXTRA_COMPILERS += header_check + SYNCQT.CLEAN_HEADER_FILES -= $$HEADERSCLEAN_EXCLUDE + } + unset(hcleanCOMMAND) + unset(hcleanFLAGS) + unset(hcleanDEFS) +} diff --git a/src/src.pro b/src/src.pro index fcdc6c32e0b..b4d62aa8b0c 100644 --- a/src/src.pro +++ b/src/src.pro @@ -81,7 +81,7 @@ src_network.depends = src_corelib src_testlib.subdir = $$PWD/testlib src_testlib.target = sub-testlib -src_testlib.depends = src_corelib # src_gui & src_widgets are not build-depends +src_testlib.depends = src_corelib # testlib links only to corelib, but see below for the headers src_3rdparty_pcre.subdir = $$PWD/3rdparty/pcre src_3rdparty_pcre.target = sub-3rdparty-pcre @@ -166,10 +166,12 @@ contains(QT_CONFIG, concurrent):SUBDIRS += src_concurrent SUBDIRS += src_gui src_platformsupport src_platformheaders contains(QT_CONFIG, opengl(es2)?):SUBDIRS += src_openglextensions src_plugins.depends += src_gui src_platformsupport src_platformheaders + src_testlib.depends += src_gui # if QtGui is enabled, QtTest requires QtGui's headers !contains(QT_CONFIG, no-widgets) { SUBDIRS += src_tools_uic src_widgets TOOLS += src_tools_uic src_plugins.depends += src_widgets + src_testlib.depends += src_widgets # if QtWidgets is enabled, QtTest requires QtWidgets's headers contains(QT_CONFIG, opengl(es2)?) { SUBDIRS += src_opengl src_plugins.depends += src_opengl diff --git a/src/testlib/testlib.pro b/src/testlib/testlib.pro index 841d9131059..83f217dde9b 100644 --- a/src/testlib/testlib.pro +++ b/src/testlib/testlib.pro @@ -97,4 +97,9 @@ mac { } } +# Exclude these headers from the clean check if their dependencies aren't +# being built +contains(QT_CONFIG, no-widgets): HEADERSCLEAN_EXCLUDE += qtest_widgets.h +contains(QT_CONFIG, no-gui): HEADERSCLEAN_EXCLUDE += qtest_gui.h + load(qt_module) diff --git a/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp index 04909525d11..c0e98507c7a 100644 --- a/tools/configure/configureapp.cpp +++ b/tools/configure/configureapp.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. -** Copyright (C) 2013 Intel Corporation +** Copyright (C) 2014 Intel Corporation ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the tools applications of the Qt Toolkit. @@ -927,6 +927,10 @@ void Configure::parseCmdLine() dictionary[ "WERROR" ] = "yes"; } else if (configCmdLine.at(i) == "-no-warnings-are-errors") { dictionary[ "WERROR" ] = "no"; + } else if (configCmdLine.at(i) == "-no-headersclean") { + dictionary[ "HEADERSCLEAN" ] = "no"; + } else if (configCmdLine.at(i) == "-headersclean") { + dictionary[ "HEADERSCLEAN" ] = "yes"; } else if (configCmdLine.at(i) == "-no-eventfd") { dictionary[ "QT_EVENTFD" ] = "no"; } else if (configCmdLine.at(i) == "-eventfd") { @@ -1459,9 +1463,13 @@ void Configure::parseCmdLine() qtConfig << "private_tests"; if (dictionary["WERROR"] != "no") qmakeConfig << "warnings_are_errors"; + if (dictionary["HEADERSCLEAN"] != "no") + qmakeConfig << "headersclean"; } else { if (dictionary["WERROR"] == "yes") qmakeConfig << "warnings_are_errors"; + if (dictionary["HEADERSCLEAN"] == "yes") + qmakeConfig << "headersclean"; } if (dictionary["FORCE_ASSERTS"] == "yes")