2

I need to build some internal libraries that mix pure C files and CUDA files. In addition, different libraries may be built from the same sources with different preprocessor options.

I've managed to get something that works, but what happens is that I don't like the fact that I have to explicitly define the CUDA objects when they could be easily inferred from the _SOURCES var:

$ cat Makefile.am

AUTOMAKE_OPTIONS = foreign

NVCC=cc
cuda_libs = libfoobar1.a libfoobar2.a

noinst_LIBRARIES = $(cuda_libs)

foobar_sources = foo.c bar.cu
libfoobar1_a_SOURCES  = $(foobar_sources)
libfoobar1_a_CPPFLAGS = -DLIB1
libfoobar1_a_LIBADD   = libfoobar1_a-bar.o # CUDA object explicit definition 
libfoobar2_a_SOURCES  = $(foobar_sources)
libfoobar2_a_CPPFLAGS = -DLIB2
libfoobar2_a_LIBADD   = libfoobar2_a-bar.o # CUDA object explicit definition 

$(eval include cuda.mk) # why eval? Check comment #1

clean-local:
    rm -rf libfoobar2_a-bar.o libfoobar1_a-bar.o

$ cat cuda.mk

# $(get_conincal names)
get_canonical = $(subst .,_,$(1))

# $(call cuda_rule prefix) http://blog.jgc.org/2012/01/using-gnu-makes-define-and-eval-to.html
define cuda_rule
$(1)-%.o: %.cu
    $(NVCC) $(CUDA_CFLAGS) $($(1)_CPPFLAGS) --compiler-options="$($(1)_CFLAGS)" -c -o $$@ $$<
endef

$(foreach cuda_prefix,$(call get_canonical,$(cuda_libs)),$(eval $(call cuda_rule,$(cuda_prefix))))

If I try to generalize the CUDA objects definitions moving them to the rule generation with something like this:

define cuda_rule
$(1)_LIBADD = $(addsuffix .o,$(addprefix $(1)-,$(basename $(filter %.cu,$($(1)_SOURCES)))))
[...]
endef

make now seems to not get the rule, and does not know how to build the CUDA objects:

ar cru libfoobar1.a libfoobar1_a-foo.o libfoobar1_a-bar.o
ar: libfoobar1_a-bar.o: No such file or directory

Any ideas? Thanks in advance.

Albert

P.S. All files that you need if you want to play along with this

Albert
  • 169
  • 2
  • 7
  • 1
    This almost certainly isn't related but why are you `eval`-ing `include cuda.mk`? – Etan Reisner Nov 24 '15 at 14:01
  • Because otherwise automake parses the included file and when it founds thinks like `$(1)_LIBADD` does not like and will complain. http://stackoverflow.com/questions/7437116/is-there-a-way-to-tell-automake-not-to-interpret-part-of-the-automakefile – Albert Nov 24 '15 at 14:19
  • 1
    Ok. I think that answers your question here though. Since automake scans for `_LIBADD`/etc. at automake time you can't really expect it to work if you don't generate those variables until make time, can you? Unless you only need it to use the value of the variable at make time but these don't work that way I don't think. You'll probably need to do your looping at automake time if that's possible. – Etan Reisner Nov 24 '15 at 14:45
  • Thanks for the comments @EtanReisner you have pointed me to the solution – Albert Nov 24 '15 at 16:05

1 Answers1

2

It was all about how Makefiles are read and variables get expanded.

Examining the Makefile generated by automake one can see that $eval calls end up at the end of the Makefile. The order in the final Makefile seems to be: AM vars, user vars, AM rules and finally user rules. As we are modifying the prerequisites of a AM rule, we have to do it before the rule itself gets parsed because rule prerequisites have immediate expansion.

A rule is always expanded the same way, regardless of the form:

immediate : immediate ; deferred

         deferred

https://www.gnu.org/software/make/manual/html_node/Reading-Makefiles.html

As the result of the eval function being always the empty string, we can assign it to a disposable variable forcing immediate expansion. Then, it will be placed with user vars before AM rules definitions in the final Makefile.

ignore:=$(eval include cuda.mk)
Albert
  • 169
  • 2
  • 7