Search code examples
unixmakefilels

How to get non-recursive list of directory contents with their paths in Makefile?


My project's directory structure is as follows:

enter image description here

My Makefile looks like this:

dir1_contents = $(shell ls dir1/*)
dir3_contents = $(shell ls dir3/*)

all: clean_dir1 clean_dir3

clean_dir1:
    echo 'dir1_contents = $(dir1_contents)'

clean_dir3:
    echo 'dir3_contents = $(dir3_contents)'

When I run make, this is what I get:

$ pwd
make-test

$ make -s
dir1_contents = dir1/file2.junk  dir1/dir2: file1.junk
dir3_contents = dir3/file3.junk

I want to get the contents of dir1 in dir1_contents. But I don't want the recursive contents. Just the contents that are immediately below the dir1 directory. How can I do it?

If I remove the /* from the first two lines of the Makefile, I get the right contents I want. But then they are missing their paths which I also need:

$ pwd
make-test

$ make -s
dir1_contents = dir2 file2.junk
dir3_contents = file3.junk

How can I get the right contents I want with the file paths that I need also?


Solution

  • The problem you're having is related to the way ls works, it's nothing to do with GNU make. If you run ls dir1/* then the shell expands the wildcard before invoking ls, so this is the same as running:

    ls dir1/dir2 dir1/file2.junk
    

    And when you ls a directory it shows the contents of the directory by default, so the output is:

    $ ls dir1/dir2 dir1/file2.junk
    dir1/file2.junk
    
    dir1/dir2:
    file1.junk
    

    (go ahead and try this at the command prompt). Since that's what the shell prints, that's the result you get back.

    If you just want to see the directories but not the files in the directories, you can add the -d option to the ls command:

    $ ls -d dir1/dir2 dir1/file2.junk
    dir1/file2.junk dir1/dir2
    

    Or, in the makefile:

    dir1_contents = $(shell ls -d dir1/*)
    dir3_contents = $(shell ls -d dir3/*)
    

    However, I think this is not a great way to do it anyway. Why not use GNU make's built-in wildcard function instead? In addition to being simpler to understand it's a LOT more portable and much more efficient (the above version requires invoking a shell and the ls program). I also recommend you use := not =, so that you only perform the wildcard operation one time:

    dir1_contents := $(wildcard dir1/*)
    dir3_contents := $(wildcard dir3/*)