mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-05-28 17:16:29 +03:00
Project Build WIP.
This commit is contained in:
@@ -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 );
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ void StatusTerminalController::show() {
|
||||
if ( nullptr == mUITerminal ) {
|
||||
mMainSplitter->updateLayout();
|
||||
mUITerminal = createTerminal();
|
||||
if ( !mUITerminal )
|
||||
return;
|
||||
mUITerminal->setId( "terminal" );
|
||||
mUITerminal->setVisible( false );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user