1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-29 19:45:01 +02:00
This commit is contained in:
Christoph Läubrich 2025-04-24 10:55:52 +02:00 committed by GitHub
commit 707b40f55f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 13 additions and 587 deletions

View file

@ -126,14 +126,6 @@ $(OS_DIR_WIN32_X86_64)/spawner.dll: win/iostream.c win/raise.c win/spawner.c win
$^ \
-Wl,--kill-at --shared
$(OS_DIR_WIN32_X86_64)/pty.dll: win/pty.cpp win/pty_dllmain.cpp win/util.c
mkdir -p $(dir $@) && \
$(REPRODUCIBLE_BUILD_WRAPPER) \
x86_64-w64-mingw32-g++ $(COMMON_CCFLAGS) -o $@ -Iinclude -Iwin/include -I"$(JAVA_HOME)/include" -I"$(JAVA_HOME)/include/win32" \
-DUNICODE \
$^ \
-Wl,--kill-at --shared -L$(OS_DIR_WIN32_X86_64) -lwinpty -static-libstdc++ -static-libgcc
# Windows aarch64
$(OS_DIR_WIN32_AARCH64)/starter.exe: win/starter.c win/util.c
mkdir -p $(dir $@) && \

View file

@ -1,105 +0,0 @@
/*
* Copyright (c) 2011-2012 Ryan Prichard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef WINPTY_H
#define WINPTY_H
#include <stdlib.h>
#include <windows.h>
#ifdef WINPTY
#define WINPTY_API __declspec(dllexport)
#else
#define WINPTY_API __declspec(dllimport)
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct winpty_s winpty_t;
/*
* winpty API.
*/
/*
* Starts a new winpty instance with the given size.
*
* This function creates a new agent process and connects to it.
*/
WINPTY_API winpty_t *winpty_open(int cols, int rows);
/*
* Start a child process. Either (but not both) of appname and cmdline may
* be NULL. cwd and env may be NULL. env is a pointer to an environment
* block like that passed to CreateProcess.
*
* This function never modifies the cmdline, unlike CreateProcess.
*
* Only one child process may be started. After the child process exits, the
* agent will scrape the console output one last time, then close the data pipe
* once all remaining data has been sent.
*
* Returns 0 on success or a Win32 error code on failure.
*/
WINPTY_API int winpty_start_process(winpty_t *pc, const wchar_t *appname, const wchar_t *cmdline, const wchar_t *cwd,
const wchar_t *env);
/*
* Returns the exit code of the process started with winpty_start_process,
* or -1 none is available.
*/
WINPTY_API int winpty_get_exit_code(winpty_t *pc);
/*
* Returns the process id of the process started with winpty_start_process,
* or -1 none is available.
*/
WINPTY_API int winpty_get_process_id(winpty_t *pc);
/*
* Returns an overlapped-mode pipe handle that can be read and written
* like a Unix terminal.
*/
WINPTY_API HANDLE winpty_get_data_pipe(winpty_t *pc);
/*
* Change the size of the Windows console.
*/
WINPTY_API int winpty_set_size(winpty_t *pc, int cols, int rows);
/*
* Toggle the console mode. If in console mode, no terminal escape sequences are send.
*/
WINPTY_API int winpty_set_console_mode(winpty_t *pc, int mode);
/*
* Closes the winpty.
*/
WINPTY_API void winpty_close(winpty_t *pc);
#ifdef __cplusplus
}
#endif
#endif /* WINPTY_H */

View file

@ -1,382 +0,0 @@
/*******************************************************************************
* Copyright (c) 2013, 2016 Wind River Systems, 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:
* Wind River Systems - initial API and implementation
*******************************************************************************/
#include "org_eclipse_cdt_utils_pty_PTY.h"
#include "org_eclipse_cdt_utils_pty_PTYInputStream.h"
#include "org_eclipse_cdt_utils_pty_PTYOutputStream.h"
#include "winpty.h"
#include <string>
#include <vector>
#include <map>
#include <stdlib.h>
#include <assert.h>
#include <ctime>
static std::map<int, winpty_t *> fd2pty;
static std::map<int, int> fd2rc;
JNIEXPORT jstring JNICALL Java_org_eclipse_cdt_utils_pty_PTY_openMaster(JNIEnv *env, jobject jobj, jboolean console) {
jfieldID fid; /* Store the field ID */
jstring jstr = NULL;
jclass cls;
int master = -1;
char line[1024];
line[0] = '\0';
/* Open new winpty handle */
winpty_t *winpty = winpty_open(80, 40);
if (!winpty) {
return NULL;
}
/* Configure console mode */
if (console) {
winpty_set_console_mode(winpty, 1);
}
/* Generate masterFD based on current system time */
srand((unsigned int)time(NULL));
master = rand();
/* Make sure masterFD does not exist */
while (fd2pty.find(master) != fd2pty.end()) {
master++;
}
sprintf(line, "winpty_%i", master);
/* Remember the winpty handle for the generated masterFD */
fd2pty.insert(std::pair<int, winpty_t *>(master, winpty));
/* Get a reference to the obj's class */
cls = env->GetObjectClass(jobj);
/* Set the master fd. */
fid = env->GetFieldID(cls, "master", "I");
if (!fid) {
return NULL;
}
env->SetIntField(jobj, fid, (jint)master);
/* Create a new String for the slave. */
jstr = env->NewStringUTF(line);
return jstr;
}
JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTY_change_1window_1size(JNIEnv *env, jobject jobj, jint fdm,
jint width, jint height) {
int fd;
std::map<int, winpty_t *>::const_iterator fd2pty_Iter;
fd = fdm;
fd2pty_Iter = fd2pty.find(fd);
if (fd2pty_Iter != fd2pty.end()) {
winpty_t *winpty = fd2pty_Iter->second;
if (winpty) {
return winpty_set_size(winpty, width, height);
}
}
return 0;
}
JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYInputStream_read0(JNIEnv *env, jobject jobj, jint jfd,
jbyteArray buf, jint buf_len) {
DWORD amount = -1;
OVERLAPPED over;
int fd;
std::map<int, winpty_t *>::const_iterator fd2pty_Iter;
fd = jfd;
fd2pty_Iter = fd2pty.find(fd);
if (fd2pty_Iter != fd2pty.end()) {
winpty_t *winpty = fd2pty_Iter->second;
if (winpty) {
/* Get the pipe handle */
HANDLE handle = winpty_get_data_pipe(winpty);
memset(&over, 0, sizeof(over));
over.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
char *buffer = new char[buf_len];
memset(buffer, 0, sizeof(*buffer));
jbyte *data = env->GetByteArrayElements(buf, 0);
memset(data, 0, sizeof(*data));
amount = 0;
BOOL ret = ReadFile(handle, buffer, buf_len, &amount, &over);
if (!ret) {
DWORD error = GetLastError();
if (error == ERROR_IO_PENDING) {
ret = GetOverlappedResult(handle, &over, &amount, TRUE);
}
}
if (ret && amount > 0) {
memcpy(data, buffer, amount);
}
if (!ret || amount == 0) {
amount = -1;
}
if (!ret && fd2pty.find(fd) != fd2pty.end()) {
int rc = winpty_get_exit_code(winpty);
fd2rc.insert(std::pair<int, int>(fd, rc));
}
delete[] buffer;
env->ReleaseByteArrayElements(buf, data, 0);
ResetEvent(over.hEvent);
}
}
return amount;
}
JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYInputStream_close0(JNIEnv *env, jobject jobj, jint jfd) {
int fd;
std::map<int, winpty_t *>::iterator fd2pty_Iter;
fd = jfd;
fd2pty_Iter = fd2pty.find(fd);
if (fd2pty_Iter != fd2pty.end()) {
winpty_t *winpty = fd2pty_Iter->second;
fd2pty.erase(fd2pty_Iter);
if (winpty) {
winpty_close(winpty);
winpty = NULL;
}
}
return 0;
}
JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYOutputStream_write0(JNIEnv *env, jobject jobj, jint jfd,
jbyteArray buf, jint buf_len) {
DWORD written = -1;
OVERLAPPED over;
int fd;
std::map<int, winpty_t *>::iterator fd2pty_Iter;
fd = jfd;
fd2pty_Iter = fd2pty.find(fd);
if (fd2pty_Iter != fd2pty.end()) {
winpty_t *winpty = fd2pty_Iter->second;
if (winpty) {
/* Get the pipe handle */
HANDLE handle = winpty_get_data_pipe(winpty);
memset(&over, 0, sizeof(over));
over.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
char *buffer = new char[buf_len];
memset(buffer, 0, sizeof(*buffer));
jbyte *data = env->GetByteArrayElements(buf, 0);
memcpy(buffer, data, buf_len);
BOOL ret = WriteFile(handle, buffer, buf_len, &written, &over);
env->ReleaseByteArrayElements(buf, data, 0);
if (!ret && GetLastError() == ERROR_IO_PENDING) {
ret = GetOverlappedResult(handle, &over, &written, TRUE);
}
if (!ret || (int)written != buf_len) {
written = -1;
}
delete[] buffer;
}
}
return written;
}
JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTYOutputStream_close0(JNIEnv *env, jobject jobj, jint jfd) {
int fd;
std::map<int, winpty_t *>::iterator fd2pty_Iter;
fd = jfd;
fd2pty_Iter = fd2pty.find(fd);
if (fd2pty_Iter != fd2pty.end()) {
winpty_t *winpty = fd2pty_Iter->second;
fd2pty.erase(fd2pty_Iter);
if (winpty) {
winpty_close(winpty);
winpty = NULL;
}
}
return 0;
}
/*
* Convert convert slashes to backslashes.
*/
static std::wstring convertSlashes(const wchar_t *path) {
std::wstring ret;
for (int i = 0; path[i] != L'\0'; ++i) {
if (path[i] == L'/') {
ret.push_back(L'\\');
} else {
ret.push_back(path[i]);
}
}
return ret;
}
// Convert argc/argv into a Win32 command-line following the escaping convention
// documented on MSDN. (e.g. see CommandLineToArgvW documentation)
static std::wstring argvToCommandLine(const std::vector<std::wstring> &argv) {
std::wstring result;
for (size_t argIndex = 0; argIndex < argv.size(); ++argIndex) {
if (argIndex > 0) {
result.push_back(L' ');
}
const wchar_t *arg = argv[argIndex].c_str();
const bool quote = wcschr(arg, L' ') || wcschr(arg, L'\t') || *arg == L'\0';
if (quote) {
result.push_back(L'\"');
}
int bsCount = 0;
for (const wchar_t *p = arg; *p != L'\0'; ++p) {
if (*p == L'\\') {
bsCount++;
} else if (*p == L'\"') {
result.append(bsCount * 2 + 1, L'\\');
result.push_back(L'\"');
bsCount = 0;
} else {
result.append(bsCount, L'\\');
bsCount = 0;
result.push_back(*p);
}
}
if (quote) {
result.append(bsCount * 2, L'\\');
result.push_back(L'\"');
} else {
result.append(bsCount, L'\\');
}
}
return result;
}
JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTY_exec2(JNIEnv *env, jobject jobj, jobjectArray jcmd,
jobjectArray jenv, jstring jdir, jobjectArray jchannels,
jstring jslaveName, jint masterFD, jboolean console) {
int fd;
std::map<int, winpty_t *>::iterator fd2pty_Iter;
const wchar_t *cwdW = (const wchar_t *)env->GetStringChars(jdir, NULL);
const char *pts_name = env->GetStringUTFChars(jslaveName, NULL);
int pid = -1;
jint argc = env->GetArrayLength(jcmd);
jint envc = env->GetArrayLength(jenv);
if (!jchannels || env->GetArrayLength(jchannels) != 3) {
goto bail_out;
}
fd = masterFD;
fd2pty_Iter = fd2pty.find(fd);
if (fd2pty_Iter != fd2pty.end()) {
winpty_t *winpty = fd2pty_Iter->second;
if (winpty) {
std::vector<std::wstring> argVector;
for (int i = 0; i < argc; i++) {
jstring j_str = (jstring)env->GetObjectArrayElement(jcmd, i);
const wchar_t *w_str = (const wchar_t *)env->GetStringChars(j_str, NULL);
if (i == 0) {
argVector.push_back(convertSlashes(w_str));
} else {
argVector.push_back(w_str);
}
env->ReleaseStringChars(j_str, (const jchar *)w_str);
env->DeleteLocalRef(j_str);
}
std::wstring envp;
for (int i = 0; i < envc; i++) {
jstring j_str = (jstring)env->GetObjectArrayElement(jenv, i);
const wchar_t *w_str = (const wchar_t *)env->GetStringChars(j_str, NULL);
envp.append(w_str);
envp.push_back(L'\0');
env->ReleaseStringChars(j_str, (const jchar *)w_str);
env->DeleteLocalRef(j_str);
}
std::wstring cmdLine = argvToCommandLine(argVector);
const wchar_t *cmdLineW = cmdLine.c_str();
int ret = winpty_start_process(winpty, NULL, cmdLineW, cwdW, envp.c_str());
if (ret == 0) {
// Success. Get the process id.
pid = winpty_get_process_id(winpty);
}
}
}
bail_out:
env->ReleaseStringChars(jdir, (const jchar *)cwdW);
env->ReleaseStringUTFChars(jslaveName, pts_name);
return pid;
}
JNIEXPORT jint JNICALL Java_org_eclipse_cdt_utils_pty_PTY_waitFor(JNIEnv *env, jobject jobj, jint masterFD, jint pid) {
int status = -1;
DWORD flags;
int fd;
std::map<int, winpty_t *>::iterator fd2pty_Iter;
std::map<int, int>::iterator fd2rc_Iter;
fd = masterFD;
fd2pty_Iter = fd2pty.find(fd);
if (fd2pty_Iter != fd2pty.end()) {
winpty_t *winpty = fd2pty_Iter->second;
if (winpty) {
HANDLE handle = winpty_get_data_pipe(winpty);
BOOL success;
do {
success = GetHandleInformation(handle, &flags);
if (success) {
Sleep(500);
}
} while (success);
fd2rc_Iter = fd2rc.find(fd);
if (fd2rc_Iter != fd2rc.end()) {
status = fd2rc_Iter->second;
fd2rc.erase(fd2rc_Iter);
}
}
}
return status;
}

View file

@ -1,47 +0,0 @@
// dllmain.cpp : Defines the entry point for the DLL application.
#include <windows.h>
#include <delayimp.h>
#include <assert.h>
static HMODULE getCurrentModule() {
HMODULE module;
if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCTSTR)getCurrentModule, &module)) {
assert(false);
}
return module;
}
HMODULE PTYExplicitLoadLibrary(LPCSTR pszModuleName) {
if (lstrcmpiA(pszModuleName, "winpty.dll") == 0) {
CHAR szPath[MAX_PATH] = "";
//_hdllInstance is the HMODULE of *this* module
DWORD cchPath = GetModuleFileNameA(getCurrentModule(), szPath, MAX_PATH);
while (cchPath > 0) {
switch (szPath[cchPath - 1]) {
case '\\':
case '/':
case ':':
break;
default:
--cchPath;
continue;
}
break; // stop searching; found path separator
}
lstrcpynA(szPath + cchPath, pszModuleName, MAX_PATH - cchPath);
return LoadLibraryA(szPath); // call with full path to dependent DLL
}
return NULL;
}
FARPROC WINAPI PTYDliNotifyHook(unsigned dliNotify, PDelayLoadInfo pdli) {
if (dliNotify == dliNotePreLoadLibrary) {
return (FARPROC)PTYExplicitLoadLibrary(pdli->szDll);
}
return NULL;
}
extern "C" {
PfnDliHook __pfnDliNotifyHook2 = PTYDliNotifyHook;
}

View file

@ -28,12 +28,6 @@ import org.eclipse.core.runtime.Platform;
*/
public class PTY {
/**
* Java property key that can be set to false disable ConPTY.
*
* Defaults to True.
*/
private static final String CONPTY_ENABLED_PROP = "org.eclipse.cdt.core.conpty_enabled"; //$NON-NLS-1$
/**
* Java property key that can be set to true to force console mode
* to be allowed on Windows.
@ -79,10 +73,6 @@ public class PTY {
* On newer Windows 10 and later, use ConPTY API to connected a console.
*/
PTY_CONPTY,
/**
* On Windows, as a fallback use WinPTY.
*/
PTY_WINPTY,
/**
* On Linux/macOS PTY operations just work and no special API is needed.
*/
@ -191,7 +181,7 @@ public class PTY {
}
inInit = new PTYInputStream(new MasterFD());
outInit = new PTYOutputStream(new MasterFD(), ptyType != PTY_TYPE.PTY_WINPTY);
outInit = new PTYOutputStream(new MasterFD(), true);
}
slave = slaveInit;
in = inInit;
@ -207,7 +197,7 @@ public class PTY {
public void validateSlaveName() throws IOException {
// on windows the slave name is just an internal identifier
// and does not represent a real device
if (ptyType == PTY_TYPE.PTY_CONPTY || ptyType == PTY_TYPE.PTY_WINPTY) {
if (ptyType == PTY_TYPE.PTY_CONPTY) {
throw new IOException("Slave name is not valid"); //$NON-NLS-1$
}
}
@ -276,8 +266,6 @@ public class PTY {
throws IOException {
if (ptyType == PTY_TYPE.PTY_CONPTY) {
return conPTY.exec(cmdarray, envp, dir);
} else if (ptyType == PTY_TYPE.PTY_WINPTY) {
return exec2(cmdarray, envp, dir, chan, slave, master, isConsole());
} else {
return spawner.exec2(cmdarray, envp, dir, chan, slave, master, isConsole());
}
@ -290,8 +278,6 @@ public class PTY {
public int waitFor(Spawner spawner, int pid) {
if (ptyType == PTY_TYPE.PTY_CONPTY) {
return conPTY.waitFor();
} else if (ptyType == PTY_TYPE.PTY_WINPTY) {
return waitFor(master, pid);
} else {
return spawner.waitFor(pid);
}
@ -314,34 +300,16 @@ public class PTY {
static {
if (Platform.OS_WIN32.equals(Platform.getOS())) {
boolean conPtyEnabled = Boolean.parseBoolean(System.getProperty(CONPTY_ENABLED_PROP, "true")); //$NON-NLS-1$
if (conPtyEnabled) {
try {
// Try creating and closing a ConPTY to see if the API is available.
ConPTY pty = new ConPTY();
pty.close();
ptyType = PTY_TYPE.PTY_CONPTY;
isConsoleModeSupported = Boolean.getBoolean(FORCE_CONSOLE_MODE_ENABLED_PROP);
} catch (NoClassDefFoundError e) {
CNativePlugin.log(Messages.PTY_NoClassDefFoundError, e);
} catch (Throwable e) {
CNativePlugin.log(Messages.PTY_FailedToStartConPTY, e);
}
}
if (ptyType == PTY_TYPE.PTY_UNKNOWN) {
try {
// When we used to build with VC++ we used DelayLoadDLLs (See Gerrit 167674 and Bug 521515) so that the winpty
// could be found. When we ported to mingw we didn't port across this feature because it was simpler to just
// manually load winpty first.
System.loadLibrary("winpty"); //$NON-NLS-1$
System.loadLibrary("pty"); //$NON-NLS-1$
ptyType = PTY_TYPE.PTY_WINPTY;
isConsoleModeSupported = Boolean.getBoolean(FORCE_CONSOLE_MODE_ENABLED_PROP);
} catch (Throwable e) {
CNativePlugin.log(Messages.PTY_FailedToStartWinPTY, e);
}
try {
// Try creating and closing a ConPTY to see if the API is available.
ConPTY pty = new ConPTY();
pty.close();
ptyType = PTY_TYPE.PTY_CONPTY;
isConsoleModeSupported = Boolean.getBoolean(FORCE_CONSOLE_MODE_ENABLED_PROP);
} catch (NoClassDefFoundError e) {
CNativePlugin.log(Messages.PTY_NoClassDefFoundError, e);
} catch (Throwable e) {
CNativePlugin.log(Messages.PTY_FailedToStartConPTY, e);
}
} else {
try {
@ -355,7 +323,7 @@ public class PTY {
if (ptyType == PTY_TYPE.PTY_UNKNOWN) {
ptyType = PTY_TYPE.PTY_BROKEN;
isConsoleModeSupported = true;
isConsoleModeSupported = false;
CNativePlugin.log(Messages.PTY_FailedToStartPTY);
}