diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7bc10394da4..8cd4acdc7be 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -64,18 +64,23 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- - name: Set up JDK 8
+ - name: Set up JDK 17
uses: actions/setup-java@v3
with:
- java-version: '8'
+ java-version: '17'
distribution: 'temurin'
+ cache: maven
+ - name: Set up Maven
+ uses: stCarolas/setup-maven@v4.5
+ with:
+ maven-version: 3.9.2
- name: Visual Studio shell
uses: egor-tensin/vs-shell@v2
with:
arch: x64
- name: Build
working-directory: features/org.eclipse.equinox.executable.feature/library/win32
- run: nmake -f make_win64.mak
+ run: nmake -f make_win64.mak test
shell: pwsh
- name: Upload artifacts
uses: actions/upload-artifact@v3
@@ -86,6 +91,49 @@ jobs:
features/org.eclipse.equinox.executable.feature/library/win32/eclipse*.exe
features/org.eclipse.equinox.executable.feature/library/win32/eclipse*.dll
if-no-files-found: error
+ - name: Upload Windows Test Results
+ uses: actions/upload-artifact@v3
+ with:
+ name: test-results
+ if-no-files-found: error
+ path: '**/target/*-reports/*.xml'
+ build-launcher-linux64:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 1
+ - name: Set up JDK 17
+ uses: actions/setup-java@v3
+ with:
+ java-version: '17'
+ distribution: 'temurin'
+ cache: maven
+ - name: Set up Maven
+ uses: stCarolas/setup-maven@v4.5
+ with:
+ maven-version: 3.9.2
+ - name: Install GTK headers
+ run: sudo apt-get install -y libgtk-3-dev
+ - name: Build
+ working-directory: features/org.eclipse.equinox.executable.feature/library/gtk
+ run: ./build.sh test
+ shell: bash
+ - name: Upload artifacts
+ uses: actions/upload-artifact@v3
+ if: success()
+ with:
+ name: Linux launcher artifacts
+ path: |
+ features/org.eclipse.equinox.executable.feature/library/gtk/eclipse
+ features/org.eclipse.equinox.executable.feature/library/gtk/eclipse*.so
+ if-no-files-found: error
+ - name: Upload Linux Test Results
+ uses: actions/upload-artifact@v3
+ with:
+ name: test-results
+ if-no-files-found: error
+ path: '**/target/*-reports/*.xml'
tck:
runs-on: ubuntu-latest
steps:
diff --git a/features/org.eclipse.equinox.executable.feature/library/eclipse.c b/features/org.eclipse.equinox.executable.feature/library/eclipse.c
index 4ad3b2053d1..66c80fd00e3 100644
--- a/features/org.eclipse.equinox.executable.feature/library/eclipse.c
+++ b/features/org.eclipse.equinox.executable.feature/library/eclipse.c
@@ -228,51 +228,6 @@ home directory.");
#define OLD_STARTUP _T_ECLIPSE("startup.jar")
#define CLASSPATH_PREFIX _T_ECLIPSE("-Djava.class.path=")
-/* Define constants for the options recognized by the launcher. */
-#define CONSOLE _T_ECLIPSE("-console")
-#define CONSOLELOG _T_ECLIPSE("-consoleLog")
-#define DEBUG _T_ECLIPSE("-debug")
-#define OS _T_ECLIPSE("-os")
-#define OSARCH _T_ECLIPSE("-arch")
-#define NOSPLASH _T_ECLIPSE("-nosplash")
-#define LAUNCHER _T_ECLIPSE("-launcher")
-#define SHOWSPLASH _T_ECLIPSE("-showsplash")
-#define EXITDATA _T_ECLIPSE("-exitdata")
-#define STARTUP _T_ECLIPSE("-startup")
-#define VM _T_ECLIPSE("-vm")
-#define WS _T_ECLIPSE("-ws")
-#define NAME _T_ECLIPSE("-name")
-#define VMARGS _T_ECLIPSE("-vmargs") /* special option processing required */
-#define CP _T_ECLIPSE("-cp")
-#define CLASSPATH _T_ECLIPSE("-classpath")
-#define JAR _T_ECLIPSE("-jar")
-#define PROTECT _T_ECLIPSE("-protect")
-
-#define OPENFILE _T_ECLIPSE("--launcher.openFile")
-#define DEFAULTACTION _T_ECLIPSE("--launcher.defaultAction")
-#define TIMEOUT _T_ECLIPSE("--launcher.timeout")
-#define LIBRARY _T_ECLIPSE("--launcher.library")
-#define SUPRESSERRORS _T_ECLIPSE("--launcher.suppressErrors")
-#define INI _T_ECLIPSE("--launcher.ini")
-#define APPEND_VMARGS _T_ECLIPSE("--launcher.appendVmargs")
-#define OVERRIDE_VMARGS _T_ECLIPSE("--launcher.overrideVmargs")
-#define SECOND_THREAD _T_ECLIPSE("--launcher.secondThread")
-#define PERM_GEN _T_ECLIPSE("--launcher.XXMaxPermSize")
-
-#define XXPERMGEN _T_ECLIPSE("-XX:MaxPermSize=")
-#define ADDMODULES _T_ECLIPSE("--add-modules")
-#define ACTION_OPENFILE _T_ECLIPSE("openFile")
-#define GTK_VERSION _T_ECLIPSE("--launcher.GTK_version")
-
-/* constants for ee options file */
-#define EE_EXECUTABLE _T_ECLIPSE("-Dee.executable=")
-#define EE_CONSOLE _T_ECLIPSE("-Dee.executable.console=")
-#define EE_VM_LIBRARY _T_ECLIPSE("-Dee.vm.library=")
-#define EE_LIBRARY_PATH _T_ECLIPSE("-Dee.library.path=")
-#define EE_HOME _T_ECLIPSE("-Dee.home=")
-#define EE_FILENAME _T_ECLIPSE("-Dee.filename=")
-#define EE_HOME_VAR _T_ECLIPSE("${ee.home}")
-
/* Splash screen names to look for when -showsplash points to a directory or plugin */
#define SPLASH_IMAGES _T_ECLIPSE("splash.png\0" "splash.jpg\0" "splash.jpeg\0" "splash.gif\0" "splash.bmp\0" "\0")
@@ -383,7 +338,7 @@ static _TCHAR* formatVmCommandMsg( _TCHAR* args[], _TCHAR* vmArgs[], _TCHAR* pr
static _TCHAR* getDefaultOfficialName();
static _TCHAR* findStartupJar();
static _TCHAR* findSplash(_TCHAR* splashArg);
-static _TCHAR** getRelaunchCommand( _TCHAR **vmCommand );
+static _TCHAR** getRelaunchCommand( _TCHAR **newLaucherArgs );
static const _TCHAR* getVMArch();
static int _run(int argc, _TCHAR* argv[], _TCHAR* vmArgs[]);
static _TCHAR** mergeConfigurationFilesVMArgs();
@@ -512,7 +467,7 @@ static int _run(int argc, _TCHAR* argv[], _TCHAR* vmArgs[])
_TCHAR* errorMsg = NULL, *msg = NULL;
JavaResults* javaResults = NULL;
int launchMode;
- int running = 1;
+ int exitCode;
/* Initialize official program name */
officialName = name != NULL ? _tcsdup( name ) : getDefaultOfficialName();
@@ -674,137 +629,124 @@ static int _run(int argc, _TCHAR* argv[], _TCHAR* vmArgs[])
vmCommand = buildLaunchCommand(javaVM, vmCommandArgs, progCommandArgs);
}
- /* While the Java VM should be restarted */
- while(running)
- {
- msg = formatVmCommandMsg( vmCommand, vmCommandArgs, progCommandArgs );
- if (debug) _tprintf( goVMMsg, msg );
+ msg = formatVmCommandMsg( vmCommand, vmCommandArgs, progCommandArgs );
+ if (debug) _tprintf( goVMMsg, msg );
- if(launchMode == LAUNCH_JNI) {
- javaResults = startJavaVM(jniLib, vmCommandArgs, progCommandArgs, jarFile);
- } else {
- javaResults = launchJavaVM(vmCommand);
- }
+ if(launchMode == LAUNCH_JNI) {
+ javaResults = startJavaVM(jniLib, vmCommandArgs, progCommandArgs, jarFile);
+ } else {
+ javaResults = launchJavaVM(vmCommand);
+ }
- if (javaResults == NULL) {
- /* shouldn't happen, but just in case */
- javaResults = malloc(sizeof(JavaResults));
- javaResults->launchResult = -11;
- javaResults->runResult = 0;
- javaResults->errorMessage = _tcsdup(javaFailureMsg);
- }
+ if (javaResults == NULL) {
+ /* shouldn't happen, but just in case */
+ javaResults = malloc(sizeof(JavaResults));
+ javaResults->launchResult = -11;
+ javaResults->runResult = 0;
+ javaResults->errorMessage = _tcsdup(javaFailureMsg);
+ }
- switch( javaResults->launchResult + javaResults->runResult ) {
- case 0: /* normal exit */
- running = 0;
- break;
- case RESTART_LAST_EC:
- if (launchMode == LAUNCH_JNI) {
- /* copy for relaunch, +1 to ensure NULL terminated */
- relaunchCommand = malloc((initialArgc + 1) * sizeof(_TCHAR*));
- memcpy(relaunchCommand, initialArgv, (initialArgc + 1) * sizeof(_TCHAR*));
- relaunchCommand[initialArgc] = 0;
- relaunchCommand[0] = program;
- running = 0;
- }
- break;
-
- case RESTART_NEW_EC:
- if(launchMode == LAUNCH_EXE) {
- if (exitData != NULL) free(exitData);
- if (getSharedData( sharedID, &exitData ) != 0)
- exitData = NULL;
- }
- if (exitData != 0) {
- if (vmCommand != NULL) free( vmCommand );
- vmCommand = parseArgList( exitData );
- if (launchMode == LAUNCH_JNI) {
- relaunchCommand = getRelaunchCommand(vmCommand);
- running = 0;
- }
- } else {
- running = 0;
- if (debug) {
- if (!suppressErrors)
- displayMessage( officialName, shareMsg );
- else
- _ftprintf(stderr, _T_ECLIPSE("%s:\n%s\n"), officialName, shareMsg);
- }
- }
- break;
- default: {
- _TCHAR *title = _tcsdup(officialName);
- running = 0;
- errorMsg = NULL;
- if (launchMode == LAUNCH_EXE) {
- if (exitData != NULL) free(exitData);
- if (getSharedData( sharedID, &exitData ) != 0)
- exitData = NULL;
- }
- if (exitData != 0) {
- errorMsg = exitData;
- exitData = NULL;
- if (_tcslen( errorMsg ) > 0) {
- _TCHAR *str;
- if (_tcsncmp(errorMsg, _T_ECLIPSE("
"), _tcslen(_T_ECLIPSE(""))) == 0) {
- str = _tcsstr(errorMsg, _T_ECLIPSE(""));
- if (str != NULL) {
- free( title );
- str[0] = _T_ECLIPSE('\0');
- title = _tcsdup( errorMsg + _tcslen(_T_ECLIPSE("")) );
- str = _tcsdup( str + _tcslen(_T_ECLIPSE("")) );
- free( errorMsg );
- errorMsg = str;
- }
- }
- }
- } else {
- if (debug) {
- if (!suppressErrors)
- displayMessage( title, shareMsg );
- else
- _ftprintf(stderr, _T_ECLIPSE("%s:\n%s\n"), title, shareMsg);
- }
- }
- if (errorMsg == NULL) {
- if (javaResults->runResult) {
- /* java was started ok, but returned non-zero exit code */
- errorMsg = malloc( (_tcslen(returnCodeMsg) + _tcslen(msg) + 10) *sizeof(_TCHAR));
- _stprintf(errorMsg, returnCodeMsg,javaResults->runResult, msg);
- } else if (javaResults->errorMessage != NULL){
- /* else we had a problem launching java, use custom error message */
- errorMsg = javaResults->errorMessage;
- } else {
- /* no custom message, use generic message */
- errorMsg = malloc( (_tcslen(exitMsg) + _tcslen(msg) + 10) * sizeof(_TCHAR) );
- _stprintf( errorMsg, exitMsg, javaResults->launchResult, msg );
- }
- }
-
- if (_tcslen(errorMsg) > 0) {
- if (!suppressErrors)
- displayMessage( title, errorMsg );
- else
- _ftprintf(stderr, _T_ECLIPSE("%s:\n%s\n"), title, errorMsg);
- }
- free( errorMsg );
- free( title );
- break;
- }
- }
- free( msg );
- }
+ switch( javaResults->launchResult + javaResults->runResult ) {
+ case 0: /* normal exit */
+ break;
+ case RESTART_LAST_EC:
+ /* copy for relaunch, +1 to ensure NULL terminated */
+ relaunchCommand = malloc((initialArgc + 1) * sizeof(_TCHAR*));
+ memcpy(relaunchCommand, initialArgv, (initialArgc + 1) * sizeof(_TCHAR*));
+ relaunchCommand[initialArgc] = 0;
+ relaunchCommand[0] = program;
+ break;
+
+ case RESTART_NEW_EC:
+ if(launchMode == LAUNCH_EXE) {
+ if (exitData != NULL) free(exitData);
+ if (getSharedData( sharedID, &exitData ) != 0)
+ exitData = NULL;
+ }
+ if (exitData != 0) {
+ if (vmCommand != NULL) free( vmCommand );
+ vmCommand = parseArgList( exitData );
+ relaunchCommand = getRelaunchCommand(vmCommand);
+ } else {
+ if (debug) {
+ if (!suppressErrors)
+ displayMessage( officialName, shareMsg );
+ else
+ _ftprintf(stderr, _T_ECLIPSE("%s:\n%s\n"), officialName, shareMsg);
+ }
+ }
+ break;
+ default: {
+ _TCHAR *title = _tcsdup(officialName);
+ errorMsg = NULL;
+ if (launchMode == LAUNCH_EXE) {
+ if (exitData != NULL) free(exitData);
+ if (getSharedData( sharedID, &exitData ) != 0)
+ exitData = NULL;
+ }
+ if (exitData != 0) {
+ errorMsg = exitData;
+ exitData = NULL;
+ if (_tcslen( errorMsg ) > 0) {
+ _TCHAR *str;
+ if (_tcsncmp(errorMsg, _T_ECLIPSE(""), _tcslen(_T_ECLIPSE(""))) == 0) {
+ str = _tcsstr(errorMsg, _T_ECLIPSE(""));
+ if (str != NULL) {
+ free( title );
+ str[0] = _T_ECLIPSE('\0');
+ title = _tcsdup( errorMsg + _tcslen(_T_ECLIPSE("")) );
+ str = _tcsdup( str + _tcslen(_T_ECLIPSE("")) );
+ free( errorMsg );
+ errorMsg = str;
+ }
+ }
+ }
+ } else {
+ if (debug) {
+ if (!suppressErrors)
+ displayMessage( title, shareMsg );
+ else
+ _ftprintf(stderr, _T_ECLIPSE("%s:\n%s\n"), title, shareMsg);
+ }
+ }
+ if (errorMsg == NULL) {
+ if (javaResults->runResult) {
+ /* java was started ok, but returned non-zero exit code */
+ errorMsg = malloc( (_tcslen(returnCodeMsg) + _tcslen(msg) + 10) *sizeof(_TCHAR));
+ _stprintf(errorMsg, returnCodeMsg,javaResults->runResult, msg);
+ } else if (javaResults->errorMessage != NULL){
+ /* else we had a problem launching java, use custom error message */
+ errorMsg = javaResults->errorMessage;
+ } else {
+ /* no custom message, use generic message */
+ errorMsg = malloc( (_tcslen(exitMsg) + _tcslen(msg) + 10) * sizeof(_TCHAR) );
+ _stprintf( errorMsg, exitMsg, javaResults->launchResult, msg );
+ }
+ }
- if(relaunchCommand != NULL)
- restartLauncher(NULL, relaunchCommand);
+ if (_tcslen(errorMsg) > 0) {
+ if (!suppressErrors)
+ displayMessage( title, errorMsg );
+ else
+ _ftprintf(stderr, _T_ECLIPSE("%s:\n%s\n"), title, errorMsg);
+ }
+ free( errorMsg );
+ free( title );
+ break;
+ }
+ }
+ free( msg );
- if (launchMode == LAUNCH_JNI)
- cleanupVM(javaResults->launchResult ? javaResults->launchResult : javaResults->runResult);
+ if (sharedID != NULL) {
+ destroySharedData( sharedID );
+ free( sharedID );
+ }
- if (sharedID != NULL) {
- destroySharedData( sharedID );
- free( sharedID );
- }
+ if(relaunchCommand != NULL)
+ restartLauncher(NULL, relaunchCommand);
+
+ if (launchMode == LAUNCH_JNI)
+ cleanupVM(javaResults->launchResult ? javaResults->launchResult : javaResults->runResult);
/* Cleanup time. */
free( vmCommandArgs );
@@ -823,10 +765,9 @@ static int _run(int argc, _TCHAR* argv[], _TCHAR* vmArgs[])
if (javaResults == NULL)
return -1;
- /* reuse the running variable for convenience */
- running = javaResults->launchResult != 0 ? javaResults->launchResult : javaResults->runResult;
+ exitCode = javaResults->launchResult != 0 ? javaResults->launchResult : javaResults->runResult;
free(javaResults);
- return running;
+ return exitCode;
}
static _TCHAR** buildLaunchCommand( _TCHAR* program, _TCHAR** vmArgs, _TCHAR** progArgs ) {
@@ -1522,42 +1463,121 @@ static _TCHAR* findStartupJar(){
}
/*
- * Return the portion of the vmCommand that should be used for relaunching
+ * Return the new launcher arguments that should be used for relaunching
*
* The memory allocated for the command array must be freed
*/
-static _TCHAR ** getRelaunchCommand( _TCHAR **vmCommand )
+static _TCHAR ** getRelaunchCommand( _TCHAR **newLaucherArgs )
{
- int i = -1, req = 0, begin = -1;
+ int newArgsSize = -1, newVmargsStart = -1, skipOldUserArgs = 0;
int idx = 0;
_TCHAR ** relaunch;
- if (vmCommand == NULL) return NULL;
- while(vmCommand[++i] != NULL){
- if ( begin == -1 && _tcsicmp( vmCommand[i], *reqVMarg[req] ) == 0) {
- if(reqVMarg[++req] == NULL || *reqVMarg[req] == NULL){
- begin = i + 1;
- }
+ if (newLaucherArgs == NULL) return NULL;
+ /*
+ * Visit new args to find
+ * 1. New args size
+ * 2. Starting index of new vmargs
+ * 3. See if old user args should be ignored
+ */
+ while(newLaucherArgs[++newArgsSize] != NULL){
+ if (_tcsicmp( newLaucherArgs[newArgsSize], VMARGS ) == 0) {
+ newVmargsStart = newArgsSize + 1;
+ }
+ if (_tcsicmp( newLaucherArgs[newArgsSize], SKIP_OLD_ARGS ) == 0) {
+ skipOldUserArgs = 1;
+ }
+ }
+
+ int oldUserArgsStart = -1, oldUserArgsEnd = -1, oldUserArgsSize = 0;
+ int oldUserVMArgsStart = -1, oldUserVMArgsEnd = -1;
+ // Gather the old user args and old user vmargs
+ for (int i = 1 ; i < initialArgc ; i++) {
+ if (_tcsicmp(initialArgv[i], OLD_ARGS_START) == 0) {
+ oldUserArgsStart = i + 1;
+ }
+ if (_tcsicmp(initialArgv[i], VMARGS) == 0) {
+ oldUserVMArgsStart = i + 1;
+ }
+ if (_tcsicmp(initialArgv[i], OLD_ARGS_END) == 0) {
+ oldUserArgsEnd = oldUserVMArgsEnd = i - 1;
+ if (oldUserArgsStart != -1 && oldUserArgsStart <= oldUserArgsEnd)
+ oldUserArgsSize = oldUserArgsEnd - oldUserArgsStart + 1;
+ break;
+ }
+ if (i + 1 == initialArgc && oldUserVMArgsStart != -1 && oldUserVMArgsEnd == -1) {
+ oldUserVMArgsEnd = i;
}
}
- relaunch = malloc((1 + i + 1) * sizeof(_TCHAR *));
+ // "--launcher.oldUserArgsStart" is not found in old args which means the launcher was
+ // invoked by user and its the first restart request after it. Hence track all the args
+ // provided by user as old user args
+ if (oldUserArgsStart == -1) {
+ oldUserArgsStart = 1;
+ oldUserArgsEnd = initialArgc - 1;
+ oldUserArgsSize = oldUserArgsEnd - oldUserArgsStart + 1;
+ }
+
+ relaunch = malloc((1 + (oldUserArgsSize + 2)+ oldUserArgsSize + newArgsSize + 1) * sizeof(_TCHAR *));
+
+ // Step 1. Add program path
relaunch[idx++] = program;
- if(begin == -1) {
- begin = 1;
+
+ // Step 2. Add old args with --launcher.oldUserArgsStart and --launcher.oldUserArgsEnd
+ // Over multiple restarts, this is required to keep track of args that were provided by user to launcher first time
+ relaunch[idx++] = OLD_ARGS_START;
+ for (int j = oldUserArgsStart; oldUserArgsSize > 0 && j <= oldUserArgsEnd; j++) {
+ relaunch[idx++] = initialArgv[j];
}
- for (i = begin; vmCommand[i] != NULL; i++){
- if (_tcsicmp(vmCommand[i], SHOWSPLASH) == 0) {
+ relaunch[idx++] = OLD_ARGS_END;
+
+ // Step 3. Add old non-vmargs launch arguments if old args are not skipped
+ if (skipOldUserArgs == 0) {
+ for (int j = oldUserArgsStart; oldUserArgsSize > 0 && j <= oldUserArgsEnd; j++) {
+ if (_tcsicmp(initialArgv[j], VMARGS) == 0) {
+ break;
+ }
+ relaunch[idx++] = initialArgv[j];
+ }
+ }
+
+ // Step 4. Add new non-vmargs launch arguments which will get precedence over old arguments
+ for (int i = 0; newLaucherArgs[i] != NULL && i != (newVmargsStart - 1); i++){
+ if (_tcsicmp(newLaucherArgs[i], SHOWSPLASH) == 0) {
/* remove if the next argument is not the bitmap to show */
- if(vmCommand[i + 1] != NULL && vmCommand[i + 1][0] == _T_ECLIPSE('-')) {
+ if(newLaucherArgs[i + 1] != NULL && newLaucherArgs[i + 1][0] == _T_ECLIPSE('-')) {
continue;
}
- } else if(_tcsncmp(vmCommand[i], CLASSPATH_PREFIX, _tcslen(CLASSPATH_PREFIX)) == 0) {
+ } else if(_tcsncmp(newLaucherArgs[i], CLASSPATH_PREFIX, _tcslen(CLASSPATH_PREFIX)) == 0) {
/* skip -Djava.class.path=... */
continue;
+ } else if(_tcscmp(newLaucherArgs[i], EXITDATA) == 0) {
+ /* skip -exitdata argument and value as new shm will be created on relaunch */
+ i++;
+ continue;
+ } else if(_tcscmp(newLaucherArgs[i], SKIP_OLD_ARGS) == 0) {
+ /* skip --launcher.skipOldUserArgs */
+ continue;
}
- relaunch[idx++] = vmCommand[i];
+ relaunch[idx++] = newLaucherArgs[i];
}
+
+ // Step 5. Add vmargs; first old user vmargs and then new vmargs so that new vmargs get precedence
+ if ((skipOldUserArgs == 0 && oldUserVMArgsStart != -1) || newVmargsStart != -1) {
+ relaunch[idx++] = VMARGS;
+ if (skipOldUserArgs == 0 && oldUserVMArgsStart != -1) {
+ for (int i = oldUserVMArgsStart; i <= oldUserVMArgsEnd ; i++) {
+ relaunch[idx++] = initialArgv[i];
+ }
+ }
+ if (newVmargsStart != -1) {
+ for (int i = newVmargsStart; newLaucherArgs[i] != NULL; i++)
+ relaunch[idx++] = newLaucherArgs[i];
+ }
+ }
+
+ // Step 6. place null at the end to indicate end of arguments
if(_tcsicmp(relaunch[idx - 1], VMARGS) == 0)
relaunch[idx - 1] = NULL;
relaunch[idx] = NULL;
diff --git a/features/org.eclipse.equinox.executable.feature/library/eclipseCommon.h b/features/org.eclipse.equinox.executable.feature/library/eclipseCommon.h
index e956600fb7d..14b362d9388 100644
--- a/features/org.eclipse.equinox.executable.feature/library/eclipseCommon.h
+++ b/features/org.eclipse.equinox.executable.feature/library/eclipseCommon.h
@@ -18,6 +18,55 @@
#include "eclipseUnicode.h"
+/* Define constants for the options recognized by the launcher. */
+#define CONSOLE _T_ECLIPSE("-console")
+#define CONSOLELOG _T_ECLIPSE("-consoleLog")
+#define DEBUG _T_ECLIPSE("-debug")
+#define OS _T_ECLIPSE("-os")
+#define OSARCH _T_ECLIPSE("-arch")
+#define NOSPLASH _T_ECLIPSE("-nosplash")
+#define LAUNCHER _T_ECLIPSE("-launcher")
+#define SHOWSPLASH _T_ECLIPSE("-showsplash")
+#define EXITDATA _T_ECLIPSE("-exitdata")
+#define STARTUP _T_ECLIPSE("-startup")
+#define VM _T_ECLIPSE("-vm")
+#define WS _T_ECLIPSE("-ws")
+#define NAME _T_ECLIPSE("-name")
+#define VMARGS _T_ECLIPSE("-vmargs") /* special option processing required */
+#define CP _T_ECLIPSE("-cp")
+#define CLASSPATH _T_ECLIPSE("-classpath")
+#define JAR _T_ECLIPSE("-jar")
+#define PROTECT _T_ECLIPSE("-protect")
+#define ROOT _T_ECLIPSE("root") /* the only level of protection we care now */
+
+#define OPENFILE _T_ECLIPSE("--launcher.openFile")
+#define DEFAULTACTION _T_ECLIPSE("--launcher.defaultAction")
+#define TIMEOUT _T_ECLIPSE("--launcher.timeout")
+#define LIBRARY _T_ECLIPSE("--launcher.library")
+#define SUPRESSERRORS _T_ECLIPSE("--launcher.suppressErrors")
+#define INI _T_ECLIPSE("--launcher.ini")
+#define APPEND_VMARGS _T_ECLIPSE("--launcher.appendVmargs")
+#define OVERRIDE_VMARGS _T_ECLIPSE("--launcher.overrideVmargs")
+#define SECOND_THREAD _T_ECLIPSE("--launcher.secondThread")
+#define PERM_GEN _T_ECLIPSE("--launcher.XXMaxPermSize")
+#define OLD_ARGS_START _T_ECLIPSE("--launcher.oldUserArgsStart")
+#define OLD_ARGS_END _T_ECLIPSE("--launcher.oldUserArgsEnd")
+#define SKIP_OLD_ARGS _T_ECLIPSE("--launcher.skipOldUserArgs")
+
+#define XXPERMGEN _T_ECLIPSE("-XX:MaxPermSize=")
+#define ADDMODULES _T_ECLIPSE("--add-modules")
+#define ACTION_OPENFILE _T_ECLIPSE("openFile")
+#define GTK_VERSION _T_ECLIPSE("--launcher.GTK_version")
+
+/* constants for ee options file */
+#define EE_EXECUTABLE _T_ECLIPSE("-Dee.executable=")
+#define EE_CONSOLE _T_ECLIPSE("-Dee.executable.console=")
+#define EE_VM_LIBRARY _T_ECLIPSE("-Dee.vm.library=")
+#define EE_LIBRARY_PATH _T_ECLIPSE("-Dee.library.path=")
+#define EE_HOME _T_ECLIPSE("-Dee.home=")
+#define EE_FILENAME _T_ECLIPSE("-Dee.filename=")
+#define EE_HOME_VAR _T_ECLIPSE("${ee.home}")
+
/* Variables and Methods that will be needed by both the executable and the library */
#define MAX_PATH_LENGTH 2000
diff --git a/features/org.eclipse.equinox.executable.feature/library/eclipseMain.c b/features/org.eclipse.equinox.executable.feature/library/eclipseMain.c
index f31bd7e36b6..9cd975b7bd3 100644
--- a/features/org.eclipse.equinox.executable.feature/library/eclipseMain.c
+++ b/features/org.eclipse.equinox.executable.feature/library/eclipseMain.c
@@ -45,15 +45,6 @@ static _TCHAR* rootMsg =
_T_ECLIPSE("The %s executable launcher is configured to not start with \n\
administrative privileges.");
-#define NAME _T_ECLIPSE("-name")
-#define VMARGS _T_ECLIPSE("-vmargs") /* special option processing required */
-/* New arguments have the form --launcher. to avoid collisions */
-#define LIBRARY _T_ECLIPSE("--launcher.library")
-#define SUPRESSERRORS _T_ECLIPSE("--launcher.suppressErrors")
-#define INI _T_ECLIPSE("--launcher.ini")
-#define PROTECT _T_ECLIPSE("-protect") /* This argument is also handled in eclipse.c for Mac specific processing */
-#define ROOT _T_ECLIPSE("root") /* the only level of protection we care now */
-
/* this typedef must match the run method in eclipse.c */
typedef int (*RunMethod)(int argc, _TCHAR* argv[], _TCHAR* vmArgs[]);
typedef void (*SetInitialArgs)(int argc, _TCHAR*argv[], _TCHAR* library, int consoleLauncher);
@@ -284,13 +275,20 @@ static _TCHAR* findProgram(_TCHAR* argv[]) {
static void parseArgs( int* pArgc, _TCHAR* argv[], int useVMargs )
{
int index;
+ int skipOldArgs = 0;
/* Ensure the list of user argument is NULL terminated. */
argv[ *pArgc ] = NULL;
/* For each user defined argument */
for (index = 0; index < *pArgc; index++){
- if(_tcsicmp(argv[index], VMARGS) == 0) {
+ if (_tcsicmp(argv[index], OLD_ARGS_START) == 0) {
+ skipOldArgs = 1;
+ } else if (_tcsicmp(argv[index], OLD_ARGS_END) == 0) {
+ skipOldArgs = 0;
+ } else if (skipOldArgs) {
+ continue;
+ } else if(_tcsicmp(argv[index], VMARGS) == 0) {
if (useVMargs == 1) { //Use the VMargs as the user specified vmArgs
userVMarg = &argv[ index+1 ];
}
@@ -306,7 +304,7 @@ static void parseArgs( int* pArgc, _TCHAR* argv[], int useVMargs )
if(_tcsicmp(argv[++index], ROOT) == 0){
protectRoot = 1;
}
- }
+ }
}
}
@@ -336,17 +334,40 @@ static _TCHAR* checkForIni(int argc, _TCHAR* argv[])
*/
static int createUserArgs(int configArgc, _TCHAR **configArgv, int *argc, _TCHAR ***argv)
{
- _TCHAR** newArray = (_TCHAR **)malloc((configArgc + *argc + 1) * sizeof(_TCHAR *));
+ int argsSize = configArgc + *argc;
+ int skipOldArgs = 0;
+ _TCHAR** newArray = (_TCHAR **)malloc((argsSize + 1) * sizeof(_TCHAR *));
newArray[0] = (*argv)[0]; /* use the original argv[0] */
memcpy(newArray + 1, configArgv, configArgc * sizeof(_TCHAR *));
+ int startIndex = 1 + configArgc;
/* Skip the argument zero (program path and name) */
- memcpy(newArray + 1 + configArgc, *argv + 1, (*argc - 1) * sizeof(_TCHAR *));
+ for (int i = 1 ; i < *argc ; i++) {
+ if (_tcsicmp((*argv)[i], OLD_ARGS_START) == 0) {
+ argsSize--;
+ skipOldArgs = 1;
+ continue;
+ }
+ if (_tcsicmp((*argv)[i], OLD_ARGS_END) == 0) {
+ argsSize--;
+ skipOldArgs = 0;
+ continue;
+ }
+ if (skipOldArgs) {
+ argsSize--;
+ continue;
+ }
+ if (_tcscmp((*argv)[i], SKIP_OLD_ARGS) == 0) {
+ argsSize--;
+ continue;
+ }
+ newArray[startIndex++] = (*argv)[i];
+ }
/* Null terminate the new list of arguments and return it. */
*argv = newArray;
- *argc += configArgc;
+ *argc = argsSize;
(*argv)[*argc] = NULL;
return 0;
diff --git a/features/org.eclipse.equinox.executable.feature/library/gtk/build.sh b/features/org.eclipse.equinox.executable.feature/library/gtk/build.sh
index f837c0328af..02e459530a6 100755
--- a/features/org.eclipse.equinox.executable.feature/library/gtk/build.sh
+++ b/features/org.eclipse.equinox.executable.feature/library/gtk/build.sh
@@ -16,7 +16,7 @@
# Martin Oberhuber (Wind River) - [517013] Avoid memcpy@GLIBC_2.14 dependency
#*******************************************************************************
#
-# Usage: sh build.sh [] [clean]
+# Usage: sh build.sh [] [clean] [test]
#
# where the optional switches are:
# -output - executable filename ("eclipse")
@@ -30,6 +30,8 @@
#
# Examples:
# sh build.sh clean
+# sh build.sh test
+# sh build.sh clean test
# sh build.sh -java /usr/j2se OPTFLAG=-g PICFLAG=-fpic
cd `dirname $0`
diff --git a/features/org.eclipse.equinox.executable.feature/library/gtk/make_linux.mak b/features/org.eclipse.equinox.executable.feature/library/gtk/make_linux.mak
index b9238d51a93..a0f4a8868a9 100644
--- a/features/org.eclipse.equinox.executable.feature/library/gtk/make_linux.mak
+++ b/features/org.eclipse.equinox.executable.feature/library/gtk/make_linux.mak
@@ -43,7 +43,8 @@ PROGRAM_LIBRARY = $(PROGRAM_OUTPUT)_$(LIB_VERSION).so
EXEC_DIR ?= ../../../../../rt.equinox.binaries/org.eclipse.equinox.executable
OUTPUT_DIR ?= $(EXEC_DIR)/bin/$(DEFAULT_WS)/$(DEFAULT_OS)/$(DEFAULT_OS_ARCH)
-LIBRARY_DIR ?= $(EXEC_DIR)/../org.eclipse.equinox.launcher.$(DEFAULT_WS).$(DEFAULT_OS).$(DEFAULT_OS_ARCH)
+LIBRARY_FRAGMENT_NAME ?= org.eclipse.equinox.launcher.$(DEFAULT_WS).$(DEFAULT_OS).$(DEFAULT_OS_ARCH)
+LIBRARY_DIR ?= $(EXEC_DIR)/../$(LIBRARY_FRAGMENT_NAME)
# 64 bit specific flag:
ifeq ($(M_CFLAGS),)
@@ -147,16 +148,21 @@ clean:
# Convienience method to install produced output into a developer's eclipse for testing/development.
dev_build_install: all
-ifeq "$(origin DEV_ECLIPSE)" "environment"
+ifneq ($(filter "$(origin DEV_ECLIPSE)", "environment" "command line"),)
$(info Copying $(EXEC) and $(DLL) into your development eclipse folder:)
mkdir -p ${DEV_ECLIPSE}/
cp $(EXEC) ${DEV_ECLIPSE}/
- mkdir -p ${DEV_ECLIPSE}/plugins/org.eclipse.equinox.launcher.gtk.linux.x86_64/
- cp $(DLL) ${DEV_ECLIPSE}/plugins/org.eclipse.equinox.launcher.gtk.linux.x86_64/
+ mkdir -p ${DEV_ECLIPSE}/plugins/$(LIBRARY_FRAGMENT_NAME)/
+ cp $(DLL) ${DEV_ECLIPSE}/plugins/$(LIBRARY_FRAGMENT_NAME)/
else
$(error $(DEV_INSTALL_ERROR_MSG))
endif
+test:
+ mvn -f ../org.eclipse.launcher.tests/pom.xml clean verify -Dmaven.test.skip=true
+ make -f $(firstword $(MAKEFILE_LIST)) dev_build_install LIBRARY_FRAGMENT_NAME=org.eclipse.equinox.launcher DEV_ECLIPSE=../org.eclipse.launcher.tests/target/test-run
+ mvn -f ../org.eclipse.launcher.tests/pom.xml test
+
define DEV_INSTALL_ERROR_MSG =
Note:
DEV_ECLIPSE environmental variable is not defined.
diff --git a/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/.classpath b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/.classpath
new file mode 100644
index 00000000000..3e7d5e55fff
--- /dev/null
+++ b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/.classpath
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/.gitignore b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/.gitignore
new file mode 100644
index 00000000000..ae3c1726048
--- /dev/null
+++ b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/.gitignore
@@ -0,0 +1 @@
+/bin/
diff --git a/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/.project b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/.project
new file mode 100644
index 00000000000..f135598dbeb
--- /dev/null
+++ b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/.project
@@ -0,0 +1,23 @@
+
+
+ org.eclipse.launcher.tests
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.m2e.core.maven2Nature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/.settings/org.eclipse.jdt.core.prefs b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 00000000000..43c8d716b15
--- /dev/null
+++ b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,15 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=17
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=17
diff --git a/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/.settings/org.eclipse.m2e.core.prefs b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 00000000000..f897a7f1cb2
--- /dev/null
+++ b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/pom.xml b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/pom.xml
new file mode 100644
index 00000000000..1d7aa527df1
--- /dev/null
+++ b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/pom.xml
@@ -0,0 +1,58 @@
+
+ 4.0.0
+ org.eclipse.launcher.tests
+ org.eclipse.launcher.tests
+ 1.0.0
+ jar
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.10.0
+
+
+
+ src
+ test.launcher
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.11.0
+
+ 17
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.3.0
+
+
+ default-jar
+ compile
+
+ jar
+
+
+
+
+ main.TestLauncherApp
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.1.2
+
+ ${project.build.directory}/test-run
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/src/main/TestLauncherApp.java b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/src/main/TestLauncherApp.java
new file mode 100644
index 00000000000..0e868cd21a1
--- /dev/null
+++ b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/src/main/TestLauncherApp.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Eclipse Foundation, Inc. and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Umair Sair - initial API and implementation
+ *******************************************************************************/
+package main;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.InputStreamReader;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.equinox.launcher.JNIBridge;
+
+/**
+ * Dummy test application used for eclipse launcher testing. JUnit tests
+ * launches the eclipse launcher with startup application pointing to the jar file
+ * of this application. Test starts a server and set the port number via
+ * 'eclipse_test_port' environment variable. This application connects to that
+ * port and accepts following queries
+ * - -args - returns the arguments passed to this application. Test verify if the
+ * arguments are passed to application correctly by the launcher.
+ *
+ * And accepts following information
+ * - -exitdata - The exit data this application should set before exiting
+ * - -exitcode - The exit code with which this application should exit
+ *
+ * @author umairsair
+ *
+ */
+public class TestLauncherApp {
+
+ private static JNIBridge bridge;
+ private static String sharedId;
+ private static String[] args;
+ private static List exitData = new ArrayList<>();
+ private static int exitCode = 0;
+
+
+ public int run(String[] args) {
+ parseArgs(args);
+ return exitCode;
+ }
+
+ public static void main(String[] args) {
+ parseArgs(args);
+
+ System.exit(exitCode);
+ }
+
+ private static void parseArgs(String[] args) {
+ TestLauncherApp.args = args;
+ for (int i = 0; i < args.length; i++) {
+ if ("--launcher.library".equals(args[i])) {
+ try {
+ bridge = new JNIBridge(args[i + 1]);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ if ("-exitdata".equals(args[i])) {
+ sharedId = args[i + 1];
+ }
+ }
+
+ try {
+ communicateToServer();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ if (!exitData.isEmpty()) {
+ bridge.setExitData(sharedId, String.join("\n", exitData) + "\n");
+ }
+ }
+
+ private static void communicateToServer() throws Exception {
+ String port = System.getenv(TestLauncherConstants.PORT_ENV_KEY);
+ try (Socket socket = new Socket()) {
+ socket.connect(new InetSocketAddress("localhost", Integer.parseInt(port)), 10000);
+ BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+ DataOutputStream out = new DataOutputStream(socket.getOutputStream());
+ String line;
+ while ((line = in.readLine()) != null) {
+ if (TestLauncherConstants.ARGS_PARAMETER.equals(line)) {
+ out.writeBytes(String.join("\n", args) + "\n" + TestLauncherConstants.MULTILINE_ARG_VALUE_TERMINATOR + "\n");
+ out.flush();
+ } else if (TestLauncherConstants.EXITDATA_PARAMETER.equals(line)) {
+ while ((line = in.readLine()) != null) {
+ if (TestLauncherConstants.MULTILINE_ARG_VALUE_TERMINATOR.equals(line))
+ break;
+ exitData.add(line);
+ }
+ } else if (TestLauncherConstants.EXITCODE_PARAMETER.equals(line)) {
+ TestLauncherApp.exitCode = Integer.parseInt(in.readLine());
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/src/main/TestLauncherConstants.java b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/src/main/TestLauncherConstants.java
new file mode 100644
index 00000000000..5bd1e0bed41
--- /dev/null
+++ b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/src/main/TestLauncherConstants.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Eclipse Foundation, Inc. and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Umair Sair - initial API and implementation
+ *******************************************************************************/
+package main;
+
+public interface TestLauncherConstants {
+ public static final String ARGS_PARAMETER = "-args"; //$NON-NLS-1$
+ public static final String EXITDATA_PARAMETER = "-exitdata"; //$NON-NLS-1$
+ public static final String EXITCODE_PARAMETER = "-exitcode"; //$NON-NLS-1$
+ public static final String MULTILINE_ARG_VALUE_TERMINATOR = "---"; //$NON-NLS-1$
+
+ public static final String PORT_ENV_KEY = "eclipse_test_port"; //$NON-NLS-1$
+}
diff --git a/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/src/org/eclipse/equinox/launcher/JNIBridge.java b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/src/org/eclipse/equinox/launcher/JNIBridge.java
new file mode 120000
index 00000000000..7a2f31094d8
--- /dev/null
+++ b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/src/org/eclipse/equinox/launcher/JNIBridge.java
@@ -0,0 +1 @@
+../../../../../../../../../bundles/org.eclipse.equinox.launcher/src/org/eclipse/equinox/launcher/JNIBridge.java
\ No newline at end of file
diff --git a/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/src/test/java/LauncherTests.java b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/src/test/java/LauncherTests.java
new file mode 100644
index 00000000000..1baaeaf66a5
--- /dev/null
+++ b/features/org.eclipse.equinox.executable.feature/library/org.eclipse.launcher.tests/src/test/java/LauncherTests.java
@@ -0,0 +1,483 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Eclipse Foundation, Inc. and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Umair Sair - initial API and implementation
+ *******************************************************************************/
+package test.java;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import main.TestLauncherConstants;
+
+public class LauncherTests {
+ private static final String ECLIPSE_INI_FILE_NAME = "eclipse.ini";
+ // @formatter:off
+ private static final String DEFAULT_ECLIPSE_INI_CONTENT = "-startup\n"
+ + "../test.launcher.jar\n"
+ + "--launcher.library\n"
+ + "plugins/org.eclipse.equinox.launcher\n"
+ + "-vmargs\n"
+ + "-Xms40m\n"
+ + "";
+ // @formatter:on
+ public static final Integer EXIT_OK = Integer.valueOf(0);
+ public static final Integer EXIT_RESTART = Integer.valueOf(23);
+ public static final Integer EXIT_RELAUNCH = Integer.valueOf(24);
+
+ private ServerSocket server;
+
+ @BeforeEach
+ void setUp() throws IOException {
+ server = new ServerSocket(0, 1);
+ server.setSoTimeout(10000);
+ }
+
+ @AfterEach
+ void tearDown() throws IOException {
+ if (server != null) {
+ server.close();
+ server = null;
+ }
+ }
+
+ @Test
+ void test_appTerminatesWithCodeZeroOnExit() throws IOException, InterruptedException {
+ writeEclipseIni(DEFAULT_ECLIPSE_INI_CONTENT);
+
+ Process launcherProcess = startEclipseLauncher(Collections.emptyList());
+
+ Socket socket = server.accept();
+
+ List appArgs = new ArrayList<>();
+ analyzeLaunchedTestApp(socket, appArgs, null, EXIT_OK);
+
+ // Make sure arguments contain default vmargs
+ assertTrue(appArgs.containsAll(Arrays.asList("-vmargs", "-Xms40m")));
+ // Make sure launcher exited with code zero
+ launcherProcess.waitFor(5, TimeUnit.SECONDS);
+ assertTrue(launcherProcess.exitValue() == 0);
+ }
+
+ @Test
+ void test_eclipseIniChangesShouldBePickedOnRestart() throws IOException {
+ writeEclipseIni(DEFAULT_ECLIPSE_INI_CONTENT);
+
+ startEclipseLauncher(Collections.emptyList());
+
+ Socket socket = server.accept();
+
+ // Before restarting, update eclipse.ini and check if extra arg is read
+ writeEclipseIni(DEFAULT_ECLIPSE_INI_CONTENT + "-Dtest");
+
+ List appArgs1 = new ArrayList<>();
+ analyzeLaunchedTestApp(socket, appArgs1, null, EXIT_RESTART);
+
+ socket = server.accept();
+
+ List appArgs2 = new ArrayList<>();
+ analyzeLaunchedTestApp(socket, appArgs2, null, EXIT_OK);
+
+ // Args after restart contains new argument in eclipse.ini
+ assertTrue(appArgs2.contains("-Dtest"));
+
+ // Other than exitdata arg, all other args should be same over restarts
+ appArgs1.remove(appArgs1.indexOf(TestLauncherConstants.EXITDATA_PARAMETER) + 1);
+ appArgs2.remove(appArgs2.indexOf(TestLauncherConstants.EXITDATA_PARAMETER) + 1);
+ // After restart, only -Dtest arg should be extra, other than that args should
+ // be same
+ appArgs2.remove(appArgs2.indexOf("-Dtest"));
+
+ // Convert backslashes to forward slashes before comparison so that all paths are consistent
+ assertEquals(appArgs1.stream().map(s -> s.replace('\\', '/')).collect(Collectors.toList()),
+ appArgs2.stream().map(s -> s.replace('\\', '/')).collect(Collectors.toList()));
+ }
+
+ @Test
+ void test_eclipseIniChangesShouldBePickedOnRelaunch() throws IOException {
+ writeEclipseIni(DEFAULT_ECLIPSE_INI_CONTENT);
+
+ startEclipseLauncher(Collections.emptyList());
+
+ Socket socket = server.accept();
+
+ // Before relaunching, update eclipse.ini and check if extra arg is read
+ writeEclipseIni(DEFAULT_ECLIPSE_INI_CONTENT + "-Dtest");
+
+ List appArgs1 = new ArrayList<>();
+ analyzeLaunchedTestApp(socket, appArgs1, "-data\ndir1", EXIT_RELAUNCH);
+
+ socket = server.accept();
+
+ List appArgs2 = new ArrayList<>();
+ analyzeLaunchedTestApp(socket, appArgs2, null, EXIT_OK);
+
+ // Args after relaunch contains new argument in eclipse.ini
+ assertTrue(appArgs2.contains("-Dtest"));
+
+ // Other than exitdata arg, all other args should be same over relaunches
+ appArgs1.remove(appArgs1.indexOf(TestLauncherConstants.EXITDATA_PARAMETER) + 1);
+ appArgs2.remove(appArgs2.indexOf(TestLauncherConstants.EXITDATA_PARAMETER) + 1);
+ // After relaunch, -Dtest and -data args should be extra, other than that args should
+ // be same
+ appArgs2.remove(appArgs2.indexOf("-Dtest"));
+ appArgs2.remove(appArgs2.indexOf("-data"));
+ appArgs2.remove(appArgs2.indexOf("dir1"));
+
+ // Convert backslashes to forward slashes before comparison so that all paths are consistent
+ assertEquals(appArgs1.stream().map(s -> s.replace('\\', '/')).collect(Collectors.toList()),
+ appArgs2.stream().map(s -> s.replace('\\', '/')).collect(Collectors.toList()));
+ }
+
+ @Test
+ void test_newNonVMArgsForRelaunchShouldBeEffective() throws IOException {
+ writeEclipseIni(DEFAULT_ECLIPSE_INI_CONTENT);
+
+ startEclipseLauncher(Collections.emptyList());
+
+ Socket socket = server.accept();
+
+ List appArgs1 = new ArrayList<>();
+ analyzeLaunchedTestApp(socket, appArgs1, "-data\ndir1", EXIT_RELAUNCH);
+
+ socket = server.accept();
+
+ List appArgs2 = new ArrayList<>();
+ analyzeLaunchedTestApp(socket, appArgs2, null, EXIT_OK);
+
+ // Make sure on relaunch, new args is provided
+ assertTrue(appArgs2.contains("-data"));
+ assertTrue(appArgs2.contains("dir1"));
+ }
+
+ @Test
+ void test_newNonVMArgsForRelaunchShouldOverrideOlderSameArg() throws IOException {
+ writeEclipseIni(DEFAULT_ECLIPSE_INI_CONTENT);
+
+ // Start eclipse with arguments '-data dir1'
+ startEclipseLauncher(List.of("-data", "dir1"));
+
+ Socket socket = server.accept();
+
+ List appArgs1 = new ArrayList<>();
+ // Relaunch should provide arguments '-data dir2'
+ analyzeLaunchedTestApp(socket, appArgs1, "-data\ndir2", EXIT_RELAUNCH);
+
+ socket = server.accept();
+
+ List appArgs2 = new ArrayList<>();
+ analyzeLaunchedTestApp(socket, appArgs2, null, EXIT_OK);
+
+ // Relaunched app should contain '-data dir1 -data dir2'
+ assertEquals(Collections.frequency(appArgs2, "-data"), 2);
+ assertTrue(appArgs2.contains("dir1"));
+ assertTrue(appArgs2.contains("dir2"));
+ // dir2 argument should appear later so that it gets effective
+ assertTrue(appArgs2.indexOf("dir2") > appArgs2.indexOf("dir1"));
+ }
+
+ @Test
+ void test_newNonVMArgsForRelaunchWithSkipOldUserArgs() throws IOException {
+ writeEclipseIni(DEFAULT_ECLIPSE_INI_CONTENT);
+
+ // Start eclipse with arguments '-data dir1'
+ startEclipseLauncher(List.of("-data", "dir1"));
+
+ Socket socket = server.accept();
+
+ List appArgs1 = new ArrayList<>();
+ // Relaunch should provide arguments '-data dir2' and
+ // '--launcher.skipOldUserArgs' so that 'data dir1 is not
+ // provided on relaunch
+ analyzeLaunchedTestApp(socket, appArgs1, "-data\ndir2\n--launcher.skipOldUserArgs", EXIT_RELAUNCH);
+
+ socket = server.accept();
+
+ List appArgs2 = new ArrayList<>();
+ analyzeLaunchedTestApp(socket, appArgs2, null, EXIT_OK);
+
+ assertTrue(appArgs1.contains("-data"));
+ assertTrue(appArgs1.contains("dir1"));
+
+ // -data argument should be once
+ assertEquals(1, Collections.frequency(appArgs2, "-data"));
+ // -data argument should exist with only value dir2
+ assertTrue(appArgs2.contains("dir2"));
+ assertFalse(appArgs2.contains("dir1"));
+ }
+
+ @Test
+ void test_newVMArgsForRelaunchShouldBeEffective() throws IOException {
+ writeEclipseIni(DEFAULT_ECLIPSE_INI_CONTENT);
+
+ // Start eclipse with arguments '-vmargs -Dtest=1'. Note that by default
+ // --launcher.overrideVmargs is set
+ startEclipseLauncher(List.of("-vmargs", "-Dtest=1"));
+
+ Socket socket = server.accept();
+
+ List appArgs1 = new ArrayList<>();
+ // Relaunch should provide arguments '-vmargs -Dtest=2'
+ analyzeLaunchedTestApp(socket, appArgs1, "-vmargs\n-Dtest=2", EXIT_RELAUNCH);
+
+ socket = server.accept();
+
+ List appArgs2 = new ArrayList<>();
+ analyzeLaunchedTestApp(socket, appArgs2, null, EXIT_OK);
+
+ // First launch of eclipse should not have vmargs provided by eclipse.ini i.e.,
+ // -Xms40m
+ // and have the argument mentioned on commandline i.e., -Dtest=1
+ assertFalse(appArgs1.contains("-Xms40m"));
+ assertTrue(appArgs1.contains("-Dtest=1"));
+
+ // After relaunch, vmargs should also contain -Dtest=2 and it should appear
+ // after -Dtest=1
+ assertFalse(appArgs2.contains("-Xms40m"));
+ assertTrue(appArgs2.contains("-Dtest=1"));
+ assertTrue(appArgs2.contains("-Dtest=2"));
+ assertTrue(appArgs2.indexOf("-Dtest=2") > appArgs2.indexOf("-Dtest=1"));
+ }
+
+ @Test
+ void test_newVMArgsForRelaunchhWithSkipOldUserArgs() throws IOException {
+ writeEclipseIni(DEFAULT_ECLIPSE_INI_CONTENT);
+
+ // Start eclipse with arguments '-vmargs -Dtest=1'. Note that by default
+ // --launcher.overrideVmargs is set
+ startEclipseLauncher(List.of("-vmargs", "-Dtest=1"));
+
+ Socket socket = server.accept();
+
+ List appArgs1 = new ArrayList<>();
+ // Relaunch should provide arguments '-vmargs -Dtest=2' and
+ // '--launcher.skipOldUserArgs'
+ analyzeLaunchedTestApp(socket, appArgs1, "--launcher.skipOldUserArgs\n-vmargs\n-Dtest=2", EXIT_RELAUNCH);
+
+ socket = server.accept();
+
+ List appArgs2 = new ArrayList<>();
+ analyzeLaunchedTestApp(socket, appArgs2, null, EXIT_OK);
+
+ // First launch of eclipse should not have vmargs provided by eclipse.ini i.e.,
+ // -Xms40m
+ // and have the argument mentioned on commandline i.e., -Dtest=1
+ assertFalse(appArgs1.contains("-Xms40m"));
+ assertTrue(appArgs1.contains("-Dtest=1"));
+
+ // After relaunch, vmargs should only contain -Dtest=2
+ assertFalse(appArgs2.contains("-Xms40m"));
+ assertFalse(appArgs2.contains("-Dtest=1"));
+ assertTrue(appArgs2.contains("-Dtest=2"));
+ }
+
+ @Test
+ void test_newVMArgsForRelaunchhWithAppendVMArgs() throws IOException {
+ writeEclipseIni(DEFAULT_ECLIPSE_INI_CONTENT + "-Dtest=0");
+
+ // Start eclipse with arguments '-vmargs -Dtest=1'. Note that by default
+ // --launcher.overrideVmargs is set
+ startEclipseLauncher(List.of("-vmargs", "-Dtest=1"));
+
+ Socket socket = server.accept();
+
+ List appArgs1 = new ArrayList<>();
+ // Relaunch should provide arguments '-vmargs -Dtest=2' and
+ // '--launcher.appendVmargs'
+ analyzeLaunchedTestApp(socket, appArgs1, "--launcher.appendVmargs\n-vmargs\n-Dtest=2", EXIT_RELAUNCH);
+
+ socket = server.accept();
+
+ List appArgs2 = new ArrayList<>();
+ analyzeLaunchedTestApp(socket, appArgs2, null, EXIT_OK);
+
+ // First launch of eclipse should not have vmargs provided by eclipse.ini i.e.,
+ // -Xms40m and -Dtest=0
+ // and have the argument mentioned on commandline i.e., -Dtest=1
+ assertFalse(appArgs1.contains("-Xms40m"));
+ assertFalse(appArgs1.contains("-Dtest=0"));
+ assertTrue(appArgs1.contains("-Dtest=1"));
+
+ // After relaunch, vmargs should contain all args; args from commandline,
+ // eclipse.ini and from args provided by relaunch
+ assertTrue(appArgs2.contains("-Xms40m"));
+ assertTrue(appArgs2.contains("-Dtest=0"));
+ assertTrue(appArgs2.contains("-Dtest=1"));
+ assertTrue(appArgs2.contains("-Dtest=2"));
+ // @formatter:off
+ // -Dtest args should appear in order as
+ // - from eclipse.ini
+ // - then from commandline
+ // - then from relaunch
+ // Hence relaunch one will be effective
+ // @formatter:on
+ assertTrue(appArgs2.indexOf("-Dtest=2") > appArgs2.indexOf("-Dtest=1"));
+ assertTrue(appArgs2.indexOf("-Dtest=1") > appArgs2.indexOf("-Dtest=0"));
+ }
+
+ @Test
+ void test_newVMArgsForRelaunchhWithAppendVMArgsAndSkipOldUserArgs() throws IOException {
+ writeEclipseIni(DEFAULT_ECLIPSE_INI_CONTENT + "-Dtest=0");
+
+ // Start eclipse with arguments '-vmargs -Dtest=1'. Note that by default
+ // --launcher.overrideVmargs is set
+ startEclipseLauncher(List.of("-vmargs", "-Dtest=1"));
+
+ Socket socket = server.accept();
+
+ List appArgs1 = new ArrayList<>();
+ // Relaunch should provide arguments '-vmargs -Dtest=2',
+ // '--launcher.appendVmargs' and '--launcher.skipOldUserArgs'
+ analyzeLaunchedTestApp(socket, appArgs1,
+ "--launcher.appendVmargs\n--launcher.skipOldUserArgs\n-vmargs\n-Dtest=2", EXIT_RELAUNCH);
+
+ socket = server.accept();
+
+ List appArgs2 = new ArrayList<>();
+ analyzeLaunchedTestApp(socket, appArgs2, null, EXIT_OK);
+
+ // First launch of eclipse should not have vmargs provided by eclipse.ini i.e.,
+ // -Xms40m and -Dtest=0
+ // and have the argument mentioned on commandline i.e., -Dtest=1
+ assertFalse(appArgs1.contains("-Xms40m"));
+ assertFalse(appArgs1.contains("-Dtest=0"));
+ assertTrue(appArgs1.contains("-Dtest=1"));
+
+ // After restart, user provided arg should be ignore and only eclipse.ini and
+ // relaunch provided args should exist
+ assertTrue(appArgs2.contains("-Xms40m"));
+ assertTrue(appArgs2.contains("-Dtest=0"));
+ assertFalse(appArgs2.contains("-Dtest=1")); // provided from commandline and doesn't exist on restart
+ assertTrue(appArgs2.contains("-Dtest=2"));
+ assertTrue(appArgs2.indexOf("-Dtest=2") > appArgs2.indexOf("-Dtest=0"));
+ }
+
+ @Test
+ void test_ArgsRemainSameOverRestarts() throws IOException {
+ writeEclipseIni(DEFAULT_ECLIPSE_INI_CONTENT);
+
+ startEclipseLauncher(Collections.emptyList());
+
+ Socket socket = server.accept();
+
+ List appArgs1 = new ArrayList<>();
+ analyzeLaunchedTestApp(socket, appArgs1, null, EXIT_RESTART);
+ appArgs1.remove(appArgs1.indexOf(TestLauncherConstants.EXITDATA_PARAMETER) + 1);
+
+ for (int i = 0; i < 10; i++) {
+ socket = server.accept();
+
+ List appArgs2 = new ArrayList<>();
+ analyzeLaunchedTestApp(socket, appArgs2, null, i == 9 ? EXIT_OK : EXIT_RESTART);
+ // Other than exitdata arg, all other args should be same over restarts
+ appArgs2.remove(appArgs2.indexOf(TestLauncherConstants.EXITDATA_PARAMETER) + 1);
+
+ // Convert backslashes to forward slashes before comparison so that all paths are consistent
+ assertEquals(appArgs1.stream().map(s -> s.replace('\\', '/')).collect(Collectors.toList()),
+ appArgs2.stream().map(s -> s.replace('\\', '/')).collect(Collectors.toList()));
+ }
+ }
+
+ @Test
+ void test_ArgsRemainSameOverRelaunches() throws IOException {
+ writeEclipseIni(DEFAULT_ECLIPSE_INI_CONTENT);
+
+ startEclipseLauncher(List.of("-data", "dir1"));
+
+ Socket socket = server.accept();
+
+ List appArgs1 = new ArrayList<>();
+ analyzeLaunchedTestApp(socket, appArgs1, "--launcher.skipOldUserArgs\n-data\ndir1", EXIT_RELAUNCH);
+ appArgs1.remove(appArgs1.indexOf(TestLauncherConstants.EXITDATA_PARAMETER) + 1);
+
+ for (int i = 0; i < 10; i++) {
+ socket = server.accept();
+
+ List appArgs2 = new ArrayList<>();
+ analyzeLaunchedTestApp(socket, appArgs2, "--launcher.skipOldUserArgs\n-data\ndir1", i == 9 ? EXIT_OK : EXIT_RELAUNCH);
+ // Other than exitdata arg, all other args should be same over these relaunches
+ appArgs2.remove(appArgs2.indexOf(TestLauncherConstants.EXITDATA_PARAMETER) + 1);
+
+ // Convert backslashes to forward slashes before comparison so that all paths are consistent
+ assertEquals(appArgs1.stream().map(s -> s.replace('\\', '/')).collect(Collectors.toList()),
+ appArgs2.stream().map(s -> s.replace('\\', '/')).collect(Collectors.toList()));
+ }
+ }
+
+ private void analyzeLaunchedTestApp(Socket socket, List appArgs, String restartArgs, int appExitCode)
+ throws IOException {
+ BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+ DataOutputStream out = new DataOutputStream(socket.getOutputStream());
+ out.writeBytes(TestLauncherConstants.ARGS_PARAMETER + "\n");
+ out.flush();
+ String line = null;
+ System.out.println("--- start ----");
+ while ((line = in.readLine()) != null) {
+ if (TestLauncherConstants.MULTILINE_ARG_VALUE_TERMINATOR.equals(line))
+ break;
+ System.out.println(line);
+ appArgs.add(line);
+ }
+ System.out.println("--- end ----");
+ {
+ out.writeBytes(TestLauncherConstants.EXITDATA_PARAMETER + "\n");
+ if (restartArgs != null && !restartArgs.isBlank())
+ out.writeBytes(restartArgs + "\n");
+ out.writeBytes(TestLauncherConstants.MULTILINE_ARG_VALUE_TERMINATOR + "\n");
+ out.flush();
+
+ out.writeBytes(TestLauncherConstants.EXITCODE_PARAMETER + "\n");
+ out.writeBytes(appExitCode + "\n");
+ out.flush();
+ }
+ }
+
+ private Process startEclipseLauncher(List args) throws IOException {
+ String launcherPath = new File(
+ "eclipse" + (System.getProperty("os.name").toLowerCase().contains("win") ? ".exe" : ""))
+ .getAbsolutePath();
+ List allArgs = new ArrayList<>();
+ allArgs.add(launcherPath);
+ allArgs.addAll(args);
+ ProcessBuilder pb = new ProcessBuilder(allArgs);
+ pb.environment().put(TestLauncherConstants.PORT_ENV_KEY, Integer.toString(server.getLocalPort()));
+ return pb.start();
+ }
+
+ private void writeEclipseIni(String content) throws IOException {
+ File iniFile = new File(ECLIPSE_INI_FILE_NAME);
+ iniFile.createNewFile();
+ FileWriter myWriter = new FileWriter(ECLIPSE_INI_FILE_NAME);
+ myWriter.write(content);
+ myWriter.close();
+ }
+
+}
diff --git a/features/org.eclipse.equinox.executable.feature/library/win32/make_win64.mak b/features/org.eclipse.equinox.executable.feature/library/win32/make_win64.mak
index f2b72b0232e..67b45457054 100644
--- a/features/org.eclipse.equinox.executable.feature/library/win32/make_win64.mak
+++ b/features/org.eclipse.equinox.executable.feature/library/win32/make_win64.mak
@@ -63,6 +63,7 @@ wcflags = -DUNICODE -I.. -DDEFAULT_OS="\"$(DEFAULT_OS)\"" \
-DDEFAULT_WS="\"$(DEFAULT_WS)\"" \
-I"$(JAVA_HOME)\include" -I"$(JAVA_HOME)\include\win32" \
$(cflags)
+LIBRARY_FRAGMENT_NAME = org.eclipse.equinox.launcher.$(DEFAULT_WS).$(DEFAULT_OS).$(DEFAULT_OS_ARCH)
all: $(EXEC) $(DLL) $(CONSOLE)
eclipseMain.obj: ../eclipseUnicode.h ../eclipseCommon.h ../eclipseMain.c
@@ -113,3 +114,31 @@ install: all
clean:
del $(EXEC) $(DLL) $(MAIN_OBJS) $(MAIN_CONSOLE_OBJS) $(DLL_OBJS) $(COMMON_OBJS) $(RES)
+
+# Convienience method to install produced output into a developer's eclipse for testing/development.
+dev_build_install: all
+!ifdef DEV_ECLIPSE
+ @echo Copying $(EXEC) and $(DLL) into your development eclipse folder
+ mkdir $(DEV_ECLIPSE)
+ copy $(EXEC) $(DEV_ECLIPSE)
+ mkdir $(DEV_ECLIPSE)\plugins
+ mkdir $(DEV_ECLIPSE)\plugins\$(LIBRARY_FRAGMENT_NAME)
+ copy $(DLL) $(DEV_ECLIPSE)\plugins\$(LIBRARY_FRAGMENT_NAME)
+!else
+ !error $(DEV_INSTALL_ERROR_MSG)
+!endif
+
+test:
+ mvn -f ../org.eclipse.launcher.tests/pom.xml clean verify -Dmaven.test.skip=true
+ nmake -f make_win64.mak dev_build_install LIBRARY_FRAGMENT_NAME=org.eclipse.equinox.launcher DEV_ECLIPSE=..\org.eclipse.launcher.tests\target\test-run
+ mvn -f ../org.eclipse.launcher.tests/pom.xml test
+
+DEV_INSTALL_ERROR_MSG =\
+Note:\
+ DEV_ECLIPSE environmental variable is not defined.\
+ You can download an integration build eclipse for testing and set DEV_ECLIPSE to point to it's folder\
+ as per output of 'pwd'. Note, without trailing forwardslash. Integration build can be downloaded here:\
+ See: https://download.eclipse.org/eclipse/downloads/\
+ That way you can automatically build and copy eclipse and eclipse_XXXX.so into the relevant folders for testing. \
+ E.g: you can put something like the following into your .bashrc\
+ export DEV_ECLIPSE="/home/YOUR_USER/Downloads/eclipse-SDK-I20YYMMDD-XXXX-linux-win32-x86_64/eclipse"