From c10e517fe3670b6b7c9344d9779627f5b1491ed8 Mon Sep 17 00:00:00 2001 From: yabwon Date: Mon, 4 Nov 2019 20:26:44 +0100 Subject: [PATCH] loading required packages is now available loading required packages is now available sience each package has its own unique file reference by MD5(packageName). --- dfa.zip | Bin 27120 -> 27159 bytes dynmacroarray.zip | Bin 10222 -> 10261 bytes generatePackage.sas | 81 ++- generatePackage.sas_bckp | 1096 ++++++++++++++++++++++++++++++++++++++ loadpackage.sas | 50 +- loadpackage.sas_bckp | 228 ++++++++ macroarray.zip | Bin 23406 -> 23441 bytes sqlinds.zip | Bin 9251 -> 9288 bytes 8 files changed, 1413 insertions(+), 42 deletions(-) create mode 100644 generatePackage.sas_bckp create mode 100644 loadpackage.sas_bckp diff --git a/dfa.zip b/dfa.zip index f8fde6bf01ce8d388bd5655c73ced6b2ccd0b391..1c566e96b7b82d93c9a361e9b8edafbdeba54320 100644 GIT binary patch delta 4875 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 HbdCN8akcgV delta 4820 zcmZWtXD}RG)ZSe!cJ;b?@3DvweGw%}R!Kzk8g{YDBD&Q}5M^~DdWjGvdekVXbth)IkVy3d2 ztyx}$>vB)1fhRmkyLqn+XwwUv1;l`H^Wr@ZZG`hz>fy*kLJo^Q zb%J>bmJwsd#}@ApW!%rcK4b3+5_iq8WW9qD7Hu238a-sTp9w<4x&b&TvZzxUf{S9u zm^ibjD5YO(USz@=U6?*PDFBVukY3cXfC>Y|HS;=OeI9 z>;voI?N{##o=1q;k9%_(;le_o?gnH5{obWUIP2WC3FqLvqf4FLU; gATf9nF+** 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 delta 3896 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 Pm5NyBf#8{N{f7J>ADt^3 diff --git a/generatePackage.sas b/generatePackage.sas index 9e00887..6f14d9a 100644 --- a/generatePackage.sas +++ b/generatePackage.sas @@ -1,4 +1,4 @@ -/*** HELP START ***/ +/*** HELP START ***/ /**############################################################################**/ /* */ @@ -52,7 +52,7 @@ filesLocation=%sysfunc(pathname(work))/%lowcase(&packageName.) )/secure; /*** HELP END ***/ -%local zipReferrence filesWithCodes _DESCR_ _LIC_ _RC_; +%local zipReferrence filesWithCodes _DESCR_ _LIC_ _RC_ _PackageFileref_; %let zipReferrence = _%sysfunc(datetime(), hex6.)_; %let filesWithCodes = WORK._%sysfunc(datetime(), hex16.)_; %let _DESCR_ = _%sysfunc(datetime(), hex6.)d; @@ -153,6 +153,12 @@ filename &_LIC_. "&filesLocation./license.sas" lrecl = 256; %abort; %end; +/* generate package fileref with MD5 to allow + different file reference for each package + while loading package with %loadPackage() macro + */ +%let _PackageFileref_ = P%sysfunc(MD5(%lowcase(&packageName.)),hex7.); + /* test if version is a number */ data _null_; version = input("&packageVersion.", ?? best32.); @@ -339,6 +345,7 @@ title2 "User: &SYSUSERID., datetime: %qsysfunc(datetime(), datetime21.), SAS ver title3 "Package's encoding: '&packageEncoding.', session's encoding: '&SYSENCODING.'."; title4 " ______________________________ "; title5 "List of files for package: &packageName. (version &packageVersion.), license: &packageLicense."; +title6 "MD5 hashed fileref of package lowcase name: &_PackageFileref_."; proc print data = &filesWithCodes.(drop=base); run; title; @@ -429,7 +436,7 @@ data _null_; file &zipReferrence.(load.sas) lrecl=32767; - put 'filename package list;' /; + put "filename &_PackageFileref_. list;" /; put ' %put NOTE- ;'; put ' %put NOTE: ' @; put "Loading package &packageName., version &packageVersion., license &packageLicense.; "; put ' %put NOTE: ' @; put "*** &packageTitle. ***; "; @@ -441,7 +448,7 @@ data _null_; put ' %put NOTE- ;'; put ' %put NOTE- *** START ***; ' /; - put '%include package(packagemetadata.sas) / nosource2;' /; /* <- copied also to loadPackage macro */ + put '%include ' " &_PackageFileref_.(packagemetadata.sas) / nosource2; " /; /* <- copied also to loadPackage macro */ isFunction = 0; isFormat = 0; @@ -517,10 +524,44 @@ data _null_; %if %bquote(&packageReqPackages.) ne %then %do; - put ' data _null_ ; '; - put ' length req name $ 64 SYSloadedPackages $ 32767; '; + length packageReqPackages $ 32767; packageReqPackages = lowcase(symget('packageReqPackages')); + /* try to load required packages */ + put 'data _null_ ; '; + put ' length req name $ 64 SYSloadedPackages $ 32767; '; + put ' if SYMEXIST("SYSloadedPackages") = 1 and SYMGLOBL("SYSloadedPackages") = 1 then '; + put ' do; '; + put ' do until(EOF); '; + put ' set sashelp.vmacro(where=(scope="GLOBAL" and name="SYSLOADEDPACKAGES")) end=EOF; '; + put ' substr(SYSloadedPackages, 1+offset, 200) = value; '; + put ' end; '; + put ' end; '; + put ' SYSloadedPackages = lowcase(SYSloadedPackages); '; + + put ' declare hash LP(); '; + put ' LP.defineKey("name"); '; + put ' LP.defineDone(); '; + put ' do _N_ = 1 to countw(SYSloadedPackages); '; + put ' name = scan(SYSloadedPackages, _N_); '; + put ' _RC_ = LP.add(); '; + put ' end; '; + + put ' do req = ' / packageReqPackages / ' ; '; + put ' name = lowcase(strip(scan(req, 1, " "))); '; + put ' vers = lowcase(compress(scan(req,-1, " "), ".", "KD")); '; + put ' if LP.check() ne 0 then '; + put ' do; '; + put ' put "NOTE: Trying to install required SAS package " req; '; + put ' call execute(cats(''%nrstr(%loadPackage('', name, ", requiredVersion = ", vers, "))")); '; + put ' end ; '; + put ' end ; '; + put ' stop; '; + put 'run; '; + + /* test if required packages are loaded */ + put 'data _null_ ; '; + put ' length req name $ 64 SYSloadedPackages $ 32767; '; put ' if SYMEXIST("SYSloadedPackages") = 1 and SYMGLOBL("SYSloadedPackages") = 1 then '; put ' do; '; put ' do until(EOF); '; @@ -544,8 +585,8 @@ data _null_; put ' if LP.check() ne 0 then '; put ' do; '; put ' missingPackagr = 1; '; - put ' put "ERROR: SAS package " req "is missing! " ; '; - put ' put ''ERROR- Try %loadPackage('' name ", requiredVersion = " vers ") to load it." ;'; + put ' put "ERROR: SAS package " req "is missing! Download it and" ; '; + put ' put ''ERROR- use %loadPackage('' name ", requiredVersion = " vers ") to load it." ;'; put ' end ; '; put ' end ; '; put ' if missingPackagr then call symputX("packageRequiredErrors", 1, "L"); '; @@ -557,8 +598,8 @@ data _null_; put ' do req = ' / packageReqPackages / ' ; '; put ' name = lowcase(strip(scan(req, 1, " "))); '; put ' vers = lowcase(compress(scan(req,-1, " "), ".", "KD")); '; - put ' put "ERROR: SAS package " req "is missing! " ; '; - put ' put ''ERROR- Try %loadPackage('' name ", requiredVersion = " vers ") to load it." ; '; + put ' put "ERROR: SAS package " req "is missing! Download it and" ; '; + put ' put ''ERROR- use %loadPackage('' name ", requiredVersion = " vers ") to load it." ; '; put ' end ; '; put ' end; '; put ' stop; '; @@ -592,7 +633,7 @@ data _null_; put '%put NOTE- Executing the following code: ;'; put '%put NOTE- *****************************;'; put 'data _null_;'; - put ' infile package(_' folder +(-1) "." file +(-1) ') lrecl=32767;'; + put " infile &_PackageFileref_.(_" folder +(-1) "." file +(-1) ') lrecl=32767;'; put ' input;'; put ' putlog "*> " _infile_;'; put 'run;' /; @@ -600,7 +641,7 @@ data _null_; put '%put NOTE- ;'; end; - put '%include package(_' folder +(-1) "." file +(-1) ') / nosource2;' /; + put '%include' " &_PackageFileref_.(_" folder +(-1) "." file +(-1) ') / nosource2;' /; isFunction + (upcase(type)=:'FUNCTION'); isFormat + (upcase(type)=:'FORMAT'); @@ -663,7 +704,7 @@ data _null_; file &zipReferrence.(unload.sas); - put 'filename package list;' /; + put "filename &_PackageFileref_. list;" /; put '%put NOTE: '"Unloading package &packageName., version &packageVersion., license &packageLicense.;"; put '%put NOTE- *** START ***;' /; @@ -677,14 +718,14 @@ data _null_; put '%put NOTE- Executing the following code: ;'; put '%put NOTE- *****************************;'; put 'data _null_;'; - put ' infile package(_' folder +(-1) "." file +(-1) ') lrecl=32767;'; + put " infile &_PackageFileref_.(_" folder +(-1) "." file +(-1) ') lrecl=32767;'; put ' input;'; put ' putlog "*> " _infile_;'; put 'run;' /; put '%put NOTE- *****************************;'; put '%put NOTE- ;' /; - put '%include package(_' folder +(-1) "." file +(-1) ') / nosource2;' /; + put '%include' " &_PackageFileref_.(_" folder +(-1) "." file +(-1) ') / nosource2;' /; end; /* delete macros and formats */ @@ -847,7 +888,7 @@ data _null_; file &zipReferrence.(help.sas); length strX $ 32767; - put 'filename package list;' /; + put "filename &_PackageFileref_. list;" /; put ' %put NOTE- ;'; put ' %put NOTE: '"Help for package &packageName., version &packageVersion., license &packageLicense.;"; put ' %put NOTE: ' @; put "*** &packageTitle. ***; "; @@ -864,12 +905,12 @@ data _null_; put '%let notes_tmp = %sysfunc(getoption(notes)); '; put '%let source_tmp = %sysfunc(getoption(source)); '; put 'options ls = MAX ps = MAX nonotes nosource; '; - put '%include package(packagemetadata.sas) / nosource2; ' /; + put '%include' " &_PackageFileref_.(packagemetadata.sas) / nosource2; " /; put 'data _null_; '; put ' if strip(symget("helpKeyword")) = " " then '; put ' do until (EOF); '; - put ' infile package(description.sas) end = EOF; '; + put " infile &_PackageFileref_.(description.sas) end = EOF; "; put ' input; '; put ' if upcase(strip(_infile_)) = "DESCRIPTION END:" then printer = 0; '; put ' if printer then put "*> " _infile_; '; @@ -882,7 +923,7 @@ data _null_; put 'data _null_; '; put ' if upcase(strip(symget("helpKeyword"))) = "LICENSE" then '; put ' do until (EOF); '; - put ' infile package(license.sas) end = EOF; '; + put " infile &_PackageFileref_.(license.sas) end = EOF; "; put ' input; '; put ' put "*> " _infile_; '; put ' end; '; @@ -937,7 +978,7 @@ data _null_; put ' memberX = cats("_",folder,".",file); '; /* inner datastep in call execute to read each embedded file */ put ' call execute("data _null_; ");'; - put ' call execute("infile package(" || strip(memberX) || ") end = EOF; ");'; + put " call execute('infile &_PackageFileref_.(' || strip(memberX) || ') end = EOF; ');"; put ' call execute(" printer = 0; ");'; put ' call execute(" do until(EOF); ");'; put ' call execute(" input; ");'; diff --git a/generatePackage.sas_bckp b/generatePackage.sas_bckp new file mode 100644 index 0000000..968095d --- /dev/null +++ b/generatePackage.sas_bckp @@ -0,0 +1,1096 @@ +/*** HELP START ***/ + +/**############################################################################**/ +/* */ +/* Copyright Bartosz Jablonski, September 2019. */ +/* */ +/* Code is free and open source. If you want - you can use it. */ +/* I tested it the best I could */ +/* but it comes with absolutely no warranty whatsoever. */ +/* If you cause any damage or something - it will be your own fault. */ +/* You've been warned! You are using it on your own risk. */ +/* However, if you decide to use it don't forget to mention author: */ +/* Bartosz Jablonski (yabwon@gmail.com) */ +/* */ +/* Here is the official version: */ +/* + Copyright (c) 2019 Bartosz Jablonski (yabwon@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ +/**#############################################################################**/ + +/* Macros to generate SAS packages */ +/* A SAS package is a zip file containing a group + of SAS codes (macros, functions, datasteps generating + data, etc.) wrapped up together and %INCLUDEed by + a single load.sas file (also embedded inside the zip). +*/ + +/*** HELP END ***/ + + +/*** HELP START ***/ +%macro GeneratePackge( + /* location of package files */ + filesLocation=%sysfunc(pathname(work))/%lowcase(&packageName.) +)/secure; +/*** HELP END ***/ +%local zipReferrence filesWithCodes _DESCR_ _LIC_ _RC_; +%let zipReferrence = _%sysfunc(datetime(), hex6.)_; +%let filesWithCodes = WORK._%sysfunc(datetime(), hex16.)_; +%let _DESCR_ = _%sysfunc(datetime(), hex6.)d; +%let _LIC_ = _%sysfunc(datetime(), hex6.)l; + +/* collect package metadata from the description.sas file */ +filename &_DESCR_. "&filesLocation./description.sas" lrecl = 256; +/* file contains licence */ +filename &_LIC_. "&filesLocation./license.sas" lrecl = 256; + +%if %sysfunc(fexist(&_DESCR_.)) %then + %do; + %put NOTE: Creating package%str(%')s metadata; + + %local packageName /* name of the package, required */ + packageVersion /* version of the package, required */ + packageTitle /* title of the package, required*/ + packageAuthor /* required */ + packageMaintainer /* required */ + packageEncoding /* required */ + packageLicense /* required */ + packageRequired /* optional */ + packageReqPackages /* optional */ + ; + data _null_; + infile &_DESCR_.; + input; + + select; + when(upcase(scan(_INFILE_, 1, ":")) = "PACKAGE") call symputX("packageName", scan(_INFILE_, 2, ":"),"L"); + when(upcase(scan(_INFILE_, 1, ":")) = "VERSION") call symputX("packageVersion", scan(_INFILE_, 2, ":"),"L"); + when(upcase(scan(_INFILE_, 1, ":")) = "AUTHOR") call symputX("packageAuthor", scan(_INFILE_, 2, ":"),"L"); + when(upcase(scan(_INFILE_, 1, ":")) = "MAINTAINER") call symputX("packageMaintainer", scan(_INFILE_, 2, ":"),"L"); + when(upcase(scan(_INFILE_, 1, ":")) = "TITLE") call symputX("packageTitle", scan(_INFILE_, 2, ":"),"L"); + when(upcase(scan(_INFILE_, 1, ":")) = "ENCODING") call symputX("packageEncoding", scan(_INFILE_, 2, ":"),"L"); + when(upcase(scan(_INFILE_, 1, ":")) = "LICENSE") call symputX("packageLicense", scan(_INFILE_, 2, ":"),"L"); + when(upcase(scan(_INFILE_, 1, ":")) = "REQUIRED") call symputX("packageRequired", scan(_INFILE_, 2, ":"),"L"); + when(upcase(scan(_INFILE_, 1, ":")) = "REQPACKAGES") call symputX("packageReqPackages", scan(_INFILE_, 2, ":"),"L"); + + /* stop at the beginning of description */ + when(upcase(scan(_INFILE_, 1, ":")) = "DESCRIPTION START") stop; + otherwise; + end; + run; + + /* test for required descriptors */ + %if (%nrbquote(&packageName.) = ) + or (%nrbquote(&packageVersion.) = ) + or (%nrbquote(&packageAuthor.) = ) + or (%nrbquote(&packageMaintainer.) = ) + or (%nrbquote(&packageTitle.) = ) + or (%nrbquote(&packageEncoding.) = ) + or (%nrbquote(&packageLicense.) = ) + %then + %do; + %put ERROR: At least one of descriptors is missing!; + %put ERROR- They are required to create package.; + %put ERROR- &=packageName.; + %put ERROR- &=packageTitle.; + %put ERROR- &=packageVersion.; + %put ERROR- &=packageAuthor.; + %put ERROR- &=packageMaintainer.; + %put ERROR- &=packageEncoding.; + %put ERROR- &=packageLicense.; + %put ERROR- ; + %abort; + %end; + /* test for package name */ + %if %sysfunc(lengthn(&packageName.)) > 24 %then + %do; + %put ERROR: Package name is more than 24 characters long.; + %put ERROR- The name is used for functions%str(%') dataset name; + %put ERROR- and for formats%str(%') cataloge name (with suffix).; + %put ERROR- The length is %sysfunc(lengthn(&packageName.)). Try something shorter.; + %abort; + %end; + %else %if %sysfunc(lengthn(&packageName.)) < 3 %then + %do; + %put WARNING: Package name is less than 3 characters.; + %put WARNING- Maybe consider some _meaningful_ name?; + %end; + /* test characters in package name */ + %if %qsysfunc(lengthn(%qsysfunc(compress(&packageName.,,KDF)))) NE %qsysfunc(lengthn(&packageName.)) %then + %do; + %put ERROR: Package name contains illegal symbols.; + %put ERROR- The name is used for functions%str(%') dataset name; + %put ERROR- and for formats%str(%') cataloge name.; + %put ERROR- Only English letters, underscore(_), and digits are allowed.; + %put ERROR- Try something else. Maybe: %qsysfunc(compress(&packageName.,,KDF)) will do?; + %abort; + %end; + + %end; +%else + %do; + %put ERROR: The description.sas file is missing!; + %put ERROR- The file is required to create package%str(%')s metadata; + %abort; + %end; + +/* test if version is a number */ +data _null_; + version = input("&packageVersion.", ?? best32.); + if not (version > 0) then + do; + put 'ERROR: Package version should be a positive NUMBER.'; + put 'ERROR- Current value is: ' "&packageVersion."; + put 'ERROR- Try something small, e.g. 0.1'; + put; + abort; + end; +run; + +/* create or replace the ZIP file for package */ +filename &zipReferrence. ZIP "&filesLocation./%lowcase(&packageName.).zip"; + +%if %sysfunc(fexist(&zipReferrence.)) %then + %do; + %put NOTE: Deleting file "&filesLocation./%lowcase(&packageName.).zip"; + %let _RC_ = %sysfunc(fdelete(&zipReferrence.)); + %end; + +/*** HELP START ***/ +/* + Locate all files with code in base folder (i.e. at filesLocation directory) +*/ +/* + Remember to prepare the description.sas file for you package. + The colon (:) is a field separator and is restricted + in lines of the header part. + The file should contain the following obligatory information: +-------------------------------------------------------------------------------------------- +>> **HEADER** << +Type: Package +Package: ShortPackageName +Title: A title/brief info for log note about your packages +Version: X.Y +Author: Firstname1 Lastname1 (xxxxxx1@yyyyy.com), Firstname2 Lastname2 (xxxxxx2@yyyyy.com) +Maintainer: Firstname Lastname (xxxxxx@yyyyy.com) +License: MIT +Encoding: UTF8 + +Required: "Base SAS Software" :%*optional, COMMA separated, QUOTED list, names of required SAS products, values must be like from proc setinit;run; output *; +ReqPackages: "macroArray (0.1)", "DFA (0.1)" :%*optional, COMMA separated, QUOTED list, names of required packages *; + +>> **DESCRIPTION** << +>> All the text below will be used in help << +DESCRIPTION START: + Xxxxxxxxxxx xxxxxxx xxxxxx xxxxxxxx xxxxxxxx. Xxxxxxx + xxxx xxxxxxxxxxxx xx xxxxxxxxxxx xxxxxx. Xxxxxxx xxx + xxxx xxxxxx. Xxxxxxxxxxxxx xxxxxxxxxx xxxxxxx. +DESCRIPTION END: +-------------------------------------------------------------------------------------------- + + Name of the 'type' of folder and files.sas inside must be in _low_ case letters. + + If order of loading is important, the 'sequential number' + can be used to order multiple types in the way you wish. + + The "tree structure" of the folder could be for example as follows: + +-------------------------------------------------------------------------------------------- + + .. + | + +-000_libname [one file one libname] + | | + | +-abc.sas [a file with a code creating libname ABC] + | + +-001_macro [one file one macro] + | | + | +-hij.sas [a file with a code creating macro HIJ] + | | + | +-klm.sas [a file with a code creating macro KLM] + | + +-002_function [one file one function, + | | option OUTLIB= should be: work.&packageName.fcmp.package + | | option INLIB= should be: work.&packageName.fcmp + | | (both literally with macrovariable name and "fcmp" sufix)] + | | + | +-efg.sas [a file with a code creating function EFG] + | + +-003_format [one file one format, + | | option LIB= should be: work.&packageName.format + | | (literally with macrovariable name and "format" sufix)] + | | + | +-efg.sas [a file with a code creating format EFG and informat EFG] + | + +-004_data [one file one dataset] + | | + | +-abc.efg.sas [a file with a code creating dataset EFG in library ABC] + | + +-005_exec [so called "free code", content of the files will be printed + | | to the log before execution] + | | + | +- + | + +-006_format [if your codes depend each other you can order them in folders, + | | e.g. code from 003_... will be executed before 006_...] + | | + | +-abc.sas [a file with a code creating format ABC, + | used in the definition of the format EFG] + +-007_function + | | + | +- + | + +-_ + | + +-... + | + +-00n_clean [if you need to clean something up after exec file execution, + | | content of the files will be printed to the log before execution] + | | + | +- + +-... + ... +-------------------------------------------------------------------------------------------- + +*/ +/*** HELP END ***/ + +/* collect the data */ +data &filesWithCodes.; + base = "&filesLocation."; + length folder file lowcase_name $ 256 folderRef fileRef $ 8; + drop lowcase_name; + + folderRef = "_%sysfunc(datetime(), hex6.)0"; + + rc=filename(folderRef, base); + folderid=dopen(folderRef); + + do i=1 to dnum(folderId); drop i; + folder = dread(folderId, i); + if folder NE lowcase(folder) then + do; + put 'ERROR: Folder should be named ONLY with low case letters.'; + put 'ERROR- Current value is: ' folder; + lowcase_name = lowcase(folder); + put 'ERROR- Try: ' lowcase_name; + put; + abort; + end; + order = scan(folder, 1, "_"); + type = scan(folder,-1, "_"); + + fileRef = "_%sysfunc(datetime(), hex6.)1"; + rc = filename(fileRef, catx("/", base, folder)); + fileId = dopen(fileRef); + + file = ' '; + if fileId then + do j = 1 to dnum(fileId); drop j; + file = dread(fileId, j); + if file NE lowcase(file) then + do; + put 'ERROR: File with code should be named ONLY with low case letters.'; + put 'ERROR- Current value is: ' file; + lowcase_name = lowcase(file); + put 'ERROR- Try: ' lowcase_name; + put; + abort; + end; + fileshort = substr(file, 1, length(file) - 4); /* filename.sas -> filename */ + output; + end; + rc = dclose(fileId); + rc = filename(fileRef); + end; + + rc = dclose(folderid); + rc = filename(folderRef); + stop; +run; +proc sort data = &filesWithCodes.; + by order type file; +run; +/* +proc contents data = &filesWithCodes.; +run; +*/ +title1 "Package's location is: &filesLocation."; +title2 "User: &SYSUSERID., datetime: %qsysfunc(datetime(), datetime21.), SAS version: &SYSVLONG4."; +title3 "Package's encoding: '&packageEncoding.', session's encoding: '&SYSENCODING.'."; +title4 " ______________________________ "; +title5 "List of files for package: &packageName. (version &packageVersion.), license: &packageLicense."; +proc print data = &filesWithCodes.(drop=base); +run; +title; + +/* packages description */ +data _null_; + infile &_DESCR_.; + file &zipReferrence.(description.sas); + input; + put _INFILE_; +run; + +/* package license */ +%if %sysfunc(fexist(&_LIC_.)) %then + %do; + data _null_; + infile &_LIC_.; + file &zipReferrence.(license.sas); + input; + put _INFILE_; + run; + %end; +%else + %do; + %put WARNING:[License] No license.sas file provided, default (MIT) licence file will be generated.; + %let packageLicense = MIT; + data _null_; + file &zipReferrence.(license.sas); + put " "; + put " Copyright (c) %sysfunc(today(),year4.) &packageAuthor. "; + put " "; + put " Permission is hereby granted, free of charge, to any person obtaining a copy "; + put ' of this software and associated documentation files (the "Software"), to deal '; + put " in the Software without restriction, including without limitation the rights "; + put " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell "; + put " copies of the Software, and to permit persons to whom the Software is "; + put " furnished to do so, subject to the following conditions: "; + put " "; + put " The above copyright notice and this permission notice shall be included "; + put " in all copies or substantial portions of the Software. "; + put " "; + put ' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR '; + put " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, "; + put " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE "; + put " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER "; + put " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, "; + put " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE "; + put " SOFTWARE. "; + put " "; + run; + %end; + +/* package metadata */ +data _null_; + if 0 then set &filesWithCodes. nobs=NOBS; + if NOBS = 0 then + do; + putlog "WARNING:[&sysmacroname.] No files to create package."; + stop; + end; + file &zipReferrence.(packagemetadata.sas); + + put ' data _null_; '; /* simple "%local" returns error while loading package */ + put ' call symputX("packageName", " ", "L");'; + put ' call symputX("packageVersion", " ", "L");'; + put ' call symputX("packageTitle", " ", "L");'; + put ' call symputX("packageAuthor", " ", "L");'; + put ' call symputX("packageMaintainer", " ", "L");'; + put ' call symputX("packageEncoding", " ", "L");'; + put ' call symputX("packageLicense", " ", "L");'; + put ' run; '; + + put ' %let packageName =' "&packageName.;"; + put ' %let packageVersion =' "&packageVersion.;"; + put ' %let packageTitle =' "&packageTitle.;"; + put ' %let packageAuthor =' "&packageAuthor.;"; + put ' %let packageMaintainer =' "&packageMaintainer.;"; + put ' %let packageEncoding =' "&packageEncoding.;"; + put ' %let packageLicense =' "&packageLicense.;"; + put ' ; '; + + stop; +run; + +/* loading package files */ +data _null_; + if NOBS = 0 then stop; + + file &zipReferrence.(load.sas) lrecl=32767; + + put 'filename package list;' /; + put ' %put NOTE- ;'; + put ' %put NOTE: ' @; put "Loading package &packageName., version &packageVersion., license &packageLicense.; "; + put ' %put NOTE: ' @; put "*** &packageTitle. ***; "; + put ' %put NOTE- ' @; put "Generated: %sysfunc(datetime(), datetime21.); "; + put ' %put NOTE- ' @; put "Author(s): &packageAuthor.; "; + put ' %put NOTE- ' @; put "Maintainer(s): &packageMaintainer.; "; + put ' %put NOTE- ;'; + put ' %put NOTE- Write %nrstr(%%)helpPackage(' "&packageName." ') for the description;'; + put ' %put NOTE- ;'; + put ' %put NOTE- *** START ***; ' /; + + put '%include package(packagemetadata.sas) / nosource2;' /; /* <- copied also to loadPackage macro */ + isFunction = 0; + isFormat = 0; + + %if (%bquote(&packageRequired.) ne ) + or (%bquote(&packageReqPackages.) ne ) + %then + %do; + put ' data _null_; '; + put ' call symputX("packageRequiredErrors", 0, "L"); '; + put ' run; '; + %end; + + %if %bquote(&packageRequired.) ne %then + %do; + put ' %put NOTE- *Testing required SAS components*%sysfunc(DoSubL( '; /* DoSubL() */ + put ' options nonotes nosource %str(;) '; + put ' options ls=max ps=max %str(;) '; + put ' /* temporary redirect log */ '; + put ' filename _stinit_ TEMP %str(;) '; + put ' proc printto log = _stinit_ %str(;) run %str(;) '; + put ' /* print out setinit */ '; + put ' proc setinit %str(;) run %str(;) '; + put ' proc printto %str(;) run %str(;) '; + + put ' data _null_ %str(;) '; + put ' /* loadup checklist of required SAS components */ '; + put ' if _n_ = 1 then '; + put ' do %str(;) '; + put ' length req $ 256 %str(;) '; + put ' declare hash R() %str(;) '; + put ' _N_ = R.defineKey("req") %str(;) '; + put ' _N_ = R.defineDone() %str(;) '; + put ' declare hiter iR("R") %str(;) '; + length packageRequired $ 32767; + packageRequired = upcase(symget('packageRequired')); + put ' do req = %bquote(' / packageRequired / ') %str(;) '; + put ' _N_ = R.add(key:req,data:req) %str(;) '; + put ' end %str(;) '; + put ' end %str(;) '; + put ' '; + put ' /* read in output from proc setinit */ '; + put ' infile _stinit_ end=eof %str(;) '; + put ' input %str(;) '; + /*put ' put "*> " _infile_ %str(;)';*/ /* for testing */ + put ' '; + put ' /* if component is in setinit remove it from checklist */ '; + put ' if _infile_ =: "---" then '; + put ' do %str(;) '; + put ' req = upcase(substr(_infile_, 4, 64)) %str(;) '; + put ' if R.find(key:req) = 0 then '; + put ' do %str(;) '; + put ' _N_ = R.remove() %str(;) '; + put ' end %str(;) '; + put ' end %str(;) '; + put ' '; + put ' /* if checklist is not null rise error */ '; + put ' if eof and R.num_items > 0 then '; + put ' do %str(;) '; + put ' put "ERROR- ###########################################" %str(;) '; + put ' put "ERROR- The following SAS components are missing! " %str(;) '; + put ' call symputX("packageRequiredErrors", 1, "L") %str(;) '; + put ' do while(iR.next() = 0) %str(;) '; + put ' put "ERROR- " req %str(;) '; + put ' end %str(;) '; + put ' put "ERROR- ###########################################" %str(;) '; + put ' put %str(;) '; + put ' end %str(;) '; + put ' run %str(;) '; + put ' filename _stinit_ clear %str(;) '; + put ' options notes source %str(;) '; + put ' ))*; '; + %end; + + %if %bquote(&packageReqPackages.) ne %then + %do; + put ' data _null_ ; '; + put ' length req name $ 64 SYSloadedPackages $ 32767; '; + length packageReqPackages $ 32767; + packageReqPackages = lowcase(symget('packageReqPackages')); + put ' if SYMEXIST("SYSloadedPackages") = 1 and SYMGLOBL("SYSloadedPackages") = 1 then '; + put ' do; '; + put ' do until(EOF); '; + put ' set sashelp.vmacro(where=(scope="GLOBAL" and name="SYSLOADEDPACKAGES")) end=EOF; '; + put ' substr(SYSloadedPackages, 1+offset, 200) = value; '; + put ' end; '; + put ' SYSloadedPackages = lowcase(SYSloadedPackages); '; + + put ' declare hash LP(); '; + put ' LP.defineKey("name"); '; + put ' LP.defineDone(); '; + put ' do _N_ = 1 to countw(SYSloadedPackages); '; + put ' name = scan(SYSloadedPackages, _N_); '; + put ' _RC_ = LP.add(); '; + put ' end; '; + + put ' missingPackagr = 0; '; + put ' do req = ' / packageReqPackages / ' ; '; + put ' name = lowcase(strip(scan(req, 1, " "))); '; + put ' vers = lowcase(compress(scan(req,-1, " "), ".", "KD")); '; + put ' if LP.check() ne 0 then '; + put ' do; '; + put ' missingPackagr = 1; '; + put ' put "ERROR: SAS package " req "is missing! " ; '; + put ' put ''ERROR- Try %loadPackage('' name ", requiredVersion = " vers ") to load it." ;'; + put ' end ; '; + put ' end ; '; + put ' if missingPackagr then call symputX("packageRequiredErrors", 1, "L"); '; + put ' end; '; + put ' else '; + put ' do; '; + put ' put "ERROR: No package loaded!"; '; + put ' call symputX("packageRequiredErrors", 1, "L"); '; + put ' do req = ' / packageReqPackages / ' ; '; + put ' name = lowcase(strip(scan(req, 1, " "))); '; + put ' vers = lowcase(compress(scan(req,-1, " "), ".", "KD")); '; + put ' put "ERROR: SAS package " req "is missing! " ; '; + put ' put ''ERROR- Try %loadPackage('' name ", requiredVersion = " vers ") to load it." ; '; + put ' end ; '; + put ' end; '; + put ' stop; '; + put 'run; '; + %end; + + %if (%bquote(&packageRequired.) ne ) + or (%bquote(&packageReqPackages.) ne ) + %then + %do; + put ' data _null_; '; + put ' if symget("packageRequiredErrors") = "1" then '; + put ' do; '; + put ' put "ERROR: Loading package &packageName. will be aborted!";'; + put ' put "ERROR- Required SAS components are missing."; '; + put ' put "ERROR- *** STOP ***"; '; + put ' ABORT; '; + put ' end; '; + put ' run; '; + %end; + + do until(eof); + set &filesWithCodes. end = EOF nobs=NOBS; + if (upcase(type)=:'CLEAN') then continue; /* cleaning files are only included in unload.sas */ + put '%put NOTE- ;'; + put '%put NOTE- Element of type ' type 'from the file "' file +(-1) '" will be included;' /; + + if upcase(type)=:'EXEC' then + do; + put '%put NOTE- ;'; + put '%put NOTE- Executing the following code: ;'; + put '%put NOTE- *****************************;'; + put 'data _null_;'; + put ' infile package(_' folder +(-1) "." file +(-1) ') lrecl=32767;'; + put ' input;'; + put ' putlog "*> " _infile_;'; + put 'run;' /; + put '%put NOTE- *****************************;'; + put '%put NOTE- ;'; + end; + + put '%include package(_' folder +(-1) "." file +(-1) ') / nosource2;' /; + + isFunction + (upcase(type)=:'FUNCTION'); + isFormat + (upcase(type)=:'FORMAT'); + + /* 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));' /; + 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));'/; + end; + end; + + /* update SYSloadedPackages global macrovariable */ + put ' data _null_ ; '; + put ' length SYSloadedPackages $ 32767; '; + put ' if SYMEXIST("SYSloadedPackages") = 1 and SYMGLOBL("SYSloadedPackages") = 1 then '; + put ' do; '; + put ' do until(EOF); '; + put ' set sashelp.vmacro(where=(scope="GLOBAL" and name="SYSLOADEDPACKAGES")) end=EOF; '; + put ' substr(SYSloadedPackages, 1+offset, 200) = value; '; + put ' end; '; + put ' SYSloadedPackages = cats("#", translate(strip(SYSloadedPackages), "#", " "), "#"); '; + + put " if INDEX(lowcase(SYSloadedPackages), '#%lowcase(&packageName.)#') = 0 then "; + put ' do; '; + put " SYSloadedPackages = catx('#', SYSloadedPackages, '&packageName.'); "; + put ' SYSloadedPackages = compbl(translate(SYSloadedPackages, " ", "#")); '; + put ' call symputX("SYSloadedPackages", SYSloadedPackages, "G"); '; + put ' put "NOTE: " SYSloadedPackages = ; '; + put ' end ; '; + put ' end; '; + put ' else '; + put ' do; '; + put " call symputX('SYSloadedPackages', '&packageName.', 'G'); "; + put " put 'NOTE: SYSloadedPackages = &packageName.'; "; + put ' end; '; + put ' stop; '; + put 'run; ' / ; + + put '%put NOTE- ;'; + put '%put NOTE: '"Loading package &packageName., version &packageVersion., license &packageLicense.;"; + put '%put NOTE- *** END ***;' /; + put "/* load.sas end */" /; + stop; +run; + +/* unloading package objects */ +data _null_; + /* break if no data */ + if NOBS = 0 then stop; + + file &zipReferrence.(unload.sas); + + put 'filename package list;' /; + put '%put NOTE: '"Unloading package &packageName., version &packageVersion., license &packageLicense.;"; + put '%put NOTE- *** START ***;' /; + + /* include "cleaning" files */ + EOF = 0; + do until(EOF); + set &filesWithCodes. end = EOF nobs = NOBS; + if not (upcase(type)=:'CLEAN') then continue; + put '%put NOTE- Code of type ' type 'generated from the file "' file +(-1) '" will be executed;'; + put '%put NOTE- ;' /; + put '%put NOTE- Executing the following code: ;'; + put '%put NOTE- *****************************;'; + put 'data _null_;'; + put ' infile package(_' folder +(-1) "." file +(-1) ') lrecl=32767;'; + put ' input;'; + put ' putlog "*> " _infile_;'; + put 'run;' /; + put '%put NOTE- *****************************;'; + put '%put NOTE- ;' /; + + put '%include package(_' folder +(-1) "." file +(-1) ') / nosource2;' /; + end; + + /* delete macros and formats */ + put 'proc sql;'; + put ' create table WORK._%sysfunc(datetime(), hex16.)_ as'; + put ' select memname, objname, objtype'; + put ' from dictionary.catalogs'; + put ' where '; + put ' ('; + put ' objname in ("*"' /; + /* 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. '"'; + end; + /**/ + put ' )'; + put ' and objtype = "MACRO"'; + put ' and libname = "WORK"'; + put ' )'; + put ' or'; + put ' ('; + put ' objname in ("*"' /; + /* list of formats */ + isFormat = 0; + EOF = 0; + 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. '"'; + isFormat + 1; + end; + put ' )'; + put ' and objtype in ("FORMAT" "FORMATC" "INFMT" "INFMTC")'; + put ' and libname = "WORK"'; + put " and memname = '%upcase(&packageName.format)'"; + put ' )'; + + put ' order by objtype, memname, objname'; + put ' ;'; + put 'quit;'; + + put 'data _null_;'; + put ' do until(last.memname);'; + put ' set WORK._last_;'; + put ' by objtype memname;'; + put ' if first.memname then call execute("proc catalog cat = work." !! strip(memname) !! " force;");'; + put ' call execute("delete " !! strip(objname) !! " / et =" !! objtype !! "; run;");'; + put ' end;'; + put ' call execute("quit;");'; + put 'run;'; + put 'proc delete data = WORK._last_;'; + put 'run;'; + + /* delete the link to the formats catalog */ + if isFormat then + do; + put "proc delete data = work.%lowcase(&packageName.format)(mtype = catalog);"; + put 'run;'; + 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 '%put NOTE:[FMTSEARCH] %sysfunc(getoption(fmtsearch));' /; + end; + + /* delete functions */ + put "proc fcmp outlib = work.%lowcase(&packageName.fcmp).package;"; + isFunction = 0; + EOF = 0; + do until(EOF); + 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 ';'; + isFunction + 1; + end; + put "run;" /; + + /* delete the link to the functions dataset */ + if isFunction 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 '%put; %put NOTE:[CMPLIB] %sysfunc(getoption(cmplib));' /; + end; + + /* delete datasets */ + put "proc sql noprint;"; + EOF = 0; + do until(EOF); + 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 ';'; + end; + put "quit;" /; + + /* delete libraries */ + EOF = 0; + do until(EOF); + 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;'; + end; + put "run;" /; + + /* update SYSloadedPackages global macrovariable */ + put ' data _null_ ; '; + put ' length SYSloadedPackages $ 32767; '; + put ' if SYMEXIST("SYSloadedPackages") = 1 and SYMGLOBL("SYSloadedPackages") = 1 then '; + put ' do; '; + put ' do until(EOF); '; + put ' set sashelp.vmacro(where=(scope="GLOBAL" and name="SYSLOADEDPACKAGES")) end=EOF; '; + put ' substr(SYSloadedPackages, 1+offset, 200) = value; '; + put ' end; '; + put ' SYSloadedPackages = cats("#", translate(strip(SYSloadedPackages), "#", " "), "#"); '; + + put " if INDEX(lowcase(SYSloadedPackages), '#%lowcase(&packageName.)#') > 0 then "; + put ' do; '; + put " SYSloadedPackages = tranwrd(SYSloadedPackages, '#&packageName.#', '##'); "; + put ' SYSloadedPackages = compbl(translate(SYSloadedPackages, " ", "#")); '; + put ' call symputX("SYSloadedPackages", SYSloadedPackages, "G"); '; + put ' put "NOTE: " SYSloadedPackages = ; '; + put ' end ; '; + put ' end; '; + put ' stop; '; + put 'run; ' / ; + + + put '%put NOTE: '"Unloading package &packageName., version &packageVersion., license &packageLicense.;"; + put '%put NOTE- *** END ***;'; + put '%put NOTE- ;'; + + put "/* unload.sas end */"; + stop; +run; + +/* package help */ +data _null_; + /* break if no data */ + if NOBS = 0 then stop; + + file &zipReferrence.(help.sas); + length strX $ 32767; + + put 'filename package list;' /; + put ' %put NOTE- ;'; + put ' %put NOTE: '"Help for package &packageName., version &packageVersion., license &packageLicense.;"; + put ' %put NOTE: ' @; put "*** &packageTitle. ***; "; + put ' %put NOTE- ' @; put "Generated: %sysfunc(datetime(), datetime21.); "; + put ' %put NOTE- ' @; put "Author(s): &packageAuthor.; "; + put ' %put NOTE- ' @; put "Maintainer(s): &packageMaintainer.; "; + put ' %put NOTE- ;'; + put ' %put NOTE- *** START ***;' /; + + /* Use helpKeyword macrovariable to search for content (filename and type) */ + /* put '%local ls_tmp ps_tmp notes_tmp source_tmp; ';*/ + put '%let ls_tmp = %sysfunc(getoption(ls)); '; + put '%let ps_tmp = %sysfunc(getoption(ps)); '; + put '%let notes_tmp = %sysfunc(getoption(notes)); '; + put '%let source_tmp = %sysfunc(getoption(source)); '; + put 'options ls = MAX ps = MAX nonotes nosource; '; + put '%include package(packagemetadata.sas) / nosource2; ' /; + + put 'data _null_; '; + put ' if strip(symget("helpKeyword")) = " " then '; + put ' do until (EOF); '; + put ' infile package(description.sas) end = EOF; '; + put ' input; '; + put ' if upcase(strip(_infile_)) = "DESCRIPTION END:" then printer = 0; '; + put ' if printer then put "*> " _infile_; '; + put ' if upcase(strip(_infile_)) = "DESCRIPTION START:" then printer = 1; '; + put ' end; '; + put ' else stop; '; + put 'run; ' /; + + /* license info */ + put 'data _null_; '; + put ' if upcase(strip(symget("helpKeyword"))) = "LICENSE" then '; + put ' do until (EOF); '; + put ' infile package(license.sas) end = EOF; '; + put ' input; '; + put ' put "*> " _infile_; '; + put ' end; '; + put ' else stop; '; + put 'run; ' /; + + put 'data WORK._%sysfunc(datetime(), hex16.)_; '; + put 'infile cards4 dlm = "/"; '; + put 'input @; '; + put 'if 0 then output; '; + put 'length helpKeyword $ 64; '; + put 'retain helpKeyword "*"; '; + put 'drop helpKeyword; '; + put 'if _N_ = 1 then helpKeyword = strip(symget("helpKeyword")); '; + put 'if FIND(_INFILE_, helpKeyword, "it") or helpKeyword = "*" then '; + put ' do; '; + put ' input (folder order type file fileshort) (: $ 256.); '; + put ' output; '; + put ' end; '; + put 'cards4; '; + + EOFDS = 0; + do until(EOFDS); + /* content is created during package creation */ + set &filesWithCodes. end = EOFDS nobs = NOBS; + select; + when (upcase(type) = "DATA") fileshort2 = fileshort; + when (upcase(type) = "MACRO") fileshort2 = cats('%',fileshort,'()'); + when (upcase(type) = "FUNCTION") fileshort2 = cats(fileshort,'()'); + when (upcase(type) = "FORMAT") fileshort2 = cats('$',fileshort); + otherwise fileshort2 = fileshort; + end; + strX = catx('/', folder, order, type, file, fileshort, fileshort2); + put strX; + end; + + put ";;;;"; + put "run;" /; +/* + put 'proc print;'; + put 'run;'; +*/ + /* loop through content found and print info to the log */ + put 'data _null_; '; + put 'if upcase(strip(symget("helpKeyword"))) in (" " "LICENSE") then do; stop; end; '; + put 'if NOBS = 0 then do; ' / + 'put; put '' *> No help info found. Try %helpPackage(packageName,*) to display all.''; put; stop; ' / + 'end; '; + put ' do until(EOFDS); '; + put ' set WORK._last_ end = EOFDS nobs = NOBS; '; + put ' length memberX $ 1024; '; + put ' memberX = cats("_",folder,".",file); '; + /* inner datastep in call execute to read each embedded file */ + put ' call execute("data _null_; ");'; + put ' call execute("infile package(" || strip(memberX) || ") end = EOF; ");'; + put ' call execute(" printer = 0; ");'; + put ' call execute(" do until(EOF); ");'; + put ' call execute(" input; ");'; + put ' call execute(" if strip(_infile_) = cat(""/"",""*** "",""HELP END"","" ***"",""/"") then printer = 0; ");'; /* it looks like that because of comments */ + put ' call execute(" if printer then put ""*> "" _infile_; ");'; + put ' call execute(" if strip(_infile_) = cat(""/"",""*** "",""HELP START"","" ***"",""/"") then printer = 1; ");'; /* it looks like that because of comments */ + put ' call execute(" end; ");'; + put ' call execute(" put ""*> "" / ""*> ""; ");'; + put ' call execute(" stop; ");'; + put ' call execute("run; ");'; + put ' if lowcase(type) =: "data" then '; + put ' do; '; + put ' call execute("title ""Dataset " || strip(fileshort) || " from package &packageName. ""; ");'; + put ' call execute("proc contents data = " || strip(fileshort) || "; run; title; ");'; + put ' end; '; + /**/ + put " end; "; + put " stop; "; + put "run; "; + + /* cleanup */ + put "proc delete data = WORK._last_; "; + put "run; "; + put 'options ls = &ls_tmp. ps = &ps_tmp. ¬es_tmp. &source_tmp.; ' /; + + put '%put NOTE: '"Help for package &packageName., version &packageVersion., license &packageLicense.;"; + put '%put NOTE- *** END ***;' /; + put "/* help.sas end */"; + + stop; +run; + +/* create package content */ +data _null_; + /* break if no data */ + if NOBS = 0 then stop; + + set &filesWithCodes. nobs = NOBS; + + call execute(cat ('filename _IN_ "', catx('/', base, folder, file), '";')); + call execute(cats("filename _OUT_ ZIP '", base, "/%lowcase(&packageName.).zip' member='_", folder, ".", file, "';") ); + /* copy code file into the zip */ + call execute('data _null_;'); + call execute(' rc = fcopy("_IN_", "_OUT_");'); + call execute('run;'); + /* test file content for help tags */ + call execute('data _null_;'); + call execute(' retain test .;'); + call execute(' infile _IN_ lrecl=32767 dlm="0a0d"x end=EOF;'); + call execute(' input;'); + call execute(' if strip(_infile_) = cat("/","*** ","HELP START"," ***","/") then test + (+1); '); + call execute(' if strip(_infile_) = cat("/","*** ","HELP END", " ***","/") then test + (-1); '); + call execute(' if (test not in (.,0,1)) or (EOF and test) then '); + call execute(' do; '); + call execute(' put "ERR" "OR: unmatching or nested HELP tags!" _N_=; '); + call execute(' abort; '); + call execute(' end; '); + call execute(' if (EOF and test=.) then put "WARN" "ING: no HELP tags in the file." ; '); + call execute('run;'); + + call execute('filename _IN_ clear;'); + call execute('filename _OUT_ clear;'); +run; + +proc sql; + drop table &filesWithCodes.; +quit; +filename &zipReferrence. clear; +%mend GeneratePackge; + + +/* + +options mprint; +ods html; +%GeneratePackge( + testowyPackageName +,0.01 +,author +,contact +,filesLocation=E:\SAS_WORK_5400\testyGeneratePackage +) + +*/ + +/* +TODO: +- modyfikacja helpa, sprawdzanie kodu danje funkcji/makra/typu [v] + +- opcjonalne sortowanie nazw folderow(_) [v] + +- wewnętrzna nazwaz zmiennej z nazwa pakietu (na potrzeby kompilacji) [v] + +- weryfikacja srodaowiska + +- weryfikacja "niepustosci" obowiazkowych argumentow [v] + +- dodac typ "clear" do czyszczenia po plikach 'exec' [v] + +- doadc sprawdzanie liczby wywołan procedury fcmp, format i slowa '%macro(' w plikach z kodami + +- syspackages - makrozmienna z lista zaladowanych pakietow [v] as SYSloadedPackages + +- dodac typ "iml", "ds2", "proto" + +-lista wymaganych komponentow potrzebnych do działania SASa (na bazie proc SETINIT) [v] + +-sparwdzanie domknietosci, parzystosci i wystepowania tagow HELP START - HELP END w plikach [v] + +-weryfikacja nadpisywania makr [ ] +*/ + +/* + +%include "C:\SAS_PACKAGES\generatePackage.sas"; + +ods html; +%generatePackge(filesLocation=C:\SAS_PACKAGES\SQLinDS) +*/ + +/* +*"C:\SAS_PACKAGES\testyGeneratoraPakietow"; + +libname packages "E:\SAS_WORK_5400\testyGeneratePackage"; +filename packages "E:\SAS_WORK_5400\testyGeneratePackage"; + +%include packages(loadpackage.sas); + +dm 'log;clear'; +%loadpackage(testowypackagename) + + +*/ +/* + +%let helpKeyword=*; +%helpPackage(testowypackagename) +%unloadPackage(testowypackagename) + + +filename package ZIP "E:\SAS_WORK_5400\testyGeneratePackage\testowypackagename.zip"; + +%put %sysfunc(pathname(package)); + +%include package(load.sas); +%help()??? +%include package(unload.sas); + +filename package ZIP "C:\SAS_PACKAGES\testowypackagename.zip"; +%include package(load.sas); +%include package(unload.sas); + +filename package ZIP "C:\SAS_PACKAGES\macroarray.zip"; +%include package(load.sas); +%include package(unload.sas); +*/ diff --git a/loadpackage.sas b/loadpackage.sas index 66c8f5f..eb13906 100644 --- a/loadpackage.sas +++ b/loadpackage.sas @@ -1,4 +1,4 @@ -/*** HELP START ***/ +/*** HELP START ***/ /**############################################################################**/ /* */ @@ -71,15 +71,17 @@ TODO: %let notes_tmp = %sysfunc(getoption(notes)); %let source_tmp = %sysfunc(getoption(source)); options NOnotes NOsource ls=MAX ps=MAX; + %local _PackageFileref_; + %let _PackageFileref_ = P%sysfunc(MD5(%lowcase(&packageName.)),hex7.); - filename package ZIP + filename &_PackageFileref_. ZIP /* put location of package myPackageFile.zip here */ "&path./%lowcase(&packageName.).zip" %unquote(&options.) ; - %if %sysfunc(fexist(package)) %then + %if %sysfunc(fexist(&_PackageFileref_.)) %then %do; - %include package(packagemetadata.sas) / &source2.; - filename package clear; + %include &_PackageFileref_.(packagemetadata.sas) / &source2.; + filename &_PackageFileref_. clear; /* test if required version of package is "good enough" */ %if %sysevalf(&requiredVersion. > &packageVersion.) %then @@ -90,16 +92,16 @@ TODO: %end; options ls = &ls_tmp. ps = &ps_tmp. ¬es_tmp. &source_tmp.; - filename package ZIP + filename &_PackageFileref_. ZIP "&path./%lowcase(&packageName.).zip" %unquote(&options.) ENCODING = %if %bquote(&packageEncoding.) NE %then &packageEncoding. ; %else utf8 ; ; - %include package(load.sas) / &source2.; + %include &_PackageFileref_.(load.sas) / &source2.; %end; %else %put ERROR:[&sysmacroname] File "&path./&packageName..zip" does not exist; - filename package clear; + filename &_PackageFileref_. clear; options ls = &ls_tmp. ps = &ps_tmp. ¬es_tmp. &source_tmp.; %mend loadPackage; @@ -123,26 +125,28 @@ TODO: %let notes_tmp = %sysfunc(getoption(notes)); %let source_tmp = %sysfunc(getoption(source)); options NOnotes NOsource ls=MAX ps=MAX; + %local _PackageFileref_; + %let _PackageFileref_ = P%sysfunc(MD5(%lowcase(&packageName.)),hex7.); - filename package ZIP + filename &_PackageFileref_. ZIP /* put location of package myPackageFile.zip here */ "&path./%lowcase(&packageName.).zip" %unquote(&options.) ; - %if %sysfunc(fexist(package)) %then + %if %sysfunc(fexist(&_PackageFileref_.)) %then %do; - %include package(packagemetadata.sas) / &source2.; - filename package clear; + %include &_PackageFileref_.(packagemetadata.sas) / &source2.; + filename &_PackageFileref_. clear; options ls = &ls_tmp. ps = &ps_tmp. ¬es_tmp. &source_tmp.; - filename package ZIP + filename &_PackageFileref_. ZIP "&path./%lowcase(&packageName.).zip" %unquote(&options.) ENCODING = %if %bquote(&packageEncoding.) NE %then &packageEncoding. ; %else utf8 ; ; - %include package(unload.sas) / &source2.; + %include &_PackageFileref_.(unload.sas) / &source2.; %end; %else %put ERROR:[&sysmacroname] File "&path./&packageName..zip" does not exist; - filename package clear; + filename &_PackageFileref_. clear; options ls = &ls_tmp. ps = &ps_tmp. ¬es_tmp. &source_tmp.; %mend unloadPackage; @@ -170,26 +174,28 @@ TODO: %let notes_tmp = %sysfunc(getoption(notes)); %let source_tmp = %sysfunc(getoption(source)); options NOnotes NOsource ls=MAX ps=MAX; + %local _PackageFileref_; + %let _PackageFileref_ = P%sysfunc(MD5(%lowcase(&packageName.)),hex7.); - filename package ZIP + filename &_PackageFileref_. ZIP /* put location of package myPackageFile.zip here */ "&path./%lowcase(&packageName.).zip" %unquote(&options.) ; - %if %sysfunc(fexist(package)) %then + %if %sysfunc(fexist(&_PackageFileref_.)) %then %do; - %include package(packagemetadata.sas) / &source2.; - filename package clear; + %include &_PackageFileref_.(packagemetadata.sas) / &source2.; + filename &_PackageFileref_. clear; options ls = &ls_tmp. ps = &ps_tmp. ¬es_tmp. &source_tmp.; - filename package ZIP + filename &_PackageFileref_. ZIP "&path./%lowcase(&packageName.).zip" %unquote(&options.) ENCODING = %if %bquote(&packageEncoding.) NE %then &packageEncoding. ; %else utf8 ; ; - %include package(help.sas) / &source2.; + %include &_PackageFileref_.(help.sas) / &source2.; %end; %else %put ERROR:[&sysmacroname] File "&path./&packageName..zip" does not exist; - filename package clear; + filename &_PackageFileref_. clear; options ls = &ls_tmp. ps = &ps_tmp. ¬es_tmp. &source_tmp.; %mend helpPackage; diff --git a/loadpackage.sas_bckp b/loadpackage.sas_bckp new file mode 100644 index 0000000..72219c2 --- /dev/null +++ b/loadpackage.sas_bckp @@ -0,0 +1,228 @@ +/*** HELP START ***/ + +/**############################################################################**/ +/* */ +/* Copyright Bartosz Jablonski, July 2019. */ +/* */ +/* Code is free and open source. If you want - you can use it. */ +/* I tested it the best I could */ +/* but it comes with absolutely no warranty whatsoever. */ +/* If you cause any damage or something - it will be your own fault. */ +/* You've been warned! You are using it on your own risk. */ +/* However, if you decide to use it don't forget to mention author: */ +/* Bartosz Jablonski (yabwon@gmail.com) */ +/* */ +/* Here is the official version: */ +/* + Copyright (c) 2019 Bartosz Jablonski (yabwon@gmail.com) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ +/**#############################################################################**/ + +/* Macros to load or to unload SAS packages */ +/* A SAS package is a zip file containing a group + of SAS codes (macros, functions, datasteps generating + data, etc.) wrapped up together and %INCLUDEed by + a single load.sas file (also embedded inside the zip). +*/ +/* +TODO: +- macro for testing available packages in the packages' folder [DONE] checkout: %listPackages() +*/ +/*** HELP END ***/ + +/*** HELP START ***/ + +%macro loadPackage( + packageName /* name of a package, + e.g. myPackageFile.zip, + required and not null */ +, path = %sysfunc(pathname(packages)) /* location of a package, + by default it looks for + location of "packages" fileref */ +, options = %str(LOWCASE_MEMNAME) /* possible options for ZIP filename */ +, source2 = /*source2*/ /* option to print out details, + null by default */ +, requiredVersion = . /* option to test if loaded package + is provided in required version */ +)/secure; +/*** HELP END ***/ + %local ls_tmp ps_tmp notes_tmp source_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)); + options NOnotes NOsource ls=MAX ps=MAX; + + filename package ZIP + /* put location of package myPackageFile.zip here */ + "&path./%lowcase(&packageName.).zip" %unquote(&options.) + ; + %if %sysfunc(fexist(package)) %then + %do; + %include package(packagemetadata.sas) / &source2.; + filename package clear; + + /* test if required version of package is "good enough" */ + %if %sysevalf(&requiredVersion. > &packageVersion.) %then + %do; + %put ERROR: Required version is &requiredVersion.; + %put ERROR- Provided version is &packageVersion.; + %ABORT; + %end; + + options ls = &ls_tmp. ps = &ps_tmp. ¬es_tmp. &source_tmp.; + filename package ZIP + "&path./%lowcase(&packageName.).zip" %unquote(&options.) + ENCODING = + %if %bquote(&packageEncoding.) NE %then &packageEncoding. ; + %else utf8 ; + ; + %include package(load.sas) / &source2.; + %end; + %else %put ERROR:[&sysmacroname] File "&path./&packageName..zip" does not exist; + filename package clear; + options ls = &ls_tmp. ps = &ps_tmp. ¬es_tmp. &source_tmp.; +%mend loadPackage; + +/*** HELP START ***/ + +%macro unloadPackage( + packageName /* name of a package, + e.g. myPackageFile.zip, + required and not null */ +, path = %sysfunc(pathname(packages)) /* location of a package, + by default it looks for + location of "packages" fileref */ +, options = %str(LOWCASE_MEMNAME) /* possible options for ZIP filename */ +, source2 = /*source2*/ /* option to print out details, + null by default */ +)/secure; +/*** HELP END ***/ + %local ls_tmp ps_tmp notes_tmp source_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)); + options NOnotes NOsource ls=MAX ps=MAX; + + filename package ZIP + /* put location of package myPackageFile.zip here */ + "&path./%lowcase(&packageName.).zip" %unquote(&options.) + ; + %if %sysfunc(fexist(package)) %then + %do; + %include package(packagemetadata.sas) / &source2.; + filename package clear; + options ls = &ls_tmp. ps = &ps_tmp. ¬es_tmp. &source_tmp.; + filename package ZIP + "&path./%lowcase(&packageName.).zip" %unquote(&options.) + ENCODING = + %if %bquote(&packageEncoding.) NE %then &packageEncoding. ; + %else utf8 ; + ; + %include package(unload.sas) / &source2.; + %end; + %else %put ERROR:[&sysmacroname] File "&path./&packageName..zip" does not exist; + filename package clear; + options ls = &ls_tmp. ps = &ps_tmp. ¬es_tmp. &source_tmp.; +%mend unloadPackage; + +/*** HELP START ***/ + +%macro helpPackage( + packageName /* name of a package, + e.g. myPackageFile.zip, + required and not null */ +, helpKeyword /* phrase to search in help, + when empty prints description + "*" means prints all help + "license" prints license */ +, path = %sysfunc(pathname(packages)) /* location of a package, + by default it looks for + location of "packages" fileref */ +, options = %str(LOWCASE_MEMNAME) /* possible options for ZIP filename */ +, source2 = /*source2*/ /* option to print out details, + null by default */ +)/secure; +/*** HELP END ***/ + %local ls_tmp ps_tmp notes_tmp source_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)); + options NOnotes NOsource ls=MAX ps=MAX; + + filename package ZIP + /* put location of package myPackageFile.zip here */ + "&path./%lowcase(&packageName.).zip" %unquote(&options.) + ; + %if %sysfunc(fexist(package)) %then + %do; + %include package(packagemetadata.sas) / &source2.; + filename package clear; + options ls = &ls_tmp. ps = &ps_tmp. ¬es_tmp. &source_tmp.; + filename package ZIP + "&path./%lowcase(&packageName.).zip" %unquote(&options.) + ENCODING = + %if %bquote(&packageEncoding.) NE %then &packageEncoding. ; + %else utf8 ; + ; + %include package(help.sas) / &source2.; + %end; + %else %put ERROR:[&sysmacroname] File "&path./&packageName..zip" does not exist; + filename package clear; + options ls = &ls_tmp. ps = &ps_tmp. ¬es_tmp. &source_tmp.; +%mend helpPackage; + + +/*** HELP START ***/ + +/* + * Filenames references "packages" and "package" are keywords; + * the first one should be used to point folder with packages; + * the second is used internally by macros; + + * Example 1: + * assuming that _THIS_FILE_ and the SQLinDS package (sqlinds.zip file) + * are located in the "C:/SAS_PACKAGES/" folder + * copy the following code into autoexec.sas + * or run it in your SAS session +**/ +/* + +filename packages "C:/SAS_PACKAGES"; +%include packages(loadpackage.sas); + +%loadpackage(SQLinDS) +%helpPackage(SQLinDS) +%unloadPackage(SQLinDS) + +optional: + +libname packages "C:/SAS_PACKAGES/"; +%include "%sysfunc(pathname(packages))/loadpackage.sas"; + +%loadPackage(SQLinDS) +%helpPackage(SQLinDS) +%unloadPackage(SQLinDS) + +*/ +/*** HELP END ***/ diff --git a/macroarray.zip b/macroarray.zip index 116d8dd1b05b1c45886e1fd318741212077f57c9..84655e67a53eab4e8dbaf758339166ac271baa1b 100644 GIT binary patch delta 4707 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 diff --git a/sqlinds.zip b/sqlinds.zip index 6be70de856f50bbb13fa78d9420045fc2c98fdf4..dd06faf8dab2f932169b8a753b9dd12fef8621e7 100644 GIT binary patch delta 5123 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 delta 5122 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