PageRenderTime 42ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/generator/main.cpp

https://bitbucket.org/jpe/experimental-shiboken
C++ | 448 lines | 353 code | 64 blank | 31 comment | 85 complexity | 5bc74ef67a122a63ecbb12b4bdc75cbd MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-3-Clause
  1. /*
  2. * This file is part of the PySide project.
  3. *
  4. * Copyright (C) 2013 Digia Plc 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. #include <QCoreApplication>
  24. #include <QLinkedList>
  25. #include <QLibrary>
  26. #include <QDomDocument>
  27. #include <iostream>
  28. #include <apiextractor.h>
  29. #include "generator.h"
  30. #include "shibokenconfig.h"
  31. #include "cppgenerator.h"
  32. #include "headergenerator.h"
  33. #include "qtdocgenerator.h"
  34. #ifdef _WINDOWS
  35. #define PATH_SPLITTER ";"
  36. #else
  37. #define PATH_SPLITTER ":"
  38. #endif
  39. namespace {
  40. class ArgsHandler
  41. {
  42. public:
  43. explicit ArgsHandler(const QMap<QString, QString>& other);
  44. virtual ~ArgsHandler();
  45. inline QMap<QString, QString>& args() const
  46. {
  47. return *m_args;
  48. }
  49. inline bool argExists(const QString& s) const
  50. {
  51. return m_args->contains(s);
  52. }
  53. QString removeArg(const QString& s);
  54. bool argExistsRemove(const QString& s);
  55. inline QString argValue(const QString& s) const
  56. {
  57. return m_args->value(s);
  58. }
  59. inline bool noArgs() const
  60. {
  61. return m_args->isEmpty();
  62. }
  63. private:
  64. QMap<QString, QString>* m_args;
  65. };
  66. ArgsHandler::ArgsHandler(const QMap<QString, QString>& other)
  67. : m_args(new QMap<QString, QString>(other))
  68. {
  69. }
  70. ArgsHandler::~ArgsHandler()
  71. {
  72. delete m_args;
  73. }
  74. QString ArgsHandler::removeArg(const QString& s)
  75. {
  76. QString retval;
  77. if (argExists(s)) {
  78. retval = argValue(s);
  79. m_args->remove(s);
  80. }
  81. return retval;
  82. }
  83. bool ArgsHandler::argExistsRemove(const QString& s)
  84. {
  85. bool retval = false;
  86. if (argExists(s)) {
  87. retval = true;
  88. m_args->remove(s);
  89. }
  90. return retval;
  91. }
  92. }
  93. static void printOptions(QTextStream& s, const QMap<QString, QString>& options)
  94. {
  95. QMap<QString, QString>::const_iterator it = options.constBegin();
  96. s.setFieldAlignment(QTextStream::AlignLeft);
  97. for (; it != options.constEnd(); ++it) {
  98. s << " --";
  99. s.setFieldWidth(38);
  100. s << it.key() << it.value();
  101. s.setFieldWidth(0);
  102. s << endl;
  103. }
  104. }
  105. typedef void (*getGeneratorsFunc)(QLinkedList<Generator*>*);
  106. static bool processProjectFile(QFile& projectFile, QMap<QString, QString>& args)
  107. {
  108. QByteArray line = projectFile.readLine().trimmed();
  109. if (line.isEmpty() || line != "[generator-project]")
  110. return false;
  111. QStringList includePaths;
  112. QStringList typesystemPaths;
  113. QStringList apiVersions;
  114. while (!projectFile.atEnd()) {
  115. line = projectFile.readLine().trimmed();
  116. if (line.isEmpty())
  117. continue;
  118. int split = line.indexOf("=");
  119. QString key;
  120. QString value;
  121. if (split > 0) {
  122. key = line.left(split - 1).trimmed();
  123. value = line.mid(split + 1).trimmed();
  124. } else {
  125. key = line;
  126. }
  127. if (key == "include-path")
  128. includePaths << QDir::toNativeSeparators(value);
  129. else if (key == "typesystem-path")
  130. typesystemPaths << QDir::toNativeSeparators(value);
  131. else if (key == "api-version")
  132. apiVersions << value;
  133. else if (key == "header-file")
  134. args["arg-1"] = value;
  135. else if (key == "typesystem-file")
  136. args["arg-2"] = value;
  137. else
  138. args[key] = value;
  139. }
  140. if (!includePaths.isEmpty())
  141. args["include-paths"] = includePaths.join(PATH_SPLITTER);
  142. if (!typesystemPaths.isEmpty())
  143. args["typesystem-paths"] = typesystemPaths.join(PATH_SPLITTER);
  144. if (!apiVersions.isEmpty())
  145. args["api-version"] = apiVersions.join("|");
  146. return true;
  147. }
  148. static QMap<QString, QString> getInitializedArguments()
  149. {
  150. QMap<QString, QString> args;
  151. QStringList arguments = QCoreApplication::arguments();
  152. QString appName = arguments.first();
  153. arguments.removeFirst();
  154. QString projectFileName;
  155. foreach (const QString& arg, arguments) {
  156. if (arg.startsWith("--project-file")) {
  157. int split = arg.indexOf("=");
  158. if (split > 0)
  159. projectFileName = arg.mid(split + 1).trimmed();
  160. break;
  161. }
  162. }
  163. if (projectFileName.isNull())
  164. return args;
  165. if (!QFile::exists(projectFileName)) {
  166. std::cerr << qPrintable(appName) << ": Project file \"";
  167. std::cerr << qPrintable(projectFileName) << "\" not found.";
  168. std::cerr << std::endl;
  169. return args;
  170. }
  171. QFile projectFile(projectFileName);
  172. if (!projectFile.open(QIODevice::ReadOnly))
  173. return args;
  174. if (!processProjectFile(projectFile, args)) {
  175. std::cerr << qPrintable(appName) << ": first line of project file \"";
  176. std::cerr << qPrintable(projectFileName) << "\" must be the string \"[generator-project]\"";
  177. std::cerr << std::endl;
  178. return args;
  179. }
  180. return args;
  181. }
  182. static QMap<QString, QString> getCommandLineArgs()
  183. {
  184. QMap<QString, QString> args = getInitializedArguments();
  185. QStringList arguments = QCoreApplication::arguments();
  186. arguments.removeFirst();
  187. int argNum = 0;
  188. foreach (QString arg, arguments) {
  189. arg = arg.trimmed();
  190. if (arg.startsWith("--")) {
  191. int split = arg.indexOf("=");
  192. if (split > 0)
  193. args[arg.mid(2).left(split-2)] = arg.mid(split + 1).trimmed();
  194. else
  195. args[arg.mid(2)] = QString();
  196. } else if (arg.startsWith("-")) {
  197. args[arg.mid(1)] = QString();
  198. } else {
  199. argNum++;
  200. args[QString("arg-%1").arg(argNum)] = arg;
  201. }
  202. }
  203. return args;
  204. }
  205. void printUsage(const GeneratorList& generators)
  206. {
  207. QTextStream s(stdout);
  208. s << "Usage:\n "
  209. << "shiboken [options] header-file typesystem-file\n\n"
  210. << "General options:\n";
  211. QMap<QString, QString> generalOptions;
  212. generalOptions.insert("project-file=<file>", "text file containing a description of the binding project. Replaces and overrides command line arguments");
  213. generalOptions.insert("debug-level=[sparse|medium|full]", "Set the debug level");
  214. generalOptions.insert("silent", "Avoid printing any message");
  215. generalOptions.insert("help", "Display this help and exit");
  216. generalOptions.insert("no-suppress-warnings", "Show all warnings");
  217. generalOptions.insert("output-directory=<path>", "The directory where the generated files will be written");
  218. generalOptions.insert("include-paths=<path>[" PATH_SPLITTER "<path>" PATH_SPLITTER "...]", "Include paths used by the C++ parser");
  219. generalOptions.insert("typesystem-paths=<path>[" PATH_SPLITTER "<path>" PATH_SPLITTER "...]", "Paths used when searching for typesystems");
  220. generalOptions.insert("documentation-only", "Do not generates any code, just the documentation");
  221. generalOptions.insert("license-file=<license-file>", "File used for copyright headers of generated files");
  222. generalOptions.insert("version", "Output version information and exit");
  223. generalOptions.insert("generator-set=<\"generator module\">", "generator-set to be used. e.g. qtdoc");
  224. generalOptions.insert("api-version=<\"package mask\">,<\"version\">", "Specify the supported api version used to generate the bindings");
  225. 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.");
  226. printOptions(s, generalOptions);
  227. foreach (Generator* generator, generators) {
  228. QMap<QString, QString> options = generator->options();
  229. if (!options.isEmpty()) {
  230. s << endl << generator->name() << " options:\n";
  231. printOptions(s, generator->options());
  232. }
  233. }
  234. }
  235. static inline void printVerAndBanner()
  236. {
  237. std::cout << "shiboken v" SHIBOKEN_VERSION << std::endl;
  238. std::cout << "Copyright (C) 2009-2012 Nokia Corporation and/or its subsidiary(-ies)" << std::endl;
  239. }
  240. static inline void errorPrint(const QString& s,
  241. const bool& verAndBanner = false)
  242. {
  243. if (verAndBanner)
  244. printVerAndBanner();
  245. std::cerr << s.toAscii().constData() << std::endl;
  246. }
  247. int main(int argc, char *argv[])
  248. {
  249. // needed by qxmlpatterns
  250. QCoreApplication app(argc, argv);
  251. // Store command arguments in a map
  252. QMap<QString, QString> args = getCommandLineArgs();
  253. ArgsHandler argsHandler(args);
  254. GeneratorList generators;
  255. if (argsHandler.argExistsRemove("version")) {
  256. printVerAndBanner();
  257. return EXIT_SUCCESS;
  258. }
  259. QString generatorSet = argsHandler.removeArg("generator-set");
  260. // Also check "generatorSet" command line argument for backward compatibility.
  261. if (generatorSet.isEmpty())
  262. generatorSet = argsHandler.removeArg("generatorSet");
  263. // Pre-defined generator sets.
  264. if (generatorSet == "qtdoc") {
  265. #ifndef DOCSTRINGS_ENABLED
  266. errorPrint("shiboken: Doc strings extractions was not enabled in this shiboken build.");
  267. return EXIT_FAILURE;
  268. #else
  269. generators << new QtDocGenerator;
  270. #endif
  271. } else if (generatorSet.isEmpty() || generatorSet == "shiboken") {
  272. generators << new CppGenerator << new HeaderGenerator;
  273. } else {
  274. errorPrint("shiboken: Unknown generator set, try \"shiboken\" or \"qtdoc\".");
  275. return EXIT_FAILURE;
  276. }
  277. if (argsHandler.argExistsRemove("help")) {
  278. printUsage(generators);
  279. return EXIT_SUCCESS;
  280. }
  281. QString licenseComment;
  282. QString licenseFileName = argsHandler.removeArg("license-file");
  283. if (!licenseFileName.isEmpty()) {
  284. if (QFile::exists(licenseFileName)) {
  285. QFile licenseFile(licenseFileName);
  286. if (licenseFile.open(QIODevice::ReadOnly))
  287. licenseComment = licenseFile.readAll();
  288. } else {
  289. errorPrint(QString("Couldn't find the file containing the license heading: %1").
  290. arg(qPrintable(licenseFileName)));
  291. return EXIT_FAILURE;
  292. }
  293. }
  294. QString outputDirectory = argsHandler.removeArg("output-directory");
  295. if (outputDirectory.isEmpty())
  296. outputDirectory = "out";
  297. if (!QDir(outputDirectory).exists()) {
  298. if (!QDir().mkpath(outputDirectory)) {
  299. ReportHandler::warning("Can't create output directory: "+outputDirectory);
  300. return EXIT_FAILURE;
  301. }
  302. }
  303. // Create and set-up API Extractor
  304. ApiExtractor extractor;
  305. extractor.setLogDirectory(outputDirectory);
  306. if (argsHandler.argExistsRemove("silent")) {
  307. extractor.setSilent(true);
  308. } else {
  309. QString level = argsHandler.removeArg("debug-level");
  310. if (!level.isEmpty()) {
  311. if (level == "sparse")
  312. extractor.setDebugLevel(ReportHandler::SparseDebug);
  313. else if (level == "medium")
  314. extractor.setDebugLevel(ReportHandler::MediumDebug);
  315. else if (level == "full")
  316. extractor.setDebugLevel(ReportHandler::FullDebug);
  317. }
  318. }
  319. if (argsHandler.argExistsRemove("no-suppress-warnings"))
  320. extractor.setSuppressWarnings(false);
  321. if (argsHandler.argExists("api-version")) {
  322. QStringList versions = argsHandler.removeArg("api-version").split("|");
  323. foreach (QString fullVersion, versions) {
  324. QStringList parts = fullVersion.split(",");
  325. QString package;
  326. QString version;
  327. package = parts.count() == 1 ? "*" : parts.first();
  328. version = parts.last();
  329. extractor.setApiVersion(package, version.toAscii());
  330. }
  331. }
  332. if (argsHandler.argExists("drop-type-entries"))
  333. extractor.setDropTypeEntries(argsHandler.removeArg("drop-type-entries"));
  334. QString path = argsHandler.removeArg("typesystem-paths");
  335. if (!path.isEmpty())
  336. extractor.addTypesystemSearchPath(path.split(PATH_SPLITTER));
  337. path = argsHandler.removeArg("include-paths");
  338. if (!path.isEmpty())
  339. extractor.addIncludePath(path.split(PATH_SPLITTER));
  340. QString cppFileName = argsHandler.removeArg("arg-1");
  341. QString typeSystemFileName = argsHandler.removeArg("arg-2");
  342. /* Make sure to remove the project file's arguments (if any) and
  343. * --project-file, also the arguments of each generator before
  344. * checking if there isn't any existing arguments in argsHandler.
  345. */
  346. argsHandler.removeArg("project-file");
  347. QMap<QString, QString> projectFileArgs = getInitializedArguments();
  348. if (!projectFileArgs.isEmpty()) {
  349. QMap<QString, QString>::const_iterator it =
  350. projectFileArgs.constBegin();
  351. for ( ; it != projectFileArgs.constEnd(); ++it)
  352. argsHandler.removeArg(it.key());
  353. }
  354. foreach (Generator* generator, generators) {
  355. QMap<QString, QString> options = generator->options();
  356. if (!options.isEmpty()) {
  357. QMap<QString, QString>::const_iterator it = options.constBegin();
  358. for ( ; it != options.constEnd(); ++it)
  359. argsHandler.removeArg(it.key());
  360. }
  361. }
  362. if (!argsHandler.noArgs()) {
  363. errorPrint("shiboken: Called with wrong arguments.");
  364. std::cout << "Note: use --help option for more information." << std::endl;
  365. return EXIT_FAILURE;
  366. }
  367. extractor.setCppFileName(cppFileName);
  368. extractor.setTypeSystem(typeSystemFileName);
  369. if (!extractor.run())
  370. return EXIT_FAILURE;
  371. if (!extractor.classCount())
  372. ReportHandler::warning("No C++ classes found!");
  373. foreach (Generator* g, generators) {
  374. g->setOutputDirectory(outputDirectory);
  375. g->setLicenseComment(licenseComment);
  376. if (g->setup(extractor, args))
  377. g->generate();
  378. }
  379. qDeleteAll(generators);
  380. ReportHandler::flush();
  381. std::cout << "Done, " << ReportHandler::warningCount();
  382. std::cout << " warnings (" << ReportHandler::suppressedCount() << " known issues)";
  383. std::cout << std::endl;
  384. }