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