3
votes

I want to store macros in a catalog. Doing so allows many macros to be shared with just a single file, as well as introduces a degree of separation from the user.

To store my macro, I run a program such as

/* HelloWorld.sas */
libname pwd ".";              /* assign current directory */
option mstored sasmstore=pwd; /* set pwd as storage directory */ 

%macro HelloWorld() 
    / store source;           /* store compiled macro along with its source */
  data _null_;
    put "Hello, World!";
  run;
%mend;

This creates a sasmacr.sas7bcat file in the directory in which HelloWorld.saslives. I can then move that file to another directory, such as C:\myMacrosand run the following program:

/* CallHelloWorld.sas */
libname myMacros 'C:\myMacros';
option mstored sasmstore=myMacros;

%HelloWorld();

The macro HelloWorld() is called without error.

However, if I want to consider the HelloWorld() macro as part of a "HelloWorld" macro suite, I cannot simply change the catalog name in Windows Explorer from sasmacr.sas7bcat to HelloWorld.sas7bcat. When I do this and try running CallHelloWorld.sas again (after closing and reopening SAS), the macro is not resolved.

1    /* CallHelloWorld.sas */
2    libname myMacros 'C:\myMacros';
NOTE: Libref MYMACROS was successfully assigned as follows:
      Engine:        V9
      Physical Name: C:\myMacros
3    option mstored sasmstore=myMacros;
4
5    %HelloWorld();
     -
     180
NOTE: The SAS System was unable to open the macro library referenced by the SASMSTORE = libref
      MYMACROS.
WARNING: Apparent invocation of macro HELLOWORLD not resolved.

ERROR 180-322: Statement is not valid or it is used out of proper order.

ERROR: Catalog MYMACROS.SASMACR does not exist.
NOTE: The SAS System was unable to open the macro library referenced by the SASMSTORE = libref
      MYMACROS.
ERROR: An error occurred during the execution of the %COPY statement.

How do I change the name of a catalog which contains macros so that those macros may be called in various programs? Is it possible to name the catalog something different than sasmacr up front?

3

3 Answers

3
votes

At least on Unix with SAS 9.4 you can use the SASAUTOS option to point at a FILEREF that uses the ZIP engine so that all of the macro definitions are stored in a single ZIP file.

The one "trick" is that you need to change the names of the member files in the ZIP file. Normally on unix SASAUTOS requires that the source files are named using the macro name in lowercase with a .sas extension (helloworld.sas). But for SASAUTOS to work with a ZIP file the members should be named using the macro name in uppercase with NO extension (HELLOWORLD).

filename mymacros zip '~/mymacros.zip';
options insert=(sasautos=(mymacros)) ;

EDIT

Unfortunately this method causes SAS to generate ERROR: messages when the macro source file is not found in the ZIP file, even if it is ultimately found in another file in the SASAUTOS option search path.

2
votes

I don't think you can rename the catalog away from SASMACR and still use it directly. However, you could use PROC CATALOG potentially to manage the SASMACR catalog.

What you'd want to do is, when you wanted to include a particular macro, copy it via proc catalog from its source location into your chosen SASMSTORE location.

Something like:

libname myMacros 'C:\temp';
libname pwd '.';

options mstored sasmstore=pwd;

proc catalog catalog=myMacros.HelloWorld;
  copy out=pwd.sasmacr;
run;
quit;


%HelloWorld();

Now - I would suggest this is probably overkill; there's really no reason to have separate macro catalogs this way. If you like the idea of separately including files, you may want to consider Autocall macros (where you don't store them compiled, but store their source and compile-on-demand); really compiling a macro costs nearly nothing in SAS anyway. But if using stored-compiled macros is what you like, this method is probably the best way to do it.

Of course, I think an easier way would be to leave the catalog SASMACR.SAS7BCAT and use the directory name to determine what it is, and then append librefs to your sasmstore option value.

1
votes

From DATA_NULL_ I have received the answer to how to use to existing catalogs with different names. Use the CATNAME statement. So if you have catalogs TEST.CAT1 and TEST.CAT2 you can use the CATNAME statement to make TEST.SASMACR be the concatenation of the two macros.

CATNAME test.sasmacr
  (test.cat1 (ACCESS=READONLY)
   test.cat2 (ACCESS=READONLY)
  )
;

You can now point the SASMSTORE option the the TEST libref.

option mstored sasmstore=test;

Here is an example of how you might create such separate catalogs using PROC CATALOG to copy members that have been compiled into a SASMACR catalog into catalogs with different names.

libname templib '~/test/cat1';
libname permlib '~/test';
options mstored sasmstore=templib;

%macro HelloWorld1() / store source;
  data _null_;
    put "Hello, World! &sysmacroname";
  run;
%mend;

%macro HelloWorld2() / store source;
  data _null_;
    put "Hello, World! &sysmacroname";
  run;
%mend;

proc catalog cat=templib.sasmacr ;
   copy out=permlib.cat1;
      select helloworld1 /et=macro;
   run;
   copy out=permlib.cat2;
      select helloworld2 /et=macro;
   run;
quit;

Now use the CATNAME command to chain the catalogs together.

options mstored sasmstore=permlib;
CATNAME permlib.sasmacr
  (permlib.cat1 (ACCESS=READONLY)
   permlib.cat2 (ACCESS=READONLY)
);

%helloworld1;
%helloworld2;