diff --git a/VERSIONS b/VERSIONS index cb9ad4c25..be5cd8849 100644 --- a/VERSIONS +++ b/VERSIONS @@ -6,6 +6,8 @@ ChucK VERSIONS log ======= (patch release) - (fixed, linux) a crash when the OTF handles an OTF command (add or replace) +- (added) support for crawling subdirectories for importing for packages + to be managed by the upcoming ChuMP (ChucK Manager of Packages) 1.5.4.0 (November 2024) diff --git a/src/core/chuck.cpp b/src/core/chuck.cpp index d6ef27399..355bf85bd 100644 --- a/src/core/chuck.cpp +++ b/src/core/chuck.cpp @@ -687,7 +687,7 @@ t_CKBOOL ChucK::initCompiler() //----------------------------------------------------------------------------- // name: initChugin() -// desc: initialize chugin system +// desc: initialize chugin system (auto-load system chugins) //----------------------------------------------------------------------------- t_CKBOOL ChucK::initChugins() { @@ -817,6 +817,12 @@ void ChucK::probeChugins() std::list ck_libs_to_preload; // host verison std::ostringstream ostr; ostr << CK_DLL_VERSION_MAJOR << "." << CK_DLL_VERSION_MINOR; + // chugin extension + std::string extension = ".chug"; +#ifdef __EMSCRIPTEN__ + // webchugins have extension ".chug.wasm" | 1.5.2.0 (terryzfeng) added + extension = "chug.wasm"; +#endif // print whether chugins enabled EM_log( CK_LOG_SYSTEM, "chugin system: %s", getParamInt( CHUCK_PARAM_CHUGIN_ENABLE ) ? "ON" : "OFF" ); @@ -832,9 +838,20 @@ void ChucK::probeChugins() // pop EM_poplog(); - // list of search pathes (added 1.3.0.0) + // list of search pathes (added 1.3.0.0; revisited 1.5.4.0) + // start with system paths (auto-load chugins; .ck files must be @imported) std::list dl_search_path = getParamStringList( CHUCK_PARAM_IMPORT_PATH_SYSTEM ); - append_path_list( dl_search_path, getParamStringList( CHUCK_PARAM_IMPORT_PATH_PACKAGES) ); + // next, process packages paths (e.g., as managed by ChuMP; no auto-load; all must be @imported) + std::list packages_paths = getParamStringList( CHUCK_PARAM_IMPORT_PATH_PACKAGES); + // append packages paths to search paths + append_path_list( dl_search_path, packages_paths ); + // iterate over packages paths | 1.5.4.1 (ge & nshaheed) added + for( std::list::iterator it = packages_paths.begin(); it != packages_paths.end(); it++ ) + { + // scan for subdirs, but only one-level in each packages path + scan_for_dirs_in_directory( *it, extension, FALSE, dl_search_path ); + } + // finally, add user-managed search paths (no auto-load; all must be @imported) append_path_list( dl_search_path, getParamStringList( CHUCK_PARAM_IMPORT_PATH_USER) ); // list of individually named chug-ins (added 1.3.0.0) @@ -845,13 +862,6 @@ void ChucK::probeChugins() // push indent level // EM_pushlog(); - // chugin extension - std::string extension = ".chug"; -#ifdef __EMSCRIPTEN__ - // webchugins have extension ".chug.wasm" | 1.5.2.0 (terryzfeng) added - extension = "chug.wasm"; -#endif - // load external libs; recurse changed to FALSE in 1.5.4.0 (ge) if( !Chuck_Compiler::probe_external_modules( extension.c_str(), dl_search_path, named_dls, FALSE, ck_libs_to_preload ) ) { @@ -863,9 +873,9 @@ void ChucK::probeChugins() //------------------------------------------------------------------------- // 1.5.4.0 | .ck files are no longer auto compiled; need to be @import - /*------------------------------------------------------------------------- + //------------------------------------------------------------------------- // log - EM_log( CK_LOG_SYSTEM, "probing auto-load chuck files (.ck)..." ); + EM_log( CK_LOG_SYSTEM, "probing chuck files (.ck)..." ); EM_pushlog(); // iterate over list of ck files that the compiler found @@ -875,7 +885,8 @@ void ChucK::probeChugins() // the filename std::string filename = *j; // log - EM_log( CK_LOG_SYSTEM, "[%s] '%s'...", TC::green("FOUND",true).c_str(), filename.c_str() ); + logCKFileFound( filename, CK_LOG_SYSTEM ); + // EM_log( CK_LOG_SYSTEM, "[%s] '%s'...", TC::green("FOUND",true).c_str(), filename.c_str() ); } // check @@ -884,7 +895,6 @@ void ChucK::probeChugins() // pop log EM_poplog(); - -------------------------------------------------------------------------*/ } diff --git a/src/core/chuck_compile.cpp b/src/core/chuck_compile.cpp index 63fa79818..946dbed7f 100644 --- a/src/core/chuck_compile.cpp +++ b/src/core/chuck_compile.cpp @@ -666,10 +666,21 @@ std::string Chuck_Compiler::resolveFilename( const std::string & filename, // TODO: possible caching -- search for match first in registry // match-right between each entry and fname; if found, return entry absolute path - // get search paths; order: system, packages, user - list searchPaths = this->carrier()->chuck->getParamStringList( CHUCK_PARAM_IMPORT_PATH_SYSTEM ); - append_path_list( searchPaths, this->carrier()->chuck->getParamStringList( CHUCK_PARAM_IMPORT_PATH_PACKAGES ) ); - append_path_list( searchPaths, this->carrier()->chuck->getParamStringList( CHUCK_PARAM_IMPORT_PATH_USER ) ); + // search paths: start with system paths + std::list searchPaths = this->carrier()->chuck->getParamStringList( CHUCK_PARAM_IMPORT_PATH_SYSTEM ); + // next, process packages paths (e.g., as managed by ChuMP) + std::list packages_paths = this->carrier()->chuck->getParamStringList( CHUCK_PARAM_IMPORT_PATH_PACKAGES); + // append packages paths to search paths + append_path_list( searchPaths, packages_paths ); + // iterate over packages paths | 1.5.4.1 (ge & nshaheed) added + for( std::list::iterator it = packages_paths.begin(); it != packages_paths.end(); it++ ) + { + // scan for subdirs, but only one-level in each packages path + scan_for_dirs_in_directory( *it, "", FALSE, searchPaths ); + } + // finally, add user-managed search paths (no auto-load; all must be @imported) + append_path_list( searchPaths, this->carrier()->chuck->getParamStringList( CHUCK_PARAM_IMPORT_PATH_USER) ); + // go over paths for( list::iterator it = searchPaths.begin(); it != searchPaths.end(); it++ ) { @@ -677,6 +688,8 @@ std::string Chuck_Compiler::resolveFilename( const std::string & filename, absolutePath = expand_filepath(*it+fname); // try to match hasMatch = matchFilename( absolutePath, extension, exts ); + // log + EM_log( CK_LOG_FINER, "testing match: '%s' ('%s')", absolutePath.c_str(), hasMatch ? "yes" : "no" ); // if match found, break out if( hasMatch ) break; } @@ -1419,7 +1432,7 @@ t_CKBOOL scan_external_modules_in_directory( const string & directory, vector & ckfiles2load ) { // expand directory path - string path = expand_filepath( string(directory), FALSE ); + string path = expand_filepath( directory, FALSE ); // open the directory DIR * dir = opendir( path.c_str() ); @@ -1429,7 +1442,7 @@ t_CKBOOL scan_external_modules_in_directory( const string & directory, // do first read | 1.5.0.0 (ge + eito) #chunreal struct dirent * de = readdir( dir ); // while( (de = readdir(dir)) ) <- UE5 forces us to not do this - while( de != NULL ) + for( de = readdir(dir); de != NULL; de = readdir(dir) ) { t_CKBOOL is_regular = false; t_CKBOOL is_directory = false; @@ -1490,14 +1503,83 @@ t_CKBOOL scan_external_modules_in_directory( const string & directory, chugins2load.push_back( ChuginFileInfo( de->d_name, subdirectory, true ) ); } #endif // #ifdef __PLATFORM_APPLE__ + } + + // close + closedir( dir ); + + return TRUE; +} + + + + +//----------------------------------------------------------------------------- +// name: scan_for_dirs_in_directory() | 1.5.4.1 (ge & nshaheed) added +// desc: scan all subdirectories within a directory +//----------------------------------------------------------------------------- +t_CKBOOL scan_for_dirs_in_directory( const string & directory, + const string & extensionForSubdirTest, + t_CKBOOL recursiveSearch, + list & results ) +{ + // expand directory path + string path = normalize_directory_name( expand_filepath( directory, FALSE ) ); + // open the directory + DIR * dir = opendir( path.c_str() ); + // cannot open + if( !dir ) return FALSE; + + // local results + list localResults; + + // do first read | 1.5.0.0 (ge + eito) #chunreal + struct dirent * de = readdir( dir ); + // while( (de = readdir(dir)) ) <- UE5 forces us to not do this + for( de = readdir(dir); de != NULL; de = readdir(dir) ) + { + t_CKBOOL is_regularFile = false; + t_CKBOOL is_directory = false; + // get attributes + if( !getDirEntryAttribute( de, is_directory, is_regularFile ) ) continue; - // read next | 1.5.0.0 (ge) moved here due to #chunreal - de = readdir( dir ); + // check if directory + if( is_directory ) + { + // test for special cases (e.g., chugins that are directories) + if( extensionForSubdirTest.length() && !subdir_ok2recurse( de->d_name, extensionForSubdirTest ) ) continue; + + // check dir entry + if( strncmp( de->d_name, ".", sizeof( "." ) ) != 0 && + strncmp( de->d_name, "..", sizeof( ".." ) ) != 0 ) + { + // construct absolute path (use the non-expanded path for consistency when printing) + string absolute_path = string(path) + de->d_name; + // queue sub-directory + localResults.push_back( normalize_directory_name(absolute_path) ); + } + } } // close closedir( dir ); + // sort the local results + localResults.sort(); + // copy local results + append_path_list( results, localResults ); + + // recurse? + if( recursiveSearch ) + { + // iterate over local directoriesug + for( list::iterator it = localResults.begin(); it != localResults.end(); it++ ) + { + // scan subdirs in this local dir + scan_for_dirs_in_directory( *it, extensionForSubdirTest, recursiveSearch, results ); + } + } + return TRUE; } @@ -1508,7 +1590,7 @@ t_CKBOOL scan_external_modules_in_directory( const string & directory, // name: logChuginLoad() | 1.5.2.5 (ge) // desc: local function quick-hand for logging a chugin load //----------------------------------------------------------------------------- -static void logChuginLoad( const string & name, t_CKINT logLevel ) +void logChuginLoad( const string & name, t_CKINT logLevel ) { // log with no newline; print `[chugin] X.chug` EM_log_opts( logLevel, EM_LOG_NO_NEWLINE, "[%s] %s ", TC::magenta("chugin",true).c_str(), name.c_str() ); @@ -1517,6 +1599,22 @@ static void logChuginLoad( const string & name, t_CKINT logLevel ) +//----------------------------------------------------------------------------- +// name: logCKFileFound() | 1.5.4.1 (ge & nshaheed) +// desc: local function quick-hand for logging a chuck file found +//----------------------------------------------------------------------------- +void logCKFileFound( const string & name, t_CKINT logLevel ) +{ + // log with no newline; print `[chuck file] X.ck` + EM_log_opts( logLevel, EM_LOG_NO_NEWLINE, "[%s] %s ", TC::blue("chuck file",true).c_str(), name.c_str() ); + + // print success status + EM_log_opts( logLevel, EM_LOG_NO_PREFIX, "[%s]", TC::green("FOUND",true).c_str() ); +} + + + + //----------------------------------------------------------------------------- // name: import_chugin_opt() // desc: load chugin module by path, with options @@ -1987,7 +2085,7 @@ t_CKBOOL Chuck_Compiler::probe_external_modules( const string & extension, // push EM_pushlog(); - // now recurse through search paths and load any DLs or .ck files found + // now recurse through search paths and note any DLs or .ck files found for( list::iterator i_sp = chugin_search_paths.begin(); i_sp != chugin_search_paths.end(); i_sp++ ) { diff --git a/src/core/chuck_compile.h b/src/core/chuck_compile.h index 4a688bf61..18d44152b 100644 --- a/src/core/chuck_compile.h +++ b/src/core/chuck_compile.h @@ -44,6 +44,7 @@ #include "chuck_vm.h" #include #include +#include @@ -419,5 +420,20 @@ struct Chuck_Compiler +//----------------------------------------------------------------------------- +// helper functions +//----------------------------------------------------------------------------- +// log a chugin load +void logChuginLoad( const std::string & name, t_CKINT logLevel ); +// log a chuck file found +void logCKFileFound( const std::string & name, t_CKINT logLevel ); +// scan for subdirs in a dir +t_CKBOOL scan_for_dirs_in_directory( const std::string & directory, + const std::string & extensionForSubdirTest, + t_CKBOOL recursiveSearch, + std::list & results ); + + + #endif