| 1 |
/* |
| 2 |
* This file is part of the PySide project. |
| 3 |
* |
| 4 |
* Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). |
| 5 |
* |
| 6 |
* Contact: PySide team <contact@pyside.org> |
| 7 |
* |
| 8 |
* This program is free software; you can redistribute it and/or |
| 9 |
* modify it under the terms of the GNU General Public License |
| 10 |
* version 2 as published by the Free Software Foundation. |
| 11 |
* |
| 12 |
* This program is distributed in the hope that it will be useful, but |
| 13 |
* WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 |
* General Public License for more details. |
| 16 |
* |
| 17 |
* You should have received a copy of the GNU General Public License |
| 18 |
* along with this program; if not, write to the Free Software |
| 19 |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
| 20 |
* 02110-1301 USA |
| 21 |
* |
| 22 |
*/ |
| 23 |
|
| 24 |
#include <QCoreApplication> |
| 25 |
#include <QLinkedList> |
| 26 |
#include <QLibrary> |
| 27 |
#include <QDomDocument> |
| 28 |
#include <iostream> |
| 29 |
#include <apiextractor.h> |
| 30 |
#include "generatorrunnerconfig.h" |
| 31 |
#include "generator.h" |
| 32 |
|
| 33 |
#ifdef _WINDOWS |
| 34 |
#define PATH_SPLITTER ";" |
| 35 |
#else |
| 36 |
#define PATH_SPLITTER ":" |
| 37 |
#endif |
| 38 |
|
| 39 |
static void printOptions(QTextStream& s, const QMap<QString, QString>& options) { |
| 40 |
QMap<QString, QString>::const_iterator it = options.constBegin(); |
| 41 |
s.setFieldAlignment(QTextStream::AlignLeft); |
| 42 |
for (; it != options.constEnd(); ++it) { |
| 43 |
s << " --"; |
| 44 |
s.setFieldWidth(38); |
| 45 |
s << it.key() << it.value(); |
| 46 |
s.setFieldWidth(0); |
| 47 |
s << endl; |
| 48 |
} |
| 49 |
} |
| 50 |
|
| 51 |
typedef void (*getGeneratorsFunc)(QLinkedList<Generator*>*); |
| 52 |
|
| 53 |
static bool processProjectFile(QFile& projectFile, QMap<QString, QString>& args) |
| 54 |
{ |
| 55 |
QByteArray line = projectFile.readLine().trimmed(); |
| 56 |
if (line.isEmpty() || line != "[generator-project]") |
| 57 |
return false; |
| 58 |
|
| 59 |
QStringList includePaths; |
| 60 |
QStringList typesystemPaths; |
| 61 |
QStringList apiVersions; |
| 62 |
|
| 63 |
while (!projectFile.atEnd()) { |
| 64 |
line = projectFile.readLine().trimmed(); |
| 65 |
if (line.isEmpty()) |
| 66 |
continue; |
| 67 |
|
| 68 |
int split = line.indexOf("="); |
| 69 |
QString key; |
| 70 |
QString value; |
| 71 |
if (split > 0) { |
| 72 |
key = line.left(split - 1).trimmed(); |
| 73 |
value = line.mid(split + 1).trimmed(); |
| 74 |
} else { |
| 75 |
key = line; |
| 76 |
} |
| 77 |
|
| 78 |
if (key == "include-path") |
| 79 |
includePaths << QDir::toNativeSeparators(value); |
| 80 |
else if (key == "typesystem-path") |
| 81 |
typesystemPaths << QDir::toNativeSeparators(value); |
| 82 |
else if (key == "api-version") |
| 83 |
apiVersions << value; |
| 84 |
else if (key == "header-file") |
| 85 |
args["arg-1"] = value; |
| 86 |
else if (key == "typesystem-file") |
| 87 |
args["arg-2"] = value; |
| 88 |
else |
| 89 |
args[key] = value; |
| 90 |
} |
| 91 |
|
| 92 |
if (!includePaths.isEmpty()) |
| 93 |
args["include-paths"] = includePaths.join(PATH_SPLITTER); |
| 94 |
|
| 95 |
if (!typesystemPaths.isEmpty()) |
| 96 |
args["typesystem-paths"] = typesystemPaths.join(PATH_SPLITTER); |
| 97 |
if (!apiVersions.isEmpty()) |
| 98 |
args["api-version"] = apiVersions.join("|"); |
| 99 |
return true; |
| 100 |
} |
| 101 |
|
| 102 |
static QMap<QString, QString> getInitializedArguments() |
| 103 |
{ |
| 104 |
QMap<QString, QString> args; |
| 105 |
QStringList arguments = QCoreApplication::arguments(); |
| 106 |
QString appName = arguments.first(); |
| 107 |
arguments.removeFirst(); |
| 108 |
|
| 109 |
QString projectFileName; |
| 110 |
foreach (const QString& arg, arguments) { |
| 111 |
if (arg.startsWith("--project-file")) { |
| 112 |
int split = arg.indexOf("="); |
| 113 |
if (split > 0) |
| 114 |
projectFileName = arg.mid(split + 1).trimmed(); |
| 115 |
break; |
| 116 |
} |
| 117 |
} |
| 118 |
|
| 119 |
if (projectFileName.isNull()) |
| 120 |
return args; |
| 121 |
|
| 122 |
if (!QFile::exists(projectFileName)) { |
| 123 |
std::cerr << qPrintable(appName) << ": Project file \""; |
| 124 |
std::cerr << qPrintable(projectFileName) << "\" not found."; |
| 125 |
std::cerr << std::endl; |
| 126 |
return args; |
| 127 |
} |
| 128 |
|
| 129 |
QFile projectFile(projectFileName); |
| 130 |
if (!projectFile.open(QIODevice::ReadOnly)) |
| 131 |
return args; |
| 132 |
|
| 133 |
if (!processProjectFile(projectFile, args)) { |
| 134 |
std::cerr << qPrintable(appName) << ": first line of project file \""; |
| 135 |
std::cerr << qPrintable(projectFileName) << "\" must be the string \"[generator-project]\""; |
| 136 |
std::cerr << std::endl; |
| 137 |
return args; |
| 138 |
} |
| 139 |
|
| 140 |
return args; |
| 141 |
} |
| 142 |
|
| 143 |
static QMap<QString, QString> getCommandLineArgs() |
| 144 |
{ |
| 145 |
QMap<QString, QString> args = getInitializedArguments(); |
| 146 |
QStringList arguments = QCoreApplication::arguments(); |
| 147 |
arguments.removeFirst(); |
| 148 |
|
| 149 |
int argNum = 0; |
| 150 |
foreach (QString arg, arguments) { |
| 151 |
arg = arg.trimmed(); |
| 152 |
if (arg.startsWith("--")) { |
| 153 |
int split = arg.indexOf("="); |
| 154 |
if (split > 0) |
| 155 |
args[arg.mid(2).left(split-2)] = arg.mid(split + 1).trimmed(); |
| 156 |
else |
| 157 |
args[arg.mid(2)] = QString(); |
| 158 |
} else if (arg.startsWith("-")) { |
| 159 |
args[arg.mid(1)] = QString(); |
| 160 |
} else { |
| 161 |
argNum++; |
| 162 |
args[QString("arg-%1").arg(argNum)] = arg; |
| 163 |
} |
| 164 |
} |
| 165 |
return args; |
| 166 |
} |
| 167 |
|
| 168 |
void printUsage(const GeneratorList& generators) |
| 169 |
{ |
| 170 |
QTextStream s(stdout); |
| 171 |
s << "Usage:\n " |
| 172 |
<< "generator [options] header-file typesystem-file\n\n" |
| 173 |
"General options:\n"; |
| 174 |
QMap<QString, QString> generalOptions; |
| 175 |
generalOptions.insert("project-file=<file>", "text file containing a description of the binding project. Replaces and overrides command line arguments"); |
| 176 |
generalOptions.insert("debug-level=[sparse|medium|full]", "Set the debug level"); |
| 177 |
generalOptions.insert("silent", "Avoid printing any message"); |
| 178 |
generalOptions.insert("help", "Display this help and exit"); |
| 179 |
generalOptions.insert("no-suppress-warnings", "Show all warnings"); |
| 180 |
generalOptions.insert("output-directory=<path>", "The directory where the generated files will be written"); |
| 181 |
generalOptions.insert("include-paths=<path>[" PATH_SPLITTER "<path>" PATH_SPLITTER "...]", "Include paths used by the C++ parser"); |
| 182 |
generalOptions.insert("typesystem-paths=<path>[" PATH_SPLITTER "<path>" PATH_SPLITTER "...]", "Paths used when searching for typesystems"); |
| 183 |
generalOptions.insert("documentation-only", "Do not generates any code, just the documentation"); |
| 184 |
generalOptions.insert("license-file=<license-file>", "File used for copyright headers of generated files"); |
| 185 |
generalOptions.insert("version", "Output version information and exit"); |
| 186 |
generalOptions.insert("generator-set=<\"generator module\">", "generator-set to be used. e.g. qtdoc"); |
| 187 |
generalOptions.insert("api-version=<\"package mask\">,<\"version\">", "Specify the supported api version used to generate the bindings"); |
| 188 |
generalOptions.insert("drop-type-entries=\"<TypeEntry0>[;TypeEntry1;...]\"", "Semicolon separated list of type system entries (classes, namespaces, global functions and enums) to be dropped from generation."); |
| 189 |
printOptions(s, generalOptions); |
| 190 |
|
| 191 |
foreach (Generator* generator, generators) { |
| 192 |
QMap<QString, QString> options = generator->options(); |
| 193 |
if (!options.isEmpty()) { |
| 194 |
s << endl << generator->name() << " options:\n"; |
| 195 |
printOptions(s, generator->options()); |
| 196 |
} |
| 197 |
} |
| 198 |
} |
| 199 |
|
| 200 |
int main(int argc, char *argv[]) |
| 201 |
{ |
| 202 |
// needed by qxmlpatterns |
| 203 |
QCoreApplication app(argc, argv); |
| 204 |
|
| 205 |
// Store command arguments in a map |
| 206 |
QMap<QString, QString> args = getCommandLineArgs(); |
| 207 |
GeneratorList generators; |
| 208 |
|
| 209 |
if (args.contains("version")) { |
| 210 |
std::cout << "generatorrunner v" GENERATORRUNNER_VERSION << std::endl; |
| 211 |
std::cout << "Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies)" << std::endl; |
| 212 |
return EXIT_SUCCESS; |
| 213 |
} |
| 214 |
|
| 215 |
// Try to load a generator |
| 216 |
QString generatorSet = args.value("generator-set"); |
| 217 |
|
| 218 |
// Also check "generatorSet" command line argument for backward compatibility. |
| 219 |
if (generatorSet.isEmpty()) |
| 220 |
generatorSet = args.value("generatorSet"); |
| 221 |
|
| 222 |
if (!generatorSet.isEmpty()) { |
| 223 |
QFileInfo generatorFile(generatorSet); |
| 224 |
|
| 225 |
if (!generatorFile.exists()) { |
| 226 |
QString generatorSetName(generatorSet + "_generator" + MODULE_EXTENSION); |
| 227 |
|
| 228 |
// More library paths may be added via the QT_PLUGIN_PATH environment variable. |
| 229 |
QCoreApplication::addLibraryPath(GENERATORRUNNER_PLUGIN_DIR); |
| 230 |
foreach (const QString& path, QCoreApplication::libraryPaths()) { |
| 231 |
generatorFile.setFile(QDir(path), generatorSetName); |
| 232 |
if (generatorFile.exists()) |
| 233 |
break; |
| 234 |
} |
| 235 |
} |
| 236 |
|
| 237 |
if (!generatorFile.exists()) { |
| 238 |
std::cerr << argv[0] << ": Error loading generator-set plugin: "; |
| 239 |
std::cerr << qPrintable(generatorFile.baseName()) << " module not found." << std::endl; |
| 240 |
return EXIT_FAILURE; |
| 241 |
} |
| 242 |
|
| 243 |
QLibrary plugin(generatorFile.filePath()); |
| 244 |
getGeneratorsFunc getGenerators = (getGeneratorsFunc)plugin.resolve("getGenerators"); |
| 245 |
if (getGenerators) { |
| 246 |
getGenerators(&generators); |
| 247 |
} else { |
| 248 |
std::cerr << argv[0] << ": Error loading generator-set plugin: " << qPrintable(plugin.errorString()) << std::endl; |
| 249 |
return EXIT_FAILURE; |
| 250 |
} |
| 251 |
} else if (!args.contains("help")) { |
| 252 |
std::cerr << argv[0] << ": You need to specify a generator with --generator-set=GENERATOR_NAME" << std::endl; |
| 253 |
return EXIT_FAILURE; |
| 254 |
} |
| 255 |
|
| 256 |
if (args.contains("help")) { |
| 257 |
printUsage(generators); |
| 258 |
return EXIT_SUCCESS; |
| 259 |
} |
| 260 |
|
| 261 |
|
| 262 |
QString licenseComment; |
| 263 |
if (args.contains("license-file") && !args.value("license-file").isEmpty()) { |
| 264 |
QString licenseFileName = args.value("license-file"); |
| 265 |
if (QFile::exists(licenseFileName)) { |
| 266 |
QFile licenseFile(licenseFileName); |
| 267 |
if (licenseFile.open(QIODevice::ReadOnly)) |
| 268 |
licenseComment = licenseFile.readAll(); |
| 269 |
} else { |
| 270 |
std::cerr << "Couldn't find the file containing the license heading: "; |
| 271 |
std::cerr << qPrintable(licenseFileName) << std::endl; |
| 272 |
return EXIT_FAILURE; |
| 273 |
} |
| 274 |
} |
| 275 |
|
| 276 |
QString outputDirectory = args.contains("output-directory") ? args["output-directory"] : "out"; |
| 277 |
if (!QDir(outputDirectory).exists()) { |
| 278 |
if (!QDir().mkpath(outputDirectory)) { |
| 279 |
ReportHandler::warning("Can't create output directory: "+outputDirectory); |
| 280 |
return EXIT_FAILURE; |
| 281 |
} |
| 282 |
} |
| 283 |
// Create and set-up API Extractor |
| 284 |
ApiExtractor extractor; |
| 285 |
extractor.setLogDirectory(outputDirectory); |
| 286 |
|
| 287 |
if (args.contains("silent")) { |
| 288 |
extractor.setSilent(true); |
| 289 |
} else if (args.contains("debug-level")) { |
| 290 |
QString level = args.value("debug-level"); |
| 291 |
if (level == "sparse") |
| 292 |
extractor.setDebugLevel(ReportHandler::SparseDebug); |
| 293 |
else if (level == "medium") |
| 294 |
extractor.setDebugLevel(ReportHandler::MediumDebug); |
| 295 |
else if (level == "full") |
| 296 |
extractor.setDebugLevel(ReportHandler::FullDebug); |
| 297 |
} |
| 298 |
if (args.contains("no-suppress-warnings")) |
| 299 |
extractor.setSuppressWarnings(false); |
| 300 |
|
| 301 |
if (args.contains("api-version")) { |
| 302 |
QStringList versions = args["api-version"].split("|"); |
| 303 |
foreach (QString fullVersion, versions) { |
| 304 |
QStringList parts = fullVersion.split(","); |
| 305 |
QString package; |
| 306 |
QString version; |
| 307 |
package = parts.count() == 1 ? "*" : parts.first(); |
| 308 |
version = parts.last(); |
| 309 |
extractor.setApiVersion(package, version.toAscii()); |
| 310 |
} |
| 311 |
} |
| 312 |
|
| 313 |
if (args.contains("drop-type-entries")) |
| 314 |
extractor.setDropTypeEntries(args["drop-type-entries"]); |
| 315 |
|
| 316 |
if (args.contains("typesystem-paths")) |
| 317 |
extractor.addTypesystemSearchPath(args.value("typesystem-paths").split(PATH_SPLITTER)); |
| 318 |
if (!args.value("include-paths").isEmpty()) |
| 319 |
extractor.addIncludePath(args.value("include-paths").split(PATH_SPLITTER)); |
| 320 |
|
| 321 |
|
| 322 |
QString cppFileName = args.value("arg-1"); |
| 323 |
QString typeSystemFileName = args.value("arg-2"); |
| 324 |
if (args.contains("arg-3")) { |
| 325 |
std::cerr << "Too many arguments!" << std::endl; |
| 326 |
return EXIT_FAILURE; |
| 327 |
} |
| 328 |
extractor.setCppFileName(cppFileName); |
| 329 |
extractor.setTypeSystem(typeSystemFileName); |
| 330 |
if (!extractor.run()) |
| 331 |
return EXIT_FAILURE; |
| 332 |
|
| 333 |
if (!extractor.classCount()) |
| 334 |
ReportHandler::warning("No C++ classes found!"); |
| 335 |
|
| 336 |
foreach (Generator* g, generators) { |
| 337 |
g->setOutputDirectory(outputDirectory); |
| 338 |
g->setLicenseComment(licenseComment); |
| 339 |
if (g->setup(extractor, args)) |
| 340 |
g->generate(); |
| 341 |
} |
| 342 |
qDeleteAll(generators); |
| 343 |
|
| 344 |
ReportHandler::flush(); |
| 345 |
std::cout << "Done, " << ReportHandler::warningCount(); |
| 346 |
std::cout << " warnings (" << ReportHandler::suppressedCount() << " known issues)"; |
| 347 |
std::cout << std::endl; |
| 348 |
} |