File indexing completed on 2024-05-19 05:42:03

0001 // ct_lvtclp_codebase_db.m.cpp                                      -*-C++-*--
0002 
0003 /*
0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk>
0005 // SPDX-License-Identifier: Apache-2.0
0006 //
0007 // Licensed under the Apache License, Version 2.0 (the "License");
0008 // you may not use this file except in compliance with the License.
0009 // You may obtain a copy of the License at
0010 //
0011 //     http://www.apache.org/licenses/LICENSE-2.0
0012 //
0013 // Unless required by applicable law or agreed to in writing, software
0014 // distributed under the License is distributed on an "AS IS" BASIS,
0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0016 // See the License for the specific language governing permissions and
0017 // limitations under the License.
0018 */
0019 
0020 #include <ct_lvtclp_compilerutil.h>
0021 #include <ct_lvtmdb_objectstore.h>
0022 #include <ct_lvtmdb_soci_reader.h>
0023 #include <ct_lvtmdb_soci_writer.h>
0024 
0025 #include <algorithm>
0026 #include <cstdlib>
0027 #include <filesystem>
0028 #include <iostream>
0029 #include <string>
0030 #include <vector>
0031 
0032 #include <QtGlobal>
0033 
0034 #ifdef Q_OS_WINDOWS
0035 #include <stdio.h>
0036 #include <windows.h>
0037 #else
0038 #include <csignal>
0039 #endif
0040 
0041 #include <QCommandLineOption>
0042 #include <QCommandLineParser>
0043 #include <QCoreApplication>
0044 #include <QDebug>
0045 #include <QFileInfo>
0046 #include <QJsonDocument>
0047 #include <QJsonObject>
0048 #include <QString>
0049 #include <QStringList>
0050 
0051 namespace {
0052 
0053 struct CommandLineArgs {
0054     QList<QString> databases;
0055     QString resultingDb;
0056     bool silent = false;
0057     bool force = false;
0058 };
0059 
0060 enum CommandLineParseResult { Ok, Error, Help };
0061 
0062 CommandLineParseResult parseCommandLine(QCommandLineParser& parser, CommandLineArgs& args, std::string& errorMessage)
0063 {
0064     parser.setApplicationDescription("Build code database");
0065 
0066     const QCommandLineOption outputFile({"output", "o"}, "Output database file", "OUTPUT_FILE", "");
0067     const QCommandLineOption databases("database",
0068                                        "Path to a single database to be merged. specify it multiple times.",
0069                                        "DATABASES",
0070                                        "");
0071     const QCommandLineOption silent("silent", "supress output");
0072     const QCommandLineOption force("force", "overwrite the output file if it exists.");
0073     const QCommandLineOption helpOption = parser.addHelpOption();
0074 
0075     parser.addOptions({outputFile, databases, force, silent, helpOption});
0076 
0077     if (!parser.parse(QCoreApplication::arguments())) {
0078         errorMessage = qPrintable(parser.errorText());
0079         return CommandLineParseResult::Error;
0080     }
0081 
0082     if (parser.isSet(force)) {
0083         args.force = true;
0084     }
0085 
0086     if (!parser.isSet(outputFile)) {
0087         errorMessage = "Missing output file";
0088         return CommandLineParseResult::Error;
0089     }
0090 
0091     if (!parser.isSet(databases)) {
0092         errorMessage = "Missing database parameter, please specify with --database <filename.db>";
0093         return CommandLineParseResult::Error;
0094     }
0095 
0096     const QString oFile = parser.value(outputFile);
0097     if (!oFile.endsWith("db") || oFile.size() <= 3) {
0098         errorMessage = "output database must be a in the format file.db";
0099         return CommandLineParseResult::Error;
0100     }
0101 
0102     if (parser.isSet(helpOption)) {
0103         return CommandLineParseResult::Help;
0104     }
0105 
0106     args.databases = parser.values(databases);
0107     for (const auto& db : std::as_const(args.databases)) {
0108         QFileInfo info(db);
0109         if (!info.exists()) {
0110             errorMessage = (db + " is not a file").toStdString();
0111         }
0112     }
0113 
0114     args.resultingDb = oFile;
0115     args.silent = parser.isSet("silent");
0116 
0117     return CommandLineParseResult::Ok;
0118 }
0119 
0120 } // namespace
0121 
0122 #ifdef Q_OS_WINDOWS
0123 BOOL WINAPI CtrlHandler(DWORD fdwCtrlType)
0124 {
0125     switch (fdwCtrlType) {
0126     case CTRL_C_EVENT:
0127         exit(1);
0128 
0129     case CTRL_CLOSE_EVENT:
0130         exit(1);
0131     default:
0132         return false;
0133     }
0134     return false;
0135 }
0136 #else
0137 void signal_callback_handler(int signum)
0138 {
0139     exit(signum);
0140 }
0141 #endif
0142 
0143 int main(int argc, char **argv)
0144 {
0145     QCoreApplication app(argc, argv);
0146 #ifdef Q_OS_WINDOWS
0147     SetConsoleCtrlHandler(CtrlHandler, TRUE);
0148 #else
0149     (void) signal(SIGINT, signal_callback_handler);
0150 #endif
0151     QCommandLineParser parser;
0152     CommandLineArgs args;
0153     std::string errorMessage;
0154 
0155     switch (parseCommandLine(parser, args, errorMessage)) {
0156     case CommandLineParseResult::Ok:
0157         break;
0158     case CommandLineParseResult::Error:
0159         if (!errorMessage.empty()) {
0160             std::cerr << errorMessage << "\n\n";
0161         }
0162         parser.showHelp();
0163         Q_UNREACHABLE();
0164     case CommandLineParseResult::Help:
0165         parser.showHelp();
0166         Q_UNREACHABLE();
0167     }
0168 
0169     std::filesystem::path outputDbPath = args.resultingDb.toStdString();
0170     if (!args.force && std::filesystem::exists(outputDbPath)) {
0171         qInfo() << "Resulting file already exists and --force was not set. not continuing.\n";
0172         return 1;
0173     }
0174 
0175     const int database_size = args.databases.count();
0176     int idx = 0;
0177 
0178     if (args.force) {
0179         if (std::filesystem::exists(outputDbPath)) {
0180             std::filesystem::remove(outputDbPath);
0181         }
0182     }
0183 
0184     for (const QString& db : std::as_const(args.databases)) {
0185         Codethink::lvtmdb::SociReader dbReader;
0186         Codethink::lvtmdb::SociWriter dbWriter;
0187 
0188         Codethink::lvtmdb::ObjectStore inMemoryDb;
0189 
0190         auto loaded = inMemoryDb.readFromDatabase(dbReader, db.toStdString());
0191         if (loaded.has_error()) {
0192             std::cout << "Error opening database " << loaded.error().what << "\n";
0193             return EXIT_FAILURE;
0194         }
0195 
0196         if (!args.silent) {
0197             idx += 1;
0198             std::cout << "[" << idx << " of " << database_size << ": Loading data from" << db.toStdString() << "\n";
0199         }
0200 
0201         if (!dbWriter.createOrOpen(outputDbPath.string())) {
0202             std::cerr << "Could not open resulting database\n";
0203             return EXIT_FAILURE;
0204         }
0205 
0206         inMemoryDb.writeToDatabase(dbWriter);
0207     }
0208 
0209     std::cout << "Database file saved correctly in " << outputDbPath << "\n";
0210     return EXIT_SUCCESS;
0211 }