SAS Packages Framework, version 20221121

## SAS Packages Framework, version 20221121

---

### New feature of "Cherry picking" added to the SAS Packages Framework.
Sometimes a package offers so many features that the number may be "overwhelming".
In such case only some of them may be selected for loading. Such process
is called a "cherry picking". The feature is provided by the `%loadPackage()` macro
which uses a `cherryPick=` parameter (see description below).

For example, execution of the following code:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
 %loadPackage(BasePlus, cherryPick=rainCloudPlot getVars)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
results with loading only the `rainCloudPlot` and the `getVars` elements.
If several object types (e.g., a macro and a format) share the same name
all will be loaded.

What is the trade-off?
- Since the cherry picking selects only a part of the package the `SYSloadedPackages`
macrovariable is not updated with the package name.
- Dependencies i.e., packages from the `ReqPackages` list, are not loaded automatically,
so they have to be loaded manually.
- The `%unloadPackage()` macro executed on such partially loaded package may issue
some (irrelevant) warnings.

---

### Changes in `%loadPackage()` macro:
- New `cherryPick=` parameter added to the macro.
  As a value a *space separated* list of selected elements
  of the package to be loaded into the SAS session is expected.
  Default value of an asterisk (`*`) means: "load all elements of the package".
  Empty list is equivalent to default.
- Documentation updated.

### Changes in `%generatePackage()` macro:
- Code adjustment for the cherry picking feature.
- Minor additional code refactoring.
- The `%ICEloadPackage()` macro does not support cherry picking.

---

### The following packages were regenerated with the latest version of the framework:
- BasePlus [1.17.2]
- DFA [0.5.2]
- dynMacroArray [0.2.2]
- GSM [0.20.2]
- macroArray [1.0.2]
- SQLinDS [2.2.2]
This commit is contained in:
Bartosz Jablonski
2022-11-21 14:25:56 +01:00
parent ba4b5f8c32
commit 3a824b4d8b
29 changed files with 673 additions and 313 deletions

View File

@@ -21,9 +21,9 @@
,delTestWork=1 /* indicates if `WORK` directories generated by user tests
should be deleted, i.e. the (NO)WORKTERM option is set,
default value 1 means "delete tests work" */
)/secure minoperator
)/ secure minoperator
/*** HELP END ***/
des = 'Macro to generate SAS packages, version 20221112. Run %generatePackage() for help info.'
des = 'Macro to generate SAS packages, version 20221121. 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 20221112. Run %generatePackage()
%put ### This is short help information for the `generatePackage` macro #;
%put #-------------------------------------------------------------------------------#;
%put # #;
%put # Macro to generate SAS packages, version `20221112` #;
%put # Macro to generate SAS packages, version `20221121` #;
%put # #;
%put # A SAS package is a zip file containing a group #;
%put # of SAS codes (macros, functions, data steps generating #;
@@ -218,7 +218,7 @@ filename &_LIC_. "&filesLocation./license.sas" lrecl = 1024;
%abort;
%end;
%if %bquote(&packageRequired.) ne %then
%if %superq(packageRequired) ne %then
%do;
/* turn off the note about quoted string length */
%local qlenmax_fstimer_tmp;
@@ -267,7 +267,7 @@ filename &_LIC_. "&filesLocation./license.sas" lrecl = 1024;
%if &tryExcept. %then %abort;
%end;
%if %bquote(&packageReqPackages.) ne %then
%if %superq(packageReqPackages) ne %then
%do;
/* turn off the note about quoted string length */
%local qlenmax_fstimer_tmp;
@@ -682,12 +682,12 @@ title3 "Package encoding: '&packageEncoding.', session encoding: '&SYSENCODING.'
title4 " ______________________________ ";
title5 "List of files for package: &packageName. (version &packageVersion.), license: &packageLicense.";
title6 "MD5 hashed fileref of package lowcase name: &_PackageFileref_.";
%if (%bquote(&packageRequired.) ne )
or (%bquote(&packageReqPackages.) ne )
%if (%superq(packageRequired) ne )
or (%superq(packageReqPackages) ne )
%then
%do;
title7 "Required SAS licences: %qsysfunc(compress(%bquote(&packageRequired.), %str(%'%")))" ; /* ' */
title8 "Required SAS packages: %qsysfunc(compress(%bquote(&packageReqPackages.),%str(%'%")))" ; /* " */
title7 "Required SAS licences: %qsysfunc(compress(%superq(packageRequired), %str(%'%")))" ; /* ' */
title8 "Required SAS packages: %qsysfunc(compress(%superq(packageReqPackages),%str(%'%")))" ; /* " */
%end;
@@ -860,7 +860,14 @@ data _null_;
put ' %if %bquote(&packageEncoding.) NE %then &packageEncoding. ; ';
put ' %else utf8 ; ';
put ' ; ';
put ' %local cherryPick; %let cherryPick=*; ';
put ' %local tempLoad_minoperator; ';
put ' %let tempLoad_minoperator = %sysfunc(getoption(minoperator)); ';
put ' options minoperator; ';
put ' %include &_PackageFileref_.(load.sas) / &source2.; ';
put ' options &tempLoad_minoperator.; ';
put ' filename &_PackageFileref_. clear; ';
put ' %WrongVersionOFPackage: ';
@@ -868,7 +875,6 @@ data _null_;
put " ";
run;
/* loading package files */
%put NOTE-;
%put NOTE: Preparing load file.;
@@ -891,13 +897,20 @@ data _null_;
put ' %put NOTE- Run %nrstr(%%)helpPackage(' "&packageName." ') for the description;';
put ' %put NOTE- ;';
put ' %put NOTE- *** START ***; ' /;
put '%if NOT (%str(*)=%superq(cherryPick)) %then %do; '; /* Cherry Pick test0 start */
put ' %put NOTE- ;' /
' %put NOTE- *** Cherry Picking in action. ***; ' /
' %put NOTE- ;' ;
put '%end; '; /* Cherry Pick test0 end */
put '%include ' " &_PackageFileref_.(packagemetadata.sas) / nosource2; " /; /* <- copied also to loadPackage macro */
isFunction = 0;
isFormat = 0;
%if (%bquote(&packageRequired.) ne )
or (%bquote(&packageReqPackages.) ne )
%if (%superq(packageRequired) ne )
or (%superq(packageReqPackages) ne )
%then
%do;
put ' data _null_; ';
@@ -905,7 +918,7 @@ data _null_;
put ' run; ';
%end;
%if %bquote(&packageRequired.) ne %then
%if %superq(packageRequired) ne %then
%do;
put ' %put NOTE- *Testing required SAS components*%sysfunc(DoSubL( '; /* <- DoSubL() is here */
put ' options nonotes nosource %str(;) ';
@@ -968,11 +981,13 @@ data _null_;
put ' ))*; ';
%end;
%if %bquote(&packageReqPackages.) ne %then
%if %superq(packageReqPackages) ne %then
%do;
length packageReqPackages $ 32767;
packageReqPackages = lowcase(symget('packageReqPackages'));
put '%if (%str(*)=%superq(cherryPick)) %then %do; /* test for Cherry Picking */ ';
/* try to load required packages */
put 'data _null_ ; ';
put ' length req name $ 64 vers verR $ 24 versN verRN 8 SYSloadedPackages $ 32767; ';
@@ -1086,14 +1101,21 @@ data _null_;
put ' end; ';
put ' stop; ';
put 'run; ';
put '%end; ';
put '%else %do; ';
put ' %put NOTE- ; ';
put ' %put NOTE- Cherry Picking in action!! Be advised that; ';
put ' %put NOTE- dependencies/required packages will not be loaded!; ';
put '%end; ';
%end;
%if (%bquote(&packageRequired.) ne )
or (%bquote(&packageReqPackages.) ne )
%if (%superq(packageRequired) ne )
or (%superq(packageReqPackages) ne )
%then
%do;
put ' data _null_; ';
put ' if symget("packageRequiredErrors") = "1" then ';
put ' if 1 = symgetn("packageRequiredErrors") then ';
put ' do; ';
put ' put "ERROR: Loading package &packageName. will be aborted!";';
put ' put "ERROR- Required components are missing."; ';
@@ -1103,7 +1125,9 @@ data _null_;
put ' run; ';
%end;
do until(eof);
do until(eof); /* loopOverTypes - start */
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 */
@@ -1119,90 +1143,182 @@ data _null_;
putlog 'WARNING: Type ' type 'is not yet supported.';
continue;
end;
put '%put NOTE- ;';
put '%put NOTE: Element of type ' type 'from the file "' file +(-1) '" will be included;';
if upcase(type)=:'MACRO' then
put '%put %sysfunc(ifc(%SYSMACEXIST(' fileshort ')=1, NOTE# Macro ' fileshort
"exist. It will be overwritten by the macro from the &packageName. package, ));";
put " ";
if upcase(type)=:'EXEC' then
do;
put '%put NOTE- ;';
put '%put NOTE- Executing the following code: ;';
put '%put NOTE- *****************************;';
put 'data _null_;';
put " infile &_PackageFileref_.(_" folder +(-1) "." file +(-1) ') lrecl=32767;';
put ' input;';
put ' putlog "*> " _infile_;';
put 'run;' /;
put '%put NOTE- *****************************;';
put '%put NOTE- ;';
end;
/* HEADERS for IML, FCMP, and PROTO */
if 1 = FIRST.type and upcase(type)='FUNCTIONS' then /* header, for multiple functions in one FCMP run */
do;
put "proc fcmp outlib = work.%lowcase(&packageName.fcmp).package ; ";
end;
if 1 = FIRST.type and upcase(type)='PROTO' then /* header, for multiple functions in one PROTO run */
do;
put "proc proto package = work.%lowcase(&packageName.proto).package ; ";
end;
if 1 = FIRST.type and upcase(type)='IMLMODULE' then /* header, for IML modules */
do;
put "proc iml ; ";
end;
if 1 = FIRST.type and upcase(type)='FORMATS' then /* header, for FORMATS */
do;
put "proc format lib = work.%lowcase(&packageName.format) ; ";
end;
/* include the file with the code of the element */
put '%include' " &_PackageFileref_.(_" folder +(-1) "." file +(-1) ') / nosource2;' /;
/* FOOTERS for IML, FCMP, and PROTO */
if 1 = LAST.type and upcase(type) in ('FUNCTIONS' 'PROTO' 'FORMATS') then
do; /* footer, for multiple functions in one FCMP run, one PROTO run, or one FORMAT run */
put "run; ";
end;
if 1 = LAST.type and upcase(type)='IMLMODULE' then /* footer, for IML modules */
do;
put "reset storage = WORK.&packageName.IML; "; /* set the storage location for modules */
put "store module = _ALL_; "; /* and store all created modules */
put "quit; ";
end;
isFunction + (upcase(type)=:'FUNCTION');
isFormat + (upcase(type)=:'FORMAT');
isProto + (upcase(type)=:'PROTO');
/* HEADERS for IML, FCMP, and PROTO - start */
if 1 = isFunction and upcase(type)=:'FUNCTION' then
do;
/* macro variable for test if cherry picking used FCMP */
put 'data _null_; ';
put ' call symputX("cherryPick_FCMP", 0, "L"); ';
put 'run; ';
end;
if 1 = FIRST.type and upcase(type)='FUNCTIONS' then
do;
/* header for multiple functions in one FCMP run */
put "proc fcmp outlib = work.%lowcase(&packageName.fcmp).package ; ";
end;
if 1 = FIRST.type and upcase(type)='PROTO' then
do;
/* macro variable for test if cherry picking used PROTO */
put 'data _null_; ';
put ' call symputX("cherryPick_PROTO", 0, "L"); ';
put 'run; ';
/* header for multiple functions in one PROTO run */
put "proc proto package = work.%lowcase(&packageName.proto).package ; ";
end;
if 1 = FIRST.type and upcase(type)='IMLMODULE' then
do;
/* macro variable for test if cherry picking used IML */
put 'data _null_; ';
put ' call symputX("cherryPick_IML", 0, "L"); ';
put 'run; ';
/* header, for IML modules */
put "proc iml ; ";
end;
if 1 = FIRST.type and upcase(type)='FORMATS' then
do;
/* header, for FORMATS */
put "proc format lib = work.%lowcase(&packageName.format) ; ";
end;
if 1 = isFormat and upcase(type)=:'FORMAT' then
do;
/* macro variable for test if cherry picking used FORMAT */
put 'data _null_; ';
put ' call symputX("cherryPick_FORMAT", 0, "L"); ';
put 'run; ';
end;
/* HEADERS for IML, FCMP, and PROTO - end */
put ' ' /
'%if (%str(*)=%superq(cherryPick)) or (' fileshort +(-1) ' in %superq(cherryPick)) %then %do; '; /* Cherry Pick test1 start */
put ' %put NOTE- ;';
put ' %put NOTE: >> Element of type ' type 'from the file "' file +(-1) '" will be included <<;';
if upcase(type)=:'MACRO' then
put ' %put %sysfunc(ifc(%SYSMACEXIST(' fileshort +(-1) ')=1, NOTE# Macro ' fileshort
"exist. It will be overwritten by the macro from the &packageName. package, ));";
if upcase(type)=:'EXEC' then
do;
put ' %put NOTE- ;';
put ' %put NOTE- Executing the following code: ;';
put ' %put NOTE- *****************************;';
put ' data _null_;';
put " infile &_PackageFileref_.(_" folder +(-1) "." file +(-1) ') lrecl=32767;';
put ' input;';
put ' putlog "*> " _infile_;';
put ' run;';
put ' %put NOTE- *****************************;';
put ' %put NOTE- ;';
end;
/* include the file with the code of the element */
put ' %include' " &_PackageFileref_.(_" folder +(-1) "." file +(-1) ') / nosource2;';
if upcase(type)=:'IMLMODULE' then
put ' %let cherryPick_IML = %eval(&cherryPick_IML. + 1);';
if upcase(type)=:'FUNCTION' then
put ' %let cherryPick_FCMP = %eval(&cherryPick_FCMP. + 1);';
if upcase(type)=:'PROTO' then
put ' %let cherryPick_PROTO = %eval(&cherryPick_PROTO. + 1);';
if upcase(type)=:'FORMAT' then
put ' %let cherryPick_FORMAT = %eval(&cherryPick_FORMAT. + 1);';
put '%end; ' /; /* Cherry Pick test1 end */
/* FOOTERS for IML, FCMP, and PROTO - start */
if 1 = LAST.type and upcase(type) in ('FUNCTIONS' 'PROTO' 'FORMATS') then
do; /* footer, for multiple functions in one FCMP run, one PROTO run, or one FORMAT run */
put "run; " / ;
end;
if 1 = LAST.type and upcase(type)='IMLMODULE' then /* footer, for IML modules */
do;
put '%if 0 < &cherryPick_IML. %then %do; ' /
"reset storage = WORK.&packageName.IML; " / /* set the storage location for modules */
"store module = _ALL_; " / /* and store all created modules */
'%end; ' /
"quit; " / ;
end;
/* FOOTERS for IML, FCMP, and PROTO - end */
/* add the link to the functions dataset, only for the first occurrence */
if 1 = isFunction and (upcase(type)=:'FUNCTION') then
do;
put "options APPEND=(cmplib = work.%lowcase(&packageName.fcmp));";
put '%put NOTE- ;';
put '%put NOTE:[CMPLIB] %sysfunc(getoption(cmplib));' /;
put "options APPEND=(cmplib = work.%lowcase(&packageName.fcmp));"/;
end;
/* add the link to the proto functions dataset, only for the first occurrence */
if 1 = isProto and (upcase(type)=:'PROTO') then
do;
put "options APPEND=(cmplib = work.%lowcase(&packageName.proto));";
put '%put NOTE- ;';
put '%put NOTE:[CMPLIB] %sysfunc(getoption(cmplib));' /;
put "options APPEND=(cmplib = work.%lowcase(&packageName.proto));"/;
end;
/* add the link to the formats catalog, only for the first occurrence */
if 1 = isFormat and (upcase(type)=:'FORMAT') then
do;
put "options INSERT=( fmtsearch = work.%lowcase(&packageName.format) );";
put '%put NOTE- ;';
put '%put NOTE:[FMTSEARCH] %sysfunc(getoption(fmtsearch));'/;
put "options INSERT=(fmtsearch = work.%lowcase(&packageName.format));"/;
end;
end;
end; /* loopOverTypes - start */
/* cherry pick clean in cmplib for functions */
if isFunction then
do;
put '%if 0 = &cherryPick_FCMP. %then %do;';
put 'options cmplib = (%unquote(%sysfunc(tranwrd(' /
'%lowcase(%sysfunc(getoption(cmplib)))' /
',%str(' "work.%lowcase(&packageName.fcmp)" '), %str() ))));';
put 'options cmplib = (%unquote(%sysfunc(compress(' /
'%sysfunc(getoption(cmplib))' /
',%str(()) ))));';
put '%end;';
end;
/* cherry pick clean in cmplib for proto */
if isProto then
do;
put '%if 0 = &cherryPick_PROTO. %then %do;';
put 'options cmplib = (%unquote(%sysfunc(tranwrd(' /
'%lowcase(%sysfunc(getoption(cmplib)))' /
',%str(' "work.%lowcase(&packageName.proto)" '), %str() ))));';
put 'options cmplib = (%unquote(%sysfunc(compress(' /
'%sysfunc(getoption(cmplib))' /
',%str(()) ))));';
put "proc delete data=work.%lowcase(&packageName.proto); run;";
put '%end;';
end;
/* list cmplib for functions */
if isFunction OR isProto then
do;
put '%put NOTE- ;';
put '%put NOTE:[CMPLIB] %sysfunc(getoption(cmplib));' /;
end;
/* list fmtsearch for formats */
if isFormat then
do;
put '%if 0 = &cherryPick_FCMP. %then %do;';
put 'options fmtsearch = (%unquote(%sysfunc(tranwrd(' /
'%lowcase(%sysfunc(getoption(fmtsearch)))' /
',%str(' "work.%lowcase(&packageName.)format" '), %str() ))));';
put 'options fmtsearch = (%unquote(%sysfunc(compress(' /
'%sysfunc(getoption(fmtsearch))' /
', %str(()) ))));';
put '%end;';
put '%put NOTE- ;';
put '%put NOTE:[FMTSEARCH] %sysfunc(getoption(fmtsearch));'/;
end;
/* update SYSloadedPackages global macrovariable */
put '%if (%str(*)=%superq(cherryPick)) %then %do; '; /* Cherry Pick test3 start */
put ' data _null_ ; ';
put ' length SYSloadedPackages stringPCKG $ 32767; ';
put ' if SYMEXIST("SYSloadedPackages") = 1 and SYMGLOBL("SYSloadedPackages") = 1 then ';
@@ -1237,8 +1353,9 @@ data _null_;
put " put 'NOTE: SYSloadedPackages = &packageName.(&packageVersion.)'; ";
put ' end; ';
put ' stop; ';
put 'run; ' / ;
put ' run; ';
put '%end; ' / ; /* Cherry Pick test3 end */
put '%put NOTE- ;';
put '%put NOTE: '"Loading package &packageName., version &packageVersion., license &packageLicense.;";
put '%put NOTE- *** END ***;' /;
@@ -1282,7 +1399,7 @@ data _null_;
put 'do;';
put ' put "NOTE- Dataset ' fileshort 'from the file ""' file +(-1) '"" will be loaded";';
put ' call execute(''%nrstr(%include' " &_PackageFileref_.(_" folder +(-1) "." file +(-1) ') / nosource2;)'');';
put 'end;';
put 'end;' /;
end;
/* use lazyData to reload data type */
if ( upcase(type) =: 'DATA' ) then
@@ -1291,7 +1408,7 @@ data _null_;
put 'do;';
put ' put "NOTE- Dataset ' fileshort 'from the file ""' file +(-1) '"" will be loaded";';
put ' call execute(''%nrstr(%include' " &_PackageFileref_.(_" folder +(-1) "." file +(-1) ') / nosource2;)'');';
put 'end;';
put 'end;' /;
end;
end;
@@ -1335,7 +1452,7 @@ data _null_;
put ' putlog "*> " _infile_;';
put 'run;' /;
put '%put NOTE- *****************************;';
put '%put NOTE- ;' /;
put '%put NOTE- ;';
put '%include' " &_PackageFileref_.(_" folder +(-1) "." file +(-1) ') / nosource2;' /;
end;
@@ -1354,8 +1471,8 @@ data _null_;
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- ;';
put ',"' fileshort upcase32. '"' /;
end;
/**/
put ' )';
@@ -1372,8 +1489,8 @@ data _null_;
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- ;';
put ',"' fileshort upcase32. '"' /;
isFormat + 1;
end;
put ' )';
@@ -1445,8 +1562,8 @@ data _null_;
set &filesWithCodes. end = EOF;
if not (upcase(type)=:'FUNCTION') then continue;
put '%put NOTE- Element of type ' type 'generated from the file "' file +(-1) '" will be deleted;';
put '%put NOTE- ;' /;
put 'deletefunc ' fileshort ';';
put '%put NOTE- ;';
put 'deletefunc ' fileshort ';' /;
isFunction + 1;
end;
put "run;" /;
@@ -1486,8 +1603,8 @@ data _null_;
set &filesWithCodes. end = EOF;
if not (upcase(type)=:'DATA') then continue;
put '%put NOTE- Element of type ' type 'generated from the file "' file +(-1) '" will be deleted;';
put '%put NOTE- ;' /;
put 'drop table ' fileshort ';';
put '%put NOTE- ;';
put 'drop table ' fileshort ';' /;
end;
put "quit;" /;
@@ -1497,8 +1614,8 @@ data _null_;
set &filesWithCodes. end = EOF;
if not (upcase(type)=:'LIBNAME') then continue;
put '%put NOTE- Element of type ' type 'generated from the file "' file +(-1) '" will be cleared;';
put '%put NOTE- ;' /;
put 'libname ' fileshort ' clear;';
put '%put NOTE- ;';
put 'libname ' fileshort ' clear;' /;
end;
put "run;" /;
@@ -1515,12 +1632,12 @@ data _null_;
put ' put '' %unloadPackage( '' name ")" ; ';
put ' end ; ';
put ' put "NOTE-" / "NOTE-"; stop; ';
put 'run; ';
put 'run; ' /;
%end;
/* update SYSloadedPackages global macrovariable */
put ' data _null_ ; ';
put 'data _null_ ; ';
put ' length SYSloadedPackages $ 32767; ';
put ' if SYMEXIST("SYSloadedPackages") = 1 and SYMGLOBL("SYSloadedPackages") = 1 then ';
put ' do; ';
@@ -1544,7 +1661,7 @@ data _null_;
put '%put NOTE: ' "Unloading package &packageName., version &packageVersion., license &packageLicense.;";
put '%put NOTE- *** END ***;';
put '%put NOTE- ;';
put '%put NOTE- ;' /;
put "/* unload.sas end */";
stop;
@@ -1742,7 +1859,7 @@ data _null_;
put ' end ; ';
%end;
put 'put "***"; put "* SAS package generated by generatePackage, version 20221112 *"; put "***";';
put 'put "***"; put "* SAS package generated by generatePackage, version 20221121 *"; put "***";';
put 'run; ' /;