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>
This commit is contained in:
Lars Knoll 2016-08-31 15:39:30 +02:00
parent 31a63b6903
commit 8d6f2e7e15
3 changed files with 47 additions and 13 deletions

View File

@ -200,9 +200,11 @@ sub shouldMasterInclude {
} }
###################################################################### ######################################################################
# Syntax: classNames(iheader, clean) # Syntax: classNames(iheader, clean, requires)
# Params: iheader, string, filename to parse for classname "symlinks" # Params: iheader, string, filename to parse for classname "symlinks"
# (out) clean, boolean, will be set to false if the header isn't clean # (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 # Purpose: Scans through iheader to find all classnames that should be
# synced into library's include structure. # synced into library's include structure.
@ -210,8 +212,9 @@ sub shouldMasterInclude {
###################################################################### ######################################################################
sub classNames { sub classNames {
my @ret; my @ret;
my ($iheader, $clean) = @_; my ($iheader, $clean, $requires) = @_;
$$clean = 1; $$clean = 1;
$$requires = "";
my $ihdrbase = basename($iheader); my $ihdrbase = basename($iheader);
my $classname = $classnames{$ihdrbase}; 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_(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_MODULE\(.*\)[\r\n]*$/); # QT_MODULE macro
$line .= ";" if($line =~ m/^QT_WARNING_(PUSH|POP|DISABLE_\w+\(.*\))[\r\n]*$/); # qt macros $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; $parsable .= " " . $line;
} }
} }
@ -706,6 +710,21 @@ sub isQpaHeader
return 0; 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 # check if this is an in-source build, and if so use that as the basedir too
$basedir = locateSyncProfile($out_basedir); $basedir = locateSyncProfile($out_basedir);
if ($basedir) { if ($basedir) {
@ -914,10 +933,7 @@ foreach my $lib (@modules_to_sync) {
my $pri_clean_files = ""; my $pri_clean_files = "";
my $libcapitals = uc($lib); my $libcapitals = uc($lib);
my $master_contents = my %master_contents = ();
"#ifndef QT_".$libcapitals."_MODULE_H\n" .
"#define QT_".$libcapitals."_MODULE_H\n" .
"#include <$lib/${lib}Depends>\n";
#remove the old files #remove the old files
if($remove_stale) { if($remove_stale) {
@ -1017,6 +1033,7 @@ foreach my $lib (@modules_to_sync) {
} }
my $clean_header; my $clean_header;
my $requires;
my $iheader = $subdir . "/" . $header; my $iheader = $subdir . "/" . $header;
$iheader =~ s/^\Q$basedir\E/$out_basedir/ if ($shadow); $iheader =~ s/^\Q$basedir\E/$out_basedir/ if ($shadow);
if ($check_includes) { if ($check_includes) {
@ -1025,7 +1042,7 @@ foreach my $lib (@modules_to_sync) {
&& $header =~ /_p\.h$/ && $subdir !~ /3rdparty/; && $header =~ /_p\.h$/ && $subdir !~ /3rdparty/;
check_header($lib, $header, $iheader, $public_header, $private_header); 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) { if($showonly) {
print "$header [$lib]\n"; print "$header [$lib]\n";
foreach(@classes) { foreach(@classes) {
@ -1059,7 +1076,7 @@ foreach my $lib (@modules_to_sync) {
my $injection = ""; my $injection = "";
if($public_header) { if($public_header) {
#put it into the master file #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 #deal with the install directives
foreach my $class (@classes) { foreach my $class (@classes) {
@ -1074,7 +1091,7 @@ foreach my $lib (@modules_to_sync) {
$injection .= ":$class"; $injection .= ":$class";
} }
$pri_install_files.= "$pri_install_iheader ";; $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) { elsif ($qpa_header) {
$pri_install_qpafiles.= "$pri_install_iheader ";; $pri_install_qpafiles.= "$pri_install_iheader ";;
@ -1113,8 +1130,17 @@ foreach my $lib (@modules_to_sync) {
} }
} }
# close the master include: # populate the master include:
$master_contents .= 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" . "#include \"".lc($lib)."version.h\"\n" .
"#endif\n"; "#endif\n";

View File

@ -243,16 +243,23 @@ headersclean:!internal_module {
} }
!isEmpty(hcleanCOMMAND):if(!qtConfig(debug_and_release)|CONFIG(release, debug|release)) { !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.dependency_type = TYPE_C
header_check.CONFIG += no_link header_check.CONFIG += no_link
header_check.output = ${QMAKE_VAR_OBJECTS_DIR}header_${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)} 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.variable_out = PRE_TARGETDEPS
header_check.name = headercheck ${QMAKE_FILE_IN} header_check.name = headercheck ${QMAKE_FILE_IN}
header_check.commands = $$hcleanCOMMAND header_check.commands = $$hcleanCOMMAND
silent:header_check.commands = @echo compiling[header] ${QMAKE_FILE_IN} && $$hcleanCOMMAND silent:header_check.commands = @echo compiling[header] ${QMAKE_FILE_IN} && $$hcleanCOMMAND
QMAKE_EXTRA_COMPILERS += header_check QMAKE_EXTRA_COMPILERS += header_check
SYNCQT.CLEAN_HEADER_FILES -= $$HEADERSCLEAN_EXCLUDE
} }
unset(hcleanCOMMAND) unset(hcleanCOMMAND)
unset(hcleanFLAGS) unset(hcleanFLAGS)

View File

@ -78,6 +78,7 @@
1: The feature is available 1: The feature is available
*/ */
#define QT_CONFIG(feature) (1/QT_FEATURE_##feature == 1) #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) #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
# define QT_NO_UNSHARABLE_CONTAINERS # define QT_NO_UNSHARABLE_CONTAINERS