2025-10-31 16:50:47 +01:00
/*
Crafter ® Build
Copyright ( C ) 2025 Catcrafts ®
Catcrafts . net
This library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation ;
This library is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public
License along with this library ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
module ;
# include "../lib/json.hpp"
# include <glslang/Public/ShaderLang.h>
module Crafter . Build : Configuration_impl ;
import : Configuration ;
import std ;
2025-11-16 18:44:41 +01:00
import : Project ;
2025-10-31 16:50:47 +01:00
//import :Dependency;
import : Shader ;
import : Module ;
namespace fs = std : : filesystem ;
namespace Crafter {
Configuration : : Configuration ( std : : string & & name ) : name ( std : : move ( name ) ) {
}
2025-11-26 17:55:52 +01:00
Configuration : : Configuration ( const nlohmann : : json & configs , const nlohmann : : json & config , fs : : path workingDir , const Project & project ) : type ( CRAFTER_CONFIGURATION_TYPE_EXECUTABLE ) , debug ( false ) {
2025-11-16 18:44:41 +01:00
SetDataFromJson ( configs , config , workingDir , project ) ;
2025-10-31 16:50:47 +01:00
}
2025-11-16 18:44:41 +01:00
void Configuration : : SetDataFromJson ( const nlohmann : : json & configs , const nlohmann : : json & config , fs : : path workingDir , const Project & project ) {
2025-10-31 16:50:47 +01:00
if ( config . contains ( " extends " ) ) {
const std : : vector < std : : string > extends = config [ " extends " ] . get < std : : vector < std : : string > > ( ) ;
for ( const std : : string & extendName : extends ) {
for ( auto it : configs ) {
if ( it [ " name " ] . get < std : : string > ( ) = = extendName ) {
2025-11-16 18:44:41 +01:00
SetDataFromJson ( configs , it , workingDir , project ) ;
2025-10-31 16:50:47 +01:00
}
}
}
}
if ( config . contains ( " name " ) ) {
name = config [ " name " ] . get < std : : string > ( ) ;
} else {
throw std : : runtime_error ( " Invalid config json, name field is missing " ) ;
}
if ( config . contains ( " standard " ) ) {
standard = config [ " standard " ] . get < std : : string > ( ) ;
}
if ( config . contains ( " target " ) ) {
target = config [ " target " ] . get < std : : string > ( ) ;
}
if ( config . contains ( " debug " ) ) {
debug = config [ " debug " ] . get < bool > ( ) ;
}
if ( config . contains ( " type " ) ) {
std : : string typeString = config [ " type " ] . get < std : : string > ( ) ;
if ( typeString = = " executable " ) {
type = CRAFTER_CONFIGURATION_TYPE_EXECUTABLE ;
} else if ( typeString = = " library " ) {
type = CRAFTER_CONFIGURATION_TYPE_LIBRARY ;
} else if ( typeString = = " shared-library " ) {
type = CRAFTER_CONFIGURATION_TYPE_SHARED_LIBRARY ;
} else {
throw std : : invalid_argument ( " Unknown type: " + typeString ) ;
}
}
if ( config . contains ( " march " ) ) {
march = config [ " march " ] . get < std : : string > ( ) ;
}
if ( config . contains ( " libs " ) ) {
for ( auto it : config [ " libs " ] ) {
libs . push_back ( it . get < std : : string > ( ) ) ;
}
}
if ( config . contains ( " interfaces " ) ) {
const std : : vector < std : : string > tempModuleFiles = config [ " interfaces " ] . get < std : : vector < std : : string > > ( ) ;
std : : vector < std : : tuple < fs : : path , std : : string , ModulePartition * , Module * > > tempModulePaths = std : : vector < std : : tuple < fs : : path , std : : string , ModulePartition * , Module * > > ( tempModuleFiles . size ( ) ) ;
for ( std : : uint_fast32_t i = 0 ; i < tempModuleFiles . size ( ) ; i + + ) {
const std : : filesystem : : path file = workingDir / ( tempModuleFiles [ i ] + " .cppm " ) ;
std : : ifstream t ( file ) ;
std : : stringstream buffer ;
buffer < < t . rdbuf ( ) ;
std : : string fileContent = buffer . str ( ) ;
fileContent = std : : regex_replace ( fileContent , std : : regex ( R " (//[^ \n ]*) " ) , " " ) ;
fileContent = std : : regex_replace ( fileContent , std : : regex ( R " (/ \ *.*? \ */) " ) , " " ) ;
tempModulePaths [ i ] = { file , fileContent , nullptr , nullptr } ;
}
std : : erase_if ( tempModulePaths , [ this ] ( std : : tuple < fs : : path , std : : string , ModulePartition * , Module * > & file ) {
std : : smatch match ;
if ( std : : regex_search ( std : : get < 1 > ( file ) , match , std : : regex ( R " (export module ([a-zA-Z0-9_ \ . \ -]+);) " ) ) ) {
std : : get < 0 > ( file ) . replace_extension ( " " ) ;
interfaces . push_back ( std : : make_unique < Module > ( std : : move ( match [ 1 ] . str ( ) ) , std : : move ( std : : get < 0 > ( file ) ) ) ) ;
return true ;
} else {
return false ;
}
} ) ;
for ( uint_fast32_t i = 0 ; i < tempModulePaths . size ( ) ; i + + ) {
std : : smatch match ;
if ( std : : regex_search ( std : : get < 1 > ( tempModulePaths [ i ] ) , match , std : : regex ( R " (export module ([a-zA-Z_0-9 \ . \ -]+):([a-zA-Z_0-9 \ . \ -]+);) " ) ) ) {
for ( const std : : unique_ptr < Module > & modulee : interfaces ) {
if ( modulee - > name = = match [ 1 ] ) {
std : : string name = match [ 2 ] . str ( ) ;
fs : : path pthCpy = std : : get < 0 > ( tempModulePaths [ i ] ) ;
pthCpy . replace_extension ( " " ) ;
std : : unique_ptr < ModulePartition > partition = std : : make_unique < ModulePartition > ( std : : move ( name ) , std : : move ( pthCpy ) ) ;
std : : get < 2 > ( tempModulePaths [ i ] ) = partition . get ( ) ;
modulee - > partitions . push_back ( std : : move ( partition ) ) ;
std : : get < 3 > ( tempModulePaths [ i ] ) = modulee . get ( ) ;
goto next ;
}
}
throw std : : runtime_error ( std : : format ( " Module {} not found, referenced in {} " , match [ 1 ] . str ( ) , std : : get < 0 > ( tempModulePaths [ i ] ) . string ( ) ) ) ;
} else {
throw std : : runtime_error ( std : : format ( " No module declaration found in {} " , std : : get < 0 > ( tempModulePaths [ i ] ) . string ( ) ) ) ;
}
next : ;
}
for ( std : : tuple < fs : : path , std : : string , ModulePartition * , Module * > & file : tempModulePaths ) {
std : : regex pattern ( R " (import :([a-zA-Z_ \ -0-9 \ .]+);) " ) ;
std : : sregex_iterator currentMatch ( std : : get < 1 > ( file ) . begin ( ) , std : : get < 1 > ( file ) . end ( ) , pattern ) ;
std : : sregex_iterator lastMatch ;
while ( currentMatch ! = lastMatch ) {
std : : smatch match = * currentMatch ;
for ( std : : unique_ptr < ModulePartition > & partition2 : std : : get < 3 > ( file ) - > partitions ) {
if ( partition2 - > name = = match [ 1 ] ) {
std : : get < 2 > ( file ) - > partitionDependencies . push_back ( partition2 . get ( ) ) ;
goto next2 ;
}
}
throw std : : runtime_error ( std : : format ( " imported partition {}:{} not found, referenced in {} " , std : : get < 3 > ( file ) - > name , match [ 1 ] . str ( ) , std : : get < 0 > ( file ) . string ( ) ) ) ;
next2 : + + currentMatch ;
}
}
}
if ( config . contains ( " implementations " ) ) {
const std : : vector < std : : string > tempFiles = config [ " implementations " ] . get < std : : vector < std : : string > > ( ) ;
for ( const std : : string & tempFile : tempFiles ) {
const std : : filesystem : : path file = workingDir / ( tempFile + " .cpp " ) ;
std : : ifstream t ( file ) ;
std : : stringstream buffer ;
buffer < < t . rdbuf ( ) ;
std : : string fileContent = buffer . str ( ) ;
fileContent = std : : regex_replace ( fileContent , std : : regex ( R " (//[^ \n ]*) " ) , " " ) ;
fileContent = std : : regex_replace ( fileContent , std : : regex ( R " (/ \ *.*? \ */) " ) , " " ) ;
std : : smatch match ;
fs : : path fileCopy = file ;
fileCopy . replace_extension ( " " ) ;
Implementation & implementation = implementations . emplace_back ( std : : move ( fileCopy ) ) ;
if ( std : : regex_search ( fileContent , match , std : : regex ( R " (module ([a-zA-Z0-9_ \ . \ -]+)) " ) ) ) {
for ( const std : : unique_ptr < Module > & interface : interfaces ) {
if ( interface - > name = = match [ 1 ] ) {
std : : regex pattern ( R " (import :([a-zA-Z_ \ -0-9 \ .]*);) " ) ;
std : : sregex_iterator currentMatch ( fileContent . begin ( ) , fileContent . end ( ) , pattern ) ;
std : : sregex_iterator lastMatch ;
while ( currentMatch ! = lastMatch ) {
std : : smatch match2 = * currentMatch ;
for ( const std : : unique_ptr < ModulePartition > & partition : interface - > partitions ) {
if ( partition - > name = = match2 [ 1 ] ) {
implementation . partitionDependencies . push_back ( partition . get ( ) ) ;
goto next3 ;
}
}
throw std : : runtime_error ( std : : format ( " imported partition {}:{} not found, referenced in {} " , match [ 1 ] . str ( ) , match2 [ 1 ] . str ( ) , file . string ( ) ) ) ;
next3 : + + currentMatch ;
}
goto next4 ;
}
}
throw std : : runtime_error ( std : : format ( " Module {} not found not found, referenced in {} " , match [ 1 ] . str ( ) , file . string ( ) ) ) ;
next4 : ;
} else {
std : : regex pattern ( R " (import ([a-zA-Z_ \ -0-9 \ .]*);) " ) ;
std : : sregex_iterator currentMatch ( fileContent . begin ( ) , fileContent . end ( ) , pattern ) ;
std : : sregex_iterator lastMatch ;
while ( currentMatch ! = lastMatch ) {
std : : smatch match = * currentMatch ;
for ( const std : : unique_ptr < Module > & interface : interfaces ) {
if ( interface - > name = = match [ 1 ] ) {
implementation . moduleDependencies . push_back ( interface . get ( ) ) ;
goto next5 ;
}
}
//throw std::runtime_error(std::format("imported module {} not found, referenced in {}", match[1].str(), file.string()));
next5 : + + currentMatch ;
}
}
}
}
if ( config . contains ( " shaders " ) ) {
for ( auto it : config [ " shaders " ] ) {
shaders . emplace_back ( workingDir / it [ " path " ] . get < std : : string > ( ) , it [ " entrypoint " ] . get < std : : string > ( ) , static_cast < EShLanguage > ( it [ " type " ] . get < std : : uint32_t > ( ) ) ) ;
}
}
2025-11-16 18:44:41 +01:00
if ( config . contains ( " c_files " ) ) {
for ( auto it : config [ " c_files " ] ) {
c_files . push_back ( workingDir / it . get < std : : string > ( ) ) ;
}
}
2025-10-31 16:50:47 +01:00
if ( config . contains ( " defines " ) ) {
for ( auto it : config [ " defines " ] ) {
2025-11-26 17:55:52 +01:00
if ( it . contains ( " value " ) ) {
defines . emplace_back ( it [ " name " ] . get < std : : string > ( ) , it [ " value " ] . get < std : : string > ( ) ) ;
} else {
defines . emplace_back ( it [ " name " ] . get < std : : string > ( ) , " " ) ;
}
2025-10-31 16:50:47 +01:00
}
}
if ( config . contains ( " additional_files " ) ) {
const std : : vector < std : : string > tempAdditionalFiles = config [ " additional_files " ] . get < std : : vector < std : : string > > ( ) ;
for ( const std : : string & file : tempAdditionalFiles ) {
additionalFiles . push_back ( workingDir / file ) ;
}
}
if ( config . contains ( " dependencies " ) ) {
for ( auto it : config [ " dependencies " ] ) {
2025-11-16 18:44:41 +01:00
fs : : path path = it [ " path " ] . get < std : : string > ( ) ;
std : : string configName = it [ " configuration " ] . get < std : : string > ( ) ;
if ( path . string ( ) . ends_with ( " .git " ) ) {
fs : : path name = path . filename ( ) ;
name . replace_extension ( ) ;
2025-11-26 17:55:52 +01:00
std : : string depFolder = name . string ( ) + " - " + configName ;
if ( it . contains ( " commit " ) ) {
depFolder + it [ " commit " ] . get < std : : string > ( ) ;
} else if ( it . contains ( " branch " ) ) {
depFolder + it [ " branch " ] . get < std : : string > ( ) ;
}
if ( ! fs : : exists ( project . buildDir / depFolder ) ) {
fs : : create_directories ( project . buildDir / depFolder ) ;
std : : string cloneCommand = std : : format ( " cd {} && git clone {} {} && cd {} " ,
project . buildDir . string ( ) ,
path . string ( ) ,
depFolder ,
depFolder ) ;
if ( it . contains ( " branch " ) ) {
cloneCommand + = std : : format ( " && git switch {} " , it [ " branch " ] . get < std : : string > ( ) ) ;
} else if ( it . contains ( " commit " ) ) {
cloneCommand + = std : : format ( " && git checkout {} " , it [ " commit " ] . get < std : : string > ( ) ) ;
2025-11-16 18:44:41 +01:00
}
2025-11-26 17:55:52 +01:00
// Execute the clone command
system ( cloneCommand . c_str ( ) ) ;
} else if ( ! it . contains ( " commit " ) ) {
system ( std : : format ( " cd {} && git pull " , ( project . buildDir / depFolder ) . string ( ) ) . c_str ( ) ) ;
2025-11-16 18:44:41 +01:00
}
2025-11-26 17:55:52 +01:00
// Update path to the project.json of the dependency
path = project . buildDir / depFolder / " project.json " ;
2025-10-31 16:50:47 +01:00
}
2025-11-26 17:55:52 +01:00
// Create a Project object for the dependency
2025-11-16 18:44:41 +01:00
std : : unique_ptr < Project > depProject = std : : make_unique < Project > ( std : : move ( path ) , configName ) ;
2025-11-26 17:55:52 +01:00
// Add the dependency to the list
2025-11-16 18:44:41 +01:00
dependencies . emplace_back ( std : : move ( depProject ) , depProject - > configurations [ 0 ] ) ;
2025-10-31 16:50:47 +01:00
}
}
}
}