Project Build WIP.

This commit is contained in:
Martín Lucas Golini
2023-04-07 21:23:05 -03:00
parent 0970685fd1
commit 9eef0748f6
5 changed files with 137 additions and 7 deletions

View File

@@ -63,6 +63,16 @@ class EE_API Process {
const std::unordered_map<std::string, std::string>& environment = {},
const std::string& workingDirectory = "" );
/** @brief Create a process.
** @param command Command line to execute for this process.
** @param args Command line arguments to execute for this process.
** @param options A bit field of Options's to pass.
** @return On success true is returned. */
bool create( const std::string& command, const std::string& args,
const Uint32& options = getDefaultOptions(),
const std::unordered_map<std::string, std::string>& environment = {},
const std::string& workingDirectory = "" );
/** @brief Starts a new thread to receive all stdout and stderr data */
void startAsyncRead( ReadFn readStdOut = nullptr, ReadFn readStdErr = nullptr );

View File

@@ -80,6 +80,41 @@ bool Process::create( const std::string& command, const Uint32& options,
return ret;
}
bool Process::create( const std::string& command, const std::string& args, const Uint32& options,
const std::unordered_map<std::string, std::string>& environment,
const std::string& workingDirectory ) {
if ( mProcess )
return false;
std::vector<std::string> cmdArr = String::split( args, " ", "", "\"", true );
std::vector<const char*> strings;
strings.push_back( command.c_str() );
for ( size_t i = 0; i < cmdArr.size(); ++i )
strings.push_back( cmdArr[i].c_str() );
strings.push_back( NULL );
mProcess = eeMalloc( sizeof( subprocess_s ) );
memset( mProcess, 0, sizeof( subprocess_s ) );
if ( !environment.empty() ) {
std::vector<std::string> envArr;
std::vector<const char*> envStrings;
for ( const auto& pair : environment ) {
envArr.push_back( String::format( "%s=%s", pair.first.c_str(), pair.second.c_str() ) );
envStrings.push_back( envArr[envArr.size() - 1].c_str() );
}
envStrings.push_back( NULL );
auto ret = 0 == subprocess_create_ex( strings.data(), options, envStrings.data(),
!workingDirectory.empty() ? workingDirectory.c_str()
: nullptr,
PROCESS_PTR );
return ret;
}
auto ret =
0 == subprocess_create_ex( strings.data(), options, nullptr,
!workingDirectory.empty() ? workingDirectory.c_str() : nullptr,
PROCESS_PTR );
return ret;
}
size_t Process::readAllStdOut( std::string& buffer ) {
size_t bytesRead = 0;
size_t totalBytesRead = 0;

View File

@@ -1,8 +1,10 @@
#include "projectbuild.hpp"
#include "scopedop.hpp"
#include <eepp/core/string.hpp>
#include <eepp/system/clock.hpp>
#include <eepp/system/filesystem.hpp>
#include <eepp/system/log.hpp>
#include <eepp/system/process.hpp>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
@@ -59,6 +61,26 @@ ProjectBuildManager::~ProjectBuildManager() {
mShuttingDown = true;
while ( mLoading )
Sys::sleep( Milliseconds( 0.1f ) );
while ( mBuilding )
Sys::sleep( Milliseconds( 0.1f ) );
}
ProjectBuildCommandsRes
ProjectBuildManager::run( const std::string& buildName,
std::function<String( const std::string&, const String& )> i18n,
const std::string& buildType, const ProjectBuildProgressFn& progressFn,
const ProjectBuildDoneFn& doneFn ) {
ProjectBuildCommandsRes res = generateBuildCommands( 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]() { runBuild( res, progressFn, doneFn ); } );
return res;
};
static bool isValidType( const std::string& typeStr ) {
@@ -76,7 +98,7 @@ static ProjectOutputParserTypes outputParserType( const std::string& typeStr ) {
}
bool ProjectBuildManager::load() {
ScopedOp op( [this]() { mLoading = true; }, [this]() { mLoading = false; } );
ScopedOp scopedOp( [this]() { mLoading = true; }, [this]() { mLoading = false; } );
mProjectFile = mProjectRoot + ".ecode/project_build.json";
if ( !FileSystem::fileExists( mProjectFile ) )
@@ -155,17 +177,17 @@ bool ProjectBuildManager::load() {
ProjectBuildOutputParser outputParser;
for ( const auto& op : op.items() ) {
if ( op.key() == "config" ) {
const auto& config = op.value();
for ( const auto& item : op.items() ) {
if ( item.key() == "config" ) {
const auto& config = item.value();
outputParser.mRelativeFilePaths = config.value( "output_parser", true );
} else {
auto typeStr = String::toLower( op.key() );
auto typeStr = String::toLower( item.key() );
if ( !isValidType( typeStr ) )
continue;
const auto& ptrnCfgs = op.value();
const auto& ptrnCfgs = item.value();
if ( ptrnCfgs.is_array() ) {
for ( const auto& ptrnCfg : ptrnCfgs ) {
ProjectBuildOutputParserConfig opc;
@@ -232,11 +254,55 @@ ProjectBuildCommandsRes ProjectBuildManager::generateBuildCommands(
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;
}
void ProjectBuildManager::runBuild( const ProjectBuildCommandsRes& res,
const ProjectBuildProgressFn& progressFn,
const ProjectBuildDoneFn& doneFn ) {
ScopedOp scopedOp( [this]() { mBuilding = true; }, [this]() { mBuilding = false; } );
Clock clock;
for ( const auto& cmd : res.cmds ) {
Process process;
auto options = Process::SearchUserPath | Process::NoWindow | Process::CombinedStdoutStderr;
if ( !cmd.config.clearSysEnv )
options |= Process::InheritEnvironment;
if ( process.create( cmd.cmd, cmd.args, options, cmd.envs, cmd.workingDir ) ) {
std::string buffer( 1024, '\0' );
unsigned bytesRead = 0;
int returnCode;
do {
bytesRead = process.readStdOut( buffer );
std::string data( buffer.substr( 0, bytesRead ) );
if ( progressFn )
progressFn( 50, std::move( data ) );
} while ( bytesRead != 0 && process.isAlive() && !mShuttingDown );
if ( mShuttingDown ) {
process.kill();
doneFn( EXIT_FAILURE );
return;
}
if ( progressFn )
progressFn( 90, {} );
process.join( &returnCode );
process.destroy();
if ( doneFn && returnCode != EXIT_SUCCESS ) {
progressFn( 100, {} );
doneFn( returnCode );
return;
}
}
}
doneFn( EXIT_SUCCESS );
}
} // namespace ecode

View File

@@ -137,6 +137,7 @@ using ProjectBuildMap = std::unordered_map<std::string, ProjectBuild>;
struct ProjectBuildCommand : public ProjectBuildStep {
ProjectBuildKeyVal envs;
ProjectBuildConfig config;
ProjectBuildCommand( const ProjectBuildStep& step, const ProjectBuildKeyVal& envs ) :
ProjectBuildStep( step ), envs( envs ) {}
@@ -157,12 +158,21 @@ struct ProjectBuildCommandsRes {
bool isValid() { return errorMsg.empty(); }
};
using ProjectBuildProgressFn = std::function<void( int curProgress, std::string buffer )>;
using ProjectBuildDoneFn = std::function<void( int exitCode )>;
class ProjectBuildManager {
public:
ProjectBuildManager( const std::string& projectRoot, std::shared_ptr<ThreadPool> pool );
~ProjectBuildManager();
ProjectBuildCommandsRes
run( const std::string& buildName,
std::function<String( const std::string& /*key*/, const String& /*defaultvalue*/ )> i18n,
const std::string& buildType = "", const ProjectBuildProgressFn& progressFn = {},
const ProjectBuildDoneFn& doneFn = {} );
ProjectBuildCommandsRes generateBuildCommands(
const std::string& buildName,
std::function<String( const std::string& /*key*/, const String& /*defaultvalue*/ )> i18n,
@@ -178,6 +188,8 @@ class ProjectBuildManager {
bool loading() const { return mLoading; }
bool isBuilding() const { return mBuilding; }
protected:
std::string mProjectRoot;
std::string mProjectFile;
@@ -185,8 +197,13 @@ class ProjectBuildManager {
std::shared_ptr<ThreadPool> mThreadPool;
bool mLoaded{ false };
bool mLoading{ false };
bool mBuilding{ false };
bool mShuttingDown{ false };
void runBuild( const ProjectBuildCommandsRes& res,
const ProjectBuildProgressFn& progressFn = {},
const ProjectBuildDoneFn& doneFn = {} );
bool load();
};

View File

@@ -41,6 +41,8 @@ void StatusTerminalController::show() {
if ( nullptr == mUITerminal ) {
mMainSplitter->updateLayout();
mUITerminal = createTerminal();
if ( !mUITerminal )
return;
mUITerminal->setId( "terminal" );
mUITerminal->setVisible( false );
}