1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "processcreator.h"
27
28 #include "processexception.h"
29 #include "processstartedevent.h"
30
31 #include <QCoreApplication>
32 #include <QFileInfo>
33 #include <QTemporaryDir>
34
35 namespace ClangBackEnd {
36
37 using namespace std::chrono_literals;
38
ProcessCreator()39 ProcessCreator::ProcessCreator()
40 {
41 }
42
setTemporaryDirectoryPattern(const QString & temporaryDirectoryPattern)43 void ProcessCreator::setTemporaryDirectoryPattern(const QString &temporaryDirectoryPattern)
44 {
45 m_temporaryDirectoryPattern = temporaryDirectoryPattern;
46 resetTemporaryDirectory();
47 }
48
setProcessPath(const QString & processPath)49 void ProcessCreator::setProcessPath(const QString &processPath)
50 {
51 m_processPath = processPath;
52 }
53
setArguments(const QStringList & arguments)54 void ProcessCreator::setArguments(const QStringList &arguments)
55 {
56 m_arguments = arguments;
57 }
58
setEnvironment(const Utils::Environment & environment)59 void ProcessCreator::setEnvironment(const Utils::Environment &environment)
60 {
61 m_environment = environment;
62 }
63
createProcess() const64 std::future<QProcessUniquePointer> ProcessCreator::createProcess() const
65 {
66 return std::async(std::launch::async, [&] {
67 checkIfProcessPathExists();
68 auto process = QProcessUniquePointer(new QProcess);
69 process->setProcessChannelMode(QProcess::QProcess::ForwardedChannels);
70 process->setProcessEnvironment(processEnvironment());
71 process->start(m_processPath, m_arguments);
72 process->waitForStarted(5000);
73
74 checkIfProcessWasStartingSuccessful(process.get());
75
76 postProcessStartedEvent();
77
78 process->moveToThread(QCoreApplication::instance()->thread());
79
80 return process;
81 });
82 }
83
setObserver(QObject * observer)84 void ProcessCreator::setObserver(QObject *observer)
85 {
86 this->m_observer = observer;
87 }
88
checkIfProcessPathExists() const89 void ProcessCreator::checkIfProcessPathExists() const
90 {
91 if (!QFileInfo::exists(m_processPath)) {
92 const QString messageTemplate = QCoreApplication::translate("ProcessCreator",
93 "Executable does not exist: %1");
94 throwProcessException(messageTemplate.arg(m_processPath));
95 }
96 }
97
checkIfProcessWasStartingSuccessful(QProcess * process) const98 void ProcessCreator::checkIfProcessWasStartingSuccessful(QProcess *process) const
99 {
100 if (process->exitStatus() == QProcess::CrashExit || process->exitCode() != 0)
101 dispatchProcessError(process);
102 }
103
dispatchProcessError(QProcess * process) const104 void ProcessCreator::dispatchProcessError(QProcess *process) const
105 {
106 switch (process->error()) {
107 case QProcess::UnknownError: {
108 const QString message = QCoreApplication::translate("ProcessCreator",
109 "Unknown error occurred.");
110 throwProcessException(message);
111 };
112 case QProcess::Crashed: {
113 const QString message = QCoreApplication::translate("ProcessCreator",
114 "Process crashed.");
115 throwProcessException(message);
116 };
117 case QProcess::FailedToStart: {
118 const QString message = QCoreApplication::translate("ProcessCreator",
119 "Process failed at startup.");
120 throwProcessException(message);
121 };
122 case QProcess::Timedout: {
123 const QString message = QCoreApplication::translate("ProcessCreator",
124 "Process timed out.");
125 throwProcessException(message);
126 };
127 case QProcess::WriteError: {
128 const QString message = QCoreApplication::translate("ProcessCreator",
129 "Cannot write to process.");
130 throwProcessException(message);
131 };
132 case QProcess::ReadError: {
133 const QString message = QCoreApplication::translate("ProcessCreator",
134 "Cannot read from process.");
135 throwProcessException(message);
136 };
137 }
138
139 throwProcessException("Internal impossible error!");
140 }
141
postProcessStartedEvent() const142 void ProcessCreator::postProcessStartedEvent() const
143 {
144 if (m_observer)
145 QCoreApplication::postEvent(m_observer, new ProcessStartedEvent);
146 }
147
throwProcessException(const QString & message) const148 void ProcessCreator::throwProcessException(const QString &message) const
149 {
150 postProcessStartedEvent();
151 throw ProcessException(message);
152 }
153
temporaryDirectory() const154 const QTemporaryDir &ProcessCreator::temporaryDirectory() const
155 {
156 return *m_temporaryDirectory.get();
157 }
158
resetTemporaryDirectory()159 void ProcessCreator::resetTemporaryDirectory()
160 {
161 m_temporaryDirectory = std::make_unique<Utils::TemporaryDirectory>(m_temporaryDirectoryPattern);
162 }
163
processEnvironment() const164 QProcessEnvironment ProcessCreator::processEnvironment() const
165 {
166 auto processEnvironment = QProcessEnvironment::systemEnvironment();
167
168 if (temporaryDirectory().isValid()) {
169 const QString temporaryDirectoryPath = temporaryDirectory().path();
170 processEnvironment.insert("TMPDIR", temporaryDirectoryPath);
171 processEnvironment.insert("TMP", temporaryDirectoryPath);
172 processEnvironment.insert("TEMP", temporaryDirectoryPath);
173 }
174
175 const Utils::Environment &env = m_environment;
176 for (auto it = env.constBegin(); it != env.constEnd(); ++it) {
177 if (env.isEnabled(it))
178 processEnvironment.insert(env.key(it), env.expandedValueForKey(env.key(it)));
179 }
180
181 return processEnvironment;
182 }
183
184 } // namespace ClangBackEnd
185