Makefile targets as cross product of two lists











up vote
1
down vote

favorite












I spent the better part of the day trying to figure this out. I want to use make for building a golang project with different os targets and multiple binaries. I think I want to use gnu make



My stripped down Makefile looks like:



main= mailworker websocket worker init
os= linux freebsd darwin

all: $(main)

$(main): ensure_build_path
@echo "Build $@ Version: $(GIT_REV)"
@rm -rf $(BUILD_PREFIX)/$@
GO111MODULE=on GOARCH=amd64 $(GO) build -o $(BUILD_PREFIX)/$@ ./bin/$@/$@.go


Now I want to add GOOS=$(os) to the build command / generate a target for every main X os combination. My test / lern Makefile looks like:



os=linux freebsd darwin
main=mailworker websocket worker init

define build_template =
t:=$(addprefix $(1), $(2))
$(info $(t))
$(t):
@echo $(1) $(2) $$@
endef

$(foreach P, $(main), $(foreach O, $(os), $(eval $$(call build_template, $O, $P))) )


There are at least a dozen variants of this I tried. I think my main problem is how to declare t inside the template. I tried many "concat" methods I found regarding make. Also := or = for t. I think := is the right one. But I am not sure about anything anymore ;)



To be clear, I want make to "generate" targets that look like:



mailworker_linux:
GO111MODULE=on GOOS=linux GOARCH=amd64 $(GO) build mailworker

mailworker_darwin:
GO111MODULE=on GOOS=darwin GOARCH=amd64 $(GO) build mailworker


Is this a good way doing this? And if so, where is my misunderstanding.



Thanks a lot










share|improve this question


























    up vote
    1
    down vote

    favorite












    I spent the better part of the day trying to figure this out. I want to use make for building a golang project with different os targets and multiple binaries. I think I want to use gnu make



    My stripped down Makefile looks like:



    main= mailworker websocket worker init
    os= linux freebsd darwin

    all: $(main)

    $(main): ensure_build_path
    @echo "Build $@ Version: $(GIT_REV)"
    @rm -rf $(BUILD_PREFIX)/$@
    GO111MODULE=on GOARCH=amd64 $(GO) build -o $(BUILD_PREFIX)/$@ ./bin/$@/$@.go


    Now I want to add GOOS=$(os) to the build command / generate a target for every main X os combination. My test / lern Makefile looks like:



    os=linux freebsd darwin
    main=mailworker websocket worker init

    define build_template =
    t:=$(addprefix $(1), $(2))
    $(info $(t))
    $(t):
    @echo $(1) $(2) $$@
    endef

    $(foreach P, $(main), $(foreach O, $(os), $(eval $$(call build_template, $O, $P))) )


    There are at least a dozen variants of this I tried. I think my main problem is how to declare t inside the template. I tried many "concat" methods I found regarding make. Also := or = for t. I think := is the right one. But I am not sure about anything anymore ;)



    To be clear, I want make to "generate" targets that look like:



    mailworker_linux:
    GO111MODULE=on GOOS=linux GOARCH=amd64 $(GO) build mailworker

    mailworker_darwin:
    GO111MODULE=on GOOS=darwin GOARCH=amd64 $(GO) build mailworker


    Is this a good way doing this? And if so, where is my misunderstanding.



    Thanks a lot










    share|improve this question
























      up vote
      1
      down vote

      favorite









      up vote
      1
      down vote

      favorite











      I spent the better part of the day trying to figure this out. I want to use make for building a golang project with different os targets and multiple binaries. I think I want to use gnu make



      My stripped down Makefile looks like:



      main= mailworker websocket worker init
      os= linux freebsd darwin

      all: $(main)

      $(main): ensure_build_path
      @echo "Build $@ Version: $(GIT_REV)"
      @rm -rf $(BUILD_PREFIX)/$@
      GO111MODULE=on GOARCH=amd64 $(GO) build -o $(BUILD_PREFIX)/$@ ./bin/$@/$@.go


      Now I want to add GOOS=$(os) to the build command / generate a target for every main X os combination. My test / lern Makefile looks like:



      os=linux freebsd darwin
      main=mailworker websocket worker init

      define build_template =
      t:=$(addprefix $(1), $(2))
      $(info $(t))
      $(t):
      @echo $(1) $(2) $$@
      endef

      $(foreach P, $(main), $(foreach O, $(os), $(eval $$(call build_template, $O, $P))) )


      There are at least a dozen variants of this I tried. I think my main problem is how to declare t inside the template. I tried many "concat" methods I found regarding make. Also := or = for t. I think := is the right one. But I am not sure about anything anymore ;)



      To be clear, I want make to "generate" targets that look like:



      mailworker_linux:
      GO111MODULE=on GOOS=linux GOARCH=amd64 $(GO) build mailworker

      mailworker_darwin:
      GO111MODULE=on GOOS=darwin GOARCH=amd64 $(GO) build mailworker


      Is this a good way doing this? And if so, where is my misunderstanding.



      Thanks a lot










      share|improve this question













      I spent the better part of the day trying to figure this out. I want to use make for building a golang project with different os targets and multiple binaries. I think I want to use gnu make



      My stripped down Makefile looks like:



      main= mailworker websocket worker init
      os= linux freebsd darwin

      all: $(main)

      $(main): ensure_build_path
      @echo "Build $@ Version: $(GIT_REV)"
      @rm -rf $(BUILD_PREFIX)/$@
      GO111MODULE=on GOARCH=amd64 $(GO) build -o $(BUILD_PREFIX)/$@ ./bin/$@/$@.go


      Now I want to add GOOS=$(os) to the build command / generate a target for every main X os combination. My test / lern Makefile looks like:



      os=linux freebsd darwin
      main=mailworker websocket worker init

      define build_template =
      t:=$(addprefix $(1), $(2))
      $(info $(t))
      $(t):
      @echo $(1) $(2) $$@
      endef

      $(foreach P, $(main), $(foreach O, $(os), $(eval $$(call build_template, $O, $P))) )


      There are at least a dozen variants of this I tried. I think my main problem is how to declare t inside the template. I tried many "concat" methods I found regarding make. Also := or = for t. I think := is the right one. But I am not sure about anything anymore ;)



      To be clear, I want make to "generate" targets that look like:



      mailworker_linux:
      GO111MODULE=on GOOS=linux GOARCH=amd64 $(GO) build mailworker

      mailworker_darwin:
      GO111MODULE=on GOOS=darwin GOARCH=amd64 $(GO) build mailworker


      Is this a good way doing this? And if so, where is my misunderstanding.



      Thanks a lot







      makefile gnu-make






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 9 at 12:20









      huehnerhose

      16115




      16115
























          1 Answer
          1






          active

          oldest

          votes

















          up vote
          4
          down vote



          accepted










          You have a few problems here. First, your invocation of call is incorrect: you're escaping it which means it won't be expanded before it's given to eval; that's not correct it must be expanded first. You want:



          ..., $(eval $(call build_template,$P,$O))..


          (only one $ before call.



          Second, the rule for a variable you define that is used with an eval/call pair is that every macro expansion inside that variable that you want to be seen by the eval has to be escaped. In your case you did escape the $@ as $$@ which is good, but the other thing you need to escape is uses of $(t), because that variable is not set until the eval sets it. So you need to write your variable like this:



          define build_template =
          t := $(addprefix $(1)_,$(2))
          $$(info $$(t))
          $$(t):
          @echo $(1) $(2) $$@
          endef


          (I added in the missing _ to your addprefix). It doesn't matter whether you use = or := here; they both work the same since you're always using this variable in an immediate-expansion context.



          As an aside, it seems everyone runs straight to eval and call which are extremely powerful but also difficult to use and understand. An alternative that uses more straightforward make constructs might be something like this:



          getmain = $(word 1,$(subst _, ,$@))
          getos = $(word 2,$(subst _, ,$@))

          TARGETS := $(foreach P,$(main),$(addprefix $P_,$(os)))

          all: $(TARGETS)

          $(TARGETS): %:
          @echo $(getmain) $(getos) $@





          share|improve this answer





















            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53225617%2fmakefile-targets-as-cross-product-of-two-lists%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            4
            down vote



            accepted










            You have a few problems here. First, your invocation of call is incorrect: you're escaping it which means it won't be expanded before it's given to eval; that's not correct it must be expanded first. You want:



            ..., $(eval $(call build_template,$P,$O))..


            (only one $ before call.



            Second, the rule for a variable you define that is used with an eval/call pair is that every macro expansion inside that variable that you want to be seen by the eval has to be escaped. In your case you did escape the $@ as $$@ which is good, but the other thing you need to escape is uses of $(t), because that variable is not set until the eval sets it. So you need to write your variable like this:



            define build_template =
            t := $(addprefix $(1)_,$(2))
            $$(info $$(t))
            $$(t):
            @echo $(1) $(2) $$@
            endef


            (I added in the missing _ to your addprefix). It doesn't matter whether you use = or := here; they both work the same since you're always using this variable in an immediate-expansion context.



            As an aside, it seems everyone runs straight to eval and call which are extremely powerful but also difficult to use and understand. An alternative that uses more straightforward make constructs might be something like this:



            getmain = $(word 1,$(subst _, ,$@))
            getos = $(word 2,$(subst _, ,$@))

            TARGETS := $(foreach P,$(main),$(addprefix $P_,$(os)))

            all: $(TARGETS)

            $(TARGETS): %:
            @echo $(getmain) $(getos) $@





            share|improve this answer

























              up vote
              4
              down vote



              accepted










              You have a few problems here. First, your invocation of call is incorrect: you're escaping it which means it won't be expanded before it's given to eval; that's not correct it must be expanded first. You want:



              ..., $(eval $(call build_template,$P,$O))..


              (only one $ before call.



              Second, the rule for a variable you define that is used with an eval/call pair is that every macro expansion inside that variable that you want to be seen by the eval has to be escaped. In your case you did escape the $@ as $$@ which is good, but the other thing you need to escape is uses of $(t), because that variable is not set until the eval sets it. So you need to write your variable like this:



              define build_template =
              t := $(addprefix $(1)_,$(2))
              $$(info $$(t))
              $$(t):
              @echo $(1) $(2) $$@
              endef


              (I added in the missing _ to your addprefix). It doesn't matter whether you use = or := here; they both work the same since you're always using this variable in an immediate-expansion context.



              As an aside, it seems everyone runs straight to eval and call which are extremely powerful but also difficult to use and understand. An alternative that uses more straightforward make constructs might be something like this:



              getmain = $(word 1,$(subst _, ,$@))
              getos = $(word 2,$(subst _, ,$@))

              TARGETS := $(foreach P,$(main),$(addprefix $P_,$(os)))

              all: $(TARGETS)

              $(TARGETS): %:
              @echo $(getmain) $(getos) $@





              share|improve this answer























                up vote
                4
                down vote



                accepted







                up vote
                4
                down vote



                accepted






                You have a few problems here. First, your invocation of call is incorrect: you're escaping it which means it won't be expanded before it's given to eval; that's not correct it must be expanded first. You want:



                ..., $(eval $(call build_template,$P,$O))..


                (only one $ before call.



                Second, the rule for a variable you define that is used with an eval/call pair is that every macro expansion inside that variable that you want to be seen by the eval has to be escaped. In your case you did escape the $@ as $$@ which is good, but the other thing you need to escape is uses of $(t), because that variable is not set until the eval sets it. So you need to write your variable like this:



                define build_template =
                t := $(addprefix $(1)_,$(2))
                $$(info $$(t))
                $$(t):
                @echo $(1) $(2) $$@
                endef


                (I added in the missing _ to your addprefix). It doesn't matter whether you use = or := here; they both work the same since you're always using this variable in an immediate-expansion context.



                As an aside, it seems everyone runs straight to eval and call which are extremely powerful but also difficult to use and understand. An alternative that uses more straightforward make constructs might be something like this:



                getmain = $(word 1,$(subst _, ,$@))
                getos = $(word 2,$(subst _, ,$@))

                TARGETS := $(foreach P,$(main),$(addprefix $P_,$(os)))

                all: $(TARGETS)

                $(TARGETS): %:
                @echo $(getmain) $(getos) $@





                share|improve this answer












                You have a few problems here. First, your invocation of call is incorrect: you're escaping it which means it won't be expanded before it's given to eval; that's not correct it must be expanded first. You want:



                ..., $(eval $(call build_template,$P,$O))..


                (only one $ before call.



                Second, the rule for a variable you define that is used with an eval/call pair is that every macro expansion inside that variable that you want to be seen by the eval has to be escaped. In your case you did escape the $@ as $$@ which is good, but the other thing you need to escape is uses of $(t), because that variable is not set until the eval sets it. So you need to write your variable like this:



                define build_template =
                t := $(addprefix $(1)_,$(2))
                $$(info $$(t))
                $$(t):
                @echo $(1) $(2) $$@
                endef


                (I added in the missing _ to your addprefix). It doesn't matter whether you use = or := here; they both work the same since you're always using this variable in an immediate-expansion context.



                As an aside, it seems everyone runs straight to eval and call which are extremely powerful but also difficult to use and understand. An alternative that uses more straightforward make constructs might be something like this:



                getmain = $(word 1,$(subst _, ,$@))
                getos = $(word 2,$(subst _, ,$@))

                TARGETS := $(foreach P,$(main),$(addprefix $P_,$(os)))

                all: $(TARGETS)

                $(TARGETS): %:
                @echo $(getmain) $(getos) $@






                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 9 at 13:58









                MadScientist

                45.3k44865




                45.3k44865






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.





                    Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                    Please pay close attention to the following guidance:


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53225617%2fmakefile-targets-as-cross-product-of-two-lists%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    這個網誌中的熱門文章

                    Academy of Television Arts & Sciences

                    L'Équipe

                    1995 France bombings