-
Notifications
You must be signed in to change notification settings - Fork 106
Qt header discovery & framework support; vendor simplecpp update #302
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d9df6e0
b67548d
e89a171
286942f
4d93e7a
dd42ec8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -58,6 +58,7 @@ | |
#include <QDir> | ||
#include <QFileInfo> | ||
#include <QFile> | ||
#include <QLibraryInfo> | ||
#include <QTextStream> | ||
#include <QRegularExpression> | ||
|
||
|
@@ -67,77 +68,162 @@ void displayHelp(GeneratorSet *generatorSet); | |
|
||
namespace | ||
{ | ||
static const QStringList candidatesQtModules { | ||
QStringLiteral("QtCore"), | ||
QStringLiteral("QtGui"), | ||
QStringLiteral("QtNetwork"), | ||
QStringLiteral("QtOpenGL"), | ||
QStringLiteral("QtXml"), | ||
}; | ||
|
||
static bool isDir(const QString& p) { return QFileInfo(p).isDir(); } | ||
static QString joinPath(const QString& base, const QString& child) { | ||
return QDir(base).filePath(child); | ||
} | ||
#if QT_VERSION < QT_VERSION_CHECK(5,10,0) | ||
QString qEnvironmentVariable(const char *varName){ | ||
return QString::fromLocal8Bit(qgetenv(varName)); | ||
} | ||
#endif | ||
|
||
QStringList getIncludeDirectories(const QString &commandLineIncludes) | ||
{ | ||
QStringList includes; | ||
includes << QString("."); | ||
|
||
QChar pathSplitter = QDir::listSeparator(); | ||
includes << QStringLiteral("."); | ||
|
||
// Environment PYTHONQT_INCLUDE | ||
QString includePath = getenv("PYTHONQT_INCLUDE"); | ||
if (!includePath.isEmpty()) | ||
includes += includePath.split(pathSplitter, Qt::SkipEmptyParts); | ||
const QChar pathSplitter = QDir::listSeparator(); | ||
|
||
// Includes from the command line | ||
if (!commandLineIncludes.isEmpty()) | ||
includes += commandLineIncludes.split(pathSplitter, Qt::SkipEmptyParts); | ||
for (auto it = includes.begin(); it != includes.end();) | ||
{ | ||
if (!QDir(*it).exists()) | ||
{ | ||
qWarning() << "Include path " << it->toUtf8() << " does not exist, ignoring it."; | ||
it = includes.erase(it); | ||
// From env var PYTHONQT_INCLUDE | ||
const QString envInclude = qEnvironmentVariable("PYTHONQT_INCLUDE"); | ||
if (!envInclude.isEmpty()) { | ||
QStringList envIncludes = envInclude.split(pathSplitter, Qt::SkipEmptyParts); | ||
for(const QString& include: qAsConst(envIncludes)) { | ||
if (isDir(include)) { | ||
includes << include; | ||
} | ||
else { | ||
qWarning() << "Include path" << include << "does not exist, ignoring."; | ||
} | ||
} | ||
else | ||
{ | ||
++it; | ||
} | ||
|
||
// CLI-provided include paths | ||
if (!commandLineIncludes.isEmpty()) { | ||
const QStringList cliIncludes = commandLineIncludes.split(QDir::listSeparator(), Qt::SkipEmptyParts); | ||
for (const QString& include : cliIncludes) { | ||
if (isDir(include)) { | ||
includes << QDir::cleanPath(include); | ||
} | ||
else { | ||
qWarning() << "Include path" << include << "does not exist, ignoring."; | ||
} | ||
} | ||
} | ||
|
||
// Include Qt | ||
QString qtdir = getenv("QTDIR"); | ||
if (qtdir.isEmpty() || !QDir(qtdir).exists(qtdir)) | ||
{ | ||
QString reason = "The QTDIR environment variable " + qtdir.isEmpty() ? | ||
"is not set. " : "points to a non-existing directory. "; | ||
#if defined(Q_OS_MAC) | ||
qWarning() << reason << "Assuming standard binary install using frameworks."; | ||
QString frameworkDir = "/Library/Frameworks"; | ||
includes << (frameworkDir + "/QtXml.framework/Headers"); | ||
includes << (frameworkDir + "/QtNetwork.framework/Headers"); | ||
includes << (frameworkDir + "/QtCore.framework/Headers"); | ||
includes << (frameworkDir + "/QtGui.framework/Headers"); | ||
includes << (frameworkDir + "/QtOpenGL.framework/Headers"); | ||
includes << frameworkDir; | ||
#else | ||
qWarning() << reason << "This may cause problems with finding the necessary include files."; | ||
#endif | ||
// Prefer QLibraryInfo (works without QTDIR) | ||
QString qtInclude = QLibraryInfo::location(QLibraryInfo::HeadersPath); | ||
if (!isDir(qtInclude)) { | ||
// Fallback to QTDIR/include | ||
const QString qtDir = qEnvironmentVariable("QTDIR"); | ||
jcfr marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+123
to
+127
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I dislike a little bit the fact that now we can't generate the bindings for a different Qt version than the version with which the generator was built. Sure, this is a little bit of a corner case, but I know that I have used this at some time during development. Could we get another override mechanism for the Qt include directory? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Very sensible point. This is an oversight & I will revisit. |
||
if (qtDir.isEmpty() || !isDir(qtDir)) { | ||
const QString reason = QStringLiteral("The QTDIR environment variable ") | ||
+ (qtDir.isEmpty() | ||
? "is not set." | ||
: "points to a non-existing directory."); | ||
qWarning() << reason << "This may cause problems with finding the necessary include files."; | ||
} else { | ||
qtInclude = joinPath(qtDir, QStringLiteral("include")); | ||
} | ||
} | ||
else | ||
{ | ||
std::cout << "-------------------------------------------------------------" << std::endl; | ||
std::cout << "Using QT at: " << qtdir.toLocal8Bit().constData() << std::endl; | ||
std::cout << "-------------------------------------------------------------" << std::endl; | ||
qtdir += "/include"; | ||
includes << (qtdir + "/QtXml"); | ||
includes << (qtdir + "/QtNetwork"); | ||
includes << (qtdir + "/QtCore"); | ||
includes << (qtdir + "/QtGui"); | ||
includes << (qtdir + "/QtOpenGL"); | ||
includes << qtdir; | ||
if (!qtInclude.isEmpty() && isDir(qtInclude)) { | ||
qInfo() << "Using Qt headers at:" << qtInclude; | ||
// Check for <qtInclude>/<Module> | ||
for (const QString& qtModule : qAsConst(candidatesQtModules)) { | ||
const QString qtModuleInclude = joinPath(qtInclude, qtModule); | ||
if (isDir(qtModuleInclude)) { | ||
includes << qtModuleInclude; | ||
} | ||
} | ||
includes << qtInclude; | ||
} | ||
|
||
return includes; | ||
} | ||
|
||
QStringList getFrameworkDirectories(const QString &commandLineFrameworks) | ||
{ | ||
QStringList frameworks; | ||
frameworks << QStringLiteral("."); | ||
|
||
const QChar pathSplitter = QDir::listSeparator(); | ||
|
||
// From env var PYTHONQT_FRAMEWORK | ||
const QString envFramework = qEnvironmentVariable("PYTHONQT_FRAMEWORK"); | ||
if (!envFramework.isEmpty()) { | ||
QStringList envFrameworks = envFramework.split(pathSplitter, Qt::SkipEmptyParts); | ||
for(const QString& framework: qAsConst(envFrameworks)) { | ||
if (isDir(framework)) { | ||
frameworks << framework; | ||
} | ||
else { | ||
qWarning() << "Framework path" << framework << "does not exist, ignoring."; | ||
} | ||
} | ||
} | ||
|
||
// CLI-provided framework paths | ||
if (!commandLineFrameworks.isEmpty()) { | ||
const QStringList cliFrameworks = commandLineFrameworks.split(QDir::listSeparator(), Qt::SkipEmptyParts); | ||
for (const QString& framework : cliFrameworks) { | ||
if (isDir(framework)) { | ||
frameworks << QDir::cleanPath(framework); | ||
} | ||
else { | ||
qWarning() << "Framework path" << framework << "does not exist, ignoring."; | ||
} | ||
} | ||
} | ||
|
||
// Prefer QLibraryInfo (works without QTDIR) | ||
QString qtLib = QLibraryInfo::location(QLibraryInfo::LibrariesPath); | ||
if (!isDir(qtLib)) { | ||
// Fallback to QTDIR/include | ||
const QString qtDir = qEnvironmentVariable("QTDIR"); | ||
if (qtDir.isEmpty() || !isDir(qtDir)) { | ||
const QString reason = QStringLiteral("The QTDIR environment variable ") | ||
+ (qtDir.isEmpty() | ||
? "is not set." | ||
: "points to a non-existing directory."); | ||
qWarning() << reason << "This may cause problems with finding the necessary framework files."; | ||
} else { | ||
qtLib = joinPath(qtDir, QStringLiteral("lib")); | ||
} | ||
} | ||
if (!qtLib.isEmpty() && isDir(qtLib)) { | ||
qInfo() << "Using Qt frameworks at:" << qtLib; | ||
// Check for <lib>/<Module>.framework bundles | ||
for (const QString& qtModule : qAsConst(candidatesQtModules)) { | ||
const QString qtModuleFramework = QDir(qtLib).filePath(qtModule + ".framework"); | ||
if (QDir(qtModuleFramework).exists()) { | ||
frameworks << qtModuleFramework; | ||
} | ||
} | ||
frameworks << qtLib; | ||
} | ||
|
||
return frameworks; | ||
} | ||
|
||
bool | ||
preprocess(const QString& sourceFile, const QString& targetFile, const QStringList& includePaths) | ||
preprocess(const QString& sourceFile, const QString& targetFile, const QStringList& includePaths, const QStringList& frameworkPaths) | ||
{ | ||
simplecpp::DUI dui; // settings | ||
|
||
for(QString include : includePaths) { | ||
dui.includePaths.push_back(QDir::toNativeSeparators(include).toStdString()); | ||
for(const QString& include : includePaths) { | ||
dui.addIncludePath(QDir::toNativeSeparators(include).toStdString()); | ||
} | ||
for(const QString& framework : frameworkPaths) { | ||
dui.addFrameworkPath(QDir::toNativeSeparators(framework).toStdString()); | ||
} | ||
dui.defines.push_back("__cplusplus=1"); | ||
dui.defines.push_back("__STDC__"); | ||
|
@@ -217,15 +303,36 @@ namespace | |
return true; | ||
} | ||
|
||
unsigned int getQtVersion(const QStringList& includePaths) | ||
unsigned int getQtVersion(const QStringList& paths) | ||
{ | ||
QRegularExpression re("#define\\s+QTCORE_VERSION\\s+0x([0-9a-f]+)", QRegularExpression::CaseInsensitiveOption); | ||
for (const QString &includeDir: includePaths) | ||
|
||
// Iterate through provided paths to find the qtcoreversion.h header file | ||
for (const QString &path: paths) | ||
{ | ||
QFileInfo fi(QDir(includeDir), "qtcoreversion.h"); | ||
if (fi.exists()) | ||
#ifdef Q_OS_MACOS | ||
// Define potential locations for the header file on macOS | ||
const QStringList candidates = { | ||
joinPath(path, "Versions/A/Headers/qtcoreversion.h"), | ||
joinPath(path, "Versions/5/Headers/qtcoreversion.h"), | ||
joinPath(path, "Headers/qtcoreversion.h"), // top-level Headers (often a symlink into Versions) | ||
}; | ||
#else | ||
// Define the location for other platforms | ||
const QStringList candidates = { joinPath(path, "qtcoreversion.h") }; | ||
#endif | ||
// Pick the first existing candidate | ||
QString qtCoreVersionHeader; | ||
for (const QString& candidate : candidates) { | ||
if (QFile::exists(candidate)) { | ||
qtCoreVersionHeader = candidate; | ||
break; | ||
} | ||
} | ||
|
||
if (QFile::exists(qtCoreVersionHeader)) | ||
{ | ||
QString filePath = fi.absoluteFilePath(); | ||
QString filePath = QFileInfo(qtCoreVersionHeader).absoluteFilePath(); | ||
QFile f(filePath); | ||
if (f.open(QIODevice::ReadOnly)) | ||
{ | ||
|
@@ -249,7 +356,7 @@ namespace | |
} | ||
} | ||
printf("Error: Could not find Qt version (looked for qtcoreversion.h in %s)\n", | ||
qPrintable(includePaths.join(QDir::listSeparator()))); | ||
qPrintable(paths.join(QDir::listSeparator()))); | ||
return 0; | ||
} | ||
}; | ||
|
@@ -354,16 +461,29 @@ int main(int argc, char *argv[]) | |
printf("Please wait while source files are being generated...\n"); | ||
|
||
QStringList includePaths = getIncludeDirectories(args.value("include-paths")); | ||
if (!qtVersion && !includePaths.empty()) { | ||
printf("Trying to determine Qt version...\n"); | ||
qtVersion = getQtVersion(includePaths); | ||
} | ||
#ifdef Q_OS_MACOS | ||
QStringList frameworkPaths = getFrameworkDirectories(args.value("framework-paths")); | ||
if (!qtVersion && !frameworkPaths.empty()) { | ||
printf("Trying to determine Qt version...\n"); | ||
qtVersion = getQtVersion(frameworkPaths); | ||
} | ||
if (!qtVersion) { | ||
printf("Trying to determine Qt version...\n"); | ||
qtVersion = getQtVersion(includePaths); | ||
if (!qtVersion) | ||
{ | ||
fprintf(stderr, "Aborting\n"); // the error message was printed by getQtVersion | ||
return 1; | ||
} | ||
printf("Determined Qt version is %d.%d.%d\n", qtVersion >> 16, (qtVersion >> 8) & 0xFF, qtVersion & 0xFF); | ||
fprintf(stderr, "Aborting. Could not determine Qt version from include or framework paths.\n"); | ||
return 1; | ||
} | ||
#else | ||
Q_UNUSED(getFrameworkDirectories); | ||
QStringList frameworkPaths; | ||
if (!qtVersion) { | ||
fprintf(stderr, "Aborting. Could not determine Qt version from include paths.\n"); | ||
return 1; | ||
} | ||
#endif | ||
printf("Determined Qt version is %d.%d.%d\n", qtVersion >> 16, (qtVersion >> 8) & 0xFF, qtVersion & 0xFF); | ||
|
||
printf("Parsing typesystem file [%s]\n", qPrintable(typesystemFileName)); | ||
fflush(stdout); | ||
|
@@ -379,7 +499,7 @@ int main(int argc, char *argv[]) | |
qPrintable(pp_file), qPrintable(fileName), qPrintable(args.value("include-paths"))); | ||
ReportHandler::setContext("Preprocess"); | ||
|
||
if (!preprocess(fileName, pp_file, includePaths)) { | ||
if (!preprocess(fileName, pp_file, includePaths, frameworkPaths)) { | ||
fprintf(stderr, "Preprocessor failed on file: '%s'\n", qPrintable(fileName)); | ||
return 1; | ||
} | ||
|
@@ -420,6 +540,9 @@ void displayHelp(GeneratorSet* generatorSet) { | |
" --no-suppress-warnings \n" | ||
" --output-directory=[dir] \n" | ||
" --include-paths=<path>[%c<path>%c...] \n" | ||
#ifdef Q_OS_MACOS | ||
" --framework-paths=<path>[%c<path>%c...] \n" | ||
#endif | ||
" --qt-version=x.y.z \n" | ||
" --print-stdout \n", | ||
path_splitter, path_splitter); | ||
|
Uh oh!
There was an error while loading. Please reload this page.