From e18c29c59525b8ad4a182ce1e9f5a21f00d10ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Mon, 15 Apr 2024 01:28:27 -0300 Subject: [PATCH] Added a Run step for the Build Settings (SpartanJ/ecode#222). --- .ecode/project_build.json | 7 +++ include/eepp/system/sys.hpp | 2 +- src/eepp/system/sys.cpp | 15 +++-- src/tools/ecode/ecode.cpp | 2 + src/tools/ecode/ecode.hpp | 4 ++ src/tools/ecode/projectbuild.cpp | 71 +++++++++++++++++++--- src/tools/ecode/projectbuild.hpp | 10 +++ src/tools/ecode/terminalmanager.cpp | 22 +++++-- src/tools/ecode/terminalmanager.hpp | 2 + src/tools/ecode/uibuildsettings.cpp | 94 +++++++++++++++++++++-------- 10 files changed, 186 insertions(+), 43 deletions(-) diff --git a/.ecode/project_build.json b/.ecode/project_build.json index b5ceeec77..0417c4d53 100644 --- a/.ecode/project_build.json +++ b/.ecode/project_build.json @@ -146,6 +146,13 @@ "relative_file_paths": true } }, + "run": [ + { + "args": "-x", + "command": "ecode-debug", + "working_dir": "${project_root}/bin/" + } + ], "var": { "build_dir": "${project_root}/make/${os}" } diff --git a/include/eepp/system/sys.hpp b/include/eepp/system/sys.hpp index 0cf41e1a7..ef1e0f9fc 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 ); + static void execute( const std::string& cmd, const std::string& workingDir = "" ); /** @return True if current running platform / os is a mobile one */ static bool isMobile(); diff --git a/src/eepp/system/sys.cpp b/src/eepp/system/sys.cpp index 94de612ef..d10e4914c 100644 --- a/src/eepp/system/sys.cpp +++ b/src/eepp/system/sys.cpp @@ -1208,24 +1208,27 @@ bool Sys::windowAttachConsole() { } #if EE_PLATFORM == EE_PLATFORM_WIN -static void windowsSystem( const std::string& programPath ) { +static void windowsSystem( const std::string& programPath, const std::string& workingDirectory ) { STARTUPINFOW si; PROCESS_INFORMATION pi; ZeroMemory( &si, sizeof( si ) ); si.cb = sizeof( si ); ZeroMemory( &pi, sizeof( pi ) ); + std::wstring workingDir = String( workingDirectory ).toWideString(); + if ( CreateProcessW( NULL, (LPWSTR)String( programPath ).toWideString().c_str(), NULL, NULL, - FALSE, 0, NULL, NULL, &si, &pi ) ) { + FALSE, 0, NULL, workingDir.empty() ? NULL : workingDir.c_str(), &si, + &pi ) ) { CloseHandle( pi.hProcess ); CloseHandle( pi.hThread ); } } #endif -void Sys::execute( const std::string& cmd ) { +void Sys::execute( const std::string& cmd, const std::string& workingDir ) { #if EE_PLATFORM == EE_PLATFORM_WIN - windowsSystem( cmd ); + windowsSystem( cmd, workingDir ); #elif EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN pid_t pid = fork(); if ( pid == 0 ) { @@ -1234,6 +1237,10 @@ void Sys::execute( const std::string& cmd ) { for ( size_t i = 0; i < cmdArr.size(); ++i ) strings.push_back( cmdArr[i].c_str() ); strings.push_back( NULL ); + + if ( !workingDir.empty() ) + FileSystem::changeWorkingDirectory( workingDir ); + execvp( strings[0], (char* const*)strings.data() ); exit( 0 ); } diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index 5095d171b..3277aa0a8 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -2049,6 +2049,7 @@ std::map App::getLocalKeybindings() { { { KEY_4, KEYMOD_LALT }, "toggle-status-build-output" }, { { KEY_B, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "project-build-start" }, { { KEY_C, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "project-build-cancel" }, + { { KEY_F5 }, "project-run-executable" }, { { KEY_O, KEYMOD_LALT | KEYMOD_SHIFT }, "show-open-documents" }, { { KEY_K, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "open-workspace-symbol-search" }, { { KEY_P, KeyMod::getDefaultModifier() | KEYMOD_SHIFT }, "open-document-symbol-search" }, @@ -2086,6 +2087,7 @@ std::vector App::getUnlockedCommands() { "open-global-search", "project-build-start", "project-build-cancel", + "project-run-executable", "toggle-status-locate-bar", "toggle-status-global-search-bar", "toggle-status-build-output", diff --git a/src/tools/ecode/ecode.hpp b/src/tools/ecode/ecode.hpp index af748724e..8c0d713cf 100644 --- a/src/tools/ecode/ecode.hpp +++ b/src/tools/ecode/ecode.hpp @@ -258,6 +258,10 @@ class App : public UICodeEditorSplitter::Client { mProjectBuildManager->cancelBuild(); } } ); + t.setCommand( "project-run-executable", [this] { + if ( mProjectBuildManager && mStatusBuildOutputController ) + mProjectBuildManager->runCurrentConfig( mStatusBuildOutputController.get() ); + } ); t.setCommand( "show-folder-treeview-tab", [this] { showFolderTreeViewTab(); } ); t.setCommand( "show-build-tab", [this] { showBuildTab(); } ); t.setCommand( "open-workspace-symbol-search", diff --git a/src/tools/ecode/projectbuild.cpp b/src/tools/ecode/projectbuild.cpp index 146b571a6..8ab494fb8 100644 --- a/src/tools/ecode/projectbuild.cpp +++ b/src/tools/ecode/projectbuild.cpp @@ -80,6 +80,24 @@ json ProjectBuild::serialize( const ProjectBuild::Map& builds ) { jclean.push_back( step ); } + // TODO: Support multiple-runs + if ( curBuild.hasRun() ) { + bj["run"] = json::array(); + auto& jrun = bj["run"]; + { + const auto& run = curBuild.mRun; + json step; + step["working_dir"] = run.workingDir; + step["args"] = run.args; + step["command"] = run.cmd; + if ( !run.enabled ) + step["enabled"] = run.enabled; + if ( run.runInTerminal ) + step["run_in_terminal"] = run.runInTerminal; + jrun.push_back( step ); + } + } + bj["build_types"] = curBuild.buildTypes(); bj["config"]["clear_sys_env"] = curBuild.getConfig().clearSysEnv; bj["os"] = curBuild.os(); @@ -474,14 +492,27 @@ ProjectBuild::Map ProjectBuild::deserialize( const json& j, const std::string& p } if ( buildObj.contains( "clean" ) && buildObj["clean"].is_array() ) { - const auto& buildArray = buildObj["clean"]; - for ( const auto& step : buildArray ) { - ProjectBuildStep bstep; - bstep.cmd = step.value( "command", "" ); - bstep.args = step.value( "args", "" ); - bstep.workingDir = step.value( "working_dir", "" ); - bstep.enabled = step.value( "enabled", true ); - b.mClean.emplace_back( std::move( bstep ) ); + const auto& cleanArray = buildObj["clean"]; + for ( const auto& step : cleanArray ) { + ProjectBuildStep cstep; + cstep.cmd = step.value( "command", "" ); + cstep.args = step.value( "args", "" ); + cstep.workingDir = step.value( "working_dir", "" ); + cstep.enabled = step.value( "enabled", true ); + b.mClean.emplace_back( std::move( cstep ) ); + } + } + + if ( buildObj.contains( "run" ) && buildObj["run"].is_array() ) { + const auto& runArray = buildObj["run"]; + for ( const auto& step : runArray ) { + ProjectBuildStep rstep; + rstep.cmd = step.value( "command", "" ); + rstep.args = step.value( "args", "" ); + rstep.workingDir = step.value( "working_dir", "" ); + rstep.enabled = step.value( "enabled", true ); + rstep.runInTerminal = step.value( "run_in_terminal", false ); + b.mRun = std::move( rstep ); } } @@ -692,6 +723,30 @@ void ProjectBuildManager::cleanCurrentConfig( StatusBuildOutputController* sboc } } +void ProjectBuildManager::runCurrentConfig( StatusBuildOutputController* sboc ) { + if ( sboc && !isBuilding() && !getBuilds().empty() ) { + const ProjectBuild* build = nullptr; + for ( const auto& buildIt : getBuilds() ) + if ( buildIt.second.getName() == mConfig.buildName ) + build = &buildIt.second; + + if ( build && build->hasRun() ) { + auto cmd = build->mRun.cmd + " " + build->mRun.args; + if ( build->mRun.runInTerminal ) { + UITerminal* term = mApp->getTerminalManager()->createNewTerminal( + "", nullptr, build->mRun.workingDir ); + if ( term == nullptr || term->getTerm() == nullptr ) { + mApp->getTerminalManager()->openInExternalTerminal( cmd ); + } else { + term->executeFile( cmd ); + } + } else { + Sys::execute( cmd, build->mRun.workingDir ); + } + } + } +} + std::string ProjectBuildManager::getCurrentDocument() { return mApp->getSplitter() && mApp->getSplitter()->getCurEditor() && mApp->getSplitter()->getCurEditor()->getDocument().hasFilepath() diff --git a/src/tools/ecode/projectbuild.hpp b/src/tools/ecode/projectbuild.hpp index fdafdf048..70cb2e3ca 100644 --- a/src/tools/ecode/projectbuild.hpp +++ b/src/tools/ecode/projectbuild.hpp @@ -83,6 +83,7 @@ struct ProjectBuildStep { std::string args; std::string workingDir; bool enabled{ true }; + bool runInTerminal{ false }; }; using ProjectBuildSteps = std::vector; @@ -171,6 +172,8 @@ class ProjectBuild { const ProjectBuildSteps& cleanSteps() const { return mClean; } + const ProjectBuildStep& runStep() const { return mRun; } + const ProjectBuildKeyVal& envs() const { return mEnvs; } const ProjectBuildKeyVal& vars() const { return mVars; } @@ -179,6 +182,10 @@ class ProjectBuild { bool hasClean() const { return !mClean.empty(); } + bool hasRun() const { + return !mRun.cmd.empty() || !mRun.args.empty() || !mRun.workingDir.empty(); + } + ProjectBuildSteps replaceVars( const ProjectBuildSteps& steps ) const; static json serialize( const ProjectBuild::Map& builds ); @@ -195,6 +202,7 @@ class ProjectBuild { std::set mBuildTypes; ProjectBuildSteps mBuild; ProjectBuildSteps mClean; + ProjectBuildStep mRun; ProjectBuildKeyVal mEnvs; ProjectBuildKeyVal mVars; ProjectBuildConfig mConfig; @@ -288,6 +296,8 @@ class ProjectBuildManager { void cleanCurrentConfig( StatusBuildOutputController* sboc ); + void runCurrentConfig( StatusBuildOutputController* sboc ); + protected: std::string mProjectRoot; std::string mProjectFile; diff --git a/src/tools/ecode/terminalmanager.cpp b/src/tools/ecode/terminalmanager.cpp index f3d1ddceb..29ffd221d 100644 --- a/src/tools/ecode/terminalmanager.cpp +++ b/src/tools/ecode/terminalmanager.cpp @@ -235,25 +235,39 @@ void TerminalManager::updateMenuColorScheme( UIMenuSubMenu* colorSchemeMenu ) { } #if EE_PLATFORM == EE_PLATFORM_WIN -static void openExternal( const std::string& defShell = "" ) { +static void openExternal( const std::string& defShell, const std::string& cmd = "" ) { std::vector options; if ( !defShell.empty() ) options.push_back( defShell ); options.push_back( "powershell" ); options.push_back( "cmd" ); #else -static void openExternal( const std::string& ) { +static void openExternal( const std::string&, const std::string& cmd = "" ) { std::vector options = { "gnome-terminal", "konsole", "xterm", "st" }; #endif for ( const auto& option : options ) { auto externalShell( Sys::which( option ) ); if ( !externalShell.empty() ) { - Sys::execute( externalShell ); - return; + if ( !cmd.empty() ) { +#if EE_PLATFORM == EE_PLATFORM_WIN + auto fcmd = externalShell + " /c " + cmd; +#else + auto fcmd = externalShell + " -e " + cmd; +#endif + Sys::execute( fcmd ); + return; + } else { + Sys::execute( externalShell ); + return; + } } } } +void TerminalManager::openInExternalTerminal( const std::string& cmd ) { + openExternal( mApp->termConfig().shell, cmd ); +} + void TerminalManager::displayError() { if ( mApp->getConfig().term.unsupportedOSWarnDisabled ) { openExternal( mApp->termConfig().shell ); diff --git a/src/tools/ecode/terminalmanager.hpp b/src/tools/ecode/terminalmanager.hpp index 92f1ca5a3..40db51c1a 100644 --- a/src/tools/ecode/terminalmanager.hpp +++ b/src/tools/ecode/terminalmanager.hpp @@ -56,6 +56,8 @@ class TerminalManager { void displayError(); + void openInExternalTerminal( const std::string& cmd ); + protected: App* mApp; std::string mTerminalColorSchemesPath; diff --git a/src/tools/ecode/uibuildsettings.cpp b/src/tools/ecode/uibuildsettings.cpp index 026ccb66e..2bd6fc970 100644 --- a/src/tools/ecode/uibuildsettings.cpp +++ b/src/tools/ecode/uibuildsettings.cpp @@ -198,9 +198,11 @@ class UICustomOutputParserWindow : public UIWindow { class UIBuildStep : public UILinearLayout { public: - static UIBuildStep* New( bool isBuildStep, UIBuildSettings* buildSettings, size_t stepNum, + enum class StepType { Build, Clean, Run }; + + static UIBuildStep* New( StepType stepType, UIBuildSettings* buildSettings, size_t stepNum, ProjectBuildStep* buildStep ) { - return eeNew( UIBuildStep, ( isBuildStep, buildSettings, stepNum, buildStep ) ); + return eeNew( UIBuildStep, ( stepType, buildSettings, stepNum, buildStep ) ); } void clearBindings() { mDataBindHolder.clear(); } @@ -213,12 +215,16 @@ class UIBuildStep : public UILinearLayout { mStep = buildStep; addClass( String::toString( mStepNum ) ); - findByClass( "step_name" ) - ->setText( String::format( mBuildSettings->getUISceneNode() - ->i18n( "build_step_num", "Step %u: %s" ) - .toUtf8() - .c_str(), - mStepNum + 1, mStep->cmd.c_str() ) ); + auto stepName = findByClass( "step_name" ); + if ( isBuildOrClean() ) { + stepName->setText( String::format( mBuildSettings->getUISceneNode() + ->i18n( "build_step_num", "Step %u: %s" ) + .toUtf8() + .c_str(), + mStepNum + 1, mStep->cmd.c_str() ) ); + } else { + stepName->setText( getUISceneNode()->i18n( "executable_to_run", "Executable to Run" ) ); + } mDataBindHolder += UIDataBindBool::New( &mStep->enabled, findByClass( "enabled_checkbox" ) ); @@ -226,13 +232,15 @@ class UIBuildStep : public UILinearLayout { mDataBindHolder += UIDataBindString::New( &mStep->args, findByClass( "input_args" ) ); mDataBindHolder += UIDataBindString::New( &mStep->workingDir, findByClass( "input_working_dir" ) ); + mDataBindHolder += + UIDataBindBool::New( &mStep->runInTerminal, findByClass( "run_in_terminal" ) ); } protected: - UIBuildStep( bool isBuildStep, UIBuildSettings* buildSettings, size_t stepNum, + UIBuildStep( StepType stepType, UIBuildSettings* buildSettings, size_t stepNum, ProjectBuildStep* buildStep ) : UILinearLayout( "buildstep", UIOrientation::Vertical ), - mIsBuildStep( isBuildStep ), + mStepType( stepType ), mBuildSettings( buildSettings ), mStepNum( stepNum ), mStep( buildStep ) { @@ -241,7 +249,6 @@ class UIBuildStep : public UILinearLayout { addClass( String::toString( stepNum ) ); static const auto BUILD_STEP_XML = R"xml( - @@ -263,34 +270,59 @@ class UIBuildStep : public UILinearLayout { + - )xml"; getUISceneNode()->loadLayoutFromString( BUILD_STEP_XML, this ); + if ( !isBuildOrClean() ) { + findByClass( "enabled_checkbox" )->setVisible( false ); + auto runInTerminal = findByClass( "run_in_terminal" )->asType(); + runInTerminal->setVisible( true ); + runInTerminal->setChecked( buildStep->runInTerminal ); + } + findByClass( "details_but" )->onClick( [this]( const MouseEvent* event ) { auto me = event->getNode()->asType(); findByClass( "details" )->setVisible( me->hasClass( "contracted" ) ); me->toggleClass( "contracted" ); } ); - findByClass( "move_down" )->onClick( [this]( auto ) { - mBuildSettings->moveStepDown( mStepNum, !mIsBuildStep ); - } ); + auto moveDown = findByClass( "move_down" ); + if ( isBuildOrClean() ) { + moveDown->onClick( [this]( auto ) { + mBuildSettings->moveStepDown( mStepNum, mStepType == StepType::Clean ); + } ); + } else { + moveDown->setVisible( false ); + } - findByClass( "move_up" )->onClick( [this]( auto ) { - mBuildSettings->moveStepUp( mStepNum, !mIsBuildStep ); - } ); + auto moveUp = findByClass( "move_up" ); + if ( isBuildOrClean() ) { + moveUp->onClick( [this]( auto ) { + mBuildSettings->moveStepUp( mStepNum, mStepType == StepType::Clean ); + } ); + } else { + moveUp->setVisible( false ); + } - findByClass( "remove_item" )->onClick( [this]( auto ) { - mBuildSettings->deleteStep( mStepNum, !mIsBuildStep ); - } ); + auto removeItem = findByClass( "remove_item" ); + + if ( isBuildOrClean() ) { + removeItem->onClick( [this]( auto ) { + mBuildSettings->deleteStep( mStepNum, mStepType == StepType::Clean ); + } ); + } else { + removeItem->setVisible( false ); + } updateStep( mStepNum, mStep ); } - bool mIsBuildStep{ true }; + bool isBuildOrClean() { return mStepType == StepType::Build || mStepType == StepType::Clean; } + + StepType mStepType{ true }; UIBuildSettings* mBuildSettings{ nullptr }; size_t mStepNum{ 0 }; ProjectBuildStep* mStep; @@ -332,6 +364,11 @@ static const auto SETTINGS_PANEL_XML = R"xml( + + + + + @@ -465,29 +502,34 @@ UIBuildSettings::UIBuildSettings( auto buildStepsParent = find( "build_steps_cont" ); for ( size_t step = 0; step < mBuild.mBuild.size(); ++step ) { - auto bs = UIBuildStep::New( true, this, step, &mBuild.mBuild[step] ); + auto bs = + UIBuildStep::New( UIBuildStep::StepType::Build, this, step, &mBuild.mBuild[step] ); bs->setParent( buildStepsParent ); } find( "add_build_step" )->onClick( [this, buildStepsParent]( const Event* ) { mBuild.mBuild.push_back( {} ); auto step = mBuild.mBuild.size() - 1; - UIBuildStep::New( true, this, step, &mBuild.mBuild[step] )->setParent( buildStepsParent ); + UIBuildStep::New( UIBuildStep::StepType::Build, this, step, &mBuild.mBuild[step] ) + ->setParent( buildStepsParent ); } ); auto buildCleanStepsParent = find( "build_clean_steps_cont" ); for ( size_t step = 0; step < mBuild.mClean.size(); ++step ) { - UIBuildStep::New( false, this, step, &mBuild.mClean[step] ) + UIBuildStep::New( UIBuildStep::StepType::Clean, this, step, &mBuild.mClean[step] ) ->setParent( buildCleanStepsParent ); } find( "add_clean_step" )->onClick( [this, buildCleanStepsParent]( const Event* ) { mBuild.mClean.push_back( {} ); auto step = mBuild.mClean.size() - 1; - UIBuildStep::New( false, this, step, &mBuild.mClean[step] ) + UIBuildStep::New( UIBuildStep::StepType::Clean, this, step, &mBuild.mClean[step] ) ->setParent( buildCleanStepsParent ); } ); + UIBuildStep::New( UIBuildStep::StepType::Run, this, 0, &mBuild.mRun ) + ->setParent( find( "run_step_cont" ) ); + auto buildTypeDropDown = find( "build_type_list" ); auto panelBuildTypeDDL = getUISceneNode() ->getRoot()