How can I pass a argument to a makefile dependency? - makefile

I have the following Makefile where the all make target depends on a separate setup make target that also takes an argument. However when I make all the setup target is not invoked with the argument
setup:
...command
clean:
...command
all: setup myarg=value clean myarg=value
#echo "setup & clean"

I think what you're asking is if a prerequisite can inherit a target-specific variable. In which case, yes it can -- Note, in your example you tried to intersperse the target specific variables and the prerequisites, which you can't do. But beware -- this has sharp sticks attached. Consider the following makefile:
all:
setup:
#echo "building $#: myarg=$(myarg)"
all: myarg:=value
all: setup
#echo "building $#: myarg=$(myarg)"
blah: setup
#echo "building $#: myarg=$(myarg)"
If I do make all, I get:
tmp> make all
building setup: myarg=value
building all: myarg=value
Which is what you want. But if I do make blah, then setup is run as a prerequisite of blah, and does not have the value set as you might expect. It will not be rebuilt for main, even though the variable is different:
tmp> make blah all
building setup: myarg=
building blah: myarg=
building all: myarg=value
See the make manual for more details

The command line of the make program is not free-form. You can't just pass it a bunch of stuff and have that "stuff" passed through make to appear somehow inside your recipes. make can only take arguments that it's defined to take: all arguments (not options) are either targets or variable assignments. See the documentation or the man page.
It is not possible in general to pass arbitrary values on the make command line. However, as I said, make does allow variables to be set on its command line.
If you run make setup myarg=value then this will set the make variable myarg to have the value value, and ask make to build the setup target.
So, if you write your makefile:
setup:
...command $(myarg)
referencing the make variable myarg, then it will "work" (I guess, you haven't made clear exactly what you want to run using myarg).

Related

Makefile passing additional arguments to targets command from make command

I have a Makefile that defines docker-compose project.
It essentially assembles me a command:
COMMAND := docker-compose --project-name=$(PREFIX) --file=$(FILE_PATH)
up:
$(COMMAND) up -d
I would like to add a target named dc to which I would be able to pass any arguments I want.
I know there is one solution:
target:
$(COMMAND) $(ARGS)
And then call it with make target ARGS="--help" for example.
But isn't there an easier way like in bash $# ? I would like to skip the ARGS=... part and send everything to the command after target name.
Not really. The make program interprets all arguments (that don't contain =) as target names to be built and there's no way you can override that. So even though you can obtain the list of arguments given on the command line (via the GNU make-specific $(MAKECMDGOALS) variable) you can't prevent those arguments from being considered targets.
You could do something like this, which is incredibly hacky:
KNOWN_TARGETS = target
ARGS := $(filter-out $(KNOWN_TARGETS),$(MAKECMDGOALS))
.DEFAULT: ;: do nothing
.SUFFIXES:
target:
$(COMMAND) $(ARGS)
(untested). The problem here is you have to keep KNOWN_TARGETS up to date with all the "real" targets so you can remove them from the list of targets given on the command line. Then add the .DEFAULT target which will be run for any target make doesn't know how to build, which does nothing. Reset the .SUFFIXES meta-target to remove built-in rules.
I suspect this still will have weird edge-cases where it doesn't work.
Also note you can't just add options like --help to the make command line, because make will interpret them itself. You'll have to prefix them with -- to force make to ignore them:
make target -- --help
Another option would be to add a target like this:
target%:
$(COMMAND) $*
Then you can run this:
make "target --help"
But you have to include the quotes.
In general I just recommend you reconsider what you want to do.
You could write a bash wrapper script to do what you'd like:
#/bin/bash
make target ARGS=\"$#\"
The reason you don't want to do it in make, is that make parses the command line parameters before it parse the makefile itself, so by the time you read the makefile, the targets, variables, etc have already been set. This means that make will have already interpreted the extra parameters as new targets, variables etc.
A target that re-run make containerized
.PHONY: all containerized
ifeq ($(filter containerized,$(MAKECMDGOALS)),containerized)
.NOTPARALLEL: containerized
MAKEOVERRIDES ?=
containerized: ## Build inside a container
#docker run image_with_make make $(MAKEOVERRIDES) $(filter-out containerized,$(MAKECMDGOALS))
else
# other targets here
all: xxxx
endif
Executing
make containerized all runs make all in container
The first answer is correct, no passthru of args. However, here is a plausible path for experimentation, use of branch by include selection:
# Makefile:
COMMAND := $(PYTHON) this_shit_got_real.py
LOCAL_MK ?= local.mk
# '-' important, absence of LOCAL_MK is not cause for error, just run with no overrides
- include $(LOCAL_MK)
target:
$(COMMAND) $(ARGS)
Now see how you add branching with env:
echo "ARGS=--help">>local.mk
# make target
And the other cli controlled branch
echo "ARGS=--doit">>runner.mk
# LOCAL_MK=runner.mk make target

GNU make: rule prequisites in nested variables not executed

The point is that I want to have some dependencies centralized in one variable, but the dependencies themselves are contained in variables.
a=meow
b=squeek
$(a):
touch $#
$(b):
touch $#
targs=$(a) $(b)
all: $(targs)
In the real case rules for a and b differ so I need them to be in separate targets.
When I try to build such a target, only last nested dependency gets executed:
$ make
touch meow
$ ls
. .. Makefile meow
Could anyone please explain me how do I fix the situation or where I'm wrong?
I can make a phony target like targs: $(a) $(b), but if there's a way to keep the structure I mentioned, I'd like to know about it.
Thanks in advance!
UPD: solved. My mistake: instead of running make all I ran make and make executed the first target instead of all.
Make's default is to use the first target in the Makefile. Either move the all target to the beginning or use the following line somewhere in your Makefile
.DEFAULT_GOAL := all

OpenCobol Makefile

I am trying to compile an OpenCobol program using make. I am always getting "make: Nothing to be done for test1.cob". Here is my makefile. I had put a TAB before cobc. But still I am getting that message. Please help.
Thanks.
COBCWARN = -W
%: %.cob
cobc $(COBCWARN) -free -x $^ -o $#
And here is my cobol program.
IDENTIFICATION DIVISION.
PROGRAM-ID. TEST1.
PROCEDURE DIVISION.
DISPLAY 'Hello world!'.
STOP RUN.
Your makefile contains no actual targets. You have defined only a pattern rule which tells make how to build targets that match the pattern. But make doesn't go looking for targets out on the filesystem that could match the pattern, it only checks the pattern against targets that have been specifically requested.
You don't have any specific targets (files) listed in your makefile, so the only way make can know about a target is if you give the target to be built on the command line.
You are running this command from within vim, using the % special token, which expands to the name of the file currently being edited. That means you are running the command:
make test1.cob
because you are editing the file test1.cob. So, you are telling make "please try to create the target (file) test1.cob". But, that file already exists (it's the file you're writing). So make says "nothing to do".
If you run make and ask it to create the target you really want created, it will work:
make test1
Now the file test1 doesn't exist, and make can find a pattern rule that knows how to build it, so make will run that rule.
Alternatively, you can edit your makefile to add the specific target, like this:
COBCWARN = -W
test1: test1.cob
%: %.cob
cobc $(COBCWARN) -free -x $^ -o $#
Then you can run make with no arguments at all. Without any command line arguments, make will look in the makefile for explicit targets and find test1 as the first one. It sees that there is a rule (the pattern rule) that matches that target, so it will build that target.
UPDATE
If you want to allow a simple command make to build multiple programs, write your makefile like this:
COBCWARN = -W
all: test1 test2 test3
.PHONY: all
%: %.cob
cobc $(COBCWARN) -free -x $^ -o $#
Now from vim you can just say :!make and that's it.
If you run make with no arguments then it will find the first explicit target in the makefile and build that. In this example the first target is all, and its prerequisites are the possible programs to build. To build each one make sees that it can apply the pattern rule, and so it will do so (if the .cob file has been modified since the last time the program was built).

Alias target name in Makefile

The Problem:
Is it possible to give a target a different name or alias, such that it can be invoked using either the original target name or the alias.
For example something like
/very/long/path/my_binary: dep_a dep_b dep_c
# Compile
# Desired command
ALIAS my_binary = /very/long/path/my_binary
# NOTE: Notice the use of 'my_binary' in the dependencies
data1: my_binary datafile
# Build data file using compiled my_binary
Attempt 1: .PHONY
I have tried using a .PHONY target:
.PHONY: my_binary
my_binary: /very/long/path/my_binary
This works great when invoked from the command-line:
# Runs rule 'my_binary' and then *only* runs rule '/very/long/path/my_binary'
# if the rule '/very/long/path/my_binary' needs updating.
make my_binary
However, this does not work well when the alias my_binary is listed as a dependency:
# *Always* thinks that rule 'data1' needs updating, because it always thinks that
# the .PHONY target 'my_binary' "needs updating". As a result, 'data1' is
# rebuilt every time.
make /very/long/path/my_binary
Possible hack?
A possible hack is to use an empty target as suggested in an answer to this question, but that would require introducing fake files with names corresponding to the alias:
my_binary: /very/long/path/my_binary
touch my_binary
This will clutter the main directory with files! Placing the fake files in a sub-directory would defeat the purpose, as the alias would have to be referred to as 'directory/my_binary'
Okay, I needed something similar. The path to my output artifacts were quite long, but I wanted short target names and also benefit easily from bash-completion.
Here is what I'm came up with:
os := [arbitrary long path to an artifact]
platform := [arbitrary long path to a differ artifact]
packer := [common parts of my packer build command]
.PHONY: all
all: $(platform)
.PHONY: platform
platform: $(platform)
$(platform): platform.json $(os)
#$(packer) $<
.PHONY: os
os: $(os)
$(os): os.json
#$(packer) $<
.PHONY: clean
clean:
rm -fr build/
With the Makefile above you can say:
$ make os
$ make platform
Which will be aliases for the long artifact names. I've made the snippet above quite long, because it's important to see the relationships between the .PHONY aliases and the real targets. I hope that works for you.
Note: I did not delete the clean target from the above example, because many people does not make that a .PHONY target. However, semantically it should be.
I don't think there's any way to do it so that you can use the alias from within your makefile as well as the command line, except by creating those temporary files.
Why can't you just set a variable in the makefile, like:
my_binary = /very/long/path/my_binary
then use $(my_binary) everywhere in the makefile? I don't see any point in creating a real alias target for use inside the makefile.
I had a somewhat similar need. I wanted users of my makefile to be able to enter any of the following to accomplish the same result, such that the following were effectively synonyms of each other:
make hit list
make hitlist
make hit_list
What I did in my makefile was the following:
hit_list:
#echo Got here
<the real recipe goes here>
hit: hit_list
hitlist: hit_list
.PHONY: list
list:
#echo > /dev/null
Then, when I tested it using any of the commands "make hit list", "make hitlist", or "make hit_list", I got identical results, as intended.
By extension, if one of your targets was the one with the long name but you used this approach whereby a simple short name identified the target with the long name as a prerequisite, I think that you should be able to say "make short_name" and accomplish what you're asking about.
This differs from your Approach 1 in that none of the synonyms is defined as a phony target (considering that "make hit list" is a command to make two targets, the second being effectively a noop), so the complication that you described would not arise.

How does "make" app know default target to build if no target is specified?

Most Linux apps are compiled with:
make
make install clean
As I understand it, the make command takes names of build targets as arguments. So for example install is usually a target that copies some files to standard locations, and clean is a target that removes temporary files.
But what target will make build if no arguments are specified (e.g. the first command in my example)?
By default, it begins by processing the first target that does not begin with a . aka the default goal; to do that, it may have to process other targets - specifically, ones the first target depends on.
The GNU Make Manual covers all this stuff, and is a surprisingly easy and informative read.
To save others a few seconds, and to save them from having to read the manual, here's the short answer. Add this to the top of your make file:
.DEFAULT_GOAL := mytarget
mytarget will now be the target that is run if "make" is executed and no target is specified.
If you have an older version of make (<= 3.80), this won't work. If this is the case, then you can do what anon mentions, simply add this to the top of your make file:
.PHONY: default
default: mytarget ;
References:
https://www.gnu.org/software/make/manual/html_node/How-Make-Works.html
GNU Make also allows you to specify the default make target using a special variable called .DEFAULT_GOAL. You can even unset this variable in the middle of the Makefile, causing the next target in the file to become the default target.
Ref: The Gnu Make manual - Special Variables
bmake's equivalent of GNU Make's .DEFAULT_GOAL is .MAIN:
$ cat Makefile
.MAIN: foo
all:
#echo all
foo:
#echo foo
$ bmake
foo
See the bmake(1) manual page.

Resources