1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qbs.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28 #include "commandlineparser.h"
29 
30 #include "exception.h"
31 
32 #include <QtCore/qcommandlineoption.h>
33 #include <QtCore/qcommandlineparser.h>
34 #include <QtCore/qcoreapplication.h>
35 #include <QtCore/qfileinfo.h>
36 
37 namespace qbsBenchmarker {
38 
resolveActivity()39 static QString resolveActivity() { return "resolving"; }
ruleExecutionActivity()40 static QString ruleExecutionActivity() { return "rule-execution"; }
nullBuildActivity()41 static QString nullBuildActivity() { return "null-build"; }
allActivities()42 static QString allActivities() { return "all"; }
43 
44 CommandLineParser::CommandLineParser() = default;
45 
parse()46 void CommandLineParser::parse()
47 {
48     QCommandLineParser parser;
49     parser.setApplicationDescription("This tool aims to detect qbs performance regressions "
50                                      "using valgrind.");
51     parser.addHelpOption();
52     QCommandLineOption oldCommitOption(QStringList{"old-commit", "o"}, "The old qbs commit.",
53                                        "old commit");
54     parser.addOption(oldCommitOption);
55     QCommandLineOption newCommitOption(QStringList{"new-commit", "n"}, "The new qbs commit.",
56                                        "new commit");
57     parser.addOption(newCommitOption);
58     QCommandLineOption testProjectOption(QStringList{"test-project", "p"},
59             "The example project to use for the benchmark.", "project file path");
60     parser.addOption(testProjectOption);
61     QCommandLineOption qbsRepoOption(QStringList{"qbs-repo", "r"}, "The qbs repository.",
62                                      "repo path");
63     parser.addOption(qbsRepoOption);
64     QCommandLineOption activitiesOption(QStringList{"activities", "a"},
65             QStringLiteral("The activities to benchmark. Possible values (CSV): %1,%2,%3,%4")
66                     .arg(resolveActivity(), ruleExecutionActivity(), nullBuildActivity(),
67                          allActivities()), "activities", allActivities());
68     parser.addOption(activitiesOption);
69     QCommandLineOption thresholdOption(QStringList{"regression-threshold", "t"},
70             "A relative increase higher than this is considered a performance regression. "
71             "All temporary data from running the benchmarks will be kept if that happens.",
72             "value in per cent");
73     parser.addOption(thresholdOption);
74     parser.process(*QCoreApplication::instance());
75     const QList<QCommandLineOption> mandatoryOptions = QList<QCommandLineOption>()
76             << oldCommitOption << newCommitOption << testProjectOption << qbsRepoOption;
77     for (const QCommandLineOption &o : mandatoryOptions) {
78         if (!parser.isSet(o))
79             throwException(o.names().constFirst(), parser.helpText());
80         if (parser.value(o).isEmpty())
81             throwException(o.names().constFirst(), QString(), parser.helpText());
82     }
83     m_oldCommit = parser.value(oldCommitOption);
84     m_newCommit = parser.value(newCommitOption);
85     if (m_oldCommit == m_newCommit) {
86         throw Exception(QStringLiteral("Error parsing command line: "
87                 "'new commit' and 'old commit' must be different commits.\n%1").arg(parser.helpText()));
88     }
89     m_testProjectFilePath = parser.value(testProjectOption);
90     m_qbsRepoDirPath = parser.value(qbsRepoOption);
91     const QStringList activitiesList = parser.value(activitiesOption).split(',');
92     m_activities = Activities();
93     for (const QString &activityString : activitiesList) {
94         if (activityString == allActivities()) {
95             m_activities = ActivityResolving | ActivityRuleExecution | ActivityNullBuild;
96             break;
97         } else if (activityString == resolveActivity()) {
98             m_activities = ActivityResolving;
99         } else if (activityString == ruleExecutionActivity()) {
100             m_activities |= ActivityRuleExecution;
101         } else if (activityString == nullBuildActivity()) {
102             m_activities |= ActivityNullBuild;
103         } else {
104             throwException(activitiesOption.names().constFirst(),
105                            activityString,
106                            parser.helpText());
107         }
108     }
109     m_regressionThreshold = 5;
110     if (parser.isSet(thresholdOption)) {
111         bool ok = true;
112         const QString rawThresholdValue = parser.value(thresholdOption);
113         m_regressionThreshold = rawThresholdValue.toInt(&ok);
114         if (!ok)
115             throwException(thresholdOption.names().constFirst(),
116                            rawThresholdValue,
117                            parser.helpText());
118     }
119 }
120 
throwException(const QString & optionName,const QString & illegalValue,const QString & helpText)121 void CommandLineParser::throwException(const QString &optionName, const QString &illegalValue,
122                                        const QString &helpText)
123 {
124     const QString errorText(QStringLiteral("Error parsing command line: Illegal value '%1' "
125             "for option '--%2'.\n%3").arg(illegalValue, optionName, helpText));
126     throw Exception(errorText);
127 }
128 
throwException(const QString & missingOption,const QString & helpText)129 void CommandLineParser::throwException(const QString &missingOption, const QString &helpText)
130 {
131     const QString errorText(QStringLiteral("Error parsing command line: Missing mandatory "
132             "option '--%1'.\n%3").arg(missingOption, helpText));
133     throw Exception(errorText);
134 }
135 
136 
137 } // namespace qbsBenchmarker
138