SAS Packages Framework, version 20221212

SAS Packages Framework, version 20221212

- New type `CASLUDF` for CASL User Defined Functions added to the framework.
- If a package contains IML modules or CASL user defined functions additional utility macros for IML Modules and CASL UDFs are generated when package is loaded.
- Documentation updated.
- Minor bugfix.
This commit is contained in:
Bartosz Jablonski
2022-12-12 23:39:33 +01:00
parent edca6a8a8c
commit f3f4747df6
15 changed files with 643 additions and 106 deletions

View File

@@ -23,7 +23,7 @@
default value 1 means "delete tests work" */
)/ secure minoperator
/*** HELP END ***/
des = 'Macro to generate SAS packages, version 20221125. Run %generatePackage() for help info.'
des = 'Macro to generate SAS packages, version 20221212. Run %generatePackage() for help info.'
;
%if (%superq(filesLocation) = ) OR (%qupcase(&filesLocation.) = HELP) %then
%do;
@@ -38,7 +38,7 @@ des = 'Macro to generate SAS packages, version 20221125. Run %generatePackage()
%put ### This is short help information for the `generatePackage` macro #;
%put #-------------------------------------------------------------------------------#;
%put # #;
%put # Macro to generate SAS packages, version `20221125` #;
%put # Macro to generate SAS packages, version `20221212` #;
%put # #;
%put # A SAS package is a zip file containing a group #;
%put # of SAS codes (macros, functions, data steps generating #;
@@ -545,7 +545,10 @@ data &filesWithCodes.;
/* ignore unknown types for folders */
if fileId AND not (upcase(type) in:
('LIBNAME' 'MACRO' /*'MACROS'*/ 'DATA' 'FUNCTION' /*'FUNCTIONS'*/ 'FORMAT' /*'FORMATS'*/ 'IMLMODULE' 'PROTO' 'EXEC' 'CLEAN' 'LAZYDATA' 'TEST'))
('LIBNAME' 'MACRO' /*'MACROS'*/ 'DATA'
'FUNCTION' /*'FUNCTIONS'*/ 'FORMAT' /*'FORMATS'*/
'IMLMODULE' 'PROTO' 'EXEC' 'CLEAN'
'LAZYDATA' 'TEST' 'CASLUDF'))
then
do;
put "WARNING: Type " type 'is not yet supported.' /
@@ -911,8 +914,11 @@ data _null_;
put '%include ' " &_PackageFileref_.(packagemetadata.sas) / nosource2; " /; /* <- copied also to loadPackage macro */
isFunction = 0;
isFormat = 0;
isFunction = 0;
isFormat = 0;
isProto = 0;
isIMLmodule = 0;
isCASLudf = 0;
%if (%superq(packageRequired) ne )
or (%superq(packageReqPackages) ne )
@@ -1129,23 +1135,29 @@ data _null_;
set &filesWithCodes. end = EOF nobs=NOBS;
by TYPE notsorted;
if (upcase(type) in: ('CLEAN' 'LAZYDATA' 'TEST')) then continue; /* cleaning files are only included in unload.sas */
/* lazy data are only loaded on demand
%loadPackage(packagename, lazyData=set1 set2 set3)
test files are used only during package generation
*/
if (upcase(type) in: ('CLEAN' 'LAZYDATA' 'TEST' 'CASLUDF'))
then continue; /* CASLUDF type will go in the next loop */
/* cleaning files are only included in unload.sas */
/* lazy data are only loaded on demand
%loadPackage(packagename, lazyData=set1 set2 set3)
test files are used only during package generation
*/
/* test for supported types */
if not (upcase(type) in:
('LIBNAME' 'MACRO' /*'MACROS'*/ 'DATA' 'FUNCTION' /*'FUNCTIONS'*/ 'FORMAT' /*'FORMATS'*/ 'IMLMODULE' 'PROTO' 'EXEC' 'CLEAN' 'LAZYDATA' 'TEST'))
('LIBNAME' 'MACRO' /*'MACROS'*/ 'DATA'
'FUNCTION' /*'FUNCTIONS'*/ 'FORMAT' /*'FORMATS'*/
'IMLMODULE' 'PROTO' 'EXEC' 'CLEAN'
'LAZYDATA' 'TEST'))
then
do;
putlog 'WARNING: Type ' type 'is not yet supported.';
continue;
end;
isFunction + (upcase(type)=:'FUNCTION');
isFormat + (upcase(type)=:'FORMAT');
isProto + (upcase(type)=:'PROTO');
isFunction + (upcase(type)=:'FUNCTION');
isFormat + (upcase(type)=:'FORMAT');
isProto + (upcase(type)=:'PROTO');
isIMLmodule + (upcase(type)=:'IMLMODULE');
/* HEADERS for IML, FCMP, and PROTO - start */
if 1 = isFunction and upcase(type)=:'FUNCTION' then
@@ -1173,7 +1185,8 @@ data _null_;
do;
/* macro variable for test if cherry picking used IML */
put 'data _null_; ';
put ' call symputX("cherryPick_IML", 0, "L"); ';
put ' call symputX("cherryPick_IML", 0, "L"); ';
put ' call symputX("cherryPick_IML_ALL", 0, "L"); ';
put 'run; ';
/* header, for IML modules */
put "proc iml ; ";
@@ -1240,7 +1253,8 @@ data _null_;
end;
if 1 = LAST.type and upcase(type)='IMLMODULE' then /* footer, for IML modules */
do;
put '%if 0 < &cherryPick_IML. %then %do; ' /
put '%if 0 < &cherryPick_IML. %then %do; ' /
'%let cherryPick_IML_ALL = %eval(&cherryPick_IML_ALL. + &cherryPick_IML.);' /
"reset storage = WORK.&packageName.IML; " / /* set the storage location for modules */
"store module = _ALL_; " / /* and store all created modules */
'%end; ' /
@@ -1268,6 +1282,125 @@ data _null_;
end; /* loopOverTypes - start */
/* this is a header for CASLudf macro */
put 'data _null_; ';
put ' call symputX("cherryPick_CASLUDF", 0, "L"); ';
put 'run; ';
put 'data _null_;';
put 'length CASLUDF $ 32767;';
put 'dtCASLudf = datetime();';
put 'CASLUDF = ';
put ' ''%macro ' "&packageName.CASLudf(' ";
put ' !! "list=1,depList=" ';
%if %superq(packageReqPackages) ne %then
%do;
length reqPackage $ 32;
do i = 1 to countw(packageReqPackages, ",", "Q");
reqPackage = compress(scan(scan(packageReqPackages, i, ",", "Q"), 1, "[{( )}]"),"_","KAD") ;
put ' !! " ' reqPackage ' " ';
end;
%end;
put " !! ')/ des = ''CASL User Defined Functions loader for &packageName. package'';'";
put ' !! '' %if HELP = %superq(list) %then ''' /
' !! '' %do; ''' /
' !! '' %put ****************************************************************************;''' /
' !! '' %put This is help for the `' "&packageName.CASLudf" '` macro;''' /
' !! '' %put Parameters (optional) are the following:;''' /
' !! '' %put - `list` indicates if the list of loaded CASL UDFs should be displayed,;''' /
' !! '' %put %str( )when set to the value of `1` (the default) runs `FUNCTIONLIST USER%str(;)`,;''' /
' !! '' %put %str( )when set to the value of `HELP` (upcase letters!) displays this help message.;''' /
' !! '' %put - `depList` [technical] contains the list of dependencies required by the package.;''' /
' !! '' %put - %str( )for _this_ macro the default value is: `';
%if %superq(packageReqPackages) ne %then
%do;
do i = 1 to countw(packageReqPackages, ",", "Q");
reqPackage = compress(scan(scan(packageReqPackages, i, ",", "Q"), 1, "[{( )}]"),"_","KAD") ;
put reqPackage @;
end;
%end;
put '`.;''' /
' !! '' %put The macro generated: '' !! put(dtCASLudf, datetime19.-L) !! ";"' /
' !! '' %put with the SAS Packages Framework version 20221212.;''' /
' !! '' %put ****************************************************************************;''' /
' !! '' %GOTO theEndOfTheMacro;''' /
' !! '' %end;''' ;
put ' !! '' %if %superq(depList) ne %then ''' /
' !! '' %do; ''' /
' !! '' %do i = 1 %to %sysfunc(countw(&depList.,%str( ))); ''' /
' !! '' %let depListNm = %scan(&depList.,&i.,%str( )); ''' /
' !! '' %if %SYSMACEXIST(&depListNm.CASLudf) %then ''' /
' !! '' %do; ''' /
' !! '' %&depListNm.CASLudf(list=0) ''' /
' !! '' %end; ''' /
' !! '' %end; ''' /
' !! '' %end; ''' ;
put ' !! '' %local tmp_NOTES;'' ';
put ' !! '' %let tmp_NOTES = %sysfunc(getoption(NOTES));'' ';
/* the PATH macrovariable will be resolved when the load.sas file is executed */
put ' !! " filename ' "&_PackageFileref_." ' &ZIP. ''&path./' "%lowcase(&packageName.)" '.&zip.'';"';
/* this loop lists includes for CASLUDFs in the macro definition */
do until(eof1); /* loopOverTypes1 - start */
set &filesWithCodes. end = EOF1;
by TYPE notsorted;
if not (upcase(type) = 'CASLUDF') then continue; /* only CASLUDF type in this loop */
isCASLudf + 1;
put ' ' /
'%if (%str(*)=%superq(cherryPick)) or (' fileshort +(-1) ' in %superq(cherryPick)) %then %do; '; /* Cherry Pick test2 start */
put ' %put NOTE- ;';
put ' %put NOTE: >> Element of type ' type 'from the file "' file +(-1) '" will be included <<;';
/* for CASLUDF we are building code of a macro to be run while loading */
put ' !! '' %include' " &_PackageFileref_.(_" folder +(-1) "." file +(-1) ') / nosource2;''';
put ' %let cherryPick_CASLUDF = %eval(&cherryPick_CASLUDF. + 1);';
put '%end; ' /; /* Cherry Pick test2 end */
end; /* loopOverTypes1 - start */
/* this is a footer for CASLudf macro */
put ' !! " options nonotes;" ' /
" !! ' filename &_PackageFileref_. clear;' " /
' !! '' options &tmp_NOTES.;'' ' /
' !! '' %if 1 = %superq(list) %then '' ' /
' !! '' %do; '' ' /
' !! " FUNCTIONLIST USER;" ' /
' !! " run;" ' /
' !! '' %end; '' ' ;
put ' !! ''%theEndOfTheMacro: %mend;''; ' ;
/* generate macro for CASL user defined functions */
if 0 < isCASLudf then
do;
put '%if 0 < &cherryPick_CASLUDF. %then %do; ';
/*put ' put / / CASLUDF / / ; ';*/
put " rc = resolve(CASLUDF); ";
put ' put "NOTE: Macro named:"; ';
put " put @7 '%' '&packageName.CASLudf()'; ";
put ' put "NOTE- will be generated. Its purpose";';
put ' put "NOTE- is to allow fast load of CASL"; ';
put ' put "NOTE- user defined functions into"; ';
put ' put "NOTE- the Proc CAS session."; ';
put ' put "NOTE-"; ';
put ' put "NOTE- Use it the following way:"; ';
put " put @7 'Proc CAS;'; ";
put " put @7 ' %' '&packageName.CASLudf()'; ";
put " put @7 ' <... your code ...>'; ";
put " put @7 'quit;'; ";
put ' put "NOTE-"; ';
put ' put "NOTE-To get help run:"; ';
put " put @7 ' %' '&packageName.CASLudf(list=HELP)';";
put ' put "NOTE-"; ';
put '%end; ';
end;
put 'run;'/;
/* cherry pick clean in cmplib for functions */
if isFunction then
@@ -1304,7 +1437,7 @@ data _null_;
/* list fmtsearch for formats */
if isFormat then
do;
put '%if 0 = &cherryPick_FCMP. %then %do;';
put '%if 0 = &cherryPick_FORMAT. %then %do;';
put 'options fmtsearch = (%unquote(%sysfunc(tranwrd(' /
'%lowcase(%sysfunc(getoption(fmtsearch)))' /
',%str(' "work.%lowcase(&packageName.)format" '), %str() ))));';
@@ -1316,6 +1449,116 @@ data _null_;
put '%put NOTE:[FMTSEARCH] %sysfunc(getoption(fmtsearch));'/;
end;
/* create a macro loader for IML modules with dependencies */
if isIMLmodule then
do;
put '%if (%str(*)=%superq(cherryPick)) or 0 < &cherryPick_IML_ALL. %then %do;';
/* this "text wrapper" was added to get datetime generated when macro is compiled */
put "data _null_; dtIML=datetime(); IML='"; /* wrapper start */
put '%macro ' " &packageName.IML(list=1,err=ERROR,resetIMLstorage=1,depList=" ;
%if %superq(packageReqPackages) ne %then
%do;
length reqPackage $ 32;
do i = 1 to countw(packageReqPackages, ",", "Q");
reqPackage = compress(scan(scan(packageReqPackages, i, ",", "Q"), 1, "[{( )}]"),"_","KAD") ;
put " " reqPackage @;
end;
put ;
%end;
put ")/ des = ""IML Modules loader for &packageName. package""; " /
' %if HELP = %superq(list) %then ' /
' %do; ' /
' %put ****************************************************************************; ' /
' %put This is help for the `' "&packageName.IML" '` macro; ' /
' %put Parameters (optional) are the following:; ' /
' %put - `list` indicates if the list of loaded moduls should be displayed,; ' /
' %put %str( )when set to the value of `1` (the default) runs `SHOW MODULES%str(;)`,; ' /
' %put %str( )when set to the value of `HELP` (upcase letters!) displays this help message.;' /
' %put - `resetIMLstorage` indicates if to reset default moduls storage,; ' /
' %put %str( )when set to `1` (the default) runs `RESET STORAGE = WORK.IMLSTOR%str(;)`.; ' /
' %put - `err` [technical] indicates message type in case of missing modules catalog,; ' /
' %put %str( )when set to the value of `ERROR` (the default) prints Error message.; ' /
' %put - `depList` [technical] contains the list of dependencies required by the package.; ' /
' %put - %str( )for _this_ macro the default value is: `';
%if %superq(packageReqPackages) ne %then
%do;
do i = 1 to countw(packageReqPackages, ",", "Q");
reqPackage = compress(scan(scan(packageReqPackages, i, ",", "Q"), 1, "[{( )}]"),"_","KAD") ;
put reqPackage @;
end;
%end;
put '`.;' /
' %put The macro generated: ''' " !! put(dtIML, datetime19.-L) !! " '''; ' /
' %put with the SAS Packages Framework version 20221212.; ' /
' %put ****************************************************************************; ' /
' %GOTO theEndOfTheMacro; ' /
' %end; ' /
' %local localSYSmacroName localPackageName i depListNm; ' /
' %let localSYSmacroName = &sysmacroname.; ' /
' %let localSYSmacroName = %LOWCASE(&localSYSmacroName.); ' /
' %let localPackageName = %substr(&localSYSmacroName.,1,%eval(%length(&localSYSmacroName.)-3));' /
' ' /
' %if %superq(depList) ne %then ' /
' %do; ' /
' %do i = 1 %to %sysfunc(countw(&depList.,%str( ))); ' /
' %let depListNm = %scan(&depList.,&i.,%str( )); ' /
' %if %SYSMACEXIST(&depListNm.IML) %then ' /
' %do; ' /
' %&depListNm.IML(list=0,err=&err.,resetIMLstorage=0) ' /
' %end; ' /
' %end; ' /
' %end; ' /
' %if %sysfunc(CEXIST(WORK.&localSYSmacroName.)) %then ' /
' %do; ' /
' %put NOTE: Loading IML Modules from package &localPackageName.; ' /
' RESET STORAGE = WORK.&localSYSmacroName.; ' /
' LOAD MODULE = _all_; ' /
' %end; ' /
' %else ' /
' %do; ' /
' %put %superq(err): IML Modules not provided; ' /
' %let list = 0; ' /
' %end; ' /
' %if 1 = %superq(list) %then ' /
' %do; ' /
' SHOW MODULES; ' /
' %end; ' /
' %if 1 = %superq(resetIMLstorage) %then ' /
' %do; ' /
' RESET STORAGE = WORK.IMLSTOR; ' /
' %end; ' /
'%theEndOfTheMacro: %mend; ' ;
put "'; rc = resolve(IML); run;"; /* wrapper end */
put '%put NOTE: Macro named:; ';
put '%put %nrstr( %%)' "&packageName." 'IML(); ';
put '%put NOTE- will be generated. Its purpose; ';
put '%put NOTE- is to allow fast load of IML; ';
put '%put NOTE- user defined modules into; ';
put '%put NOTE- the Proc IML session.; ';
put '%put NOTE-; ';
put '%put NOTE- Use it the following way:; ';
put '%put %nrstr( )Proc IML%str(;); ';
put '%put %nrstr( %%)' "&packageName." 'IML(); ';
put '%put %nrstr( )<... your code ...>; ';
put '%put %nrstr( )quit%str(;); ';
put '%put NOTE- ; ';
put '%put NOTE- To get help info run:; ';
put '%put %nrstr( %%)' "&packageName." 'IML(list=HELP);';
put '%put NOTE-; ';
put '%end;';
end;
/* update SYSloadedPackages global macrovariable */
put '%if (%str(*)=%superq(cherryPick)) %then %do; '; /* Cherry Pick test3 start */
put ' data _null_ ; ';
@@ -1463,15 +1706,17 @@ data _null_;
put ' from dictionary.catalogs';
put ' where ';
put ' (';
put ' objname in ("*"' /;
put ' objname in ("*"' ;
put " ,%UPCASE('&packageName.IML')" ;
put " ,%UPCASE('&packageName.CASLUDF')" /;
/* list of macros */
EOF = 0;
do until(EOF);
set &filesWithCodes. end = EOF nobs = NOBS;
if not (upcase(type)=:'MACRO') then continue;
put '%put NOTE- Element of type ' type 'generated from the file "' file +(-1) '" will be deleted;';
put '%put NOTE- ;';
put ',"' fileshort upcase32. '"' /;
put ' %put NOTE- Element of type ' type 'generated from the file "' file +(-1) '" will be deleted;';
put ' %put NOTE- ;';
put ' ,"' fileshort upcase32. '"' /;
end;
/**/
put ' )';
@@ -1487,9 +1732,9 @@ data _null_;
do until(EOF);
set &filesWithCodes. end = EOF;
if not (upcase(type)=:'FORMAT') then continue;
put '%put NOTE- Element of type ' type 'generated from the file "' file +(-1) '" will be deleted;';
put '%put NOTE- ;';
put ',"' fileshort upcase32. '"' /;
put ' %put NOTE- Element of type ' type 'generated from the file "' file +(-1) '" will be deleted;';
put ' %put NOTE- ;';
put ' ,"' fileshort upcase32. '"' /;
isFormat + 1;
end;
put ' )';
@@ -1858,7 +2103,7 @@ data _null_;
put ' end ; ';
%end;
put 'put "***"; put "* SAS package generated by generatePackage, version 20221125 *"; put "***";';
put 'put "***"; put "* SAS package generated by generatePackage, version 20221212 *"; put "***";';
put 'run; ' /;