mirror of
https://github.com/yabwon/SAS_PACKAGES.git
synced 2026-01-14 18:10:06 +00:00
SAS Packages Framework, version 20251231
SAS Packages Framework, version 20251231
Changes:
- New macro: %bundlePackages().
- New macro: %unbundlePackages().
- Bug fix in %verifyPackage() macro.
- Minor updates in %generatePackage(), %listPackages(), and %relocatePackage() macros.
- Documentation updated.
This commit is contained in:
391
SPF/Macros/unbundlePackages.sas
Normal file
391
SPF/Macros/unbundlePackages.sas
Normal file
@@ -0,0 +1,391 @@
|
||||
/*+unbundlePackages+*/
|
||||
%macro unbundlePackages(
|
||||
bundleName
|
||||
,path=
|
||||
,pathRef=
|
||||
,packagesPath=
|
||||
,packagesRef=packages
|
||||
,ods= /* data set for report file */
|
||||
,verify=0
|
||||
)/
|
||||
des='Macro to extract a bundle of SAS packages, version 20251231. Run %unbundlePackages(HELP) for help info.'
|
||||
secure
|
||||
minoperator
|
||||
;
|
||||
|
||||
%if (%superq(bundleName) = ) OR (%qupcase(&bundleName.) = HELP) %then
|
||||
%do;
|
||||
%local options_tmp ;
|
||||
%let options_tmp = ls=%sysfunc(getoption(ls)) ps=%sysfunc(getoption(ps))
|
||||
%sysfunc(getoption(notes)) %sysfunc(getoption(source))
|
||||
msglevel=%sysfunc(getoption(msglevel))
|
||||
;
|
||||
options NOnotes NOsource ls=MAX ps=MAX msglevel=N;
|
||||
%put ;
|
||||
%put #################################################################################;
|
||||
%put ### This is short help information for the `unbundlePackages` macro #;
|
||||
%put #-------------------------------------------------------------------------------#;
|
||||
%put # #;
|
||||
%put # Macro to *extract* SAS packages from a bundle, version `20251231` #;
|
||||
%put # #;
|
||||
%put # A SAS package is a zip file containing a group #;
|
||||
%put # of SAS codes (macros, functions, data steps generating #;
|
||||
%put # data, etc.) wrapped up together and included by #;
|
||||
%put # a single `load.sas` file (also embedded inside the zip). #;
|
||||
%put # #;
|
||||
%put # The `%nrstr(%%unbundlePackages())` macro allows to extract SAS packages from #;
|
||||
%put # a bundle into a single directory. #;
|
||||
%put # #;
|
||||
%put #-------------------------------------------------------------------------------#;
|
||||
%put #### Parameters: #;
|
||||
%put # #;
|
||||
%put # 1. `bundleName` *Required.* Name of a bundle, e.g. myBundle, #;
|
||||
%put # extension `.bundle.zip` is automatically added. #;
|
||||
%put # For empty value or `HELP` this help information #;
|
||||
%put # is displayed. #;
|
||||
%put # #;
|
||||
%put # - `path=` *Required.* Location of the bundle. Must be a valid #;
|
||||
%put # directory. Takes precedence over `pathRef` parameter. #;
|
||||
%put # Either `path=` or `pathRef=` must be non-empty. #;
|
||||
%put # #;
|
||||
%put # - `pathRef=` *Optional.* Fileref to location of the bundle. #;
|
||||
%put # Either `path=` or `pathRef=` must be non-empty. #;
|
||||
%put # #;
|
||||
%put # - `packagesPath=` *Optional.* Location for packages extracted from #;
|
||||
%put # the bundle. Takes precedence over `packagesRef`. #;
|
||||
%put # When non-empty, must be a valid directory. #;
|
||||
%put # #;
|
||||
%put # - `packagesRef=` *Optional.* Fileref to location where packages will #;
|
||||
%put # be extracted. Default value is `packages`. #;
|
||||
%put # #;
|
||||
%put # - `ods=` *Optional.* Name of SAS data set for the report. #;
|
||||
%put # #;
|
||||
%put # - `verify=` *Optional.* Indicates if verification code should #;
|
||||
%put # be executed after bundle extraction. #;
|
||||
%put # Value `1` means yes, Value `0` means no. #;
|
||||
%put # #;
|
||||
%put #-------------------------------------------------------------------------------#;
|
||||
%put # #;
|
||||
%put # Visit: `https://github.com/yabwon/SAS_PACKAGES/tree/main/SPF/Documentation` #;
|
||||
%put # to learn more. #;
|
||||
%put # Tutorials available at: `https://github.com/yabwon/HoW-SASPackages` #;
|
||||
%put # #;
|
||||
%put ### Example 1 ###################################################################;
|
||||
%put # #;
|
||||
%put # Enabling the SAS Package Framework #;
|
||||
%put # from the local directory and extract a bundle of #;
|
||||
%put # packages from user home directory to packages. #;
|
||||
%put # #;
|
||||
%put # Assume that the `SPFinit.sas` file #;
|
||||
%put # is located in the "/sas/PACKAGES/" folder. #;
|
||||
%put # #;
|
||||
%put # Run the following code in your SAS session: #;
|
||||
%put ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas;
|
||||
%put %nrstr( filename packages "/sas/PACKAGES/"; %%* setup a directory for packages;);
|
||||
%put %nrstr( %%include packages(SPFinit.sas); %%* enable the framework; );
|
||||
%put ;
|
||||
%put %nrstr( %%unbundlePackages%(myLittleBundle );
|
||||
%put %nrstr( ,path=/home/user/bundles );
|
||||
%put %nrstr( ,verify=1 );
|
||||
%put %nrstr( ,packagesRef=PACKAGES%) );
|
||||
%put ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
|
||||
%put # #;
|
||||
%put #################################################################################;
|
||||
%put ;
|
||||
options &options_tmp.;
|
||||
%GOTO ENDofunbundlePackages;
|
||||
%end;
|
||||
/* local variables for options */
|
||||
%local ls_tmp ps_tmp notes_tmp source_tmp stimer_tmp fullstimer_tmp msglevel_tmp mautocomploc_tmp;
|
||||
%let ls_tmp = %sysfunc(getoption(ls));
|
||||
%let ps_tmp = %sysfunc(getoption(ps));
|
||||
%let notes_tmp = %sysfunc(getoption(notes));
|
||||
%let source_tmp = %sysfunc(getoption(source));
|
||||
%let stimer_tmp = %sysfunc(getoption(stimer));
|
||||
%let fullstimer_tmp = %sysfunc(getoption(fullstimer));
|
||||
%let msglevel_tmp = %sysfunc(getoption(msglevel));
|
||||
%let mautocomploc_tmp = %sysfunc(getoption(mautocomploc));
|
||||
|
||||
options NOnotes NOsource ls=128 ps=MAX NOfullstimer NOstimer msglevel=N NOmautocomploc;
|
||||
/*===================================================================================================*/
|
||||
|
||||
%if NOT(%superq(verify) in (0 1)) %then %let verify=0;
|
||||
|
||||
%local HASHING_FILE_exist;
|
||||
%let HASHING_FILE_exist = 0;
|
||||
|
||||
%if %sysfunc(exist(sashelp.vfunc, VIEW)) %then
|
||||
%do;
|
||||
data _null_;
|
||||
set sashelp.vfunc(keep=fncname);
|
||||
where fncname = "HASHING_FILE";
|
||||
call symputX('HASHING_FILE_exist', 1, "L");
|
||||
stop;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%local reportFile datetime;
|
||||
%let datetime = %sysfunc(datetime());
|
||||
%let reportFile = WORK.tmpbundlefile%sysfunc(int(&datetime.), b8601dt15.)_;
|
||||
|
||||
data _null_ ;
|
||||
datetime=symgetn('datetime');
|
||||
|
||||
length packagesList $ 32767 bundleName $ 128;
|
||||
|
||||
bundleName = compress(symget('bundleName'),"_.","KAD"); /* bundle name is letters, digits, and underscore, up to 128 symbols */
|
||||
|
||||
if bundleName NE symget('bundleName') then /* warn about illegal characters */
|
||||
do;
|
||||
put "ERROR: Bundle name contains illegal characters. Exiting";
|
||||
stop;
|
||||
end;
|
||||
|
||||
bundleName=lowcase(bundleName);
|
||||
/* if there is ".bundle.zip" extension added, remove it */
|
||||
if substr(strip(reverse(bundleName)),1,11) = 'piz.eldnub.' then bundleName=scan(bundleName,-3,".");
|
||||
else /* if there is ".bundle" extension added, remove it */
|
||||
if substr(strip(reverse(bundleName)),1,7) = 'eldnub.' then bundleName=scan(bundleName,-2,".");
|
||||
|
||||
put / "INFO: Bundle name is: " bundleName / ;
|
||||
|
||||
length packagesPath $ 32767 packagesRef $ 8;
|
||||
packagesPath = dequote(symget('packagesPath'));
|
||||
packagesRef = upcase(strip(symget('packagesRef')));
|
||||
|
||||
|
||||
/* organize target path (location for packages) */
|
||||
if " "=packagesPath then
|
||||
do;
|
||||
if 0 then set SASHELP.VEXTFL(keep=level xpath xengine fileref exists);
|
||||
DECLARE HASH sH(dataset:'SASHELP.VEXTFL(where=(fileref=' !! quote(packagesRef) !! '))', ordered: "A");
|
||||
sH.DefineKey("level");
|
||||
sH.DefineData("xpath","xengine","exists");
|
||||
sH.DefineDone();
|
||||
|
||||
if sH.NUM_ITEMS=0 then
|
||||
do;
|
||||
put "ERROR: Fileref in packagesRef= does NOT exist. Exiting!";
|
||||
stop;
|
||||
end;
|
||||
|
||||
packagesPath=" ";
|
||||
|
||||
rc = sH.FIND(key:NOT(1=sH.NUM_ITEMS)); /* if only 1 element select level 0, if more than 1 select level 1 */
|
||||
if xengine = "DISK" AND exists='yes' then
|
||||
packagesPath=quote(strip(xpath)); /* add quotes to the packagesPath */
|
||||
else
|
||||
do;
|
||||
put "ERROR: Path: " xpath "in packagesRef= is invalid! Exiting!";
|
||||
stop;
|
||||
end;
|
||||
end;
|
||||
else
|
||||
do;
|
||||
rcPckPath = fileexist(strip(packagesPath));
|
||||
if 0=rcPckPath then
|
||||
do;
|
||||
put "ERROR: Path in packagesPath= does NOT exist. Exiting!";
|
||||
stop;
|
||||
end;
|
||||
else packagesPath=quote(strip(packagesPath)); /* add quotes to the packagesPath */
|
||||
end;
|
||||
|
||||
length path $ 32767 pathRef $ 8;
|
||||
path = dequote(symget('path'));
|
||||
pathRef = upcase(strip(symget('pathRef')));
|
||||
|
||||
if " "=path and " "=pathRef then
|
||||
do;
|
||||
put "ERROR: Path= and pathRef= are empty! Exiting!";
|
||||
stop;
|
||||
end;
|
||||
|
||||
/* verify source path (location of the bundle) */
|
||||
if " "=path then
|
||||
do;
|
||||
DECLARE HASH tH(dataset:'SASHELP.VEXTFL(where=(fileref=' !! quote(pathRef) !! '))', ordered: "A");
|
||||
tH.DefineKey("level");
|
||||
tH.DefineData("xpath","xengine","exists");
|
||||
tH.DefineDone();
|
||||
DECLARE HITER tI("tH");
|
||||
|
||||
do while (tI.next()=0);
|
||||
put "Checking in: " xpath;
|
||||
if fileexist(cats(xpath,"/",bundleName,'.bundle.zip')) then
|
||||
do;
|
||||
path=strip(xpath);
|
||||
put "INFO: Bundle file " bundleName +(-1) ".bundle.zip found under: " xpath;
|
||||
leave;
|
||||
end;
|
||||
end;
|
||||
|
||||
if " "=path then
|
||||
do;
|
||||
put "ERROR: Bundle: " bundleName "does NOT exist in any directory in pathRef=. Exiting!";
|
||||
stop;
|
||||
end;
|
||||
end;
|
||||
else
|
||||
do;
|
||||
rcPath = fileexist(strip(path));
|
||||
if 0=rcPath then
|
||||
do;
|
||||
put "ERROR: Path in Path= does NOT exist. Exiting!";
|
||||
stop;
|
||||
end;
|
||||
end;
|
||||
|
||||
/* get the list of packages to unbundle from bundlecontent.sas */
|
||||
length bundlecontentFR $ 8;
|
||||
rc1 = filename(bundlecontentFR, cats(path,"/",bundleName,'.bundle.zip'));
|
||||
rcE = fexist(bundlecontentFR);
|
||||
rc2 = filename(bundlecontentFR);
|
||||
|
||||
if 0=rcE then
|
||||
do;
|
||||
put "ERROR: The " bundleName "file does NOT exist!. Exiting!";
|
||||
stop;
|
||||
end;
|
||||
|
||||
length bundlecontentFR $ 8;
|
||||
rc1 = filename(bundlecontentFR, cats(path,"/",bundleName,'.bundle.zip'), 'zip', 'member="bundlecontent.sas"');
|
||||
rcE = fexist(bundlecontentFR);
|
||||
rc2 = filename(bundlecontentFR);
|
||||
|
||||
if 0=rcE then
|
||||
do;
|
||||
put "ERROR: The bundlecontent.sas file does NOT exist inside bundle. Exiting!";
|
||||
stop;
|
||||
end;
|
||||
|
||||
length bundlecontentfile $ 1024;
|
||||
bundlecontentfile = cats(path,"/",bundleName,'.bundle.zip');
|
||||
|
||||
infile _DUMMY_ ZIP FILEVAR=bundlecontentfile member="bundlecontent.sas" end=EOF dlm=",";
|
||||
|
||||
DECLARE HASH Q(ordered:"A");
|
||||
Q.defineKey("package");
|
||||
Q.defineData("package",'pckVer','pckDtm','hash');
|
||||
Q.defineDone();
|
||||
DECLARE HITER IQ("Q");
|
||||
|
||||
/*--------------------------------------------------*/
|
||||
do until(EOF);
|
||||
input package :$32. pckVer :$16. pckDtm :$16. hash :$128.;
|
||||
if " " NE package then rc = Q.ADD();
|
||||
end;
|
||||
label package="Package name"
|
||||
pckVer="Version"
|
||||
pckDtm="Generation timestamp"
|
||||
hash="SHA256 for the Package";
|
||||
/*--------------------------------------------------*/
|
||||
|
||||
|
||||
if 0=Q.NUM_ITEMS then /* ... if empty then exit */
|
||||
do;
|
||||
put "WARNING: No packages to unbundle. Exiting!";
|
||||
stop;
|
||||
end;
|
||||
else
|
||||
do while(iQ.next()=0);
|
||||
packagesList = catx(" ", packagesList, package);
|
||||
end;
|
||||
|
||||
rc = Q.output(dataset:"&reportFile.1");
|
||||
|
||||
/* code executed for unbundling */
|
||||
length code1 code2 $ 32767;
|
||||
code1=
|
||||
'options ps=min nofullstimer nostimer msglevel=N; filename PACKAGES ' !! strip(packagesPath) !! ';' !!
|
||||
'%relocatePackage(' !! strip(packagesList) !! ',source=' !! catx("/", path, bundleName) !!
|
||||
'.bundle.zip, sDevice=ZIP,psMAX=MIN)';
|
||||
|
||||
/*put code=;*/
|
||||
|
||||
put / "INFO: The " bundleName "bundle extraction in progress...";
|
||||
|
||||
rc = doSubL(code1);
|
||||
|
||||
put / "INFO: The " bundleName "bundle extraction ended.";
|
||||
|
||||
/* code executed for verification */
|
||||
%if 1=&verify. %then
|
||||
%do;
|
||||
put / "INFO: The " bundleName "bundle verification in progress...";
|
||||
code2=
|
||||
'options ps=min nofullstimer nostimer msglevel=N; filename PACKAGES ' !! strip(packagesPath) !! ';' !!
|
||||
'filename _ ZIP ' !! quote(cats(path, "/", bundleName, ".bundle.zip")) !! ';' !!
|
||||
'%include _(verification.sas);%listPackages()';
|
||||
rc = doSubL(code2);
|
||||
put / "INFO: The " bundleName "bundle verification ended.";
|
||||
%end;
|
||||
|
||||
put " ";
|
||||
rc=sleep(1,1);
|
||||
|
||||
rc = doSubL("title 'Summary of the extracted bundle file';" !!
|
||||
"proc print data=" !!
|
||||
%if %superq(ods) NE %then
|
||||
%do; "%scan(&ods.,1,())" %end;
|
||||
%else
|
||||
%do; "&reportFile.1" %end; !!
|
||||
" label; var package pckVer pckDtm hash; run;" !!
|
||||
%if %superq(ods) NE %then
|
||||
%do; %put INFO: Report file: %scan(&ods.,1,()); %end;
|
||||
%else
|
||||
%do; "proc delete data=&reportFile.1; run;" %end; !!
|
||||
"title;");
|
||||
|
||||
stop;
|
||||
run;
|
||||
/*===================================================================================================*/
|
||||
/* restore optionos */
|
||||
options ls = &ls_tmp. ps = &ps_tmp.
|
||||
¬es_tmp. &source_tmp.
|
||||
&stimer_tmp. &fullstimer_tmp.
|
||||
msglevel=&msglevel_tmp. &mautocomploc_tmp.;
|
||||
|
||||
%ENDofunbundlePackages:
|
||||
%mend unbundlePackages;
|
||||
|
||||
/*
|
||||
options mprint;
|
||||
%unbundlePackages(
|
||||
bundlenametest123
|
||||
,path=R:\
|
||||
,packagesPath=R:\check
|
||||
,verify=1
|
||||
)
|
||||
|
||||
%unbundlePackages(
|
||||
bundlenametest124
|
||||
,path=R:\
|
||||
,packagesPath=R:\check2
|
||||
,verify=1
|
||||
)
|
||||
|
||||
%unbundlePackages(
|
||||
bundlenametest124.bundle.zip
|
||||
,path=R:\
|
||||
,packagesPath=R:\check3
|
||||
)
|
||||
|
||||
%unbundlePackages(
|
||||
bundlenametest124.bundle.zip
|
||||
,path=R:\
|
||||
,packagesPath=R:\check4
|
||||
)
|
||||
|
||||
%unbundlePackages()
|
||||
|
||||
%unbundlePackages(
|
||||
nobundlenametest123
|
||||
,path=R:\
|
||||
,packagesPath=R:\check
|
||||
,verify=1
|
||||
)
|
||||
|
||||
*/
|
||||
|
||||
/* end of SPFinit.sas file */
|
||||
Reference in New Issue
Block a user