diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index c0a3b89ca..4c22ecc9b 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -3448,7 +3448,8 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe { "refresh", 0xf064 }, { "hearth-pulse", 0xee10 }, { "add", 0xea12 }, - { "hammer", 0xedee } }; + { "hammer", 0xedee }, + { "eraser", 0xec9e } }; for ( const auto& icon : icons ) iconTheme->add( UIGlyphIcon::New( icon.first, iconFont, icon.second ) ); diff --git a/src/tools/ecode/projectbuild.cpp b/src/tools/ecode/projectbuild.cpp index 74b40e66f..f12a8b9a3 100644 --- a/src/tools/ecode/projectbuild.cpp +++ b/src/tools/ecode/projectbuild.cpp @@ -103,11 +103,49 @@ ProjectBuildManager::~ProjectBuildManager() { Sys::sleep( Milliseconds( 0.1f ) ); } -ProjectBuildCommandsRes ProjectBuildManager::run( const std::string& buildName, - const ProjectBuildi18nFn& i18n, - const std::string& buildType, - const ProjectBuildProgressFn& progressFn, - const ProjectBuildDoneFn& doneFn ) { +ProjectBuildCommandsRes ProjectBuildManager::generateBuildCommands( const std::string& buildName, + const ProjectBuildi18nFn& i18n, + const std::string& buildType ) { + if ( !mLoaded ) + return { i18n( "project_build_not_loaded", "No project build loaded!" ) }; + + const auto& buildIt = mBuilds.find( buildName ); + + if ( buildIt == mBuilds.end() ) + return { i18n( "build_name_not_found", "Build name not found!" ) }; + + const auto& build = buildIt->second; + + if ( !build.mBuildTypes.empty() && buildType.empty() ) + return { i18n( "build_type_required", "Build type must be set!" ) }; + + std::string currentOS = String::toLower( Sys::getPlatform() ); + + if ( !build.isOSSupported( currentOS ) ) + return { + i18n( "build_os_not_supported", "Operating System not supported for this build!" ) }; + + std::string nproc = String::format( "%d", Sys::getCPUCount() ); + ProjectBuildCommandsRes res; + + for ( const auto& step : build.mBuild ) { + ProjectBuildCommand buildCmd( step, build.mEnvs ); + replaceVar( buildCmd, VAR_OS, currentOS ); + replaceVar( buildCmd, VAR_NPROC, nproc ); + if ( !buildType.empty() ) + replaceVar( buildCmd, VAR_BUILD_TYPE, buildType ); + buildCmd.config = build.mConfig; + res.cmds.emplace_back( std::move( buildCmd ) ); + } + + return res; +} + +ProjectBuildCommandsRes ProjectBuildManager::build( const std::string& buildName, + const ProjectBuildi18nFn& i18n, + const std::string& buildType, + const ProjectBuildProgressFn& progressFn, + const ProjectBuildDoneFn& doneFn ) { ProjectBuildCommandsRes res = generateBuildCommands( buildName, i18n, buildType ); if ( !res.isValid() ) return res; @@ -123,6 +161,64 @@ ProjectBuildCommandsRes ProjectBuildManager::run( const std::string& buildName, return res; }; +ProjectBuildCommandsRes ProjectBuildManager::generateCleanCommands( const std::string& buildName, + const ProjectBuildi18nFn& i18n, + const std::string& buildType ) { + if ( !mLoaded ) + return { i18n( "project_build_not_loaded", "No project build loaded!" ) }; + + const auto& buildIt = mBuilds.find( buildName ); + + if ( buildIt == mBuilds.end() ) + return { i18n( "build_name_not_found", "Build name not found!" ) }; + + const auto& build = buildIt->second; + + if ( !build.mBuildTypes.empty() && buildType.empty() ) + return { i18n( "build_type_required", "Build type must be set!" ) }; + + std::string currentOS = String::toLower( Sys::getPlatform() ); + + if ( !build.isOSSupported( currentOS ) ) + return { + i18n( "build_os_not_supported", "Operating System not supported for this build!" ) }; + + std::string nproc = String::format( "%d", Sys::getCPUCount() ); + ProjectBuildCommandsRes res; + + for ( const auto& step : build.mClean ) { + ProjectBuildCommand buildCmd( step, build.mEnvs ); + replaceVar( buildCmd, VAR_OS, currentOS ); + replaceVar( buildCmd, VAR_NPROC, nproc ); + if ( !buildType.empty() ) + replaceVar( buildCmd, VAR_BUILD_TYPE, buildType ); + buildCmd.config = build.mConfig; + res.cmds.emplace_back( std::move( buildCmd ) ); + } + + return res; +} + +ProjectBuildCommandsRes ProjectBuildManager::clean( const std::string& buildName, + const ProjectBuildi18nFn& i18n, + const std::string& buildType, + const ProjectBuildProgressFn& progressFn, + const ProjectBuildDoneFn& doneFn ) { + ProjectBuildCommandsRes res = generateCleanCommands( buildName, i18n, buildType ); + if ( !res.isValid() ) + return res; + if ( !mThreadPool ) { + res.errorMsg = i18n( "no_threads", "Threaded ecode required to compile builds." ); + return res; + } + + mThreadPool->run( [this, res, progressFn, doneFn, i18n, buildName, buildType]() { + runBuild( buildName, buildType, i18n, res, progressFn, doneFn ); + } ); + + return res; +}; + static bool isValidType( const std::string& typeStr ) { return "error" == typeStr || "warning" == typeStr || "notice" == typeStr; } @@ -269,44 +365,6 @@ bool ProjectBuildManager::load() { return true; } -ProjectBuildCommandsRes ProjectBuildManager::generateBuildCommands( const std::string& buildName, - const ProjectBuildi18nFn& i18n, - const std::string& buildType ) { - if ( !mLoaded ) - return { i18n( "project_build_not_loaded", "No project build loaded!" ) }; - - const auto& buildIt = mBuilds.find( buildName ); - - if ( buildIt == mBuilds.end() ) - return { i18n( "build_name_not_found", "Build name not found!" ) }; - - const auto& build = buildIt->second; - - if ( !build.mBuildTypes.empty() && buildType.empty() ) - return { i18n( "build_type_required", "Build type must be set!" ) }; - - std::string currentOS = String::toLower( Sys::getPlatform() ); - - if ( !build.isOSSupported( currentOS ) ) - return { - i18n( "build_os_not_supported", "Operating System not supported for this build!" ) }; - - std::string nproc = String::format( "%d", Sys::getCPUCount() ); - ProjectBuildCommandsRes res; - - for ( const auto& step : build.mBuild ) { - ProjectBuildCommand buildCmd( step, build.mEnvs ); - replaceVar( buildCmd, VAR_OS, currentOS ); - replaceVar( buildCmd, VAR_NPROC, nproc ); - if ( !buildType.empty() ) - replaceVar( buildCmd, VAR_BUILD_TYPE, buildType ); - buildCmd.config = build.mConfig; - res.cmds.emplace_back( std::move( buildCmd ) ); - } - - return res; -} - ProjectBuildOutputParser ProjectBuildManager::getOutputParser( const std::string& buildName ) { auto buildIt = mBuilds.find( buildName ); if ( buildIt != mBuilds.end() ) @@ -314,6 +372,20 @@ ProjectBuildOutputParser ProjectBuildManager::getOutputParser( const std::string return {}; } +bool ProjectBuildManager::hasBuildCommands( const std::string& name ) { + auto buildIt = mBuilds.find( name ); + if ( buildIt != mBuilds.end() ) + return buildIt->second.hasBuild(); + return false; +} + +bool ProjectBuildManager::hasCleanCommands( const std::string& name ) { + auto buildIt = mBuilds.find( name ); + if ( buildIt != mBuilds.end() ) + return buildIt->second.hasClean(); + return false; +} + void ProjectBuildManager::cancelBuild() { mCancelBuild = true; if ( mProcess ) { @@ -338,7 +410,21 @@ void ProjectBuildManager::buildCurrentConfig( StatusBuildOutputController* sboc build = &buildIt.second; if ( build ) - sboc->run( build->getName(), mConfig.buildType, getOutputParser( build->getName() ) ); + sboc->runBuild( build->getName(), mConfig.buildType, + getOutputParser( build->getName() ) ); + } +} + +void ProjectBuildManager::cleanCurrentConfig( 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 ) + sboc->runClean( build->getName(), mConfig.buildType, + getOutputParser( build->getName() ) ); } } @@ -487,6 +573,7 @@ void ProjectBuildManager::buildSidePanelTab() { + )html" ); @@ -501,6 +588,7 @@ void ProjectBuildManager::updateSidePanelTab() { UIWidget* buildTab = mTab->getOwnedWidget()->find( "build_tab" ); UIDropDownList* buildList = buildTab->find( "build_list" ); UIPushButton* buildButton = buildTab->find( "build_button" ); + UIPushButton* cleanButton = buildTab->find( "clean_button" ); buildList->getListBox()->clear(); @@ -561,7 +649,11 @@ void ProjectBuildManager::updateSidePanelTab() { mConfig.buildType = buildTypeList->getListBox()->getItemSelectedText(); } ); - buildButton->setEnabled( !mConfig.buildName.empty() ); + buildButton->setEnabled( !mConfig.buildName.empty() && hasBuild( mConfig.buildName ) && + hasBuildCommands( mConfig.buildName ) ); + + cleanButton->setEnabled( !mConfig.buildName.empty() && hasBuild( mConfig.buildName ) && + hasCleanCommands( mConfig.buildName ) ); buildButton->addMouseClickListener( [this]( const Event* ) { @@ -572,6 +664,16 @@ void ProjectBuildManager::updateSidePanelTab() { } }, MouseButton::EE_BUTTON_LEFT ); + + cleanButton->addMouseClickListener( + [this]( const Event* ) { + if ( isBuilding() ) { + cancelBuild(); + } else { + cleanCurrentConfig( mApp->getStatusBuildOutputController() ); + } + }, + MouseButton::EE_BUTTON_LEFT ); } } // namespace ecode diff --git a/src/tools/ecode/projectbuild.hpp b/src/tools/ecode/projectbuild.hpp index 90bd2808f..f558a5c1a 100644 --- a/src/tools/ecode/projectbuild.hpp +++ b/src/tools/ecode/projectbuild.hpp @@ -135,6 +135,10 @@ class ProjectBuild { const ProjectBuildOutputParser& getOutputParser() const { return mOutputParser; } + bool hasBuild() const { return !mBuild.empty(); } + + bool hasClean() const { return !mClean.empty(); } + protected: friend class ProjectBuildManager; @@ -189,15 +193,24 @@ class ProjectBuildManager { ~ProjectBuildManager(); - ProjectBuildCommandsRes run( const std::string& buildName, const ProjectBuildi18nFn& i18n, - const std::string& buildType = "", - const ProjectBuildProgressFn& progressFn = {}, - const ProjectBuildDoneFn& doneFn = {} ); + ProjectBuildCommandsRes build( const std::string& buildName, const ProjectBuildi18nFn& i18n, + const std::string& buildType = "", + const ProjectBuildProgressFn& progressFn = {}, + const ProjectBuildDoneFn& doneFn = {} ); ProjectBuildCommandsRes generateBuildCommands( const std::string& buildName, const ProjectBuildi18nFn& i18n, const std::string& buildType = "" ); + ProjectBuildCommandsRes clean( const std::string& buildName, const ProjectBuildi18nFn& i18n, + const std::string& buildType = "", + const ProjectBuildProgressFn& progressFn = {}, + const ProjectBuildDoneFn& doneFn = {} ); + + ProjectBuildCommandsRes generateCleanCommands( const std::string& buildName, + const ProjectBuildi18nFn& i18n, + const std::string& buildType = "" ); + ProjectBuildOutputParser getOutputParser( const std::string& buildName ); const ProjectBuildMap& getBuilds() const { return mBuilds; } @@ -206,6 +219,12 @@ class ProjectBuildManager { const std::string& getProjectFile() const { return mProjectFile; } + bool hasBuild( const std::string& name ) { return mBuilds.find( name ) != mBuilds.end(); } + + bool hasBuildCommands( const std::string& name ); + + bool hasCleanCommands( const std::string& name ); + bool loaded() const { return mLoaded; } bool loading() const { return mLoading; } @@ -220,6 +239,8 @@ class ProjectBuildManager { void buildCurrentConfig( StatusBuildOutputController* sboc ); + void cleanCurrentConfig( StatusBuildOutputController* sboc ); + protected: std::string mProjectRoot; std::string mProjectFile; diff --git a/src/tools/ecode/statusbuildoutputcontroller.cpp b/src/tools/ecode/statusbuildoutputcontroller.cpp index 494af3c43..a04d2119c 100644 --- a/src/tools/ecode/statusbuildoutputcontroller.cpp +++ b/src/tools/ecode/statusbuildoutputcontroller.cpp @@ -81,8 +81,18 @@ UIPushButton* StatusBuildOutputController::getBuildButton( App* app ) { return nullptr; } -void StatusBuildOutputController::run( const std::string& buildName, const std::string& buildType, - const ProjectBuildOutputParser& outputParser ) { +UIPushButton* StatusBuildOutputController::getCleanButton( App* app ) { + if ( app->getSidePanel() ) { + UIWidget* tab = app->getSidePanel()->find( "build_tab" ); + if ( tab ) + return tab->find( "clean_button" ); + } + return nullptr; +} + +void StatusBuildOutputController::runBuild( const std::string& buildName, + const std::string& buildType, + const ProjectBuildOutputParser& outputParser ) { if ( !mApp->getProjectBuildManager() ) return; @@ -115,8 +125,14 @@ void StatusBuildOutputController::run( const std::string& buildName, const std:: UIPushButton* buildButton = getBuildButton( mApp ); if ( buildButton ) buildButton->setText( mApp->i18n( "cancel_build", "Cancel Build" ) ); + UIPushButton* cleanButton = getCleanButton( mApp ); + bool enableCleanButton = false; + if ( cleanButton && cleanButton->isEnabled() ) { + cleanButton->setEnabled( false ); + enableCleanButton = true; + } - auto res = pbm->run( + auto res = pbm->build( buildName, [this]( const auto& key, const auto& def ) { return mApp->i18n( key, def ); }, buildType, [this]( auto, auto buffer ) { @@ -127,7 +143,7 @@ void StatusBuildOutputController::run( const std::string& buildName, const std:: mContainer->setScrollY( mContainer->getMaxScroll().y ); } ); }, - [this]( auto exitCode ) { + [this, enableCleanButton]( auto exitCode ) { String buffer; if ( EXIT_SUCCESS == exitCode ) { @@ -145,9 +161,102 @@ void StatusBuildOutputController::run( const std::string& buildName, const std:: mContainer->setScrollY( mContainer->getMaxScroll().y ); } ); - UIPushButton* buildButton = nullptr; - if ( ( buildButton = getBuildButton( mApp ) ) ) + UIPushButton* buildButton = getBuildButton( mApp ); + if ( buildButton ) buildButton->setText( mApp->i18n( "build", "Build" ) ); + + if ( enableCleanButton ) { + UIPushButton* cleanButton = getCleanButton( mApp ); + if ( cleanButton ) + cleanButton->setEnabled( true ); + } + } ); + + if ( !res.isValid() ) { + mApp->getNotificationCenter()->addNotification( res.errorMsg ); + } +} + +void StatusBuildOutputController::runClean( const std::string& buildName, + const std::string& buildType, + const ProjectBuildOutputParser& outputParser ) { + if ( !mApp->getProjectBuildManager() ) + return; + + auto pbm = mApp->getProjectBuildManager(); + + show(); + + mContainer->getDocument().reset(); + mContainer->setScrollY( mContainer->getMaxScroll().y ); + + std::vector patterns; + + for ( const auto& parser : outputParser.getConfig() ) { + SyntaxPattern ptn( { parser.pattern }, getProjectOutputParserTypeToString( parser.type ) ); + patterns.emplace_back( std::move( ptn ) ); + } + + patterns.emplace_back( + SyntaxPattern( { "%d%d%d%d%-%d%d%-%d%d%s%d%d%:%d%d%:%d%d%:.*error.*[^\n]+" }, "error" ) ); + patterns.emplace_back( SyntaxPattern( + { "%d%d%d%d%-%d%d%-%d%d%s%d%d%:%d%d%:%d%d%:.*warning.*[^\n]+" }, "warning" ) ); + patterns.emplace_back( + SyntaxPattern( { "%d%d%d%d%-%d%d%-%d%d%s%d%d%:%d%d%:%d%d%:[^\n]+" }, "notice" ) ); + + SyntaxDefinition synDef( "custom_build", {}, patterns ); + + mContainer->getDocument().setSyntaxDefinition( synDef ); + mContainer->getVScrollBar()->setValue( 1.f ); + + UIPushButton* buildButton = getBuildButton( mApp ); + bool enableBuildButton = false; + if ( buildButton && buildButton->isEnabled() ) { + buildButton->setEnabled( false ); + enableBuildButton = true; + } + UIPushButton* cleanButton = getCleanButton( mApp ); + if ( cleanButton ) + cleanButton->setText( mApp->i18n( "cancel_clean", "Cancel Clean" ) ); + + auto res = pbm->clean( + buildName, [this]( const auto& key, const auto& def ) { return mApp->i18n( key, def ); }, + buildType, + [this]( auto, auto buffer ) { + mContainer->runOnMainThread( [this, buffer]() { + bool scrollToBottom = mContainer->getVScrollBar()->getValue() == 1.f; + mContainer->getDocument().textInput( buffer ); + if ( scrollToBottom ) + mContainer->setScrollY( mContainer->getMaxScroll().y ); + } ); + }, + [this, enableBuildButton]( auto exitCode ) { + String buffer; + + if ( EXIT_SUCCESS == exitCode ) { + buffer = Sys::getDateTimeStr() + ": " + + mApp->i18n( "build_successful", "Build run successfully\n" ); + } else { + buffer = Sys::getDateTimeStr() + ": " + + mApp->i18n( "build_failed", "Build run with errors\n" ); + } + + mContainer->runOnMainThread( [this, buffer]() { + bool scrollToBottom = mContainer->getVScrollBar()->getValue() == 1.f; + mContainer->getDocument().textInput( buffer ); + if ( scrollToBottom ) + mContainer->setScrollY( mContainer->getMaxScroll().y ); + } ); + + UIPushButton* cleanButton = getCleanButton( mApp ); + if ( cleanButton ) + cleanButton->setText( mApp->i18n( "clean", "Clean" ) ); + + if ( enableBuildButton ) { + UIPushButton* buildButton = getBuildButton( mApp ); + if ( buildButton ) + buildButton->setEnabled( true ); + } } ); if ( !res.isValid() ) { diff --git a/src/tools/ecode/statusbuildoutputcontroller.hpp b/src/tools/ecode/statusbuildoutputcontroller.hpp index 62c7bfa49..52ea0785d 100644 --- a/src/tools/ecode/statusbuildoutputcontroller.hpp +++ b/src/tools/ecode/statusbuildoutputcontroller.hpp @@ -25,8 +25,11 @@ class StatusBuildOutputController { void show(); - void run( const std::string& buildName, const std::string& buildType, - const ProjectBuildOutputParser& outputParser = {} ); + void runBuild( const std::string& buildName, const std::string& buildType, + const ProjectBuildOutputParser& outputParser = {} ); + + void runClean( const std::string& buildName, const std::string& buildType, + const ProjectBuildOutputParser& outputParser = {} ); UICodeEditor* getContainer(); @@ -40,6 +43,8 @@ class StatusBuildOutputController { UICodeEditor* createContainer(); UIPushButton* getBuildButton( App* app ); + + UIPushButton* getCleanButton( App* app ); }; } // namespace ecode