[v3,bpf-next,07/14] samples: bpf: add makefile.target for separate CC target build
diff mbox series

Message ID 20190916105433.11404-8-ivan.khoronzhuk@linaro.org
State Superseded
Headers show
Series
  • samples: bpf: improve/fix cross-compilation
Related show

Commit Message

Ivan Khoronzhuk Sept. 16, 2019, 10:54 a.m. UTC
The makefile.target is added only and will be used in
sample/bpf/Makefile later in order to switch cross-compiling on CC
from HOSTCC environment.

The HOSTCC is supposed to build binaries and tools running on the host
afterwards, in order to simplify build or so, like "fixdep" or else.
In case of cross compiling "fixdep" is executed on host when the rest
samples should run on target arch. In order to build binaries for
target arch with CC and tools running on host with HOSTCC, lets add
Makefile.target for simplicity, having definition and routines similar
to ones, used in script/Makefile.host. This allows later add
cross-compilation to samples/bpf with minimum changes.

The tprog stands for target programs built with CC.

Makefile.target contains only stuff needed for samples/bpf, potentially
can be reused later and now needed only for unblocking tricky
samples/bpf cross compilation.

Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
---
 samples/bpf/Makefile.target | 75 +++++++++++++++++++++++++++++++++++++
 1 file changed, 75 insertions(+)
 create mode 100644 samples/bpf/Makefile.target

Comments

Andrii Nakryiko Sept. 17, 2019, 11:19 p.m. UTC | #1
On Mon, Sep 16, 2019 at 3:58 AM Ivan Khoronzhuk
<ivan.khoronzhuk@linaro.org> wrote:
>
> The makefile.target is added only and will be used in

typo: Makefile

> sample/bpf/Makefile later in order to switch cross-compiling on CC

on -> to

> from HOSTCC environment.
>
> The HOSTCC is supposed to build binaries and tools running on the host
> afterwards, in order to simplify build or so, like "fixdep" or else.
> In case of cross compiling "fixdep" is executed on host when the rest
> samples should run on target arch. In order to build binaries for
> target arch with CC and tools running on host with HOSTCC, lets add
> Makefile.target for simplicity, having definition and routines similar
> to ones, used in script/Makefile.host. This allows later add
> cross-compilation to samples/bpf with minimum changes.
>
> The tprog stands for target programs built with CC.

Why tprog? Could we just use prog: hostprog vs prog.

>
> Makefile.target contains only stuff needed for samples/bpf, potentially
> can be reused later and now needed only for unblocking tricky
> samples/bpf cross compilation.
>
> Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
> ---
>  samples/bpf/Makefile.target | 75 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 75 insertions(+)
>  create mode 100644 samples/bpf/Makefile.target
>
> diff --git a/samples/bpf/Makefile.target b/samples/bpf/Makefile.target
> new file mode 100644
> index 000000000000..fb6de63f7d2f
> --- /dev/null
> +++ b/samples/bpf/Makefile.target
> @@ -0,0 +1,75 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# ==========================================================================
> +# Building binaries on the host system
> +# Binaries are not used during the compilation of the kernel, and intendent

typo: intended

> +# to be build for target board, target board can be host ofc. Added to build

What's ofc, is it "of course"?

> +# binaries to run not on host system.
> +#
> +# Sample syntax (see Documentation/kbuild/makefiles.rst for reference)
> +# tprogs-y := xsk_example
> +# Will compile xdpsock_example.c and create an executable named xsk_example

You mix references to xsk_example and xdpsock_example, which is very
confusing. I'm guessing you meant to use xdpsock_example consistently.

> +#
> +# tprogs-y    := xdpsock
> +# xdpsock-objs := xdpsock_1.o xdpsock_2.o
> +# Will compile xdpsock_1.c and xdpsock_2.c, and then link the executable
> +# xdpsock, based on xdpsock_1.o and xdpsock_2.o
> +#
> +# Inherited from scripts/Makefile.host

"Inspired by" or "Derived from" would be probably more appropriate term :)

> +#
> +__tprogs := $(sort $(tprogs-y))
> +
> +# C code
> +# Executables compiled from a single .c file
> +tprog-csingle  := $(foreach m,$(__tprogs), \
> +                       $(if $($(m)-objs),,$(m)))
> +
> +# C executables linked based on several .o files
> +tprog-cmulti   := $(foreach m,$(__tprogs),\
> +                       $(if $($(m)-objs),$(m)))
> +
> +# Object (.o) files compiled from .c files
> +tprog-cobjs    := $(sort $(foreach m,$(__tprogs),$($(m)-objs)))
> +
> +tprog-csingle  := $(addprefix $(obj)/,$(tprog-csingle))
> +tprog-cmulti   := $(addprefix $(obj)/,$(tprog-cmulti))
> +tprog-cobjs    := $(addprefix $(obj)/,$(tprog-cobjs))
> +
> +#####
> +# Handle options to gcc. Support building with separate output directory
> +
> +_tprogc_flags   = $(TPROGS_CFLAGS) \
> +                 $(TPROGCFLAGS_$(basetarget).o)
> +
> +# $(objtree)/$(obj) for including generated headers from checkin source files
> +ifeq ($(KBUILD_EXTMOD),)
> +ifdef building_out_of_srctree
> +_tprogc_flags   += -I $(objtree)/$(obj)
> +endif
> +endif
> +
> +tprogc_flags    = -Wp,-MD,$(depfile) $(_tprogc_flags)
> +
> +# Create executable from a single .c file
> +# tprog-csingle -> Executable
> +quiet_cmd_tprog-csingle        = CC  $@
> +      cmd_tprog-csingle        = $(CC) $(tprogc_flags) $(TPROGS_LDFLAGS) -o $@ $< \
> +               $(TPROGS_LDLIBS) $(TPROGLDLIBS_$(@F))
> +$(tprog-csingle): $(obj)/%: $(src)/%.c FORCE
> +       $(call if_changed_dep,tprog-csingle)
> +
> +# Link an executable based on list of .o files, all plain c
> +# tprog-cmulti -> executable
> +quiet_cmd_tprog-cmulti = LD  $@
> +      cmd_tprog-cmulti = $(CC) $(tprogc_flags) $(TPROGS_LDFLAGS) -o $@ \
> +                         $(addprefix $(obj)/,$($(@F)-objs)) \
> +                         $(TPROGS_LDLIBS) $(TPROGLDLIBS_$(@F))
> +$(tprog-cmulti): $(tprog-cobjs) FORCE
> +       $(call if_changed,tprog-cmulti)
> +$(call multi_depend, $(tprog-cmulti), , -objs)
> +
> +# Create .o file from a single .c file
> +# tprog-cobjs -> .o
> +quiet_cmd_tprog-cobjs  = CC  $@
> +      cmd_tprog-cobjs  = $(CC) $(tprogc_flags) -c -o $@ $<
> +$(tprog-cobjs): $(obj)/%.o: $(src)/%.c FORCE
> +       $(call if_changed_dep,tprog-cobjs)
> --
> 2.17.1
>

tprogs is quite confusing, but overall looks good to me.
Ivan Khoronzhuk Sept. 18, 2019, 10:12 a.m. UTC | #2
On Tue, Sep 17, 2019 at 04:19:40PM -0700, Andrii Nakryiko wrote:
>On Mon, Sep 16, 2019 at 3:58 AM Ivan Khoronzhuk
><ivan.khoronzhuk@linaro.org> wrote:
>>
>> The makefile.target is added only and will be used in
>
>typo: Makefile
>
>> sample/bpf/Makefile later in order to switch cross-compiling on CC
>
>on -> to
>
>> from HOSTCC environment.
>>
>> The HOSTCC is supposed to build binaries and tools running on the host
>> afterwards, in order to simplify build or so, like "fixdep" or else.
>> In case of cross compiling "fixdep" is executed on host when the rest
>> samples should run on target arch. In order to build binaries for
>> target arch with CC and tools running on host with HOSTCC, lets add
>> Makefile.target for simplicity, having definition and routines similar
>> to ones, used in script/Makefile.host. This allows later add
>> cross-compilation to samples/bpf with minimum changes.
>>
>> The tprog stands for target programs built with CC.
>
>Why tprog? Could we just use prog: hostprog vs prog.
Prev. version was with prog, but Yonghong Song found it ambiguous.
As prog can be bpf also. So, decision was made to follow logic:
* target prog - non bpf progs
* bpf prog = bpf prog, that can be later smth similar, providing build options
  for each bpf object separately.

Details here:
https://lkml.org/lkml/2019/9/13/1037

>
>>
>> Makefile.target contains only stuff needed for samples/bpf, potentially
>> can be reused later and now needed only for unblocking tricky
>> samples/bpf cross compilation.
>>
>> Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
>> ---
>>  samples/bpf/Makefile.target | 75 +++++++++++++++++++++++++++++++++++++
>>  1 file changed, 75 insertions(+)
>>  create mode 100644 samples/bpf/Makefile.target
>>
>> diff --git a/samples/bpf/Makefile.target b/samples/bpf/Makefile.target
>> new file mode 100644
>> index 000000000000..fb6de63f7d2f
>> --- /dev/null
>> +++ b/samples/bpf/Makefile.target
>> @@ -0,0 +1,75 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +# ==========================================================================
>> +# Building binaries on the host system
>> +# Binaries are not used during the compilation of the kernel, and intendent
>
>typo: intended
>
>> +# to be build for target board, target board can be host ofc. Added to build
>
>What's ofc, is it "of course"?
yes, ofc )

>
>> +# binaries to run not on host system.
>> +#
>> +# Sample syntax (see Documentation/kbuild/makefiles.rst for reference)
>> +# tprogs-y := xsk_example
>> +# Will compile xdpsock_example.c and create an executable named xsk_example
>
>You mix references to xsk_example and xdpsock_example, which is very
>confusing. I'm guessing you meant to use xdpsock_example consistently.
Oh, yes. Thanks.

>
>> +#
>> +# tprogs-y    := xdpsock
>> +# xdpsock-objs := xdpsock_1.o xdpsock_2.o
>> +# Will compile xdpsock_1.c and xdpsock_2.c, and then link the executable
>> +# xdpsock, based on xdpsock_1.o and xdpsock_2.o
>> +#
>> +# Inherited from scripts/Makefile.host
>
>"Inspired by" or "Derived from" would be probably more appropriate term :)
I will replace with "Derived from", looks better.

>
>> +#
>> +__tprogs := $(sort $(tprogs-y))
>> +
>> +# C code
>> +# Executables compiled from a single .c file
>> +tprog-csingle  := $(foreach m,$(__tprogs), \
>> +                       $(if $($(m)-objs),,$(m)))
>> +
>> +# C executables linked based on several .o files
>> +tprog-cmulti   := $(foreach m,$(__tprogs),\
>> +                       $(if $($(m)-objs),$(m)))
>> +
>> +# Object (.o) files compiled from .c files
>> +tprog-cobjs    := $(sort $(foreach m,$(__tprogs),$($(m)-objs)))
>> +
>> +tprog-csingle  := $(addprefix $(obj)/,$(tprog-csingle))
>> +tprog-cmulti   := $(addprefix $(obj)/,$(tprog-cmulti))
>> +tprog-cobjs    := $(addprefix $(obj)/,$(tprog-cobjs))
>> +
>> +#####
>> +# Handle options to gcc. Support building with separate output directory
>> +
>> +_tprogc_flags   = $(TPROGS_CFLAGS) \
>> +                 $(TPROGCFLAGS_$(basetarget).o)
>> +
>> +# $(objtree)/$(obj) for including generated headers from checkin source files
>> +ifeq ($(KBUILD_EXTMOD),)
>> +ifdef building_out_of_srctree
>> +_tprogc_flags   += -I $(objtree)/$(obj)
>> +endif
>> +endif
>> +
>> +tprogc_flags    = -Wp,-MD,$(depfile) $(_tprogc_flags)
>> +
>> +# Create executable from a single .c file
>> +# tprog-csingle -> Executable
>> +quiet_cmd_tprog-csingle        = CC  $@
>> +      cmd_tprog-csingle        = $(CC) $(tprogc_flags) $(TPROGS_LDFLAGS) -o $@ $< \
>> +               $(TPROGS_LDLIBS) $(TPROGLDLIBS_$(@F))
>> +$(tprog-csingle): $(obj)/%: $(src)/%.c FORCE
>> +       $(call if_changed_dep,tprog-csingle)
>> +
>> +# Link an executable based on list of .o files, all plain c
>> +# tprog-cmulti -> executable
>> +quiet_cmd_tprog-cmulti = LD  $@
>> +      cmd_tprog-cmulti = $(CC) $(tprogc_flags) $(TPROGS_LDFLAGS) -o $@ \
>> +                         $(addprefix $(obj)/,$($(@F)-objs)) \
>> +                         $(TPROGS_LDLIBS) $(TPROGLDLIBS_$(@F))
>> +$(tprog-cmulti): $(tprog-cobjs) FORCE
>> +       $(call if_changed,tprog-cmulti)
>> +$(call multi_depend, $(tprog-cmulti), , -objs)
>> +
>> +# Create .o file from a single .c file
>> +# tprog-cobjs -> .o
>> +quiet_cmd_tprog-cobjs  = CC  $@
>> +      cmd_tprog-cobjs  = $(CC) $(tprogc_flags) -c -o $@ $<
>> +$(tprog-cobjs): $(obj)/%.o: $(src)/%.c FORCE
>> +       $(call if_changed_dep,tprog-cobjs)
>> --
>> 2.17.1
>>
>
>tprogs is quite confusing, but overall looks good to me.
I tend to leave it as tprogs, unless it's going to be progs and agreed with
Yonghong.

It follows logic:
- tprogs for bins
- bpfprogs or bojs or bprogs (could be) for bpf obj
Andrii Nakryiko Sept. 18, 2019, 9:22 p.m. UTC | #3
On Wed, Sep 18, 2019 at 3:12 AM Ivan Khoronzhuk
<ivan.khoronzhuk@linaro.org> wrote:
>
> On Tue, Sep 17, 2019 at 04:19:40PM -0700, Andrii Nakryiko wrote:
> >On Mon, Sep 16, 2019 at 3:58 AM Ivan Khoronzhuk
> ><ivan.khoronzhuk@linaro.org> wrote:
> >>
> >> The makefile.target is added only and will be used in
> >
> >typo: Makefile
> >
> >> sample/bpf/Makefile later in order to switch cross-compiling on CC
> >
> >on -> to
> >
> >> from HOSTCC environment.
> >>
> >> The HOSTCC is supposed to build binaries and tools running on the host
> >> afterwards, in order to simplify build or so, like "fixdep" or else.
> >> In case of cross compiling "fixdep" is executed on host when the rest
> >> samples should run on target arch. In order to build binaries for
> >> target arch with CC and tools running on host with HOSTCC, lets add
> >> Makefile.target for simplicity, having definition and routines similar
> >> to ones, used in script/Makefile.host. This allows later add
> >> cross-compilation to samples/bpf with minimum changes.
> >>
> >> The tprog stands for target programs built with CC.
> >
> >Why tprog? Could we just use prog: hostprog vs prog.
> Prev. version was with prog, but Yonghong Song found it ambiguous.
> As prog can be bpf also. So, decision was made to follow logic:
> * target prog - non bpf progs
> * bpf prog = bpf prog, that can be later smth similar, providing build options
>   for each bpf object separately.
>

Well, I'm not going to insist, but BPF program is a C function,
compiled BPF .o file is BPF object, so I don't think there is going to
be too much confusion to have progs and hostprogs in Makefile. But I'm
fine with tprog.

> Details here:
> https://lkml.org/lkml/2019/9/13/1037
>
> >
> >>
> >> Makefile.target contains only stuff needed for samples/bpf, potentially
> >> can be reused later and now needed only for unblocking tricky
> >> samples/bpf cross compilation.
> >>
> >> Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org>
> >> ---
> >>  samples/bpf/Makefile.target | 75 +++++++++++++++++++++++++++++++++++++
> >>  1 file changed, 75 insertions(+)
> >>  create mode 100644 samples/bpf/Makefile.target
> >>
> >> diff --git a/samples/bpf/Makefile.target b/samples/bpf/Makefile.target
> >> new file mode 100644
> >> index 000000000000..fb6de63f7d2f
> >> --- /dev/null
> >> +++ b/samples/bpf/Makefile.target
> >> @@ -0,0 +1,75 @@
> >> +# SPDX-License-Identifier: GPL-2.0
> >> +# ==========================================================================
> >> +# Building binaries on the host system
> >> +# Binaries are not used during the compilation of the kernel, and intendent
> >
> >typo: intended
> >
> >> +# to be build for target board, target board can be host ofc. Added to build
> >
> >What's ofc, is it "of course"?
> yes, ofc )

Alright, let's not try to save 5 letters, it's quite confusing.

>
> >
> >> +# binaries to run not on host system.
> >> +#
> >> +# Sample syntax (see Documentation/kbuild/makefiles.rst for reference)
> >> +# tprogs-y := xsk_example
> >> +# Will compile xdpsock_example.c and create an executable named xsk_example
> >
> >You mix references to xsk_example and xdpsock_example, which is very
> >confusing. I'm guessing you meant to use xdpsock_example consistently.
> Oh, yes. Thanks.
>
> >
> >> +#
> >> +# tprogs-y    := xdpsock
> >> +# xdpsock-objs := xdpsock_1.o xdpsock_2.o
> >> +# Will compile xdpsock_1.c and xdpsock_2.c, and then link the executable
> >> +# xdpsock, based on xdpsock_1.o and xdpsock_2.o
> >> +#
> >> +# Inherited from scripts/Makefile.host
> >
> >"Inspired by" or "Derived from" would be probably more appropriate term :)
> I will replace with "Derived from", looks better.
>

sounds good

> >
> >> +#
> >> +__tprogs := $(sort $(tprogs-y))
> >> +
> >> +# C code
> >> +# Executables compiled from a single .c file
> >> +tprog-csingle  := $(foreach m,$(__tprogs), \
> >> +                       $(if $($(m)-objs),,$(m)))
> >> +
> >> +# C executables linked based on several .o files
> >> +tprog-cmulti   := $(foreach m,$(__tprogs),\
> >> +                       $(if $($(m)-objs),$(m)))
> >> +
> >> +# Object (.o) files compiled from .c files
> >> +tprog-cobjs    := $(sort $(foreach m,$(__tprogs),$($(m)-objs)))
> >> +
> >> +tprog-csingle  := $(addprefix $(obj)/,$(tprog-csingle))
> >> +tprog-cmulti   := $(addprefix $(obj)/,$(tprog-cmulti))
> >> +tprog-cobjs    := $(addprefix $(obj)/,$(tprog-cobjs))
> >> +
> >> +#####
> >> +# Handle options to gcc. Support building with separate output directory
> >> +
> >> +_tprogc_flags   = $(TPROGS_CFLAGS) \
> >> +                 $(TPROGCFLAGS_$(basetarget).o)
> >> +
> >> +# $(objtree)/$(obj) for including generated headers from checkin source files
> >> +ifeq ($(KBUILD_EXTMOD),)
> >> +ifdef building_out_of_srctree
> >> +_tprogc_flags   += -I $(objtree)/$(obj)
> >> +endif
> >> +endif
> >> +
> >> +tprogc_flags    = -Wp,-MD,$(depfile) $(_tprogc_flags)
> >> +
> >> +# Create executable from a single .c file
> >> +# tprog-csingle -> Executable
> >> +quiet_cmd_tprog-csingle        = CC  $@
> >> +      cmd_tprog-csingle        = $(CC) $(tprogc_flags) $(TPROGS_LDFLAGS) -o $@ $< \
> >> +               $(TPROGS_LDLIBS) $(TPROGLDLIBS_$(@F))
> >> +$(tprog-csingle): $(obj)/%: $(src)/%.c FORCE
> >> +       $(call if_changed_dep,tprog-csingle)
> >> +
> >> +# Link an executable based on list of .o files, all plain c
> >> +# tprog-cmulti -> executable
> >> +quiet_cmd_tprog-cmulti = LD  $@
> >> +      cmd_tprog-cmulti = $(CC) $(tprogc_flags) $(TPROGS_LDFLAGS) -o $@ \
> >> +                         $(addprefix $(obj)/,$($(@F)-objs)) \
> >> +                         $(TPROGS_LDLIBS) $(TPROGLDLIBS_$(@F))
> >> +$(tprog-cmulti): $(tprog-cobjs) FORCE
> >> +       $(call if_changed,tprog-cmulti)
> >> +$(call multi_depend, $(tprog-cmulti), , -objs)
> >> +
> >> +# Create .o file from a single .c file
> >> +# tprog-cobjs -> .o
> >> +quiet_cmd_tprog-cobjs  = CC  $@
> >> +      cmd_tprog-cobjs  = $(CC) $(tprogc_flags) -c -o $@ $<
> >> +$(tprog-cobjs): $(obj)/%.o: $(src)/%.c FORCE
> >> +       $(call if_changed_dep,tprog-cobjs)
> >> --
> >> 2.17.1
> >>
> >
> >tprogs is quite confusing, but overall looks good to me.
> I tend to leave it as tprogs, unless it's going to be progs and agreed with
> Yonghong.
>
> It follows logic:
> - tprogs for bins
> - bpfprogs or bojs or bprogs (could be) for bpf obj

as mentioned above, we never build "BPF programs", they are always
part of BPF objects. But as I mentioned, I'm fine with sticking to
tprog.

>
> --
> Regards,
> Ivan Khoronzhuk

Patch
diff mbox series

diff --git a/samples/bpf/Makefile.target b/samples/bpf/Makefile.target
new file mode 100644
index 000000000000..fb6de63f7d2f
--- /dev/null
+++ b/samples/bpf/Makefile.target
@@ -0,0 +1,75 @@ 
+# SPDX-License-Identifier: GPL-2.0
+# ==========================================================================
+# Building binaries on the host system
+# Binaries are not used during the compilation of the kernel, and intendent
+# to be build for target board, target board can be host ofc. Added to build
+# binaries to run not on host system.
+#
+# Sample syntax (see Documentation/kbuild/makefiles.rst for reference)
+# tprogs-y := xsk_example
+# Will compile xdpsock_example.c and create an executable named xsk_example
+#
+# tprogs-y    := xdpsock
+# xdpsock-objs := xdpsock_1.o xdpsock_2.o
+# Will compile xdpsock_1.c and xdpsock_2.c, and then link the executable
+# xdpsock, based on xdpsock_1.o and xdpsock_2.o
+#
+# Inherited from scripts/Makefile.host
+#
+__tprogs := $(sort $(tprogs-y))
+
+# C code
+# Executables compiled from a single .c file
+tprog-csingle	:= $(foreach m,$(__tprogs), \
+			$(if $($(m)-objs),,$(m)))
+
+# C executables linked based on several .o files
+tprog-cmulti	:= $(foreach m,$(__tprogs),\
+			$(if $($(m)-objs),$(m)))
+
+# Object (.o) files compiled from .c files
+tprog-cobjs	:= $(sort $(foreach m,$(__tprogs),$($(m)-objs)))
+
+tprog-csingle	:= $(addprefix $(obj)/,$(tprog-csingle))
+tprog-cmulti	:= $(addprefix $(obj)/,$(tprog-cmulti))
+tprog-cobjs	:= $(addprefix $(obj)/,$(tprog-cobjs))
+
+#####
+# Handle options to gcc. Support building with separate output directory
+
+_tprogc_flags   = $(TPROGS_CFLAGS) \
+                 $(TPROGCFLAGS_$(basetarget).o)
+
+# $(objtree)/$(obj) for including generated headers from checkin source files
+ifeq ($(KBUILD_EXTMOD),)
+ifdef building_out_of_srctree
+_tprogc_flags   += -I $(objtree)/$(obj)
+endif
+endif
+
+tprogc_flags    = -Wp,-MD,$(depfile) $(_tprogc_flags)
+
+# Create executable from a single .c file
+# tprog-csingle -> Executable
+quiet_cmd_tprog-csingle 	= CC  $@
+      cmd_tprog-csingle	= $(CC) $(tprogc_flags) $(TPROGS_LDFLAGS) -o $@ $< \
+		$(TPROGS_LDLIBS) $(TPROGLDLIBS_$(@F))
+$(tprog-csingle): $(obj)/%: $(src)/%.c FORCE
+	$(call if_changed_dep,tprog-csingle)
+
+# Link an executable based on list of .o files, all plain c
+# tprog-cmulti -> executable
+quiet_cmd_tprog-cmulti	= LD  $@
+      cmd_tprog-cmulti	= $(CC) $(tprogc_flags) $(TPROGS_LDFLAGS) -o $@ \
+			  $(addprefix $(obj)/,$($(@F)-objs)) \
+			  $(TPROGS_LDLIBS) $(TPROGLDLIBS_$(@F))
+$(tprog-cmulti): $(tprog-cobjs) FORCE
+	$(call if_changed,tprog-cmulti)
+$(call multi_depend, $(tprog-cmulti), , -objs)
+
+# Create .o file from a single .c file
+# tprog-cobjs -> .o
+quiet_cmd_tprog-cobjs	= CC  $@
+      cmd_tprog-cobjs	= $(CC) $(tprogc_flags) -c -o $@ $<
+$(tprog-cobjs): $(obj)/%.o: $(src)/%.c FORCE
+	$(call if_changed_dep,tprog-cobjs)