6

I am writing a Makefile and I get stuck on a filter function limitation. Indeed, filter takes only one wildcard.

What I would like to do is: I have a list a files, some matching the regexp blabla, some not. But for this I need 2 wildcards, thus i cannot use filter function.

I would like to split my original list in 2 lists, one containing all the element containing the blabla string (filter equivalent) and the other one containing the not matching one (filter-out equivalent).

thanks for your help.

user1654361
  • 359
  • 3
  • 11
  • 1
    Are you using GNU make or some other variant? And show your current code, it's not clear what the problem is. – Gilles 'SO- stop being evil' Sep 07 '12 at 10:20
  • 2
    Please provide an example list of files, filter expression and desired result. The way you describe it now, it is not clear to me why the `$(filter ...)` and `$(filter-out ...)` functions are not good enough for you. – Reinier Torenbeek Sep 07 '12 at 12:26
  • 1
    Thanks for your reply. I am using gnu make. I don't have code to show because I can't figure out how to write it. Say i have LIST= a_old_tt x_old_da a_new_da q_ty_we. I would like to sort LIST content in: LIST_OLD that contains all members of LIST containing the expression old (a_old_tt and x_old_da in my example), and LIST_NOT_OLD that contains all members of LIST not containing the expression old (a_new_da q_ty_we in my example). – user1654361 Sep 07 '12 at 14:28

3 Answers3

11

You can do this without running any external commands. Define the two macros

containing = $(foreach v,$2,$(if $(findstring $1,$v),$v))
not-containing = $(foreach v,$2,$(if $(findstring $1,$v),,$v))

Now you can do

LIST := a_old_tt x_old_da a_new_da q_ty_we
LIST_OLD := $(call containing,old,$(LIST))
LIST_NOT_OLD := $(call not-containing,old,$(LIST))
Idelic
  • 14,976
  • 5
  • 35
  • 40
  • +1, looks reasonable, personally I would implement it in the same way. – Eldar Abusalimov Sep 07 '12 at 22:01
  • Limiting if you need full regular expressions to compute the 'containing' boolean, but it works for this particular case. I personally wouldn't worry about the external commands, since the performance/inner loop of Make will most likely be in the target, in the build/compile command. – Clayton Stanley Sep 07 '12 at 23:59
  • 1
    Sure, as Beta said in his answer, `make` cannot deal with regular expressions at all, so if you need them (but OP doesn't), you obviously cannot use this answer. The first reaction to this problem is often to bring out the big guns and invoke an external command to do the matching, but as this case shows, that can be huge overkill. I avoid running external commands as much as possible, both for portability and speed. I have seen huge performance gains from getting rid of unnecessary external commands in large make-based build systems. – Idelic Sep 08 '12 at 01:41
  • This doesn't work for me as part of a target dependencies list using %. It just comes up as an empty string. Perhaps it's searching for a literal % sign? – Keith M Nov 01 '19 at 20:17
3

One of Make's greatest shortcomings is its poor ability to handle regular expressions. The functions filter and filter-out can't find "old" in the middle of a word. I'd suggest this hack:

NOT_OLD = $(shell echo $(LIST) | sed 's/[^ ]*old[^ ]* *//g')
OLD = $(filter-out $(NOT_OLD), $(LIST))
Beta
  • 96,650
  • 16
  • 149
  • 150
1

You could take advantage of your shell's more advanced string handling capabilities. Assuming that you have bash, you could use the following in your makefile:

LIST := a_old_tt x_old_da a_new_da q_ty_we
LIST_NOT_OLD := $(shell l=($(LIST)); echo $${l[@]//*old*})
LIST_OLD := $(filter-out $(LIST_NOT_OLD),$(LIST))

You can find an explanation of the bash string replacement mechanism in how to delete elements from an array based on a pattern. The double $ is required to keep the $ sign in the shell invocation.

Community
  • 1
  • 1
Reinier Torenbeek
  • 16,669
  • 7
  • 46
  • 69