Files
eepp/src/tools/ecode/projectbuild.hpp

405 lines
10 KiB
C++

#ifndef ECODE_PROJECTBUILD_HPP
#define ECODE_PROJECTBUILD_HPP
#include "appconfig.hpp"
#include <eepp/system/process.hpp>
#include <eepp/system/threadpool.hpp>
#include <eepp/ui/uitabwidget.hpp>
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include <vector>
using namespace EE;
using namespace EE::System;
using namespace EE::UI;
namespace ecode {
class App;
class StatusBuildOutputController;
class StatusAppOutputController;
/** reference:
{
"ecode": {
"build": [
{
"args": "--with-debug-symbols gmake",
"command": "premake4",
"working_dir": "${project_root}"
},
{
"args": "-j${nproc} config=${build_type} ecode",
"command": "make",
"working_dir": "${build_dir}"
}
],
"build_types": [
"debug",
"release"
],
"clean": [
{
"args": "config=${build_type} clean",
"command": "make",
"working_dir": "${build_dir}"
}
],
"config": {
"clear_sys_env": false
},
"env": {
"SHELL": "fish"
},
"os": [
"linux"
],
"output_parser": {
"config": {
"relative_file_paths": true
},
"error": [
{
"pattern": "([^:]*):(%d+):(%d+):%s?[%w%s]*error:%s?([^\n]*)",
"pattern_order": {
"col": 3,
"file": 1,
"line": 2,
"message": 4
}
}
]
},
"run": [
{
"args": "-x",
"command": "${project_root}/bin/ecode-debug",
"name": "ecode-debug",
"working_dir": "${project_root}/bin/"
},
{
"args": "-x",
"command": "${project_root}/bin/ecode",
"name": "ecode-release",
"working_dir": "${project_root}/bin/"
}
],
"var": {
"build_dir": "${project_root}/make/${os}"
}
}
}
*/
struct ProjectBuildStep {
std::string cmd;
std::string args;
std::string workingDir;
std::string name;
bool enabled{ true };
bool runInTerminal{ false };
};
using ProjectBuildSteps = std::vector<std::unique_ptr<ProjectBuildStep>>;
using ProjectBuildKeyVal = std::vector<std::pair<std::string, std::string>>;
struct ProjectBuildConfig {
bool clearSysEnv{ false };
};
enum class ProjectOutputParserTypes { Error = 0, Warning = 1, Notice = 2 };
struct PatternOrder {
int file{ 1 };
int line{ 2 };
int col{ 3 };
int message{ 4 };
};
struct ProjectBuildOutputParserConfig {
static std::string typeToString( ProjectOutputParserTypes type ) {
switch ( type ) {
case ProjectOutputParserTypes::Notice:
return "notice";
case ProjectOutputParserTypes::Warning:
return "warning";
case ProjectOutputParserTypes::Error:
default:
return "error";
}
}
ProjectOutputParserTypes type;
std::string pattern;
PatternOrder patternOrder;
};
class ProjectBuildOutputParser {
public:
static std::map<std::string, ProjectBuildOutputParser> getPresets();
static bool existsPreset( const std::string& name );
static ProjectBuildOutputParser getGeneric();
const std::vector<ProjectBuildOutputParserConfig>& getPresetConfig() const {
return mPresetConfig;
}
const std::vector<ProjectBuildOutputParserConfig>& getConfig() const { return mConfig; }
bool useRelativeFilePaths() const { return mRelativeFilePaths; }
const std::string& getPreset() const { return mPreset; }
protected:
friend class ProjectBuildManager;
friend class ProjectBuild;
friend class UIBuildSettings;
bool mRelativeFilePaths{ true };
std::string mPreset;
std::vector<ProjectBuildOutputParserConfig> mPresetConfig;
std::vector<ProjectBuildOutputParserConfig> mConfig;
};
class ProjectBuild {
public:
using Map = std::unordered_map<std::string, ProjectBuild>;
ProjectBuildSteps deepCopySteps( const ProjectBuildSteps& steps ) const;
ProjectBuild( const ProjectBuild& other );
ProjectBuild& operator=( const ProjectBuild& other );
ProjectBuild( ProjectBuild&& ) = default;
ProjectBuild& operator=( ProjectBuild&& ) = default;
ProjectBuild( const std::string& name, const std::string& projectRoot );
const ProjectBuildConfig& getConfig() const { return mConfig; }
bool isOSSupported( const std::string& os ) const;
const std::string& getName() const { return mName; }
const std::set<std::string>& buildTypes() const { return mBuildTypes; }
const std::set<std::string>& os() const { return mOS; }
const ProjectBuildOutputParser& getOutputParser() const { return mOutputParser; }
const ProjectBuildSteps& buildSteps() const { return mBuild; }
const ProjectBuildSteps& cleanSteps() const { return mClean; }
const ProjectBuildSteps& runConfigs() const { return mRun; }
const ProjectBuildKeyVal& envs() const { return mEnvs; }
const ProjectBuildKeyVal& vars() const { return mVars; }
bool hasBuild() const { return !mBuild.empty(); }
bool hasClean() const { return !mClean.empty(); }
bool hasRun() const {
return !mRun.empty() && ( !mRun.front()->cmd.empty() || !mRun.front()->args.empty() ||
!mRun.front()->workingDir.empty() );
}
ProjectBuildStep replaceVars( const ProjectBuildStep& step ) const;
ProjectBuildSteps replaceVars( const ProjectBuildSteps& steps ) const;
static json serialize( const ProjectBuild::Map& builds );
static ProjectBuild::Map deserialize( const json& j, const std::string& projectRoot );
protected:
friend class ProjectBuildManager;
friend class UIBuildSettings;
std::string mName;
std::string mProjectRoot;
std::set<std::string> mOS;
std::set<std::string> mBuildTypes;
ProjectBuildSteps mBuild;
ProjectBuildSteps mClean;
ProjectBuildSteps mRun;
ProjectBuildKeyVal mEnvs;
ProjectBuildKeyVal mVars;
ProjectBuildConfig mConfig;
ProjectBuildOutputParser mOutputParser;
};
struct ProjectBuildCommand : public ProjectBuildStep {
ProjectBuildConfig config;
ProjectBuildCommand( const ProjectBuildStep& step ) : ProjectBuildStep( step ) {}
};
using ProjectBuildCommands = std::vector<ProjectBuildCommand>;
struct ProjectBuildCommandsRes {
String errorMsg;
ProjectBuildCommands cmds;
ProjectBuildKeyVal envs;
ProjectBuildCommandsRes() {}
ProjectBuildCommandsRes( const String& errMsg ) : errorMsg( errMsg ) {}
ProjectBuildCommandsRes( ProjectBuildCommands&& cmds ) : cmds( cmds ) {}
bool isValid() { return errorMsg.empty(); }
};
using ProjectBuildProgressFn =
std::function<void( int curProgress, std::string buffer, const ProjectBuildCommand* cmd )>;
using ProjectBuildDoneFn = std::function<void( int exitCode, const ProjectBuildCommand* cmd )>;
using ProjectBuildi18nFn =
std::function<String( const std::string& /*key*/, const String& /*defaultvalue*/ )>;
class ProjectBuildManager {
public:
ProjectBuildManager( const std::string& projectRoot, std::shared_ptr<ThreadPool> pool,
UITabWidget* sidePanel, App* app );
~ProjectBuildManager();
ProjectBuildCommandsRes build( const std::string& buildName, const ProjectBuildi18nFn& i18n,
const std::string& buildType = "",
const ProjectBuildProgressFn& progressFn = {},
const ProjectBuildDoneFn& doneFn = {}, bool isClean = false );
ProjectBuildCommandsRes run( const ProjectBuildCommand& runData, const ProjectBuildi18nFn& i18n,
const ProjectBuildProgressFn& progressFn = {},
const ProjectBuildDoneFn& doneFn = {} );
ProjectBuildCommandsRes generateBuildCommands( const std::string& buildName,
const ProjectBuildi18nFn& i18n,
const std::string& buildType = "",
bool isClean = false );
void replaceDynamicVars( ProjectBuildCommand& cmd );
ProjectBuildOutputParser getOutputParser( const std::string& buildName );
const ProjectBuild::Map& getBuilds() const { return mBuilds; }
const std::string& getProjectRoot() const { return mProjectRoot; }
const std::string& getProjectFile() const { return mProjectFile; }
std::string getCurrentDocument();
ProjectBuild* getBuild( const std::string& buildName );
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 mLoadedWithBuilds; }
bool loading() const { return mLoading; }
bool isBuilding() const { return mBuilding; }
bool isRunningApp() const { return mRunning; }
void cancelBuild();
void cancelRun();
ProjectBuildConfiguration getConfig() const;
void setConfig( const ProjectBuildConfiguration& config );
void buildCurrentConfig( StatusBuildOutputController* sboc,
std::function<void( int exitStatus )> doneFn = {} );
void cleanCurrentConfig( StatusBuildOutputController* sboc );
void runCurrentConfig( StatusAppOutputController* saoc, bool build,
StatusBuildOutputController* sboc = nullptr );
void editCurrentBuild();
bool hasRunConfig();
bool hasBuildConfig() const;
void selectTab();
bool hasBuildConfigWithBuildSteps();
std::optional<ProjectBuildStep> getCurrentRunConfig();
ProjectBuild* getCurrentBuild();
protected:
std::string mProjectRoot;
std::string mProjectFile;
ProjectBuild::Map mBuilds;
ProjectBuildConfiguration mConfig;
std::shared_ptr<ThreadPool> mThreadPool;
UITabWidget* mSidePanel{ nullptr };
UISceneNode* mUISceneNode{ nullptr };
UITab* mTab{ nullptr };
App* mApp{ nullptr };
std::unique_ptr<Process> mProcess;
std::unique_ptr<Process> mProcessRun;
ProjectBuild mNewBuild;
bool mLoadedWithBuilds{ false };
bool mLoading{ false };
bool mBuilding{ false };
bool mShuttingDown{ false };
bool mCancelBuild{ false };
bool mCancelRun{ false };
bool mRunning{ false };
std::unordered_map<Node*, std::set<Uint32>> mCbs;
void runBuild( const std::string& buildName, const std::string& buildType,
const ProjectBuildi18nFn& i18n, const ProjectBuildCommandsRes& res,
const ProjectBuildProgressFn& progressFn = {},
const ProjectBuildDoneFn& doneFn = {} );
void runApp( const ProjectBuildCommand& runStep, const ProjectBuildi18nFn& i18n,
const ProjectBuildCommandsRes& res, const ProjectBuildProgressFn& progressFn = {},
const ProjectBuildDoneFn& doneFn = {} );
bool load();
bool save();
bool saveAsync();
void buildSidePanelTab();
void updateSidePanelTab();
void updateBuildType();
void updateRunConfig();
void addNewBuild();
bool cloneBuild( const std::string& build, std::string newBuildName );
void addBuild( UIWidget* buildTab );
void editBuild( std::string buildName, UIWidget* buildTab );
void runConfig( StatusAppOutputController* saoc );
};
} // namespace ecode
#endif