758a7e6b981d0e9ee2e3302cbe27dc2230e54ee2
[idea/community.git] / platform / execution / src / com / intellij / execution / runners / ExecutionEnvironment.java
1 // Copyright 2000-2021 JetBrains s.r.o. and contributors. 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.execution.runners;
3
4 import com.intellij.execution.*;
5 import com.intellij.execution.configurations.ConfigurationPerRunnerSettings;
6 import com.intellij.execution.configurations.RunProfile;
7 import com.intellij.execution.configurations.RunProfileState;
8 import com.intellij.execution.configurations.RunnerSettings;
9 import com.intellij.execution.target.*;
10 import com.intellij.execution.ui.RunContentDescriptor;
11 import com.intellij.openapi.Disposable;
12 import com.intellij.openapi.actionSystem.DataContext;
13 import com.intellij.openapi.actionSystem.DataKey;
14 import com.intellij.openapi.project.Project;
15 import com.intellij.openapi.util.Disposer;
16 import com.intellij.openapi.util.UserDataHolderBase;
17 import org.jetbrains.annotations.*;
18
19 import java.util.HashMap;
20 import java.util.Map;
21 import java.util.concurrent.atomic.AtomicLong;
22
23 import static com.intellij.openapi.actionSystem.CommonDataKeys.*;
24 import static com.intellij.openapi.actionSystem.PlatformCoreDataKeys.MODULE;
25 import static com.intellij.openapi.actionSystem.PlatformCoreDataKeys.PROJECT_FILE_DIRECTORY;
26
27 public final class ExecutionEnvironment extends UserDataHolderBase implements Disposable {
28   private static final AtomicLong myIdHolder = new AtomicLong(1L);
29
30   @NotNull private final Project myProject;
31
32   @NotNull private final RunProfile myRunProfile;
33   @NotNull private final Executor myExecutor;
34
35   @NotNull private final ExecutionTarget myTarget;
36   private TargetEnvironmentRequest myTargetEnvironmentRequest;
37   private volatile TargetEnvironment myPrepareRemoteEnvironment;
38
39   @Nullable private RunnerSettings myRunnerSettings;
40   @Nullable private ConfigurationPerRunnerSettings myConfigurationSettings;
41   @Nullable private final RunnerAndConfigurationSettings myRunnerAndConfigurationSettings;
42   @Nullable private RunContentDescriptor myContentToReuse;
43   private final ProgramRunner<?> myRunner;
44   private long myExecutionId = 0;
45   @Nullable private DataContext myDataContext;
46   @Nullable private String myModulePath;
47
48   @Nullable
49   private ProgramRunner.Callback callback;
50   private boolean isHeadless = false;
51
52   @TestOnly
53   public ExecutionEnvironment() {
54     myProject = null;
55     myContentToReuse = null;
56     myRunnerAndConfigurationSettings = null;
57     myExecutor = null;
58     myRunner = null;
59     myRunProfile = null;
60     myTarget = null;
61   }
62
63   public ExecutionEnvironment(@NotNull Executor executor,
64                               @NotNull ProgramRunner runner,
65                               @NotNull RunnerAndConfigurationSettings settings,
66                               @NotNull Project project) {
67     this(settings.getConfiguration(),
68          executor,
69          DefaultExecutionTarget.INSTANCE,
70          project,
71          settings.getRunnerSettings(runner),
72          settings.getConfigurationSettings(runner),
73          null,
74          settings,
75          runner, null);
76   }
77
78   ExecutionEnvironment(@NotNull RunProfile runProfile,
79                        @NotNull Executor executor,
80                        @NotNull ExecutionTarget target,
81                        @NotNull Project project,
82                        @Nullable RunnerSettings runnerSettings,
83                        @Nullable ConfigurationPerRunnerSettings configurationSettings,
84                        @Nullable RunContentDescriptor contentToReuse,
85                        @Nullable RunnerAndConfigurationSettings settings,
86                        @NotNull ProgramRunner<?> runner,
87                        @Nullable ProgramRunner.Callback callback) {
88     myExecutor = executor;
89     myTarget = target;
90     myRunProfile = runProfile;
91     myRunnerSettings = runnerSettings;
92     myConfigurationSettings = configurationSettings;
93     myProject = project;
94     setContentToReuse(contentToReuse);
95     myRunnerAndConfigurationSettings = settings;
96
97     myRunner = runner;
98
99     this.callback = callback;
100   }
101
102   public @NotNull TargetEnvironmentRequest getTargetEnvironmentRequest() {
103     if (myTargetEnvironmentRequest != null) {
104       return myTargetEnvironmentRequest;
105     }
106     return myTargetEnvironmentRequest = createTargetEnvironmentRequest();
107   }
108
109   @NotNull
110   private TargetEnvironmentRequest createTargetEnvironmentRequest() {
111     return TargetEnvironmentConfigurations.createEnvironmentRequest(myRunProfile, myProject);
112   }
113
114   @ApiStatus.Experimental
115   public @NotNull TargetEnvironment getPreparedTargetEnvironment(@NotNull RunProfileState runProfileState,
116                                                                  @NotNull TargetProgressIndicator targetProgressIndicator)
117     throws ExecutionException {
118     if (myPrepareRemoteEnvironment != null) {
119       // In a correct implementation that uses the new API this condition is always true.
120       return myPrepareRemoteEnvironment;
121     }
122     // Warning: this method executes in EDT!
123     return prepareTargetEnvironment(runProfileState, targetProgressIndicator);
124   }
125
126   @ApiStatus.Experimental
127   public @NotNull TargetEnvironment prepareTargetEnvironment(@NotNull RunProfileState runProfileState,
128                                                              @NotNull TargetProgressIndicator targetProgressIndicator)
129     throws ExecutionException {
130     TargetEnvironmentRequest request = null;
131     if (runProfileState instanceof TargetEnvironmentAwareRunProfileState &&
132         myRunProfile instanceof TargetEnvironmentAwareRunProfile &&
133         TargetEnvironmentConfigurations.getEffectiveTargetName((TargetEnvironmentAwareRunProfile)myRunProfile, myProject) == null) {
134       request = ((TargetEnvironmentAwareRunProfileState)runProfileState).createCustomTargetEnvironmentRequest();
135     }
136     if (request == null) {
137       request = getTargetEnvironmentRequest();
138     }
139     if (runProfileState instanceof TargetEnvironmentAwareRunProfileState) {
140       ((TargetEnvironmentAwareRunProfileState)runProfileState)
141         .prepareTargetEnvironmentRequest(request, targetProgressIndicator);
142     }
143     myPrepareRemoteEnvironment = request.prepareEnvironment(targetProgressIndicator);
144     if (runProfileState instanceof TargetEnvironmentAwareRunProfileState) {
145       ((TargetEnvironmentAwareRunProfileState)runProfileState)
146         .handleCreatedTargetEnvironment(myPrepareRemoteEnvironment, targetProgressIndicator);
147     }
148     return myPrepareRemoteEnvironment;
149   }
150
151   @ApiStatus.Internal
152   public void setCallback(@Nullable ProgramRunner.Callback callback) {
153     this.callback = callback;
154   }
155
156   @Nullable
157   public ProgramRunner.Callback getCallback() {
158     return callback;
159   }
160
161   @Override
162   public void dispose() {
163     myContentToReuse = null;
164   }
165
166   @NotNull
167   public Project getProject() {
168     return myProject;
169   }
170
171   @NotNull
172   public ExecutionTarget getExecutionTarget() {
173     return myTarget;
174   }
175
176   @NotNull
177   public RunProfile getRunProfile() {
178     return myRunProfile;
179   }
180
181   @Nullable
182   public RunnerAndConfigurationSettings getRunnerAndConfigurationSettings() {
183     return myRunnerAndConfigurationSettings;
184   }
185
186   @Nullable
187   public RunContentDescriptor getContentToReuse() {
188     return myContentToReuse;
189   }
190
191   public void setContentToReuse(@Nullable RunContentDescriptor contentToReuse) {
192     myContentToReuse = contentToReuse;
193
194     if (contentToReuse != null) {
195       Disposer.register(contentToReuse, this);
196     }
197   }
198
199   @NotNull
200   public ProgramRunner<?> getRunner() {
201     return myRunner;
202   }
203
204   @Nullable
205   public RunnerSettings getRunnerSettings() {
206     return myRunnerSettings;
207   }
208
209   @Nullable
210   public ConfigurationPerRunnerSettings getConfigurationSettings() {
211     return myConfigurationSettings;
212   }
213
214   @Nullable
215   public RunProfileState getState() throws ExecutionException {
216     return myRunProfile.getState(myExecutor, this);
217   }
218
219   public long assignNewExecutionId() {
220     myExecutionId = myIdHolder.incrementAndGet();
221     return myExecutionId;
222   }
223
224   public void setExecutionId(long executionId) {
225     myExecutionId = executionId;
226   }
227
228   /**
229    * By default a new unique executionId is assigned to each new {@link ExecutionEnvironment} ({@see assignNewExecutionId}).
230    * Can be set manually to create a batch of {@link ExecutionEnvironment} that are semantically a "single launch".
231    * {@link RunContentDescriptor}s will not reuse each other tabs if they have the same executionId.
232    *
233    * @return An id that will be propagated to resulting {@link RunContentDescriptor}.
234    */
235   public long getExecutionId() {
236     return myExecutionId;
237   }
238
239   @NotNull
240   public Executor getExecutor() {
241     return myExecutor;
242   }
243
244   @Override
245   public String toString() {
246     if (myRunnerAndConfigurationSettings != null) {
247       return myRunnerAndConfigurationSettings.getName();
248     }
249     return myRunProfile.getName();
250   }
251
252   @ApiStatus.Experimental
253   public boolean isHeadless() {
254     return isHeadless;
255   }
256
257   @ApiStatus.Experimental
258   public void setHeadless() {
259     isHeadless = true;
260   }
261
262   void setDataContext(@NotNull DataContext dataContext) {
263     myDataContext = CachingDataContext.cacheIfNeeded(dataContext);
264   }
265
266   @Nullable
267   public DataContext getDataContext() {
268     return myDataContext;
269   }
270
271
272   void setModulePath(@NotNull String modulePath) {
273     this.myModulePath = modulePath;
274   }
275
276   @Nullable
277   public String getModulePath() {
278     return myModulePath;
279   }
280
281   private static final class CachingDataContext implements DataContext {
282     private static final DataKey[] keys = {PROJECT, PROJECT_FILE_DIRECTORY, EDITOR, VIRTUAL_FILE, MODULE, PSI_FILE};
283     private final Map<String, Object> values = new HashMap<>();
284
285     @NotNull
286     static CachingDataContext cacheIfNeeded(@NotNull DataContext context) {
287       if (context instanceof CachingDataContext) {
288         return (CachingDataContext)context;
289       }
290       return new CachingDataContext(context);
291     }
292
293     private CachingDataContext(DataContext context) {
294       for (DataKey key : keys) {
295         values.put(key.getName(), key.getData(context));
296       }
297     }
298
299     @Override
300     public Object getData(@NotNull @NonNls String dataId) {
301       return values.get(dataId);
302     }
303   }
304
305   /**
306    * @return A valid executionId that was not previously assigned to any {@link ExecutionEnvironment}.
307    */
308   public static long getNextUnusedExecutionId() {
309     return myIdHolder.incrementAndGet();
310   }
311 }