BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
jblack38
Obsidian | Level 7

I created  a compiled stored macro (I think) using this code

libname maclib '\\sasdev\CNM_EG_Projects\macros';
OPTIONS MSTORED SASMSTORE=maclib;

%MACRO adm_mLog_Event( PrjName, PrjTask, Desc ) 
	/STORE
	DES='Log EG events in admData.EG_Event_Times';

%PUT &PrjName ": " &PrjTask ": " &Desc;
libname adm_dta meta library="Admin Data" metaout=data;

%let theTime = %sysfunc(datetime(), datetime20.);
%put &theTime;

data New_Record;
format  Time_of_Day datetime20.;
format ProjectName $40. ProjectTask $40. Description $80.;
	Time_of_Day=input("&theTime",datetime20.);
	ProjectName=&PrjName;
	ProjectTask=&PrjTask;
	Description=&Desc;
run;

proc append base=adm_dta.EG_Event_Times
			data=New_Record;
run;
%MEND;

I then try to call macro in an EG project using this code

libname maclib '\\sasdev\CNM_EG_Projects\macros';
OPTIONS MSTORED SASMSTORE=maclib;

%adm_m_Log_Event( "stored macro test", "Macro Test 4", "Start");

I get this error in my log

29         OPTIONS MSTORED SASMSTORE=maclib;
WARNING: Apparent invocation of macro ADM_M_LOG_EVENT not resolved.
30         
31         %adm_m_Log_Event( "stored macro test", "Macro Test 4", "Start");
           _
           180

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

What am I doing wrong?

Thanks for your help!

Jerry

1 ACCEPTED SOLUTION

Accepted Solutions
jimbarbour
Meteorite | Level 14

OK, good.

 

Now, look at the name of the macro you're calling vs. the macro you called.  Are they the same?

 

Jim

%MACRO adm_mLog_Event( PrjName, PrjTask, Desc ) 
%adm_m_Log_Event( "stored macro test", "Macro Test 4", "Start");

View solution in original post

12 REPLIES 12
jimbarbour
Meteorite | Level 14

Have you confirmed that the macro compiled correctly?  Can you see it when you go into the location you specified in the Libname?

 

It would probably also be useful if the macro in the definition were of the same name as the macro you are calling.  🙂

 

Jim

jblack38
Obsidian | Level 7

In the location where I saved it there is a file named

sasmacr.sas7bcat

jimbarbour
Meteorite | Level 14

OK, good.

 

Now, look at the name of the macro you're calling vs. the macro you called.  Are they the same?

 

Jim

%MACRO adm_mLog_Event( PrjName, PrjTask, Desc ) 
%adm_m_Log_Event( "stored macro test", "Macro Test 4", "Start");
jblack38
Obsidian | Level 7
Thank you Jim!
I'm sorry I didn't look more closely at the code before I submitted a question.
Thank you for your patience!
Jerry
jimbarbour
Meteorite | Level 14

No worries!  Sometimes it takes a second pair of eyes.  Now, you know my approach:  Confirm compilation, then look for errors.  Maybe you're already doing that; if so I apologize for belaboring the point.  It's just that I've spent a long time sometimes researching some piece of SAS code only to realize that it hadn't compiled correctly in the first place.

 

Incidentally, I personally have found compiled macros to not be worth the effort.  Too many times I've had the library locked when I needed to make a correction or an update.  I've also found it to be inflexible, particularly if you're working on multiple machines.

 

Instead, the autocall feature of SAS macros is, to me, the most flexible.

You would insert your project or group library into the "stack" that SAS maintains.  Say you call %adm_mLog_Event.  SAS will search for a file named adm_mLog_Event.sas -- case sensitive in UNIX/LINUX -- in the first path.  If not found, then SAS will go to the next path, and so on.  I do an INSERT and pre-pend my project, personal, or group macro paths as appropriate, and SAS searches my paths before going to the SAS-provided macro paths.

 

This is nice for development because you can prepend your development library in just your session before your production library.  Thus you can test your new code without affecting anyone who needs to use the production version.

 

Flexible, fast, no locking.  The auto-call feature is the best option that I've found.

 

Sample INSERT command of a project library:

OPTION INSERT = (SasAutos="I:\commercial\&Environment\OPSI\pgm\support_pgm\macro");

 

Jim

 

jblack38
Obsidian | Level 7
I appreciate your reminders. I usually am very methodical about debugging. But sometimes I get in a hurry.
This macro is for our production environment. One of the articles I read recommended compiled macros for production and autocall was good for development.
What are your recommendations?
jimbarbour
Meteorite | Level 14

Well, compiled macros are faster -- you don't have to compile them when you run.  But I think the speed advantage is pretty minimal.  Macros typically compile in a very short period of time.  When a macro is brought in via autocall, it is compiled, and the compile persists for the duration of the SAS session.  In other words, the macro is not compiled every time it is executed.  If the macro had to be compiled each and every time it was called, then yes, pre-compiled macros might be well worth it.  But this is not the case.  It is one, typically split-second, compile per session.

 

I suppose that if there were enough macros in a system, that maybe (maybe) pre-compiled macros would improve speed somewhat, perhaps a few seconds.  However, usually it is I/O or computation speed, not compilation, that is the limiting factor in job execution time.  If one is processing a large amount of data with SAS, the macro compilation time just isn't even in the same category.  We have one job that runs about 6 hours.  Of that 6 hours, perhaps 3 seconds are consumed by macro compilation (and we use a lot of macros).  It's just not worth it to me to have the rigidity of compiled macros vs. the flexibility of auto-call macros to save a few seconds, particularly when those seconds really don't make a meaningful difference.

 

We have four production SAS environments that I know about, probably more that I don't.  It's super nice to be able to just copy a macro from one environment to another and be done.  There is no other than step placing the correctly named .sas file in a path that is in the autocall hierarchy.  As soon as someone puts a % followed by the macro's name, SAS goes out, retrieves the correspondingly named .sas file from the first path it is included in, and compiles it for you.

 

You do need to name the macro the same as the filename -- the exact name -- which is case sensitive in Linux/UNIX.  You cannot put multiple macros in one physical file.  There needs to be a 1:1 correspondence between macro definitions (i.e. SAS code) and file names.

 

I suspect that the recommendation for compiled macros is perhaps old or perhaps made by someone who learned of that advice some time ago.  I think the present day SAS compilers (and present day machines) are so fast that macro compile times really aren't relevant in most cases.  Now, perhaps if someone had a monster statistical macro that went on for pages that maybe the compile time could be more significant, but I'm thinking that if one is doing that much number crunching that one's SAS job will be computationally bound and that the compile time in comparison to the statistical calculations will be immaterial.  I of course have not worked in every shop every where, so maybe there are exceptions, but in practice, I have found little if any down side and lots of up side to using the autocall feature.

 

Jim

jblack38
Obsidian | Level 7
Hi Jim,
I appreciate the detailed reply. I will definitely give the auto-call a try. My macros are so small, speed is not an issue.
Thank you for taking the time to share your expertise.
Jerry
jimbarbour
Meteorite | Level 14

@jblack38 wrote:
Thank you for taking the time to share your expertise.

Such as it is.  🙂  (although I do do a lot of macro programming)

 

It would be interesting to pose the question here on this forum:  What are the advantages and disadvantages of using compiled macros vs. autocall macros?

 

Jim

mkeintz
PROC Star

 

I haven't needed to use stored compiled macros, but I believe a major advantage (and also a major disadvantage) is that the stored source is no longer needed.  So if the macro is proprietary, this probably serves as a type of encryption, a possible advantage.    Of course, it means you better have a location where you keep the source for a compiled macro.  Otherwise you can have a working macro, whose logic you no longer can asses, a possible disadvantage.

--------------------------
The hash OUTPUT method will overwrite a SAS data set, but not append. That can be costly. Consider voting for Add a HASH object method which would append a hash object to an existing SAS data set

Would enabling PROC SORT to simultaneously output multiple datasets be useful? Then vote for
Allow PROC SORT to output multiple datasets

--------------------------
jimbarbour
Meteorite | Level 14

If you want the contents of a Macro to not be ferreted out, SAS does provide the / SECURE option for compiled macros, but one does run the risk of losing the source code and being unable to modify or maintain a macro.  See:  https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/mcrolref/p1nypovnwon4uyn159rst8pgzqrl.htm

 

With compiled macros, one also runs the risk of having the source code not match the compile.  Someonr could code and compile in development, get everything working, and then move the code to production where for whatever reason it didn't get re-compiled.  When some future problem occurs, it may be quite perplexing to debug because the code one is looking at is not the code that was executed. 

 

With auto call macros, what you see is what you get, depending on which library the macro source code was drawn from. You could run into a problem here if you don't have good implementation procedures.  If you have multiple versions of a given macro in the calling chain, the MAUTOLOCDISPLAY option can be really helpful, but it can also give you a really unwieldy log, so use sparingly.  If you have macros that call macros that call macros, it can be a bit daunting to trace everything, but sometimes that is what is required during debugging.  The SAS MPRINT option can be another friend or it's big cousin MFILE (which is basically the same as MPRINT but writes to a file separate from the log), again used somewhat sparingly; not recommended for production.

 

Jim

jblack38
Obsidian | Level 7

Thanks again Jim for all of your help!

I finally got all three versions of referencing saved macros working (%include, autocall, compiled and stored), after struggling with syntax.

I want to post my EG autocall and compiled solutions here, since I found it difficult to find detailed examples in my searches.  These worked for me. 

To use the autocall feature to reference saved macros, you first have to save the macro code in a .sas in some folder visible from the server.  Here is the code for my Log Event macro.  I saved the adm_m_Log_Event.sas file in \\sasdev\CNM_EG_Projects\macros.  The filename must match the name of the macro.

/* 	adm_mLog_Event
	Macro used to log events in admData.EG_Event_Times
	This version is set up to be called using auto-call or % include
*/

%MACRO adm_m_Log_Event( PrjName, PrjTask, Desc );

%PUT &PrjName ": " &PrjTask ": " &Desc;
libname adm_dta meta library="Admin Data" metaout=data;

%let theTime = %sysfunc(datetime(), datetime20.);
%put &theTime;

data New_Record;
format  Time_of_Day datetime20.;
format ProjectName $40. ProjectTask $40. Description $80.;
	Time_of_Day=input("&theTime",datetime20.);
	ProjectName=&PrjName;
	ProjectTask=&PrjTask;
	Description=&Desc;
run;

proc append base=adm_dta.EG_Event_Times
			data=New_Record;
run;
%MEND;

Here is the code I use to call the macro

OPTIONS 
	mautosource
	SasAutos=('S:\SASdata\CNM_EG_Projects\macros', sasautos)
;

%adm_m_Log_Event( "auto-call macro test 7/30", "Macro Test 1", "Start. ");

Now the code for saving and referencing a stored and compiled macro.  Note, the macro definition contains some additional options.

libname maclib '\\sasdev\CNM_EG_Projects\macros\macroLib';
OPTIONS MSTORED SASMSTORE=maclib;

%MACRO adm_mLog_Event( PrjName, PrjTask, Desc ) 
	/STORE
	DES='Log EG events in admData.EG_Event_Times';

       < same body as before >

%MEND;

When you run this code, sasmacr.sas7bcat is created in \\sasdev\CNM_EG_Projects\macros\macroLib.  

To reference this macro in another program

OPTIONS MSTORED SASMSTORE=maclib;
libname maclib 'S:\SASdata\CNM_EG_Projects\macros\macroLib';

%adm_mLog_Event( "stored macro test 7/30", "Macro Test", "Start. Ref S: on SASdev");

It's important to remember that when you're running a program in EG, you are connected to the server.  So your code can only see folders that are visible from the server.

Most of the issues I had were related to typos and not understanding syntax.  I still don't fully understand the syntax for updating SASAUTOS.  There are lots of different versions I found.  The one in my code above is what I got to work.

Ready to join fellow brilliant minds for the SAS Hackathon?

Build your skills. Make connections. Enjoy creative freedom. Maybe change the world. Registration is now open through August 30th. Visit the SAS Hackathon homepage.

Register today!
How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 12 replies
  • 1809 views
  • 3 likes
  • 3 in conversation