79e765f202e198ae0282f4e8e30861904dc09493
[idea/community.git] / plugins / sh / src / com / intellij / sh / run / ShRunConfiguration.java
1 // Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.sh.run;
3
4 import com.intellij.execution.Executor;
5 import com.intellij.execution.configuration.EnvironmentVariablesData;
6 import com.intellij.execution.configurations.*;
7 import com.intellij.execution.runners.ExecutionEnvironment;
8 import com.intellij.execution.wsl.WSLDistribution;
9 import com.intellij.execution.wsl.WSLUtil;
10 import com.intellij.openapi.application.Experiments;
11 import com.intellij.openapi.options.SettingsEditor;
12 import com.intellij.openapi.project.Project;
13 import com.intellij.openapi.util.InvalidDataException;
14 import com.intellij.openapi.util.JDOMExternalizerUtil;
15 import com.intellij.openapi.util.NlsSafe;
16 import com.intellij.openapi.util.io.FileUtil;
17 import com.intellij.openapi.util.text.StringUtil;
18 import com.intellij.openapi.vfs.VirtualFile;
19 import com.intellij.psi.PsiElement;
20 import com.intellij.psi.util.PsiUtilCore;
21 import com.intellij.refactoring.listeners.RefactoringElementAdapter;
22 import com.intellij.refactoring.listeners.RefactoringElementListener;
23 import com.intellij.sh.psi.ShFile;
24 import com.intellij.util.EnvironmentUtil;
25 import com.intellij.util.containers.ContainerUtil;
26 import org.jdom.Element;
27 import org.jetbrains.annotations.NonNls;
28 import org.jetbrains.annotations.NotNull;
29 import org.jetbrains.annotations.Nullable;
30
31 import java.io.File;
32
33 import static com.intellij.openapi.util.io.FileUtil.toSystemDependentName;
34 import static com.intellij.openapi.util.text.StringUtilRt.notNullize;
35 import static com.intellij.sh.ShBundle.message;
36
37 final class ShRunConfiguration extends LocatableConfigurationBase implements RefactoringListenerProvider {
38   @NonNls private static final String TAG_PREFIX = "INDEPENDENT_";
39   @NonNls private static final String SCRIPT_TEXT_TAG = "SCRIPT_TEXT";
40   @NonNls private static final String SCRIPT_PATH_TAG = "SCRIPT_PATH";
41   @NonNls private static final String SCRIPT_OPTIONS_TAG = "SCRIPT_OPTIONS";
42   @NonNls private static final String SCRIPT_WORKING_DIRECTORY_TAG = "SCRIPT_WORKING_DIRECTORY";
43   @NonNls private static final String INTERPRETER_PATH_TAG = "INTERPRETER_PATH";
44   @NonNls private static final String INTERPRETER_OPTIONS_TAG = "INTERPRETER_OPTIONS";
45   @NonNls private static final String EXECUTE_IN_TERMINAL_TAG = "EXECUTE_IN_TERMINAL";
46   @NonNls private static final String EXECUTE_SCRIPT_FILE_TAG = "EXECUTE_SCRIPT_FILE";
47
48   private String myScriptText = "";
49   private String myScriptPath = "";
50   private String myScriptOptions = "";
51   private String myInterpreterPath = "";
52   private String myInterpreterOptions = "";
53   private String myScriptWorkingDirectory = "";
54   private boolean myExecuteInTerminal = true;
55   private boolean myExecuteScriptFile = true;
56   private EnvironmentVariablesData myEnvData = EnvironmentVariablesData.DEFAULT;
57
58   ShRunConfiguration(@NotNull Project project, @NotNull ConfigurationFactory factory, @NotNull String name) {
59     super(project, factory, name);
60   }
61
62   @NotNull
63   @Override
64   public SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
65     return new ShRunConfigurationEditor(getProject());
66   }
67
68   @Override
69   public void checkConfiguration() throws RuntimeConfigurationException {
70     if (myExecuteScriptFile) {
71       if (!FileUtil.exists(myScriptPath)) {
72         throw new RuntimeConfigurationError(message("sh.run.script.not.found"));
73       }
74       if (StringUtil.isNotEmpty(myInterpreterPath) || !new File(myScriptPath).canExecute()) {
75         // WSL can be used as an interpreter
76         if (myInterpreterPath.endsWith("sh") && getWSLDistributionIfNeeded() != null) return;
77         if (!FileUtil.exists(myInterpreterPath)) {
78           throw new RuntimeConfigurationError(message("sh.run.interpreter.not.found"));
79         }
80         if (!new File(myInterpreterPath).canExecute()) {
81           throw new RuntimeConfigurationError(message("sh.run.interpreter.should.be.executable"));
82         }
83       }
84     }
85     if (!FileUtil.exists(myScriptWorkingDirectory)) {
86       throw new RuntimeConfigurationError(message("sh.run.working.dir.not.found"));
87     }
88   }
89
90   @Override
91   public @NotNull RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) {
92     return new ShRunConfigurationProfileState(environment.getProject(), this);
93   }
94
95   @Override
96   public void writeExternal(@NotNull Element element) {
97     super.writeExternal(element);
98     JDOMExternalizerUtil.writeField(element, SCRIPT_TEXT_TAG, myScriptText);
99     writePathWithMetadata(element, myScriptPath, SCRIPT_PATH_TAG);
100     JDOMExternalizerUtil.writeField(element, SCRIPT_OPTIONS_TAG, myScriptOptions);
101     writePathWithMetadata(element, myScriptWorkingDirectory, SCRIPT_WORKING_DIRECTORY_TAG);
102     writePathWithMetadata(element, myInterpreterPath, INTERPRETER_PATH_TAG);
103     JDOMExternalizerUtil.writeField(element, INTERPRETER_OPTIONS_TAG, myInterpreterOptions);
104     JDOMExternalizerUtil.writeField(element, EXECUTE_IN_TERMINAL_TAG, String.valueOf(myExecuteInTerminal));
105     JDOMExternalizerUtil.writeField(element, EXECUTE_SCRIPT_FILE_TAG, String.valueOf(myExecuteScriptFile));
106     myEnvData.writeExternal(element);
107   }
108
109   @Override
110   public void readExternal(@NotNull Element element) throws InvalidDataException {
111     super.readExternal(element);
112     myScriptText = readStringTagValue(element, SCRIPT_TEXT_TAG);
113     myScriptPath = readPathWithMetadata(element, SCRIPT_PATH_TAG);
114     myScriptOptions = readStringTagValue(element, SCRIPT_OPTIONS_TAG);
115     myScriptWorkingDirectory = readPathWithMetadata(element, SCRIPT_WORKING_DIRECTORY_TAG);
116     myInterpreterPath = readPathWithMetadata(element, INTERPRETER_PATH_TAG);
117     myInterpreterOptions = readStringTagValue(element, INTERPRETER_OPTIONS_TAG);
118     myExecuteInTerminal = Boolean.parseBoolean(JDOMExternalizerUtil.readField(element, EXECUTE_IN_TERMINAL_TAG, Boolean.TRUE.toString()));
119     myExecuteScriptFile = Boolean.parseBoolean(JDOMExternalizerUtil.readField(element, EXECUTE_SCRIPT_FILE_TAG, Boolean.TRUE.toString()));
120     myEnvData = EnvironmentVariablesData.readExternal(element);
121   }
122
123   @Nullable
124   @Override
125   public RefactoringElementListener getRefactoringElementListener(PsiElement element) {
126     if (StringUtil.isEmpty(myScriptPath) || !(element instanceof ShFile) || !myScriptPath.equals(getPathByElement(element))) return null;
127
128     return new RefactoringElementAdapter() {
129       @Override
130       protected void elementRenamedOrMoved(@NotNull PsiElement newElement) {
131         if (newElement instanceof ShFile) {
132           setScriptPath(((ShFile)newElement).getVirtualFile().getPath());
133         }
134       }
135
136       @Override
137       public void undoElementMovedOrRenamed(@NotNull PsiElement newElement, @NotNull String oldQualifiedName) {
138         elementRenamedOrMoved(newElement);
139       }
140     };
141   }
142
143   @Nullable
144   private static String getPathByElement(@NotNull PsiElement element) {
145     VirtualFile vfile = PsiUtilCore.getVirtualFile(element);
146     if (vfile == null) return null;
147     return vfile.getPath();
148   }
149
150   private static void writePathWithMetadata(@NotNull Element element, @NotNull String path, @NotNull String pathTag) {
151     String systemIndependentPath = FileUtil.toSystemIndependentName(path);
152     JDOMExternalizerUtil.writeField(element, TAG_PREFIX + pathTag, Boolean.toString(systemIndependentPath.equals(path)));
153     JDOMExternalizerUtil.writeField(element, pathTag, systemIndependentPath);
154   }
155
156   private static String readPathWithMetadata(@NotNull Element element, @NotNull String pathTag) {
157     return Boolean.parseBoolean(JDOMExternalizerUtil.readField(element, TAG_PREFIX + pathTag))
158            ? readStringTagValue(element, pathTag)
159            : toSystemDependentName(readStringTagValue(element, pathTag));
160   }
161
162   @NotNull
163   private static String readStringTagValue(@NotNull Element element, @NotNull String tagName) {
164     return notNullize(JDOMExternalizerUtil.readField(element, tagName), "");
165   }
166
167   public String getScriptText() {
168     return myScriptText;
169   }
170
171   public void setScriptText(String scriptText) {
172     myScriptText = scriptText;
173   }
174
175   public String getScriptPath() {
176     return myScriptPath;
177   }
178
179   public void setScriptPath(@NotNull String scriptPath) {
180     myScriptPath = scriptPath.trim();
181   }
182
183   public String getScriptOptions() {
184     return myScriptOptions;
185   }
186
187   public void setScriptOptions(@NotNull String scriptOptions) {
188     myScriptOptions = scriptOptions.trim();
189   }
190
191   public String getScriptWorkingDirectory() {
192     return myScriptWorkingDirectory;
193   }
194
195   public void setScriptWorkingDirectory(String scriptWorkingDirectory) {
196     myScriptWorkingDirectory = scriptWorkingDirectory.trim();
197   }
198
199   public boolean isExecuteInTerminal() {
200     return myExecuteInTerminal;
201   }
202
203   public void setExecuteInTerminal(boolean executeInTerminal) {
204     myExecuteInTerminal = executeInTerminal;
205   }
206
207   public boolean isExecuteScriptFile() {
208     return myExecuteScriptFile;
209   }
210
211   public void setExecuteScriptFile(boolean executeScriptFile) {
212     myExecuteScriptFile = executeScriptFile;
213   }
214
215   public EnvironmentVariablesData getEnvData() {
216     return myEnvData;
217   }
218
219   public void setEnvData(EnvironmentVariablesData envData) {
220     myEnvData = envData;
221   }
222
223   public String getInterpreterPath() {
224     return myInterpreterPath;
225   }
226
227   public void setInterpreterPath(@NotNull String interpreterPath) {
228     myInterpreterPath = interpreterPath.trim();
229   }
230
231   public String getInterpreterOptions() {
232     return myInterpreterOptions;
233   }
234
235   public void setInterpreterOptions(@NotNull String interpreterOptions) {
236     myInterpreterOptions = interpreterOptions.trim();
237   }
238
239   private static WSLDistribution getWSLDistributionIfNeeded() {
240     return getWSLDistributionIfNeeded(null, null);
241   }
242
243   public static WSLDistribution getWSLDistributionIfNeeded(@Nullable String interpreterPath, @Nullable @NlsSafe String scriptPath) {
244     if (!Experiments.getInstance().isFeatureEnabled("com.intellij.sh.run.with.wsl")) {
245       return null;
246     }
247
248     if (interpreterPath != null && new File(interpreterPath).canExecute()
249         || scriptPath != null && (scriptPath.endsWith("cmd") || scriptPath.endsWith("bat"))) {
250       return null;
251     }
252     if (EnvironmentUtil.getValue("SHELL") != null) {
253       return null;
254     }
255
256     return ContainerUtil.getFirstItem(WSLUtil.getAvailableDistributions());
257   }
258 }