Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does AWK not work correctly in a makefile? [duplicate]

NOTICE: Escaping not problem, sample from shell is only sample, in Makefile $$.

GNU Makefile man says why it's doesn't work:

Note that expansion using ‘%’ in pattern rules occurs after any variable or function expansions, which take place when the makefile is read.

--Orig. question:
In pure shell, the next script works correctly:

echo "test2.cpp src2/test2.cpp src1/test1.cpp src1/test.cpp" | \
awk 'BEGIN{RS=" "}{if(NR == 1) f=$0; else if(match($0, f)) print $0;}'

Filter is first: test1.cpp
And it returns: src1/test1.cpp

But in Makefile it does not work correctly (error compared to awk):

OBJ_DIR:=obj    
SOURCES:=$(wildcard */*.cpp *.cpp)
OBJECTS_LOCAL:= $(addprefix $(OBJ_DIR)/, $(notdir $(SOURCES:.cpp=.o)))
LOCAL_PATHS_HEADERS:=$(sort $(dir $(wildcard *.h */*.h)))

TARGET:=libcommon.a

all:$(TARGET)

$(TARGET): $(OBJECTS_LOCAL)
        ar -rcs $@ $^

$(OBJECTS_LOCAL): $(OBJ_DIR)/%.o : $(shell echo %.cpp $(SOURCES) | awk 'BEGIN{RS=" "}{if(NR == 1) f=$$0; else if($$0 ~ f) print $$0;}' )
        @mkdir -p $(OBJ_DIR)
        @$(CC) -c $< -o $@ $(addprefix -I,$(LOCAL_PATHS_HEADERS))

So I take simple in Makefile and check value f, and found some strange length of f

...%.cpp $(SOURCES)  | awk '{print ("file1.cpp" ~ $$1)"."$$1"."length($$1)}' )

awk return fail in compared; print returns "0.file1.cpp.5" to fail with length, because it has forgotten .cppvalue of %, info bellow. I attempted to correct it:

...%.cpp $(SOURCES) | awk 'BEGIN{RS=" "}{if(NR == 1) f=$$0".cpp"; print ("file1.cpp.cpp" ~ f)"."("file1.cpp" ~ f)"."f"."length(f)}' )

but awk return fail in all compared; print returns "0.0.file1.cpp.cpp.9".

I check awk in manual mode, like this:

...%.cpp $(SOURCES) : $(shell echo %.cpp $(SOURCES) | awk '{print "src/"$$1}' )

It works fine, but it isn't variant, because it will kill automatic mode.

--Add
Information about lost length from parameter % to AWK

...%.cppppppp $(SOURCES) | awk '{print ("file1.cpp" ~ $$1)"."$$1"."length($$1)}' )

print returns "0.test2.cppppppp.10"

--Upd, some problem
Above, I was printing return value from $<
But file redirect show that value % does not work in prerequisites(file redirect: "0.%.cpp.5").

Can I use any automatic variable with value in prerequisites?

like image 259
Anton Riab Avatar asked May 25 '15 19:05

Anton Riab


People also ask

Is awk '{ print $2?

awk '{ print $2; }' prints the second field of each line. This field happens to be the process ID from the ps aux output. xargs kill -${2:-'TERM'} takes the process IDs from the selected sidekiq processes and feeds them as arguments to a kill command.

What does $1 $2 indicate in the awk file?

The variable $1 represents the contents of field 1 which in Figure 2 would be "-rwxr-xr-x." $2 represents field 2 which is "1" in Figure 2 and so on. The awk variables $1 or $2 through $nn represent the fields of each record and should not be confused with shell variables that use the same style of names.

What is awk '{ print $1 }'?

The awk is a very powerful command or interpreted scripting language to process different text or string data. The awk is generally used to process command output or text or configuration files. The awk provides '{print $1}' command in order to print the first column for the specified file or output.

Does awk change the file?

All files are changed in-place. In our awk code, we don't have to handle each file separately or manually control the redirection. Instead, we just change the text as the awk command reads each line from the files.


1 Answers

Almost invariably, when a question is asked about awk in a Makefile, the solution is to properly escape the $ symbols. It's not entirely clear what your question is, but there are some substantial misunderstandings that need to be resolved. In particular, the following "works", but hardly for the reasons you think:

echo "test2.cpp src2/test2.cpp src1/test1.cpp src1/test.cpp" | \
awk 'BEGIN{RS=" "}{if(NR == 1) f=$$0; else if(match($$0, f)) print $$0;}'

You almost certainly do not want $$ in any of the cases they appear here. awk is generally looking for single dollar signs, and when they appear in a Makefile, they are doubled because Make parses the $$ and invokes awk with a single $. In the quoted sample, $$0 on the first record is equivalent to $test2.cpp, but the variable test2.cpp is uninitialized and so has value 0, so on the first pass f is set to the value of $0 (the string "test2.cpp").

In short, if you are invoking awk from the shell, use single $. In the Makefile, use $$ and awk will only see $.

like image 89
William Pursell Avatar answered Sep 22 '22 17:09

William Pursell