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 "processparameters.h"
27 
28 #include <utils/fileutils.h>
29 #include <utils/macroexpander.h>
30 #include <utils/qtcprocess.h>
31 #include <utils/theme/theme.h>
32 
33 #include <QDir>
34 
35 /*!
36     \class ProjectExplorer::ProcessParameters
37 
38     \brief The ProcessParameters class aggregates all parameters needed to start
39     a process.
40 
41     It offers a set of functions which expand macros and environment variables
42     inside the raw parameters to obtain final values for starting a process
43     or for display purposes.
44 
45     \sa ProjectExplorer::AbstractProcessStep
46 */
47 
48 using namespace Utils;
49 
50 namespace ProjectExplorer {
51 
52 ProcessParameters::ProcessParameters() = default;
53 
54 /*!
55     Sets the command to run.
56 */
setCommandLine(const CommandLine & cmdLine)57 void ProcessParameters::setCommandLine(const CommandLine &cmdLine)
58 {
59     m_command = cmdLine;
60     m_effectiveCommand.clear();
61     m_effectiveArguments.clear();
62 
63     effectiveCommand();
64     effectiveArguments();
65 }
66 
67 /*!
68     Sets the \a workingDirectory for the process for a build configuration.
69 
70     Should be called from init().
71 */
72 
setWorkingDirectory(const FilePath & workingDirectory)73 void ProcessParameters::setWorkingDirectory(const FilePath &workingDirectory)
74 {
75     m_workingDirectory = workingDirectory;
76     m_effectiveWorkingDirectory.clear();
77 
78     effectiveWorkingDirectory();
79 }
80 
81 /*!
82     \fn void ProjectExplorer::ProcessParameters::setEnvironment(const Utils::Environment &env)
83     Sets the environment \a env for running the command.
84 
85     Should be called from init().
86 */
87 
88 /*!
89    \fn  void ProjectExplorer::ProcessParameters::setMacroExpander(Utils::MacroExpander *mx)
90    Sets the macro expander \a mx to use on the command, arguments, and working
91    dir.
92 
93    \note The caller retains ownership of the object.
94 */
95 
96 /*!
97     Gets the fully expanded working directory.
98 */
99 
effectiveWorkingDirectory() const100 FilePath ProcessParameters::effectiveWorkingDirectory() const
101 {
102     if (m_effectiveWorkingDirectory.isEmpty()) {
103         m_effectiveWorkingDirectory = m_workingDirectory;
104         QString path = m_workingDirectory.path();
105         if (m_macroExpander)
106             path = m_macroExpander->expand(path);
107         m_effectiveWorkingDirectory.setPath(QDir::cleanPath(m_environment.expandVariables(path)));
108     }
109     return m_effectiveWorkingDirectory;
110 }
111 
112 /*!
113     Gets the fully expanded command name to run.
114 */
115 
effectiveCommand() const116 FilePath ProcessParameters::effectiveCommand() const
117 {
118     if (m_effectiveCommand.isEmpty()) {
119         FilePath cmd = m_command.executable();
120         if (m_macroExpander)
121             cmd = m_macroExpander->expand(cmd);
122         if (cmd.needsDevice()) {
123             // Assume this is already good. FIXME: It is possibly not, so better fix  searchInPath.
124             m_effectiveCommand = cmd;
125         } else {
126             m_effectiveCommand = m_environment.searchInPath(cmd.toString(),
127                                                             {effectiveWorkingDirectory()});
128         }
129         m_commandMissing = m_effectiveCommand.isEmpty();
130         if (m_commandMissing)
131             m_effectiveCommand = cmd;
132     }
133     return m_effectiveCommand;
134 }
135 
136 /*!
137     Returns \c true if effectiveCommand() would return only a fallback.
138 */
139 
commandMissing() const140 bool ProcessParameters::commandMissing() const
141 {
142     effectiveCommand();
143     return m_commandMissing;
144 }
145 
effectiveArguments() const146 QString ProcessParameters::effectiveArguments() const
147 {
148     if (m_effectiveArguments.isEmpty()) {
149         m_effectiveArguments = m_command.arguments();
150         if (m_macroExpander)
151             m_effectiveArguments = m_macroExpander->expand(m_effectiveArguments);
152     }
153     return m_effectiveArguments;
154 }
155 
prettyCommand() const156 QString ProcessParameters::prettyCommand() const
157 {
158     QString cmd = m_command.executable().toString();
159     if (m_macroExpander)
160         cmd = m_macroExpander->expand(cmd);
161     return FilePath::fromString(cmd).fileName();
162 }
163 
prettyArguments() const164 QString ProcessParameters::prettyArguments() const
165 {
166     QString margs = effectiveArguments();
167     QString workDir = effectiveWorkingDirectory().toString();
168     ProcessArgs::SplitError err;
169     ProcessArgs args =
170             ProcessArgs::prepareArgs(margs, &err, HostOsInfo::hostOs(), &m_environment, &workDir);
171     if (err != ProcessArgs::SplitOk)
172         return margs; // Sorry, too complex - just fall back.
173     return args.toString();
174 }
175 
invalidCommandMessage(const QString & displayName)176 static QString invalidCommandMessage(const QString &displayName)
177 {
178     return QString("<b>%1:</b> <font color='%3'>%2</font>")
179                     .arg(displayName,
180                          QtcProcess::tr("Invalid command"),
181                          creatorTheme()->color(Theme::TextColorError).name());
182 }
183 
summary(const QString & displayName) const184 QString ProcessParameters::summary(const QString &displayName) const
185 {
186     if (m_commandMissing)
187         return invalidCommandMessage(displayName);
188 
189     return QString::fromLatin1("<b>%1:</b> %2 %3")
190             .arg(displayName,
191                  ProcessArgs::quoteArg(prettyCommand()),
192                  prettyArguments());
193 }
194 
summaryInWorkdir(const QString & displayName) const195 QString ProcessParameters::summaryInWorkdir(const QString &displayName) const
196 {
197     if (m_commandMissing)
198         return invalidCommandMessage(displayName);
199 
200     return QString::fromLatin1("<b>%1:</b> %2 %3 in %4")
201             .arg(displayName,
202                  ProcessArgs::quoteArg(prettyCommand()),
203                  prettyArguments(),
204                  QDir::toNativeSeparators(effectiveWorkingDirectory().toString()));
205 }
206 
207 } // ProcessExplorer
208