/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtScxml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include "scxmlcppdumper.h" #include "qscxmlc.h" #include #include #include #include #include QT_BEGIN_NAMESPACE enum { NoError = 0, CommandLineArgumentsError = -1, NoInputFilesError = -2, CannotOpenInputFileError = -3, ParseError = -4, CannotOpenOutputHeaderFileError = -5, CannotOpenOutputCppFileError = -6, ScxmlVerificationError = -7, NoTextCodecError = -8 }; int write(TranslationUnit *tu) { QTextStream errs(stderr, QIODevice::WriteOnly); QFile outH(tu->outHFileName); if (!outH.open(QFile::WriteOnly)) { errs << QStringLiteral("Error: cannot open '%1': %2").arg(outH.fileName(), outH.errorString()) << endl; return CannotOpenOutputHeaderFileError; } QFile outCpp(tu->outCppFileName); if (!outCpp.open(QFile::WriteOnly)) { errs << QStringLiteral("Error: cannot open '%1': %2").arg(outCpp.fileName(), outCpp.errorString()) << endl; return CannotOpenOutputCppFileError; } // Make sure it outputs UTF-8, as that is what C++ expects. QTextCodec *utf8 = QTextCodec::codecForName("UTF-8"); if (!utf8) { errs << QStringLiteral("Error: cannot find a QTextCodec for generating UTF-8."); return NoTextCodecError; } QTextStream h(&outH); h.setCodec(utf8); h.setGenerateByteOrderMark(true); QTextStream c(&outCpp); c.setCodec(utf8); c.setGenerateByteOrderMark(true); CppDumper dumper(h, c); dumper.dump(tu); h.flush(); outH.close(); c.flush(); outCpp.close(); return NoError; } static void collectAllDocuments(DocumentModel::ScxmlDocument *doc, QMap *docs) { docs->insert(doc, doc->root->name); foreach (DocumentModel::ScxmlDocument *subDoc, doc->allSubDocuments) { collectAllDocuments(subDoc, docs); } } int run(const QStringList &arguments) { QCommandLineParser cmdParser; QTextStream errs(stderr, QIODevice::WriteOnly); cmdParser.addHelpOption(); cmdParser.addVersionOption(); cmdParser.setApplicationDescription(QCoreApplication::translate("main", "Compiles the given input.scxml file to a header and a cpp file.")); QCommandLineOption optionNoCxx11(QLatin1String("no-c++11"), QCoreApplication::translate("main", "Don't use C++11 in generated code.")); QCommandLineOption optionNamespace(QLatin1String("namespace"), QCoreApplication::translate("main", "Put generated code into ."), QCoreApplication::translate("main", "namespace")); QCommandLineOption optionOutputBaseName(QStringList() << QLatin1String("o") << QLatin1String("output"), QCoreApplication::translate("main", "Generate .h and .cpp files."), QCoreApplication::translate("main", "name")); QCommandLineOption optionOutputHeaderName(QLatin1String("header"), QCoreApplication::translate("main", "Generate for the header file."), QCoreApplication::translate("main", "name")); QCommandLineOption optionOutputSourceName(QLatin1String("impl"), QCoreApplication::translate("main", "Generate for the source file."), QCoreApplication::translate("main", "name")); QCommandLineOption optionClassName(QLatin1String("classname"), QCoreApplication::translate("main", "Generate for state machine class name."), QCoreApplication::translate("main", "name")); QCommandLineOption optionQtMode(QLatin1String("qt-mode"), QCoreApplication::translate("main", "Enables or disables Qt mode. " "In order to unconditionally enable qt-mode, specify \"yes\". " "To unconditionally disable qt-mode, specify \"no\". " "To read the setting from the input file, specify \"from-input\". " "The default is \"from-input\"."), QCoreApplication::translate("main", "mode"), QLatin1String("from-input")); cmdParser.addPositionalArgument(QLatin1String("input"), QCoreApplication::translate("main", "Input SCXML file.")); cmdParser.addOption(optionNoCxx11); cmdParser.addOption(optionNamespace); cmdParser.addOption(optionOutputBaseName); cmdParser.addOption(optionOutputHeaderName); cmdParser.addOption(optionOutputSourceName); cmdParser.addOption(optionClassName); cmdParser.addOption(optionQtMode); cmdParser.process(arguments); const QStringList inputFiles = cmdParser.positionalArguments(); if (inputFiles.count() < 1) { errs << QCoreApplication::translate("main", "Error: no input file.") << endl; cmdParser.showHelp(NoInputFilesError); } if (inputFiles.count() > 1) { errs << QCoreApplication::translate("main", "Error: unexpected argument(s): %1") .arg(inputFiles.mid(1).join(QLatin1Char(' '))) << endl; cmdParser.showHelp(NoInputFilesError); } const QString scxmlFileName = inputFiles.at(0); TranslationUnit options; options.useCxx11 = !cmdParser.isSet(optionNoCxx11); if (cmdParser.isSet(optionNamespace)) options.namespaceName = cmdParser.value(optionNamespace); QString outFileName = cmdParser.value(optionOutputBaseName); QString outHFileName = cmdParser.value(optionOutputHeaderName); QString outCppFileName = cmdParser.value(optionOutputSourceName); QString mainClassName = cmdParser.value(optionClassName); QString qtModeName = cmdParser.value(optionQtMode); QScxmlParser::QtMode qtMode = QScxmlParser::QtModeFromInputFile; if (qtModeName == QLatin1String("yes")) { qtMode = QScxmlParser::QtModeEnabled; } else if (qtModeName == QLatin1String("no")) { qtMode = QScxmlParser::QtModeDisabled; } else if (qtModeName == QLatin1String("from-input")) { qtMode = QScxmlParser::QtModeFromInputFile; } else { errs << QCoreApplication::translate("main", "Error: unexpected value for qt-mode option: %1") .arg(qtModeName) << endl; cmdParser.showHelp(CommandLineArgumentsError); } if (outFileName.isEmpty()) outFileName = QFileInfo(scxmlFileName).baseName(); if (outHFileName.isEmpty()) outHFileName = outFileName + QLatin1String(".h"); if (outCppFileName.isEmpty()) outCppFileName = outFileName + QLatin1String(".cpp"); QFile file(scxmlFileName); if (!file.open(QFile::ReadOnly)) { errs << QStringLiteral("Error: cannot open input file %1").arg(scxmlFileName); return CannotOpenInputFileError; } QXmlStreamReader reader(&file); QScxmlParser parser(&reader); parser.setFileName(file.fileName()); parser.setQtMode(qtMode); parser.parse(); if (!parser.errors().isEmpty()) { foreach (const QScxmlError &error, parser.errors()) { errs << error.toString() << endl; } return ParseError; } auto mainDoc = QScxmlParserPrivate::get(&parser)->scxmlDocument(); if (mainDoc == nullptr) { Q_ASSERT(!parser.errors().isEmpty()); foreach (const QScxmlError &error, parser.errors()) { errs << error.toString() << endl; } return ScxmlVerificationError; } QMap docs; collectAllDocuments(mainDoc, &docs); if (mainClassName.isEmpty()) mainClassName = mainDoc->root->name; if (mainClassName.isEmpty()) { mainClassName = QFileInfo(scxmlFileName).fileName(); int dot = mainClassName.lastIndexOf(QLatin1Char('.')); if (dot != -1) mainClassName = mainClassName.left(dot); } docs.insert(mainDoc, mainClassName); TranslationUnit tu = options; tu.scxmlFileName = QFileInfo(file).fileName(); tu.mainDocument = mainDoc; tu.outHFileName = outHFileName; tu.outCppFileName = outCppFileName; for (QMap::const_iterator i = docs.begin(), ei = docs.end(); i != ei; ++i) { auto name = i.value(); if (name.isEmpty()) { name = QStringLiteral("%1_StateMachine_%2").arg(mainClassName).arg(tu.classnameForDocument.size() + 1); } tu.classnameForDocument.insert(i.key(), name); } return write(&tu); } QT_END_NAMESPACE