22
22
import java .nio .charset .StandardCharsets ;
23
23
import java .text .MessageFormat ;
24
24
import java .util .ArrayList ;
25
+ import java .util .Comparator ;
25
26
import java .util .HashMap ;
27
+ import java .util .LinkedHashMap ;
26
28
import java .util .List ;
27
29
import java .util .Map ;
30
+ import java .util .Optional ;
28
31
29
32
import javax .xml .parsers .DocumentBuilder ;
30
33
import javax .xml .parsers .FactoryConfigurationError ;
@@ -170,6 +173,14 @@ public class DebugPlugin extends Plugin {
170
173
*/
171
174
public static final String EXTENSION_POINT_PROCESS_FACTORIES = "processFactories" ; //$NON-NLS-1$
172
175
176
+ /**
177
+ * Simple identifier constant (value <code>"execFactories"</code>) for the
178
+ * exec factories extension point.
179
+ *
180
+ * @since 3.23
181
+ */
182
+ public static final String EXTENSION_POINT_EXEC_FACTORIES = "execFactories" ; //$NON-NLS-1$
183
+
173
184
/**
174
185
* Simple identifier constant (value <code>"logicalStructureTypes"</code>) for the
175
186
* logical structure types extension point.
@@ -438,6 +449,11 @@ public class DebugPlugin extends Plugin {
438
449
*/
439
450
private HashMap <String , IConfigurationElement > fProcessFactories = null ;
440
451
452
+ /**
453
+ * List of exec factories.
454
+ */
455
+ private List <ExecFactoryFacade > execFactories ;
456
+
441
457
/**
442
458
* Service tracker for the workspace service
443
459
*/
@@ -841,10 +857,13 @@ public static IProcess newProcess(ILaunch launch, Process process, String label)
841
857
*/
842
858
public static IProcess newProcess (ILaunch launch , Process process , String label , Map <String , String > attributes ) {
843
859
ILaunchConfiguration config = launch .getLaunchConfiguration ();
844
- String processFactoryID = null ;
860
+ String processFactoryID = null ;
861
+ // FIXME hack for demo purpose we need an UI for the user to decide if a
862
+ // terminal
863
+ // is wanted
845
864
if (config != null ) {
846
865
try {
847
- processFactoryID = config .getAttribute (ATTR_PROCESS_FACTORY_ID , ( String ) null );
866
+ processFactoryID = config .getAttribute (ATTR_PROCESS_FACTORY_ID , "org.eclipse.debug.terminal.processFactory.cdt" ); //$NON-NLS-1$
848
867
} catch (CoreException e ) {
849
868
}
850
869
}
@@ -981,34 +1000,45 @@ public static Process exec(String[] cmdLine, File workingDirectory, String[] env
981
1000
* @since 3.14
982
1001
*/
983
1002
public static Process exec (String [] cmdLine , File workingDirectory , String [] envp , boolean mergeOutput ) throws CoreException {
984
- Process p = null ;
1003
+ List <ExecFactoryFacade > factories = DebugPlugin .getDefault ().getExecFactories ();
1004
+ Optional <File > directory = shortenWindowsPath (workingDirectory );
1005
+ Optional <Map <String , String >> envMap = Optional .ofNullable (envp ).map (array -> {
1006
+ Map <String , String > map = new LinkedHashMap <>();
1007
+ for (String e : array ) {
1008
+ int index = e .indexOf ('=' );
1009
+ if (index != -1 ) {
1010
+ map .put (e .substring (0 , index ), e .substring (index + 1 ));
1011
+ }
1012
+ }
1013
+ return Map .copyOf (map );
1014
+ });
1015
+ for (ExecFactoryFacade holder : factories ) {
1016
+ Optional <Process > exec = holder .exec (cmdLine .clone (), directory , envMap , mergeOutput );
1017
+ if (exec .isPresent ()) {
1018
+ return exec .get ();
1019
+ }
1020
+ }
985
1021
try {
986
1022
// starting with and without merged output could be done with the
987
1023
// same process builder approach but since the handling of
988
1024
// environment variables is slightly different between
989
1025
// ProcessBuilder and Runtime.exec only the new option uses process
990
1026
// builder to not break existing caller of this method
1027
+
991
1028
if (mergeOutput ) {
992
1029
ProcessBuilder pb = new ProcessBuilder (cmdLine );
993
- if (workingDirectory != null ) {
994
- pb .directory (shortenWindowsPath (workingDirectory ));
995
- }
1030
+ directory .ifPresent (pb ::directory );
996
1031
pb .redirectErrorStream (mergeOutput );
997
- if (envp != null ) {
1032
+ if (envMap . isPresent () ) {
998
1033
Map <String , String > env = pb .environment ();
999
1034
env .clear ();
1000
- for (String e : envp ) {
1001
- int index = e .indexOf ('=' );
1002
- if (index != -1 ) {
1003
- env .put (e .substring (0 , index ), e .substring (index + 1 ));
1004
- }
1005
- }
1035
+ env .putAll (envMap .get ());
1006
1036
}
1007
- p = pb .start ();
1008
- } else if (workingDirectory == null ) {
1009
- p = Runtime .getRuntime ().exec (cmdLine , envp );
1037
+ return pb .start ();
1038
+ } else if (directory . isEmpty () ) {
1039
+ return Runtime .getRuntime ().exec (cmdLine , envp );
1010
1040
} else {
1011
- p = Runtime .getRuntime ().exec (cmdLine , envp , shortenWindowsPath ( workingDirectory ));
1041
+ return Runtime .getRuntime ().exec (cmdLine , envp , directory . get ( ));
1012
1042
}
1013
1043
} catch (IOException e ) {
1014
1044
Status status = new Status (IStatus .ERROR , getUniqueIdentifier (), ERROR , DebugCoreMessages .DebugPlugin_0 , e );
@@ -1021,18 +1051,18 @@ public static Process exec(String[] cmdLine, File workingDirectory, String[] env
1021
1051
if (handler != null ) {
1022
1052
Object result = handler .handleStatus (status , null );
1023
1053
if (result instanceof Boolean resultValue && resultValue ) {
1024
- p = exec (cmdLine , null );
1054
+ return exec (cmdLine , null );
1025
1055
}
1026
1056
}
1027
1057
}
1028
- return p ;
1058
+ return null ;
1029
1059
}
1030
1060
1031
1061
// https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
1032
1062
private static final int WINDOWS_MAX_PATH = 258 ;
1033
1063
1034
- private static File shortenWindowsPath (File path ) {
1035
- if (path .getPath ().length () > WINDOWS_MAX_PATH && Platform .OS .isWindows ()) {
1064
+ private static Optional < File > shortenWindowsPath (File path ) {
1065
+ if (path != null && path .getPath ().length () > WINDOWS_MAX_PATH && Platform .OS .isWindows ()) {
1036
1066
// When spawning new processes on Windows, there is no uniform way
1037
1067
// to use long working directory paths that exceed the default path
1038
1068
// length limit, like for example using the raw path prefix '\\?\'
@@ -1042,12 +1072,12 @@ private static File shortenWindowsPath(File path) {
1042
1072
@ SuppressWarnings ("restriction" )
1043
1073
String shortPath = org .eclipse .core .internal .filesystem .local .Win32Handler .getShortPathName (path .toString ());
1044
1074
if (shortPath != null ) {
1045
- return new File (shortPath );
1075
+ return Optional . of ( new File (shortPath ) );
1046
1076
} else {
1047
1077
log (Status .warning ("Working directory of process to create exceeds Window's MAX_PATH limit and shortening the path failed." )); //$NON-NLS-1$
1048
1078
}
1049
1079
}
1050
- return path ;
1080
+ return Optional . ofNullable ( path ) ;
1051
1081
}
1052
1082
1053
1083
/**
@@ -1189,6 +1219,39 @@ private void initializeProcessFactories() {
1189
1219
}
1190
1220
}
1191
1221
1222
+ private synchronized List <ExecFactoryFacade > getExecFactories () {
1223
+ if (execFactories == null ) {
1224
+ IExtensionPoint extensionPoint = Platform .getExtensionRegistry ().getExtensionPoint (DebugPlugin .PI_DEBUG_CORE , EXTENSION_POINT_EXEC_FACTORIES );
1225
+ IConfigurationElement [] infos = extensionPoint .getConfigurationElements ();
1226
+ List <ExecFactoryFacade > list = new ArrayList <>();
1227
+ for (IConfigurationElement configurationElement : infos ) {
1228
+ String clz = configurationElement .getAttribute ("class" ); //$NON-NLS-1$
1229
+ if (clz != null ) {
1230
+ int priority ;
1231
+ String attribute = configurationElement .getAttribute ("priority" ); //$NON-NLS-1$
1232
+ if (attribute == null ) {
1233
+ priority = 0 ;
1234
+ }
1235
+ try {
1236
+ priority = Integer .parseInt (attribute );
1237
+ } catch (NumberFormatException e ) {
1238
+ log (new Status (IStatus .ERROR , DebugPlugin .PI_DEBUG_CORE , ERROR , MessageFormat .format (DebugCoreMessages .DebugPlugin_invalid_exec_factory , new Object [] {
1239
+ configurationElement .getContributor ().getName () }), null ));
1240
+ priority = 0 ;
1241
+ }
1242
+ list .add (new ExecFactoryFacade (configurationElement , priority ));
1243
+ } else {
1244
+ String badDefiner = configurationElement .getContributor ().getName ();
1245
+ log (new Status (IStatus .ERROR , DebugPlugin .PI_DEBUG_CORE , ERROR , MessageFormat .format (DebugCoreMessages .DebugPlugin_invalid_exec_factory , new Object [] {
1246
+ badDefiner }), null ));
1247
+ }
1248
+ }
1249
+ list .sort (Comparator .comparingInt (ExecFactoryFacade ::getPriority ).reversed ());
1250
+ execFactories = List .copyOf (list );
1251
+ }
1252
+ return execFactories ;
1253
+ }
1254
+
1192
1255
private void invalidStatusHandler (Exception e , String id ) {
1193
1256
log (new Status (IStatus .ERROR , DebugPlugin .PI_DEBUG_CORE , ERROR , MessageFormat .format (DebugCoreMessages .DebugPlugin_5 , new Object [] { id }), e ));
1194
1257
}
@@ -1221,6 +1284,34 @@ public boolean equals(Object obj) {
1221
1284
}
1222
1285
}
1223
1286
1287
+ private class ExecFactoryFacade implements ExecFactory {
1288
+
1289
+ private IConfigurationElement element ;
1290
+ private int priority ;
1291
+
1292
+ ExecFactoryFacade (IConfigurationElement element , int priority ) {
1293
+ this .element = element ;
1294
+ this .priority = priority ;
1295
+ }
1296
+
1297
+ public int getPriority () {
1298
+ return priority ;
1299
+ }
1300
+
1301
+ @ Override
1302
+ public Optional <Process > exec (String [] cmdLine , Optional <File > workingDirectory , Optional <Map <String , String >> environment , boolean mergeOutput ) throws CoreException {
1303
+ ExecFactory extension ;
1304
+ try {
1305
+ extension = (ExecFactory ) element .createExecutableExtension (IConfigurationElementConstants .CLASS );
1306
+ } catch (CoreException e ) {
1307
+ log (e );
1308
+ return Optional .empty ();
1309
+ }
1310
+ return extension .exec (cmdLine , workingDirectory , environment , mergeOutput );
1311
+ }
1312
+
1313
+ }
1314
+
1224
1315
/**
1225
1316
* Executes runnables after event dispatch is complete.
1226
1317
*
0 commit comments