From 2f6f825a63fa9fca27eb8529b740394c79ad65fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sat, 11 Jan 2025 16:24:27 -0300 Subject: [PATCH] More WIP implementing more functionalities in DAP. This commit will probably fail on some OSes. --- .ecode/project_build.json | 14 + bin/assets/plugins/debugger.json | 120 +++++++- include/eepp/system/sys.hpp | 5 +- src/eepp/system/sys.cpp | 198 ++++++++++++- .../eterm/include/eterm/system/iprocess.hpp | 2 + .../eterm/include/eterm/system/process.hpp | 2 + .../eterm/terminal/terminalemulator.hpp | 2 + .../eterm/include/eterm/ui/uiterminal.hpp | 3 +- .../eterm/src/eterm/system/process.cpp | 4 + .../src/eterm/terminal/terminalemulator.cpp | 6 +- .../plugins/debugger/bussocketprocess.cpp | 49 ++++ .../plugins/debugger/bussocketprocess.hpp | 30 ++ .../debugger/dap/debuggerclientdap.cpp | 64 ++++- .../debugger/dap/debuggerclientdap.hpp | 11 + .../ecode/plugins/debugger/dap/messages.hpp | 4 + .../debugger/debuggerclientlistener.cpp | 7 +- .../ecode/plugins/debugger/debuggerplugin.cpp | 267 +++++++++++++----- .../ecode/plugins/debugger/debuggerplugin.hpp | 12 +- src/tools/ecode/projectbuild.cpp | 19 +- src/tools/ecode/projectbuild.hpp | 2 + src/tools/ecode/terminalmanager.cpp | 53 ++-- src/tools/ecode/terminalmanager.hpp | 4 +- 22 files changed, 758 insertions(+), 120 deletions(-) create mode 100644 src/tools/ecode/plugins/debugger/bussocketprocess.cpp create mode 100644 src/tools/ecode/plugins/debugger/bussocketprocess.hpp diff --git a/.ecode/project_build.json b/.ecode/project_build.json index f31128f9b..5e8d73e8a 100644 --- a/.ecode/project_build.json +++ b/.ecode/project_build.json @@ -47,6 +47,20 @@ "command": "ecode", "name": "ecode-release", "working_dir": "${project_root}/bin" + }, + { + "args": "", + "command": "eehttp-debug", + "name": "eehttp-debug", + "run_in_terminal": true, + "working_dir": "${project_root}/bin" + }, + { + "args": "", + "command": "eepp-sound", + "name": "eepp-sound", + "run_in_terminal": true, + "working_dir": "${project_root}/bin" } ], "var": { diff --git a/bin/assets/plugins/debugger.json b/bin/assets/plugins/debugger.json index 497983abc..56c7564e7 100644 --- a/bin/assets/plugins/debugger.json +++ b/bin/assets/plugins/debugger.json @@ -11,8 +11,8 @@ "languages": [ "cpp", "c", "d", "go", "objectivec", "fortran", "pascal", "rust" ], "configurations": [ { - "name": "launch binary", - "command": "launch", + "name": "Launch binary", + "request": "launch", "arguments": { "program": "${file}", "args": "${args}", @@ -20,6 +20,41 @@ "env": "${env}", "stopOnEntry": "${stopOnEntry}" } + }, + { + "name": "Launch binary in Terminal", + "request": "launch", + "arguments": { + "program": "${file}", + "args": "${args}", + "cwd": "${cwd}", + "env": "${env}", + "stopOnEntry": "${stopOnEntry}", + "runInTerminal": true + } + }, + { + "name": "Attach to Name", + "request": "attach", + "arguments": { + "program": "${file}" + } + }, + { + "name": "Attach to Name (wait)", + "request": "attach", + "arguments": { + "program": "${file}", + "waitFor": true + } + }, + { + "name": "Attach to PID", + "request": "attach", + "arguments": { + "program": "${file}", + "pid": "${pid}" + } } ] }, @@ -37,8 +72,8 @@ "languages": [ "cpp", "c", "odin", "rust", "zig" ], "configurations": [ { - "name": "launch binary", - "command": "launch", + "name": "Launch binary", + "request": "launch", "arguments": { "program": "${file}", "args": "${args}", @@ -46,6 +81,83 @@ "env": "${env}", "stopOnEntry": "${stopOnEntry}" } + }, + { + "name": "Launch binary in Terminal", + "request": "launch", + "arguments": { + "program": "${file}", + "args": "${args}", + "cwd": "${cwd}", + "env": "${env}", + "stopOnEntry": "${stopOnEntry}", + "runInTerminal": true + } + }, + { + "name": "Attach to Name", + "request": "attach", + "arguments": { + "program": "${file}" + } + }, + { + "name": "Attach to Name (wait)", + "request": "attach", + "arguments": { + "program": "${file}", + "waitFor": true + } + }, + { + "name": "Attach to PID", + "request": "attach", + "arguments": { + "program": "${file}", + "pid": "${pid}" + } + } + ] + }, + { + "name": "delve", + "url": "https://github.com/go-delve/delve", + "type": "go", + "run": { + "command": "dlv", + "command_arguments": ["dap", "--listen", "127.0.0.1:${randPort}"], + "redirectStderr": true, + "redirectStdout": true, + "supportsSourceRequest": false + }, + "languages": [ "go" ], + "configurations": [ + { + "name": "Launch (debug)", + "request": "launch", + "arguments": { + "mode": "debug", + "program": "${file}", + "args": "${args}" + } + }, + { + "name": "Launch (test)", + "request": "launch", + "arguments": { + "mode": "test", + "program": "${file}", + "args": "${args}" + } + }, + { + "name": "Launch (exec)", + "request": "launch", + "arguments": { + "mode": "exec", + "program": "${file}", + "args": "${args}" + } } ] } diff --git a/include/eepp/system/sys.hpp b/include/eepp/system/sys.hpp index e7eeeee8b..00e76e698 100644 --- a/include/eepp/system/sys.hpp +++ b/include/eepp/system/sys.hpp @@ -117,7 +117,7 @@ class EE_API Sys { static bool windowAttachConsole(); /** Executes a command */ - static void execute( const std::string& cmd, const std::string& workingDir = "" ); + static int execute( const std::string& cmd, const std::string& workingDir = "" ); /** @return True if current running platform / os is a mobile one */ static bool isMobile(); @@ -128,6 +128,9 @@ class EE_API Sys { /** @return The process ids found with the correspoding process / binary / executable name */ static std::vector pidof( const std::string& processName ); + /** @return A list of the current running processes */ + static std::vector> listProcesses(); + /** @returns The unix timestamp of the process creation time */ static Int64 getProcessCreationTime( Uint64 pid ); diff --git a/src/eepp/system/sys.cpp b/src/eepp/system/sys.cpp index e0b532cec..f6bf24b61 100644 --- a/src/eepp/system/sys.cpp +++ b/src/eepp/system/sys.cpp @@ -1232,7 +1232,7 @@ bool Sys::windowAttachConsole() { } #if EE_PLATFORM == EE_PLATFORM_WIN -static void windowsSystem( const std::string& programPath, const std::string& workingDirectory ) { +static int windowsSystem( const std::string& programPath, const std::string& workingDirectory ) { STARTUPINFOW si; PROCESS_INFORMATION pi; ZeroMemory( &si, sizeof( si ) ); @@ -1244,13 +1244,17 @@ static void windowsSystem( const std::string& programPath, const std::string& wo if ( CreateProcessW( NULL, (LPWSTR)String( programPath ).toWideString().c_str(), NULL, NULL, FALSE, 0, NULL, workingDir.empty() ? NULL : workingDir.c_str(), &si, &pi ) ) { + int pid = static_cast( pi.dwProcessId ); CloseHandle( pi.hProcess ); CloseHandle( pi.hThread ); + return pid; } + + return 0; } #endif -void Sys::execute( const std::string& cmd, const std::string& workingDir ) { +int Sys::execute( const std::string& cmd, const std::string& workingDir ) { #if EE_PLATFORM == EE_PLATFORM_WIN windowsSystem( cmd, workingDir ); #elif EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN @@ -1268,6 +1272,7 @@ void Sys::execute( const std::string& cmd, const std::string& workingDir ) { execvp( strings[0], (char* const*)strings.data() ); exit( 0 ); } + return pid; #endif } @@ -1486,8 +1491,8 @@ std::vector Sys::pidof( const std::string& processName ) { FreeLibrary( hPsapi ); return pids; -#elif EE_PLATFORM == EE_PLATFORM_LINUX || EE_PLATFORM == EE_PLATFORM_ANDROID || \ - EE_PLATFORM == EE_PLATFORM_MACOS +#elif EE_PLATFORM == EE_PLATFORM_LINUX || EE_PLATFORM == EE_PLATFORM_ANDROID + std::vector pids; DIR* dir = opendir( "/proc" ); if ( !dir ) { @@ -1515,6 +1520,37 @@ std::vector Sys::pidof( const std::string& processName ) { closedir( dir ); return pids; +#elif EE_PLATFORM == EE_PLATFORM_MACOS + std::vector pids; + + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0 }; + size_t len; + + if ( sysctl( mib, 4, NULL, &len, NULL, 0 ) == -1 ) { + return pids; + } + + struct kinfo_proc* procs = (struct kinfo_proc*)malloc( len ); + if ( !procs ) { + return pids; + } + + if ( sysctl( mib, 4, procs, &len, NULL, 0 ) == -1 ) { + free( procs ); + return pids; + } + + int proc_count = len / sizeof( struct kinfo_proc ); + + for ( int i = 0; i < proc_count; i++ ) { + std::string name( procs[i].kp_proc.p_comm ); + if ( name == processName ) { + pids.push_back( procs[i].kp_proc.p_pid ); + } + } + + free( procs ); + return pids; #elif EE_PLATFORM == EE_PLATFORM_BSD std::vector pids; @@ -1561,6 +1597,160 @@ std::vector Sys::pidof( const std::string& processName ) { #endif } +std::vector> Sys::listProcesses() { +#if EE_PLATFORM == EE_PLATFORM_WIN + std::vector> pids; + std::vector extensions = getEnvSplitted( "PATHEXT" ); + + HMODULE hPsapi = LoadLibrary( TEXT( "psapi.dll" ) ); + if ( !hPsapi ) + return pids; + + EnumProcesses_t EnumProcesses = (EnumProcesses_t)GetProcAddress( hPsapi, "EnumProcesses" ); + EnumProcessModules_t EnumProcessModules = + (EnumProcessModules_t)GetProcAddress( hPsapi, "EnumProcessModules" ); + GetModuleBaseName_t GetModuleBaseName = + (GetModuleBaseName_t)GetProcAddress( hPsapi, "GetModuleBaseNameA" ); + + if ( !EnumProcesses || !EnumProcessModules || !GetModuleBaseName ) { + FreeLibrary( hPsapi ); + eePRINTL( "EnumProcesses or EnumProcessModules or GetModuleBaseName failed" ); + return pids; + } + + DWORD processIds[1024], cbNeeded; + if ( !EnumProcesses( processIds, sizeof( processIds ), &cbNeeded ) ) { + FreeLibrary( hPsapi ); + eePRINTL( "EnumProcesses failed" ); + return pids; + } + + DWORD numProcesses = cbNeeded / sizeof( DWORD ); + pids.reserve( numProcesses ); + + for ( DWORD i = 0; i < numProcesses; ++i ) { + if ( processIds[i] == 0 ) + continue; + + HANDLE hProcess = + OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processIds[i] ); + if ( hProcess ) { + HMODULE hMod; + DWORD cbNeededMod; + if ( EnumProcessModules( hProcess, &hMod, sizeof( hMod ), &cbNeededMod ) ) { + char szProcessName[MAX_PATH]; + if ( GetModuleBaseName( hProcess, hMod, szProcessName, + sizeof( szProcessName ) / sizeof( char ) ) ) { + std::string actualName( szProcessName, std::strlen( szProcessName ) ); + pids.emplace_back( processIds[i], std::move( actualName ) ); + } + } + CloseHandle( hProcess ); + } + } + + FreeLibrary( hPsapi ); + return pids; +#elif EE_PLATFORM == EE_PLATFORM_LINUX || EE_PLATFORM == EE_PLATFORM_ANDROID + std::vector> pids; + DIR* dir = opendir( "/proc" ); + if ( !dir ) { + return pids; + } + + struct dirent* entry; + while ( ( entry = readdir( dir ) ) != NULL ) { + if ( entry->d_type == DT_DIR && isdigit( entry->d_name[0] ) ) { + std::string pidDir = "/proc/" + std::string( entry->d_name ); + std::string cmdPath = pidDir + "/comm"; + FILE* cmdFile = fopen( cmdPath.c_str(), "r" ); + if ( cmdFile ) { + char cmdline[256]; + if ( fgets( cmdline, sizeof( cmdline ), cmdFile ) != NULL ) { + cmdline[strcspn( cmdline, "\n" )] = 0; // Remove newline + pids.emplace_back( atoi( entry->d_name ), std::string{ cmdline } ); + } + fclose( cmdFile ); + } + } + } + + closedir( dir ); + return pids; +#elif EE_PLATFORM == EE_PLATFORM_MACOS + std::vector> pids; + + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0 }; + size_t len; + + if ( sysctl( mib, 4, NULL, &len, NULL, 0 ) == -1 ) { + return pids; + } + + struct kinfo_proc* procs = (struct kinfo_proc*)malloc( len ); + if ( !procs ) { + return pids; + } + + if ( sysctl( mib, 4, procs, &len, NULL, 0 ) == -1 ) { + free( procs ); + return pids; + } + + int proc_count = len / sizeof( struct kinfo_proc ); + pids.reserve( proc_count ); + + for ( int i = 0; i < proc_count; i++ ) { + std::string name( procs[i].kp_proc.p_comm ); + pids.emplace_back( procs[i].kp_proc.p_pid, name ); + } + + free( procs ); + return pids; +#elif EE_PLATFORM == EE_PLATFORM_BSD + std::vector> pids; + + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0 }; + size_t len; + + if ( sysctl( mib, 4, NULL, &len, NULL, 0 ) == -1 ) { + return pids; + } + + struct kinfo_proc* procs = (struct kinfo_proc*)malloc( len ); + if ( !procs ) { + return pids; + } + + if ( sysctl( mib, 4, procs, &len, NULL, 0 ) == -1 ) { + free( procs ); + return pids; + } + + int proc_count = len / sizeof( struct kinfo_proc ); + pids.reserve( proc_count ); + + for ( int i = 0; i < proc_count; i++ ) { + std::string name( procs[i].ki_comm ); + pids.emplace_back( procs[i].ki_pid, name ); + } + + free( procs ); + return pids; +#elif EE_PLATFORM == EE_PLATFORM_HAIKU + std::vector> pids; + int32 cookie = 0; + team_info teamInfo; + while ( get_next_team_info( &cookie, &teamInfo ) == B_OK ) { + pids.push_back( teamInfo.team, std::string{ teamInfo.name } ); + } + return pids; +#else +#warning Platform not supported + return {}; +#endif +} + #pragma pack( push, 1 ) // Basic structure of the Shell Link Header (size = 76 bytes) diff --git a/src/modules/eterm/include/eterm/system/iprocess.hpp b/src/modules/eterm/include/eterm/system/iprocess.hpp index fd2b93109..61f70b173 100644 --- a/src/modules/eterm/include/eterm/system/iprocess.hpp +++ b/src/modules/eterm/include/eterm/system/iprocess.hpp @@ -49,6 +49,8 @@ class IProcess { virtual void terminate() = 0; virtual void waitForExit() = 0; + + virtual int pid() = 0; }; }} // namespace EE::System diff --git a/src/modules/eterm/include/eterm/system/process.hpp b/src/modules/eterm/include/eterm/system/process.hpp index b17579678..3acf2bcde 100644 --- a/src/modules/eterm/include/eterm/system/process.hpp +++ b/src/modules/eterm/include/eterm/system/process.hpp @@ -52,6 +52,8 @@ class Process final : public IProcess { virtual void waitForExit() override; + virtual int pid() override; + static std::unique_ptr createWithPipe( const std::string& program, const std::vector& args, const std::string& workingDirectory, diff --git a/src/modules/eterm/include/eterm/terminal/terminalemulator.hpp b/src/modules/eterm/include/eterm/terminal/terminalemulator.hpp index b1f989c94..101310314 100644 --- a/src/modules/eterm/include/eterm/terminal/terminalemulator.hpp +++ b/src/modules/eterm/include/eterm/terminal/terminalemulator.hpp @@ -227,6 +227,8 @@ class TerminalEmulator final { Vector2i getSize() const; + System::IProcess* getProcess() const; + private: DpyPtr mDpy; PtyPtr mPty; diff --git a/src/modules/eterm/include/eterm/ui/uiterminal.hpp b/src/modules/eterm/include/eterm/ui/uiterminal.hpp index a88f4cd3b..f005ce954 100644 --- a/src/modules/eterm/include/eterm/ui/uiterminal.hpp +++ b/src/modules/eterm/include/eterm/ui/uiterminal.hpp @@ -126,11 +126,10 @@ class UITerminal : public UIWidget { int mScrollOffset; bool mScrollByBar{ false }; Clock mMouseClock; + std::shared_ptr mTerm; UITerminal( const std::shared_ptr& terminalDisplay ); - std::shared_ptr mTerm; - virtual Uint32 onTextInput( const TextInputEvent& event ); virtual Uint32 onTextEditing( const TextEditingEvent& event ); diff --git a/src/modules/eterm/src/eterm/system/process.cpp b/src/modules/eterm/src/eterm/system/process.cpp index 6437ee284..fd73470f3 100644 --- a/src/modules/eterm/src/eterm/system/process.cpp +++ b/src/modules/eterm/src/eterm/system/process.cpp @@ -94,6 +94,10 @@ void Process::waitForExit() { mExitCode = WEXITSTATUS( status ); } +int Process::pid() { + return mPID; +} + bool Process::hasExited() const { return mStatus == ProcessStatus::EXITED; } diff --git a/src/modules/eterm/src/eterm/terminal/terminalemulator.cpp b/src/modules/eterm/src/eterm/terminal/terminalemulator.cpp index 492dc998f..b3357bd74 100644 --- a/src/modules/eterm/src/eterm/terminal/terminalemulator.cpp +++ b/src/modules/eterm/src/eterm/terminal/terminalemulator.cpp @@ -98,7 +98,7 @@ static const unsigned int tabspaces = 4; #define BETWEEN( x, a, b ) ( ( a ) <= ( x ) && ( x ) <= ( b ) ) #define DIVCEIL( n, d ) ( ( ( n ) + ( ( d ) - 1 ) ) / ( d ) ) #define DEFAULT( a, b ) ( a ) = ( a ) ? ( a ) : ( b ) -#define LIMIT( x, a, b ) ( x ) = ( x ) < ( a ) ? ( a ) : ( x ) > ( b ) ? ( b ) : ( x ) +#define LIMIT( x, a, b ) ( x ) = ( x )<( a ) ? ( a ) : ( x )>( b ) ? ( b ) : ( x ) #define ATTRCMP( a, b ) ( ( a ).mode != ( b ).mode || ( a ).fg != ( b ).fg || ( a ).bg != ( b ).bg ) #define TIMEDIFF( t1, t2 ) ( ( t1.tv_sec - t2.tv_sec ) * 1000 + ( t1.tv_nsec - t2.tv_nsec ) / 1E6 ) #define MODBIT( x, set, bit ) ( ( set ) ? ( ( x ) |= ( bit ) ) : ( ( x ) &= ~( bit ) ) ) @@ -742,6 +742,10 @@ Vector2i TerminalEmulator::getSize() const { return { mTerm.col, mTerm.row }; } +IProcess* TerminalEmulator::getProcess() const { + return mProcess ? mProcess.get() : nullptr; +} + bool TerminalEmulator::isScrolling() const { return mTerm.scr != 0; } diff --git a/src/tools/ecode/plugins/debugger/bussocketprocess.cpp b/src/tools/ecode/plugins/debugger/bussocketprocess.cpp new file mode 100644 index 000000000..6951cd2fb --- /dev/null +++ b/src/tools/ecode/plugins/debugger/bussocketprocess.cpp @@ -0,0 +1,49 @@ +#include "busprocess.hpp" +#include "bussocket.hpp" +#include "bussocketprocess.hpp" + +namespace ecode { + +BusSocketProcess::BusSocketProcess( const Command& command, const Connection& connection ) : + mCommand( command ), + mConnection( connection ), + mProcess( std::make_unique( command ) ), + mSocket( std::make_unique( connection ) ) {} + +BusSocketProcess::~BusSocketProcess() { + mSocket.reset(); + mProcess.reset(); +} + +bool BusSocketProcess::start() { + if ( !mCommand.isValid() || !mConnection.isValid() ) + return false; + bool res = mProcess->start() && mSocket->start(); + if ( res ) + mState = State::Running; + return res; +} + +bool BusSocketProcess::close() { + if ( mState == State::Running ) { + if ( mSocket->state() == State::Running ) + mSocket->close(); + if ( mProcess->state() == State::Running ) + mProcess->close(); + mState = State::Closed; + return true; + } + + return false; +} + +void BusSocketProcess::startAsyncRead( ReadFn readFn ) { + mSocket->startAsyncRead( readFn ); +} + +size_t BusSocketProcess::write( const char* buffer, const size_t& size ) { + return mSocket->write( buffer, size ); + ; +} + +} // namespace ecode diff --git a/src/tools/ecode/plugins/debugger/bussocketprocess.hpp b/src/tools/ecode/plugins/debugger/bussocketprocess.hpp new file mode 100644 index 000000000..a5735199e --- /dev/null +++ b/src/tools/ecode/plugins/debugger/bussocketprocess.hpp @@ -0,0 +1,30 @@ +#include "bus.hpp" +#include "config.hpp" + +namespace ecode { + +class BusProcess; +class BusSocket; + +class BusSocketProcess : public Bus { + public: + BusSocketProcess( const Command& command, const Connection& connection ); + + virtual ~BusSocketProcess(); + + bool start() override; + + bool close() override; + + void startAsyncRead( ReadFn readFn ) override; + + size_t write( const char* buffer, const size_t& size ) override; + + protected: + Command mCommand; + Connection mConnection; + std::unique_ptr mProcess; + std::unique_ptr mSocket; +}; + +} // namespace ecode diff --git a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp index 6269e3633..0ec7e0809 100644 --- a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp +++ b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.cpp @@ -2,6 +2,7 @@ #include "messages.hpp" #include #include +#include using namespace EE::System; @@ -48,6 +49,23 @@ void DebuggerClientDap::makeRequest( const std::string_view& command, mIdx.fetch_add( 1, std::memory_order_relaxed ); } +void DebuggerClientDap::makeResponse( int reqSeq, bool success, const std::string& command, + const nlohmann::json& body ) { + nlohmann::json jsonCmd = { { "seq", reqSeq }, + { "success", success }, + { "command", command }, + { "body", body.empty() ? nlohmann::json::object() : body } }; + + std::string cmd = jsonCmd.dump(); + std::string msg( String::format( "Content-Length: %zu\r\n\r\n%s", cmd.size(), cmd ) ); + + Log::instance()->writel( mDebug ? LogLevel::Info : LogLevel::Debug, + "DebuggerClientDap::makeResponse:" ); + Log::instance()->writel( mDebug ? LogLevel::Info : LogLevel::Debug, msg ); + + mBus->write( msg.data(), msg.size() ); +} + bool DebuggerClientDap::isServerConnected() const { return ( mState != State::None ) && ( mState != State::Failed ) && ( mBus->state() == Bus::State::Running ); @@ -136,14 +154,14 @@ void DebuggerClientDap::requestInitialize() { const nlohmann::json capabilities{ { DAP_CLIENT_ID, "ecode-dap" }, { DAP_CLIENT_NAME, "ecode dap" }, - { "locale", mProtocol.locale }, + { DAP_LOCALE, mProtocol.locale }, { DAP_ADAPTER_ID, "ecode-dap" }, { DAP_LINES_START_AT1, mProtocol.linesStartAt1 }, { DAP_COLUMNS_START_AT2, mProtocol.columnsStartAt1 }, { DAP_PATH, ( mProtocol.pathFormatURI ? DAP_URI : DAP_PATH ) }, { DAP_SUPPORTS_VARIABLE_TYPE, true }, { DAP_SUPPORTS_VARIABLE_PAGING, false }, - { DAP_SUPPORTS_RUN_IN_TERMINAL_REQUEST, false }, + { DAP_SUPPORTS_RUN_IN_TERMINAL_REQUEST, true }, { DAP_SUPPORTS_MEMORY_REFERENCES, false }, { DAP_SUPPORTS_PROGRESS_REPORTING, false }, { DAP_SUPPORTS_INVALIDATED_EVENT, false }, @@ -193,6 +211,8 @@ void DebuggerClientDap::processProtocolMessage( const nlohmann::json& msg ) { processResponse( msg ); } else if ( DAP_EVENT == type ) { processEvent( msg ); + } else if ( DAP_REQUEST == type ) { + processRequest( msg ); } else { Log::warning( "DebuggerClientDap::processProtocolMessage: unknown, empty or unexpected " "ProtocolMessage::%s (%s)", @@ -200,6 +220,46 @@ void DebuggerClientDap::processProtocolMessage( const nlohmann::json& msg ) { } } +void DebuggerClientDap::processRequest( const nlohmann::json& msg ) { + const auto seq = msg.value( DAP_SEQ, 0 ); + if ( seq == 0 ) + return; + const auto command = msg.value( DAP_COMMAND, "" ); + const auto args = msg.contains( DAP_ARGUMENTS ) ? msg[DAP_ARGUMENTS] : nlohmann::json{}; + + if ( DAP_RUN_IN_TERMINAL == command ) { + if ( !runInTerminalCb ) + return; + bool isIntegrated = args.value( "kind", "integrated" ) == "integrated"; + std::vector largs; + if ( args.contains( "args" ) && args["args"].is_array() ) { + auto& jargs = args["args"]; + + for ( const auto& jarg : jargs ) { + if ( jarg.is_string() ) + largs.emplace_back( jarg.get() ); + } + } + std::unordered_map lenv; + + if ( args.contains( "env" ) && args["env"].is_object() ) { + for ( auto& [key, value] : args["env"].items() ) { + if ( value.is_string() ) + lenv.emplace( key, value.get() ); + } + } + + std::string cwd = args.value( "cwd", "" ); + if ( !largs.empty() ) { + std::string cmd = std::move( largs.front() ); + largs.erase( largs.begin() ); + runInTerminalCb( isIntegrated, cmd, largs, cwd, lenv, [this, seq, command]( int pid ) { + makeResponse( seq, pid != 0, command, nlohmann::json{ { "processId", pid } } ); + } ); + } + } +} + void DebuggerClientDap::processResponse( const nlohmann::json& msg ) { const Response response( msg ); diff --git a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp index 14bfb0e74..24054af64 100644 --- a/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp +++ b/src/tools/ecode/plugins/debugger/dap/debuggerclientdap.hpp @@ -16,6 +16,12 @@ class DebuggerClientDap : public DebuggerClient { public: typedef std::function ResponseHandler; + std::function& args, const std::string& cwd, + const std::unordered_map& env, + std::function doneFn )> + runInTerminalCb; + DebuggerClientDap( const ProtocolSettings& protocolSettings, std::unique_ptr&& bus ); virtual ~DebuggerClientDap(); @@ -98,6 +104,9 @@ class DebuggerClientDap : public DebuggerClient { void makeRequest( const std::string_view& command, const nlohmann::json& arguments, ResponseHandler onFinish = nullptr ); + void makeResponse( int reqSeq, bool success, const std::string& command, + const nlohmann::json& body ); + void asyncRead( const char* bytes, size_t n ); void processProtocolMessage( const nlohmann::json& msg ); @@ -106,6 +115,8 @@ class DebuggerClientDap : public DebuggerClient { void processEvent( const nlohmann::json& msg ); + void processRequest( const nlohmann::json& msg ); + struct HeaderInfo { Uint64 payloadStart; Uint64 payloadLength; diff --git a/src/tools/ecode/plugins/debugger/dap/messages.hpp b/src/tools/ecode/plugins/debugger/dap/messages.hpp index fd2181af9..2362cd2be 100644 --- a/src/tools/ecode/plugins/debugger/dap/messages.hpp +++ b/src/tools/ecode/plugins/debugger/dap/messages.hpp @@ -22,6 +22,7 @@ static const auto DAP_BODY = "body"sv; static const auto DAP_CLIENT_ID = "clientID"sv; static const auto DAP_CLIENT_NAME = "clientName"sv; static const auto DAP_ADAPTER_ID = "adapterID"sv; +static const auto DAP_LOCALE = "locale"sv; static const auto DAP_LINES_START_AT1 = "linesStartAt1"sv; static const auto DAP_COLUMNS_START_AT2 = "columnsStartAt1"sv; static const auto DAP_PATH_FORMAT = "pathFormat"sv; @@ -51,6 +52,9 @@ static const auto DAP_VARIABLES = "variables"sv; static const auto DAP_SCOPES = "scopes"sv; static const auto DAP_THREADS = "threads"sv; +// request commands +static const auto DAP_RUN_IN_TERMINAL = "runInTerminal"; + // event values static const auto DAP_OUTPUT = "output"sv; diff --git a/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp b/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp index 514741b0e..13bd4aad7 100644 --- a/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp @@ -1,4 +1,5 @@ #include "debuggerclientlistener.hpp" +#include "../../notificationcenter.hpp" #include "../../statusappoutputcontroller.hpp" #include "debuggerplugin.hpp" @@ -519,8 +520,10 @@ void DebuggerClientListener::outputProduced( const Output& output ) { void DebuggerClientListener::debuggingProcess( const ProcessInfo& ) {} -void DebuggerClientListener::errorResponse( const std::string& /*summary*/, - const std::optional& /*message*/ ) {} +void DebuggerClientListener::errorResponse( const std::string& summary, + const std::optional& /*message*/ ) { + mPlugin->getPluginContext()->getNotificationCenter()->addNotification( summary, Seconds( 5 ) ); +} void DebuggerClientListener::threadChanged( const ThreadEvent& ) {} diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp index e71878e24..53559c472 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp @@ -1,10 +1,13 @@ +#include "debuggerplugin.hpp" #include "../../notificationcenter.hpp" #include "../../projectbuild.hpp" +#include "../../terminalmanager.hpp" #include "../../uistatusbar.hpp" #include "../../widgetcommandexecuter.hpp" #include "busprocess.hpp" +#include "bussocket.hpp" +#include "bussocketprocess.hpp" #include "dap/debuggerclientdap.hpp" -#include "debuggerplugin.hpp" #include "models/breakpointsmodel.hpp" #include "statusdebuggercontroller.hpp" #include @@ -21,6 +24,9 @@ using namespace std::literals; using json = nlohmann::json; +static constexpr auto REQUEST_TYPE_LAUNCH = "launch"; +static constexpr auto REQUEST_TYPE_ATTACH = "attach"; + #if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN || defined( __EMSCRIPTEN_PTHREADS__ ) #define DEBUGGER_THREADED 1 #else @@ -157,6 +163,9 @@ void DebuggerPlugin::loadDAPConfig( const std::string& path, bool updateConfigFi if ( dap.contains( "run" ) ) { auto& run = dap["run"]; dapTool.run.command = run.value( "command", "" ); + dapTool.redirectStdout = run.value( "redirectStdout", false ); + dapTool.redirectStderr = run.value( "redirectStderr", false ); + dapTool.supportsSourceRequest = run.value( "supportsSourceRequest", true ); dapTool.fallbackCommand = run.value( "command_fallback", "" ); if ( run.contains( "command_arguments" ) ) { if ( run["command_arguments"].is_array() ) { @@ -179,9 +188,22 @@ void DebuggerPlugin::loadDAPConfig( const std::string& path, bool updateConfigFi DapConfig dapConfig; dapConfig.name = config.value( "name", "" ); if ( !dapConfig.name.empty() ) { - dapConfig.command = config.value( "command", "launch" ); + dapConfig.request = config.value( "request", REQUEST_TYPE_LAUNCH ); dapConfig.args = config["arguments"]; } + if ( config.contains( "command_arguments" ) ) { + if ( config["command_arguments"].is_array() ) { + auto& args = config["command_arguments"]; + dapConfig.cmdArgs.reserve( args.size() ); + for ( auto& arg : args ) { + if ( args.is_string() ) + dapConfig.cmdArgs.emplace_back( arg.get() ); + } + } else if ( config["command_arguments"].is_string() ) { + dapConfig.cmdArgs.push_back( + config["command_arguments"].get() ); + } + } dapTool.configurations.emplace_back( std::move( dapConfig ) ); } } @@ -268,13 +290,16 @@ void DebuggerPlugin::loadProjectConfiguration( const std::string& path ) { for ( const auto& conf : confs ) { DapConfig dapConfig; auto type = conf.value( "type", "" ); - if ( type != "cppdbg" ) + + if ( !std::any_of( mDaps.begin(), mDaps.end(), + [&type]( const DapTool& dap ) { return dap.type == type; } ) ) continue; + dapConfig.name = conf.value( "name", "" ); if ( dapConfig.name.empty() ) continue; - dapConfig.command = conf.value( "request", "" ); - if ( dapConfig.command != "launch" && dapConfig.command != "attach" ) + dapConfig.request = conf.value( "request", "" ); + if ( dapConfig.request != REQUEST_TYPE_LAUNCH && dapConfig.request != REQUEST_TYPE_ATTACH ) continue; dapConfig.args = std::move( conf ); @@ -570,7 +595,7 @@ void DebuggerPlugin::updateDebuggerConfigurationList() { mUIDebuggerConfList->getListBox()->setSelected( 0L ); } -void DebuggerPlugin::replaceKeysInJson( nlohmann::json& json ) { +void DebuggerPlugin::replaceKeysInJson( nlohmann::json& json, int randomPort ) { static constexpr auto KEY_FILE = "${file}"; static constexpr auto KEY_ARGS = "${args}"; static constexpr auto KEY_CWD = "${cwd}"; @@ -578,11 +603,14 @@ void DebuggerPlugin::replaceKeysInJson( nlohmann::json& json ) { static constexpr auto KEY_STOPONENTRY = "${stopOnEntry}"; static constexpr auto KEY_WORKSPACEFOLDER = "${workspaceFolder}"; static constexpr auto KEY_FILEDIRNAME = "${fileDirname}"; + static constexpr auto KEY_RANDPORT = "${randPort}"; + static constexpr auto KEY_PID = "${pid}"; auto runConfig = getPluginContext()->getProjectBuildManager()->getCurrentRunConfig(); + auto buildConfig = getPluginContext()->getProjectBuildManager()->getCurrentBuild(); for ( auto& j : json ) { if ( j.is_object() ) { - replaceKeysInJson( j ); + replaceKeysInJson( j, randomPort ); } else if ( j.is_string() ) { std::string val( j.get() ); @@ -593,8 +621,10 @@ void DebuggerPlugin::replaceKeysInJson( nlohmann::json& json ) { argsArr.push_back( arg ); j = std::move( argsArr ); continue; - } else if ( runConfig && val == KEY_ENV ) { + } else if ( runConfig && val == KEY_ENV && buildConfig ) { j = nlohmann::json{}; + for ( const auto& env : buildConfig->envs() ) + j[env.first] = env.second; } else if ( val == KEY_STOPONENTRY ) { j = false; } else if ( runConfig ) { @@ -602,6 +632,25 @@ void DebuggerPlugin::replaceKeysInJson( nlohmann::json& json ) { String::replaceAll( val, KEY_CWD, runConfig->workingDir ); String::replaceAll( val, KEY_FILEDIRNAME, runConfig->workingDir ); String::replaceAll( val, KEY_WORKSPACEFOLDER, mProjectPath ); + if ( String::contains( val, KEY_RANDPORT ) ) + String::replaceAll( val, KEY_RANDPORT, String::toString( randomPort ) ); + + bool containsPid = false; + if ( ( val == KEY_PID || ( containsPid = String::contains( val, KEY_PID ) ) ) && + json.contains( "program" ) && json["program"].is_string() ) { + auto programName( + FileSystem::fileNameFromPath( json["program"].get() ) ); + auto pids = Sys::pidof( programName ); + if ( !pids.empty() ) { + if ( containsPid ) { + String::replaceAll( val, KEY_PID, String::toString( pids[0] ) ); + } else { + j = pids[0]; + continue; + } + } + } + j = std::move( val ); } } @@ -920,16 +969,6 @@ static std::string findCommand( const std::string& findBinary, const std::string } void DebuggerPlugin::runConfig( const std::string& debugger, const std::string& configuration ) { - auto runConfig = getPluginContext()->getProjectBuildManager()->getCurrentRunConfig(); - if ( !runConfig ) { - auto msg = - i18n( "no_run_config", - "You must first have a \"Run Target\" configured and selected. Go to \"Build " - "Settings\" and create a new build and run setting (build step is optional)." ); - mManager->getPluginContext()->getNotificationCenter()->addNotification( msg, Seconds( 5 ) ); - return; - } - auto debuggerIt = std::find_if( mDaps.begin(), mDaps.end(), [&debugger]( const DapTool& dap ) { return dap.name == debugger; } ); @@ -942,6 +981,7 @@ void DebuggerPlugin::runConfig( const std::string& debugger, const std::string& debuggerIt->configurations.begin(), debuggerIt->configurations.end(), [&configuration]( const DapConfig& conf ) { return conf.name == configuration; } ); + bool usingExternalConfig = false; if ( configIt == debuggerIt->configurations.end() ) { configIt = std::find_if( mDapConfigs.begin(), mDapConfigs.end(), @@ -949,19 +989,34 @@ void DebuggerPlugin::runConfig( const std::string& debugger, const std::string& if ( configIt == debuggerIt->configurations.end() ) return; + + usingExternalConfig = true; } + auto runConfig = debuggerIt->run; + + if ( !usingExternalConfig && configIt->request == REQUEST_TYPE_LAUNCH && + !getPluginContext()->getProjectBuildManager()->getCurrentRunConfig() ) { + auto msg = + i18n( "no_run_config", + "You must first have a \"Run Target\" configured and selected. Go to \"Build " + "Settings\" and create a new build and run setting (build step is optional)." ); + mManager->getPluginContext()->getNotificationCenter()->addNotification( msg, Seconds( 5 ) ); + return; + } + + int randomPort = Math::randi( 44000, 45000 ); ProtocolSettings protocolSettings; - protocolSettings.launchCommand = configIt->command; + protocolSettings.launchCommand = configIt->request; auto args = configIt->args; - replaceKeysInJson( args ); + replaceKeysInJson( args, randomPort ); protocolSettings.launchRequest = args; + protocolSettings.redirectStdout = debuggerIt->redirectStdout; + protocolSettings.redirectStderr = debuggerIt->redirectStderr; + protocolSettings.supportsSourceRequest = debuggerIt->supportsSourceRequest; - Command cmd; - cmd.command = debuggerIt->run.command; - cmd.arguments = debuggerIt->run.args; - - mRunButton->setEnabled( false ); + for ( const std::string& cmdArg : configIt->cmdArgs ) + runConfig.args.emplace_back( cmdArg ); std::string findBinary; auto findBinaryIt = debuggerIt->findBinary.find( String::toLower( Sys::getPlatform() ) ); @@ -970,49 +1025,14 @@ void DebuggerPlugin::runConfig( const std::string& debugger, const std::string& std::string fallbackCommand = debuggerIt->fallbackCommand; + mRunButton->setEnabled( false ); + mThreadPool->run( - [this, cmd = std::move( cmd ), protocolSettings = std::move( protocolSettings ), - findBinary = std::move( findBinary ), - fallbackCommand = std::move( fallbackCommand )]() mutable { - if ( !findBinary.empty() && Sys::which( cmd.command ).empty() ) { - auto foundCmd = findCommand( findBinary, cmd.command ); - if ( !foundCmd.empty() ) { - cmd.command = std::move( foundCmd ); - } else if ( !fallbackCommand.empty() ) { - foundCmd = findCommand( findBinary, fallbackCommand ); - if ( !foundCmd.empty() ) - cmd.command = std::move( foundCmd ); - } - } - - if ( !FileSystem::fileExists( cmd.command ) && Sys::which( cmd.command ).empty() ) { - auto args = Process::parseArgs( cmd.command ); - if ( args.size() <= 1 || - ( !FileSystem::fileExists( args[0] ) && Sys::which( args[0] ).empty() ) ) { - if ( fallbackCommand.empty() || ( !FileSystem::fileExists( fallbackCommand ) && - Sys::which( fallbackCommand ).empty() ) ) { - auto msg = String::format( - i18n( "debugger_binary_not_found", - "Debugger binary not found. Binary \"%s\" must be installed." ) - .toUtf8(), - cmd.command ); - - mManager->getPluginContext()->getNotificationCenter()->addNotification( - msg ); - return; - } else { - cmd.command = std::move( fallbackCommand ); - } - } - } - - auto bus = std::make_unique( cmd ); - - mDebugger = std::make_unique( protocolSettings, std::move( bus ) ); - mListener = std::make_unique( mDebugger.get(), this ); - mDebugger->addListener( mListener.get() ); - - mDebugger->start(); + [this, protocolSettings = std::move( protocolSettings ), + runSettings = std::move( runConfig ), findBinary = std::move( findBinary ), + fallbackCommand = std::move( fallbackCommand ), randomPort]() mutable { + run( std::move( protocolSettings ), std::move( runSettings ), std::move( findBinary ), + std::move( fallbackCommand ), randomPort ); }, [this]( const Uint64& ) { if ( !mDebugger || !mDebugger->started() ) { @@ -1029,6 +1049,119 @@ void DebuggerPlugin::runConfig( const std::string& debugger, const std::string& } ); } +void DebuggerPlugin::run( ProtocolSettings&& protocolSettings, DapRunConfig&& runConfig, + std::string&& findBinary, std::string&& fallbackCommand, + int /*randPort*/ ) { + Command cmd; + cmd.command = std::move( runConfig.command ); + cmd.arguments = std::move( runConfig.args ); + + if ( !findBinary.empty() && Sys::which( cmd.command ).empty() ) { + auto foundCmd = findCommand( findBinary, cmd.command ); + if ( !foundCmd.empty() ) { + cmd.command = std::move( foundCmd ); + } else if ( !fallbackCommand.empty() ) { + foundCmd = findCommand( findBinary, fallbackCommand ); + if ( !foundCmd.empty() ) + cmd.command = std::move( foundCmd ); + } + } + + if ( protocolSettings.launchCommand == REQUEST_TYPE_LAUNCH && !cmd.command.empty() && + !FileSystem::fileExists( cmd.command ) && Sys::which( cmd.command ).empty() ) { + auto args = Process::parseArgs( cmd.command ); + if ( args.size() <= 1 || + ( !FileSystem::fileExists( args[0] ) && Sys::which( args[0] ).empty() ) ) { + if ( fallbackCommand.empty() || ( !FileSystem::fileExists( fallbackCommand ) && + Sys::which( fallbackCommand ).empty() ) ) { + auto msg = String::format( + i18n( "debugger_binary_not_found", + "Debugger binary not found. Binary \"%s\" must be installed." ) + .toUtf8(), + cmd.command ); + + mManager->getPluginContext()->getNotificationCenter()->addNotification( msg ); + return; + } else { + cmd.command = std::move( fallbackCommand ); + } + } + } + + if ( protocolSettings.launchCommand == REQUEST_TYPE_LAUNCH ) { + auto bus = std::make_unique( cmd ); + mDebugger = std::make_unique( protocolSettings, std::move( bus ) ); + } else if ( protocolSettings.launchCommand == REQUEST_TYPE_ATTACH ) { + auto mode = protocolSettings.launchRequest.value( "mode", "" ); + + Connection con; + con.host = protocolSettings.launchRequest.value( "host", "localhost" ); + con.port = protocolSettings.launchRequest.value( "port", 0 ); + bool useSocket = !con.host.empty() && con.port != 0; + if ( ( protocolSettings.launchRequest.contains( "host" ) || + protocolSettings.launchRequest.contains( "port" ) ) && + !useSocket ) { + getManager()->getPluginContext()->getNotificationCenter()->addNotification( + i18n( "host_port_required", "No host or port has been specified." ) ); + return; + } + + if ( mode == "local" ) { + if ( useSocket ) { + auto bus = std::make_unique( cmd, con ); + mDebugger = + std::make_unique( protocolSettings, std::move( bus ) ); + } else { + // Unsuported + } + } else if ( mode == "remote" ) { + auto bus = std::make_unique( con ); + mDebugger = std::make_unique( protocolSettings, std::move( bus ) ); + } + } else { + getManager()->getPluginContext()->getNotificationCenter()->addNotification( String::format( + i18n( "unknown_request_type", "Unknown request type: %s" ).toUtf8().c_str(), + cmd.command ) ); + return; + } + + if ( !mDebugger ) { + getManager()->getPluginContext()->getNotificationCenter()->addNotification( i18n( + "debugger_configuration_not_supported", "Debugger configuration not supported." ) ); + return; + } + + mListener = std::make_unique( mDebugger.get(), this ); + mDebugger->addListener( mListener.get() ); + + DebuggerClientDap* dap = static_cast( mDebugger.get() ); + dap->runInTerminalCb = [this]( bool isIntegrated, std::string cmd, + const std::vector& args, const std::string& cwd, + const std::unordered_map& /*env*/, + std::function doneFn ) { + if ( !FileSystem::fileExists( cmd ) ) + cmd = FileSystem::fileNameFromPath( cmd ); + getUISceneNode()->runOnMainThread( [=] { + if ( isIntegrated ) { + UITerminal* term = + getPluginContext()->getTerminalManager()->createTerminalInSplitter( + cwd, cmd, args, false ); + + doneFn( term && term->getTerm() && term->getTerm()->getTerminal() && + term->getTerm()->getTerminal()->getProcess() + ? term->getTerm()->getTerminal()->getProcess()->pid() + : 0 ); + } else { + std::string fcmd = cmd + ( !args.empty() ? " " : "" ) + String::join( args, ' ' ); + doneFn( + getPluginContext()->getTerminalManager()->openInExternalTerminal( fcmd, cwd ) ); + } + } ); + }; + + mDebugger->start(); +} + void DebuggerPlugin::exitDebugger() { if ( mDebugger && mListener ) mDebugger->removeListener( mListener.get() ); diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.hpp b/src/tools/ecode/plugins/debugger/debuggerplugin.hpp index 8bf0ec2b5..80684e865 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.hpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.hpp @@ -2,6 +2,7 @@ #include "../plugin.hpp" #include "../pluginmanager.hpp" +#include "config.hpp" #include "debuggerclientlistener.hpp" #include "models/breakpointsmodel.hpp" @@ -17,7 +18,8 @@ struct DapRunConfig { struct DapConfig { std::string name; - std::string command; + std::string request; + std::vector cmdArgs; nlohmann::json args; }; @@ -30,6 +32,9 @@ struct DapTool { std::vector configurations; std::unordered_map findBinary; std::string fallbackCommand; + bool redirectStdout{ false }; + bool redirectStderr{ false }; + bool supportsSourceRequest{ false }; }; class DebuggerPlugin : public PluginBase { @@ -123,9 +128,12 @@ class DebuggerPlugin : public PluginBase { void runConfig( const std::string& debugger, const std::string& configuration ); + void run( ProtocolSettings&& protocolSettings, DapRunConfig&& runConfig, + std::string&& findBinary, std::string&& fallbackCommand, int randPort ); + void exitDebugger(); - void replaceKeysInJson( nlohmann::json& json ); + void replaceKeysInJson( nlohmann::json& json, int randomPort ); void onRegisterEditor( UICodeEditor* ) override; diff --git a/src/tools/ecode/projectbuild.cpp b/src/tools/ecode/projectbuild.cpp index e1dbf7239..d9f408003 100644 --- a/src/tools/ecode/projectbuild.cpp +++ b/src/tools/ecode/projectbuild.cpp @@ -734,10 +734,14 @@ void ProjectBuildManager::setConfig( const ProjectBuildConfiguration& config ) { } } +ProjectBuild* ProjectBuildManager::getCurrentBuild() { + return getBuild( mConfig.buildName ); +} + void ProjectBuildManager::buildCurrentConfig( StatusBuildOutputController* sboc, std::function doneFn ) { if ( sboc && !isBuilding() && !getBuilds().empty() ) { - const ProjectBuild* build = getBuild( mConfig.buildName ); + const ProjectBuild* build = getCurrentBuild(); if ( build ) { mApp->saveAll(); sboc->runBuild( build->getName(), mConfig.buildType, @@ -748,7 +752,7 @@ void ProjectBuildManager::buildCurrentConfig( StatusBuildOutputController* sboc, void ProjectBuildManager::cleanCurrentConfig( StatusBuildOutputController* sboc ) { if ( sboc && !isBuilding() && !getBuilds().empty() ) { - const ProjectBuild* build = getBuild( mConfig.buildName ); + const ProjectBuild* build = getCurrentBuild(); if ( build ) sboc->runBuild( build->getName(), mConfig.buildType, getOutputParser( build->getName() ), true ); @@ -773,7 +777,7 @@ bool ProjectBuildManager::hasBuildConfig() const { bool ProjectBuildManager::hasRunConfig() { if ( hasBuildConfig() ) { - auto build = getBuild( mConfig.buildName ); + auto build = getCurrentBuild(); return build != nullptr && build->hasRun(); } return false; @@ -781,7 +785,7 @@ bool ProjectBuildManager::hasRunConfig() { bool ProjectBuildManager::hasBuildConfigWithBuildSteps() { if ( hasBuildConfig() ) { - auto build = getBuild( mConfig.buildName ); + auto build = getCurrentBuild(); return build != nullptr && build->hasBuild(); } return {}; @@ -789,7 +793,7 @@ bool ProjectBuildManager::hasBuildConfigWithBuildSteps() { std::optional ProjectBuildManager::getCurrentRunConfig() { if ( hasBuildConfig() ) { - auto build = getBuild( mConfig.buildName ); + auto build = getCurrentBuild(); if ( build != nullptr && build->hasRun() ) { for ( const auto& crun : build->mRun ) { if ( crun->name == mConfig.runName || mConfig.runName.empty() ) { @@ -804,7 +808,7 @@ std::optional ProjectBuildManager::getCurrentRunConfig() { void ProjectBuildManager::runConfig( StatusAppOutputController* saoc ) { if ( !isRunningApp() && !getBuilds().empty() ) { BoolScopedOp op( mRunning, true ); - const ProjectBuild* build = getBuild( mConfig.buildName ); + const ProjectBuild* build = getCurrentBuild(); if ( nullptr == build || !build->hasRun() ) return; @@ -834,7 +838,8 @@ void ProjectBuildManager::runConfig( StatusAppOutputController* saoc ) { auto cmd = finalBuild.cmd + " " + finalBuild.args; if ( finalBuild.runInTerminal ) { UITerminal* term = mApp->getTerminalManager()->createTerminalInSplitter( - finalBuild.workingDir, false ); + finalBuild.workingDir, "", {}, false ); + Log::info( "Running \"%s\" in terminal", cmd ); if ( term == nullptr || term->getTerm() == nullptr ) { mApp->getTerminalManager()->openInExternalTerminal( cmd, finalBuild.workingDir ); diff --git a/src/tools/ecode/projectbuild.hpp b/src/tools/ecode/projectbuild.hpp index 7be183242..6a1d3b7d5 100644 --- a/src/tools/ecode/projectbuild.hpp +++ b/src/tools/ecode/projectbuild.hpp @@ -341,6 +341,8 @@ class ProjectBuildManager { std::optional getCurrentRunConfig(); + ProjectBuild* getCurrentBuild(); + protected: std::string mProjectRoot; std::string mProjectFile; diff --git a/src/tools/ecode/terminalmanager.cpp b/src/tools/ecode/terminalmanager.cpp index 5f0f8553c..e5ea27919 100644 --- a/src/tools/ecode/terminalmanager.cpp +++ b/src/tools/ecode/terminalmanager.cpp @@ -9,6 +9,8 @@ namespace ecode { TerminalManager::TerminalManager( App* app ) : mApp( app ) {} UITerminal* TerminalManager::createTerminalInSplitter( const std::string& workingDir, + std::string program, + const std::vector& args, bool fallback ) { #if EE_PLATFORM == EE_PLATFORM_WIN std::string os = Sys::getOSName( true ); @@ -26,34 +28,34 @@ UITerminal* TerminalManager::createTerminalInSplitter( const std::string& workin UIOrientation orientation = splitter->getMainSplitOrientation(); if ( config.term.newTerminalOrientation == NewTerminalOrientation::Vertical && orientation == UIOrientation::Horizontal ) { - term = createNewTerminal( "", splitter->getTabWidgets()[1], workingDir, "", {}, - fallback ); + term = createNewTerminal( "", splitter->getTabWidgets()[1], workingDir, program, + args, fallback ); } else if ( config.term.newTerminalOrientation == NewTerminalOrientation::Horizontal && orientation == UIOrientation::Vertical ) { - term = createNewTerminal( "", splitter->getTabWidgets()[1], workingDir, "", {}, - fallback ); + term = createNewTerminal( "", splitter->getTabWidgets()[1], workingDir, program, + args, fallback ); } else { - term = createNewTerminal( "", nullptr, workingDir ); + term = createNewTerminal( "", nullptr, workingDir, program, args ); } } else { - term = createNewTerminal(); + term = createNewTerminal( "", nullptr, workingDir, program, args ); } } else if ( splitter ) { switch ( config.term.newTerminalOrientation ) { case NewTerminalOrientation::Vertical: { auto cwd = workingDir.empty() ? mApp->getCurrentWorkingDir() : workingDir; splitter->split( SplitDirection::Right, splitter->getCurWidget(), false ); - term = createNewTerminal( "", nullptr, cwd, "", {}, fallback ); + term = createNewTerminal( "", nullptr, cwd, program, args, fallback ); break; } case NewTerminalOrientation::Horizontal: { auto cwd = workingDir.empty() ? mApp->getCurrentWorkingDir() : workingDir; splitter->split( SplitDirection::Bottom, splitter->getCurWidget(), false ); - term = createNewTerminal( "", nullptr, cwd, "", {}, fallback ); + term = createNewTerminal( "", nullptr, cwd, program, args, fallback ); break; } case NewTerminalOrientation::Same: { - term = createNewTerminal( "", nullptr, "", "", {}, fallback ); + term = createNewTerminal( "", nullptr, "", program, args, fallback ); break; } } @@ -304,7 +306,7 @@ std::string quoteString( std::string str ) { return escapedStr; } -static void openExternal( const std::string& defShell, const std::string& cmd, +static int openExternal( const std::string& defShell, const std::string& cmd, const std::string& scriptsPath, const std::string& workingDir ) { // This is an utility bat script based in the Geany utility script called "geany-run-helper" static const std::string RUN_HELPER = @@ -354,8 +356,7 @@ if not %autoclose%==1 pause auto fcmd = "cmd.exe /q /c " + quoteString( "\"" + runHelperPath + "\" \"" + cmdDir + "\" 0 \"" + cmdFile + "\"" ); Log::info( "Running: %s", fcmd ); - Sys::execute( fcmd, workingDir ); - return; + return Sys::execute( fcmd, workingDir ); } else { Log::info( "Couldn't write runHelperPath %s", runHelperPath ); } @@ -372,28 +373,27 @@ if not %autoclose%==1 pause if ( !cmd.empty() ) { auto fcmd = externalShell + " /q /c " + quoteString( "\"" + cmd + "\"" ); Log::info( "Running: %s", fcmd ); - Sys::execute( fcmd, workingDir ); - return; + return Sys::execute( fcmd, workingDir ); } else { - Sys::execute( externalShell, workingDir ); - return; + return Sys::execute( externalShell, workingDir ); } } } + return 0; } #elif EE_PLATFORM == EE_PLATFORM_MACOS -static void openExternal( const std::string&, const std::string& cmd, const std::string&, +static int openExternal( const std::string&, const std::string& cmd, const std::string&, const std::string& workingDir ) { static const std::string externalShell = "open -a terminal"; if ( !cmd.empty() ) { std::string fcmd = externalShell + " \"" + cmd + "\""; Log::info( "Running: %s", fcmd ); - Sys::execute( fcmd, workingDir ); - } else - Sys::execute( externalShell, workingDir ); + return Sys::execute( fcmd, workingDir ); + } + return Sys::execute( externalShell, workingDir ); } #else -static void openExternal( const std::string&, const std::string& cmd, const std::string&, +static int openExternal( const std::string&, const std::string& cmd, const std::string&, const std::string& workingDir ) { std::vector options = { "gnome-terminal", "konsole", "xterm", "st" }; for ( const auto& option : options ) { @@ -402,21 +402,20 @@ static void openExternal( const std::string&, const std::string& cmd, const std: if ( !cmd.empty() ) { auto fcmd = externalShell + " -e \"" + cmd + "\""; Log::info( "Running: %s", fcmd ); - Sys::execute( fcmd, workingDir ); - return; + return Sys::execute( fcmd, workingDir ); } else { - Sys::execute( externalShell, workingDir ); - return; + return Sys::execute( externalShell, workingDir ); } } } + return 0; } #endif -void TerminalManager::openInExternalTerminal( const std::string& cmd, +int TerminalManager::openInExternalTerminal( const std::string& cmd, const std::string& workingDir ) { Log::info( "Trying to open in external terminal: %s %s", cmd, workingDir ); - openExternal( mApp->termConfig().shell, cmd, mApp->getScriptsPath(), workingDir ); + return openExternal( mApp->termConfig().shell, cmd, mApp->getScriptsPath(), workingDir ); } void TerminalManager::displayError( const std::string& workingDir ) { diff --git a/src/tools/ecode/terminalmanager.hpp b/src/tools/ecode/terminalmanager.hpp index 07b53ac26..568c448c0 100644 --- a/src/tools/ecode/terminalmanager.hpp +++ b/src/tools/ecode/terminalmanager.hpp @@ -16,6 +16,8 @@ class TerminalManager { TerminalManager( App* app ); UITerminal* createTerminalInSplitter( const std::string& workingDir = "", + std::string program = "", + const std::vector& args = {}, bool fallback = true ); UITerminal* createNewTerminal( const std::string& title = "", @@ -60,7 +62,7 @@ class TerminalManager { void displayError( const std::string& workingDir ); - void openInExternalTerminal( const std::string& cmd, const std::string& workingDir ); + int openInExternalTerminal( const std::string& cmd, const std::string& workingDir ); protected: App* mApp;