diff options
author | Lars Knoll <lars.knoll@qt.io> | 2016-08-31 15:39:30 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2016-09-16 12:27:59 +0000 |
commit | 8d6f2e7e157d5778e37e54cd7e90c3185970abff (patch) | |
tree | c11af6badcfbbd9525a361d38c827a4b0ac8a87e | |
parent | 31a63b69037c680101bf82a7161d4a0e5184dd86 (diff) |
Add a QT_REQUIRE_CONFIG(feature) macro
This macro expands into a static_assert and can be used to
trigger a compile error if a certain feature is not available
when trying to compile some code.
This is especially useful to protect against accidental inclusion
of headers that implement functionality related to a feature.
Change-Id: I456c55b989ce5f35f3af0e13c1886a85c23dfe29
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
-rwxr-xr-x | bin/syncqt.pl | 48 | ||||
-rw-r--r-- | mkspecs/features/qt_module_headers.prf | 11 | ||||
-rw-r--r-- | src/corelib/global/qglobal.h | 1 |
3 files changed, 47 insertions, 13 deletions
diff --git a/bin/syncqt.pl b/bin/syncqt.pl index b531dce374..0435aead0a 100755 --- a/bin/syncqt.pl +++ b/bin/syncqt.pl @@ -200,9 +200,11 @@ sub shouldMasterInclude { } ###################################################################### -# Syntax: classNames(iheader, clean) +# Syntax: classNames(iheader, clean, requires) # Params: iheader, string, filename to parse for classname "symlinks" # (out) clean, boolean, will be set to false if the header isn't clean +# (out) requires, string, will be set to non-empty if the header +# requires a feature # # Purpose: Scans through iheader to find all classnames that should be # synced into library's include structure. @@ -210,8 +212,9 @@ sub shouldMasterInclude { ###################################################################### sub classNames { my @ret; - my ($iheader, $clean) = @_; + my ($iheader, $clean, $requires) = @_; $$clean = 1; + $$requires = ""; my $ihdrbase = basename($iheader); my $classname = $classnames{$ihdrbase}; @@ -236,6 +239,7 @@ sub classNames { $line .= ";" if($line =~ m/^QT_(BEGIN|END)_NAMESPACE(_[A-Z]+)*[\r\n]*$/); #qt macro $line .= ";" if($line =~ m/^QT_MODULE\(.*\)[\r\n]*$/); # QT_MODULE macro $line .= ";" if($line =~ m/^QT_WARNING_(PUSH|POP|DISABLE_\w+\(.*\))[\r\n]*$/); # qt macros + $$requires = $1 if ($line =~ m/^QT_REQUIRE_CONFIG\((.*)\);[\r\n]*$/); $parsable .= " " . $line; } } @@ -706,6 +710,21 @@ sub isQpaHeader return 0; } +sub globosort($$) { + my ($a, $b) = @_; + if ($a =~ /^q(.*)global\.h$/) { + my $sa = $1; + if ($b =~ /^q(.*)global\.h$/) { + my $sb = $1; + # Compare stems so qglobal.h (empty stem) is first: + return $sa cmp $sb; + } + return -1; # $a is global, so before $b + } + return +1 if $b =~ /^q.*global\.h$/; # $a not global, so after $b + return $a cmp $b; +} + # check if this is an in-source build, and if so use that as the basedir too $basedir = locateSyncProfile($out_basedir); if ($basedir) { @@ -914,10 +933,7 @@ foreach my $lib (@modules_to_sync) { my $pri_clean_files = ""; my $libcapitals = uc($lib); - my $master_contents = - "#ifndef QT_".$libcapitals."_MODULE_H\n" . - "#define QT_".$libcapitals."_MODULE_H\n" . - "#include <$lib/${lib}Depends>\n"; + my %master_contents = (); #remove the old files if($remove_stale) { @@ -1017,6 +1033,7 @@ foreach my $lib (@modules_to_sync) { } my $clean_header; + my $requires; my $iheader = $subdir . "/" . $header; $iheader =~ s/^\Q$basedir\E/$out_basedir/ if ($shadow); if ($check_includes) { @@ -1025,7 +1042,7 @@ foreach my $lib (@modules_to_sync) { && $header =~ /_p\.h$/ && $subdir !~ /3rdparty/; check_header($lib, $header, $iheader, $public_header, $private_header); } - my @classes = $public_header && (!$minimal && $is_qt) ? classNames($iheader, \$clean_header) : (); + my @classes = $public_header && (!$minimal && $is_qt) ? classNames($iheader, \$clean_header, \$requires) : (); if($showonly) { print "$header [$lib]\n"; foreach(@classes) { @@ -1059,7 +1076,7 @@ foreach my $lib (@modules_to_sync) { my $injection = ""; if($public_header) { #put it into the master file - $master_contents .= "#include \"$public_header\"\n" if (!$shadow && shouldMasterInclude($iheader)); + $master_contents{$public_header} = $requires if (!$shadow && shouldMasterInclude($iheader)); #deal with the install directives foreach my $class (@classes) { @@ -1074,7 +1091,7 @@ foreach my $lib (@modules_to_sync) { $injection .= ":$class"; } $pri_install_files.= "$pri_install_iheader ";; - $pri_clean_files .= "$pri_install_iheader " if ($clean_header); + $pri_clean_files .= "$pri_install_iheader".($requires ? ":".$requires : "")." " if ($clean_header); } elsif ($qpa_header) { $pri_install_qpafiles.= "$pri_install_iheader ";; @@ -1113,8 +1130,17 @@ foreach my $lib (@modules_to_sync) { } } - # close the master include: - $master_contents .= + # populate the master include: + my $master_contents = + "#ifndef QT_".$libcapitals."_MODULE_H\n" . + "#define QT_".$libcapitals."_MODULE_H\n" . + "#include <$lib/${lib}Depends>\n" . + join("", map { + my $rq = $master_contents{$_}; + ($rq ? "#if QT_CONFIG($rq)\n" : "") . + "#include \"$_\"\n" . + ($rq ? "#endif\n" : "") + } sort globosort keys %master_contents) . "#include \"".lc($lib)."version.h\"\n" . "#endif\n"; diff --git a/mkspecs/features/qt_module_headers.prf b/mkspecs/features/qt_module_headers.prf index 229760068e..5a45007820 100644 --- a/mkspecs/features/qt_module_headers.prf +++ b/mkspecs/features/qt_module_headers.prf @@ -243,16 +243,23 @@ headersclean:!internal_module { } !isEmpty(hcleanCOMMAND):if(!qtConfig(debug_and_release)|CONFIG(release, debug|release)) { + CLEAN_HEADERS = + for (h, SYNCQT.CLEAN_HEADER_FILES) { + hh = $$split(h, :) + hr = $$member(hh, 1) + isEmpty(hr)|qtConfig($$hr): \ + CLEAN_HEADERS += $$member(hh, 0) + } + CLEAN_HEADERS -= $$HEADERSCLEAN_EXCLUDE 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.input = CLEAN_HEADERS 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) diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index f11ec127e0..dd8b6110b3 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -78,6 +78,7 @@ 1: The feature is available */ #define QT_CONFIG(feature) (1/QT_FEATURE_##feature == 1) +#define QT_REQUIRE_CONFIG(feature) Q_STATIC_ASSERT_X(QT_FEATURE_##feature == 1, "Required feature " #feature " for file " __FILE__ " not vailable.") #if QT_VERSION >= QT_VERSION_CHECK(6,0,0) # define QT_NO_UNSHARABLE_CONTAINERS |