Setting dynamic ORACLE_HOME in makefile

Viewed 560

So my organization is upgrading our Oracle database from 11g to 19c.

Previously, in my makefile, I had been setting ORACLE_HOME like this:

ORACLE_HOME=/opt/app/oracle/product/11.2.0.4/db_1

However, Oracle 19c has a fun feature that whenever they run a patch on it, the db_1 changes incrementally, becoming db_2, then db_3, with each patch, etc.

So obviously I can't hardcode the ORACLE_HOME path anymore.

In a bunch of my scripts, I'm pulling the current value from the ortab file, like this:

setenv ORACLE_SID DATABASE1
setenv ORACLE_HOME `cat /var/opt/oracle/oratab | sed 's/#.*//g' | grep -w $ORACLE_SID | awk -F: '{print $2;}'`

And this is working just fine, pulling the correct ORACLE_HOME path from the ortab file.

However, when I tried to do this in a makefile, like so:

ORACLE_SID=DATABASE1
ORACLE_HOME=`cat /var/opt/oracle/oratab | sed 's/#.*//g' | grep -w $ORACLE_SID | awk -F: '{print $2;}'`

I get this error when I try to run make:

 $ make
 `cat /var/opt/oracle/oratab | sed 's//bin/proc sys_include=/usr/include lines=yes iname=file1.pc oname=file1.c include=/path/to/include
 First RE may not be null
 *** Error code 2
 make: Fatal error: Command failed for target `file1.o'

So obviously the command isn't working the way I'm expecting, but I am unsure how to fix it. How do I fix the command to work inside makefile? I'm running Solaris 11.
This is not GNU make, this is just the default make that comes with Solaris 11.

Adding more information:

My ortab file looks like this:

$cat /var/opt/oracle/oratab
DATABASE_TEST:/opt/app/oracle/product/11.2.0.4/db_7:Y
DATABASE1:/opt/app/oracle/product/19.0.0.0/db_3:N
DATABASE2:/opt/app/oracle/product/11.2.0.4/db_13:Y
DATABASE3:/opt/app/oracle/product/11.2.0.4/db_1:Y
DATABASE_PROD:/opt/app/oracle/product/11.2.0.4/db_2:Y

So, what I need to do, is using the ORACLE_SID of DATABASE1, pull out the /opt/app/oracle/product/19.0.0.0/db_3 part, to use as my ORACLE_HOME directory in the makefile.

Update: Based on an answer below from MadScientist , this is now my makefile:

ORACLE_SID=DATABASE1
#ORACLE_HOME = /opt/app/oracle/product/19.0.0/db_3
ORACLE_HOME = `cat /var/opt/oracle/oratab | sed 's/\#.*//g' | grep -w ${ORACLE_SID} | awk -F: '{print $$2;}'`

PROC=${ORACLE_HOME}/bin/proc

E_INCLUDE=/path/to/include

print-%  : ; @echo $* = $($*)

file1.o: file1.pc
        ${PROC} sys_include=/usr/include lines=yes iname=$*.pc oname=$*.c include=${E_INCLUDE}

When I hardcode ORACLE_HOME, everything works correctly.

When I try to use the dynamically created ORACLE_HOME, I get this error:

$ make
`cat /var/opt/oracle/oratab | sed 's/\#.*//g' | grep -w DATABASE1 | awk -F: '{print $2;}'`/bin/proc sys_include=/usr/include lines=yes iname=file1.pc oname=file1.c include=/path/to/include
 make: Fatal error: Command failed for target `file1.o'

So it looks like it's setting ORACLE_HOME as the command itself, rather than as the result of the command.

Weirdly, when I run make print-ORACLE_HOME, I get the expected result /opt/app/oracle/product/19.0.0/db_3

3 Answers

Well, certainly this:

setenv ORACLE_HOME /opt/app/oracle/product/11.2.0.4/db_1

could not have been in your makefile before because this is not valid makefile syntax. Also, it surprises me that anyone is still using csh for anything, and especially scripting, in 2020. But anyway.

The problem you're having is that makefiles are not shell scripts and the rules of syntax are different. Of course a makefile contains shell scripts inside of it, but only in recipes: here you're setting a makefile variable. So just plopping a shell statement down into a variable assignment very well might not work.

Here you have three problems: first, variable reference in makefiles are of the form $(FOO) or ${FOO} but not $FOO. Second, a # is considered a comment character in a makefile and must be escaped. And finally, if you do want an actual $ not a variable reference you have to escape it, as: $$. Fixing those, this should work but note that there are likely simpler ways to do this:

ORACLE_SID = DATABASE1
ORACLE_HOME = `cat /var/opt/oracle/oratab | sed 's/\#.*//g' | grep -w $(ORACLE_SID) | awk -F: '{print $$2;}'`

You say that after this, this rule:

PROC=${ORACLE_HOME}/bin/proc

file1.o: file1.pc
        ${PROC} sys_include=/usr/include lines=yes iname=$*.pc oname=$*.c include=${E_INCLUDE}

Gives this output:

$ make
`cat /var/opt/oracle/oratab | sed 's/\#.*//g' | grep -w DATABASE1 | awk -F: '{print $2;}'`/bin/proc sys_include=/usr/include lines=yes iname=file1.pc oname=file1.c include=/path/to/include
 make: Fatal error: Command failed for target `file1.o'

That error message is not very helpful and means nothing. It's a shame this doesn't give a better message.

I recommend you change the rule to this:

file1.o: file1.pc
        echo PROC=\'${PROC}\'; ${PROC} sys_include=/usr/include lines=yes iname=$*.pc oname=$*.c include=${E_INCLUDE}

then, you should see something like this in the output:

$ make
echo PROC=\'`cat /var/opt/oracle/oratab...lots of stuff...
PROC='/...'
 make: Fatal error: Command failed for target `file1.o'

What you want to look at is the second line of the output, PROC='/...' and examine that path /..., whatever it is, to make sure it looks right. Also it should not contain any whitespace or other special characters, etc.

If that value that is printed looks wrong, you'll have to fix your script to make it right. If it looks right, then I have no idea what's going on and it must be something particular about the version of make you're using.

Here's a simplified example to start. This initial version uses awk to search, drops the sed absent any # comments

ORACLE_SID := DATABASE1
ORACLE_HOME := $(shell awk -F: "/^$(ORACLE_SID)/ { print \$$2; }" /var/opt/oracle/oratab)

(Update)  possible Solaris version from documents, unverified:

ORACLE_HOME:sh = awk -F: '/^DATABASE1/ { print $$2; }' /var/opt/oracle/oratab

I finally got it to work.

Using @Milag 's answer regarding the :sh command substitution, I was able to find the documentation regarding that for Solaris (not GNU) make: link to documentation

THIS was the answer:

ORACLE_HOME :sh =cat /var/opt/oracle/oratab | sed 's/\#.*//g' | grep -w DATABASE1 | awk -F: '{print $2;}'

The key? NOT putting a double dollar sign, as the documentation said:

"In contrast to commands in rules, the command is not subject for macro substitution; therefore, a dollar sign ($) need not be replaced with a double dollar sign ($$)."

Because of this I also had to hardcode DATABASE1 instead of using the ${ORACLE_SID} variable, but since that value will never change, I can live with that.

Related