How to evaluate a Makefile function outside a target recipe and return a value from the function? - makefile

I needed to checkout a git repository in the Makefile:
REPO_NAME = something
REPO_URL = something
REPO_BRANCH = branch
define clone_repo_function
git -C $(1) init --quiet
git -C $(1) remote add origin $(2)
git -C $(1) fetch origin --progress --quiet --depth 1 $(3)
git -C $(1) reset --quiet --hard FETCH_HEAD
endef
clone_it:
$(call clone_repo_function,$(REPO_NAME),$(REPO_URL),$(REPO_BRANCH))
do-something --input $(REPO_NAME)
And that works OK.
What I want to do is get sha1 value from the repo, so that I know at what point was the REPO_BRANCH used.
So I tried to extend the clone_repo_function with additional parameter number 4 $(REPO_SHA1) and save sha1 into it using git rev-parse HEAD:
REPO_NAME ?= something
REPO_URL ?= something
REPO_BRANCH ?= branch
REPO_SHA1 ?= 123
define clone_repo_function
git -C $(1) init --quiet
git -C $(1) remote add origin $(2)
git -C $(1) fetch origin --progress --quiet --depth 1 $(3)
git -C $(1) reset --quiet --hard FETCH_HEAD
$(4)=$$(shell git rev-parse HEAD)
endef
clone_it:
$(eval $(call clone_repo_function,$(REPO_NAME),$(REPO_URL),$(REPO_BRANCH),$(REPO_SHA1)))
do-something --input $(REPO_NAME) --sha1 $(REPO_SHA1)
However, this of course will not work and gives expected Error: recipe commences before first target.
Is there a proper way to call a function which returns a value, but outside of a make target? And then use the value within some target? Or the problem needs to be addressed in a completely different way.

Related

makefile "C:\Program" not recognized as internal or external command

I am trying to run make after downloading source for lua, but I am getting an error that says
'C:/Program' is not recognized as an internal or external command,
operable program or batch file
make: *** [guess] Error 1
I understand that some people got this error due to not placing any quotation marks around the path, but right now I am just running make without specifying any path. How should I go around this?
---EDIT--
This is the makefile I tried to run
# Makefile for installing Lua
# See doc/readme.html for installation and customization instructions.
# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================
# Your platform. See PLATS for possible values.
PLAT= guess
# Where to install. The installation starts in the src and doc directories,
# so take care if INSTALL_TOP is not an absolute path. See the local target.
# You may want to make INSTALL_LMOD and INSTALL_CMOD consistent with
# LUA_ROOT, LUA_LDIR, and LUA_CDIR in luaconf.h.
INSTALL_TOP= /usr/local
INSTALL_BIN= '$(INSTALL_TOP)/bin'
INSTALL_INC= '$(INSTALL_TOP)/include'
INSTALL_LIB= '$(INSTALL_TOP)/lib'
INSTALL_MAN= '$(INSTALL_TOP)/man/man1'
INSTALL_LMOD= '$(INSTALL_TOP)/share/lua/$V'
INSTALL_CMOD= '$(INSTALL_TOP)/lib/lua/$V'
# How to install. If your install program does not support "-p", then
# you may have to run ranlib on the installed liblua.a.
INSTALL= install -p
INSTALL_EXEC= $(INSTALL) -m 0755
INSTALL_DATA= $(INSTALL) -m 0644
#
# If you don't have "install" you can use "cp" instead.
# INSTALL= cp -p
# INSTALL_EXEC= $(INSTALL)
# INSTALL_DATA= $(INSTALL)
# Other utilities.
MKDIR= mkdir -p
RM= rm -f
# == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE =======
# Convenience platforms targets.
PLATS= guess aix bsd c89 freebsd generic linux linux-readline macosx mingw posix solaris
# What to install.
TO_BIN= lua luac
TO_INC= lua.h luaconf.h lualib.h lauxlib.h lua.hpp
TO_LIB= liblua.a
TO_MAN= lua.1 luac.1
# Lua version and release.
V= 5.4
R= $V.2
# Targets start here.
all: $(PLAT)
$(PLATS) help test clean:
#cd src && $(MAKE) $#
install: dummy
cd src && $(MKDIR) $(INSTALL_BIN) $(INSTALL_INC) $(INSTALL_LIB) $(INSTALL_MAN) $(INSTALL_LMOD) $(INSTALL_CMOD)
cd src && $(INSTALL_EXEC) $(TO_BIN) $(INSTALL_BIN)
cd src && $(INSTALL_DATA) $(TO_INC) $(INSTALL_INC)
cd src && $(INSTALL_DATA) $(TO_LIB) $(INSTALL_LIB)
cd doc && $(INSTALL_DATA) $(TO_MAN) $(INSTALL_MAN)
uninstall:
cd src && cd $(INSTALL_BIN) && $(RM) $(TO_BIN)
cd src && cd $(INSTALL_INC) && $(RM) $(TO_INC)
cd src && cd $(INSTALL_LIB) && $(RM) $(TO_LIB)
cd doc && cd $(INSTALL_MAN) && $(RM) $(TO_MAN)
local:
$(MAKE) install INSTALL_TOP=../install
# make may get confused with install/ if it does not support .PHONY.
dummy:
# Echo config parameters.
echo:
#cd src && $(MAKE) -s echo
#echo "PLAT= $(PLAT)"
#echo "V= $V"
#echo "R= $R"
#echo "TO_BIN= $(TO_BIN)"
#echo "TO_INC= $(TO_INC)"
#echo "TO_LIB= $(TO_LIB)"
#echo "TO_MAN= $(TO_MAN)"
#echo "INSTALL_TOP= $(INSTALL_TOP)"
#echo "INSTALL_BIN= $(INSTALL_BIN)"
#echo "INSTALL_INC= $(INSTALL_INC)"
#echo "INSTALL_LIB= $(INSTALL_LIB)"
#echo "INSTALL_MAN= $(INSTALL_MAN)"
#echo "INSTALL_LMOD= $(INSTALL_LMOD)"
#echo "INSTALL_CMOD= $(INSTALL_CMOD)"
#echo "INSTALL_EXEC= $(INSTALL_EXEC)"
#echo "INSTALL_DATA= $(INSTALL_DATA)"
# Echo pkg-config data.
pc:
#echo "version=$R"
#echo "prefix=$(INSTALL_TOP)"
#echo "libdir=$(INSTALL_LIB)"
#echo "includedir=$(INSTALL_INC)"
# Targets that do not create files (not all makes understand .PHONY).
.PHONY: all $(PLATS) help test clean install uninstall local dummy echo pc
# (end of Makefile)

"variable" target name in makefile

how good/bad practice is it to have "nonconstant" targets in a makefile?
The company I work for produces embedded equipment, and I'm a firmware engineer.
The coding rules of the company I work for require that files produced by a build must have a name tagged with the version and also with a hash of information from a git repository (for test and production department needs).
An additional constraint is that a build is produced with a single command.
That's why I'm faced with makefiles every day whose targets have names that can change from build to build.
I wonder if these are common problems or not.I wonder if the example I wrote is a good practice to solve it:
# This is just an example to (hopefully) help to understand.
define populate_variable_with_remote_file_content
$(if $(findstring $(origin $(1)),undefined), \
$(eval $(1) = $(shell git archive master --remote=git#gitserver:project.git $(2) | tar -x --to-stdout)) \
)
endef
.PHONY: clean populate_TAG
all: populate_TAG
$(eval TARGET = dummy_$(TAG))
$(eval export TAG TARGET)
$(MAKE) -f $(firstword $(MAKEFILE_LIST)) $(TARGET)
$(TARGET):
echo hallo > $#
populate_TAG:
$(eval $(call populate_variable_with_remote_file_content,TAG,somefile))
clean:
rm dummy_*
I wonder if there are other (better) ways to solve this.

Build dynamic targets for different architectures using make?

I'm looking for a way to basically iterate over a list of architectures and build unique ones using make. For example, I have a variable containing all the architectures, and a static target currently.
Below is the (simplified) logic I have.
ALL_ARCHES=amd64 arm arm64
VERSION=$(shell git symbolic-ref --short HEAD)-$(shell git rev-parse --short HEAD)
cmd/mything/mything: cmd/mything/*.go
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-X main.version=$(VERSION)" -o $# cmd/mything/*.go
...
I basically want to replace GOARCH=amd64 by iterating over the ALL_ARCHES variable, creating a unique binary that represents the given architecture.
Assuming you accept to name your binaries cmd/mything/mything-ARCH, where ARCH is the target architecture, you could try:
ALL_ARCHES := amd64 arm arm64
VERSION := $(shell git symbolic-ref --short HEAD)-$(shell git rev-parse --short HEAD)
.PHONY: all
all: $(addprefix cmd/mything/mything-,$(ALL_ARCHES))
cmd/mything/mything-%: cmd/mything/*.go
CGO_ENABLED=0 GOOS=linux GOARCH=$* go build -ldflags "-X main.version=$(VERSION)" -o $# $^
$* is the automatic variable that expands as the stem of the target name in a pattern rule.

makefile - build mulitple files with multiple targets

I have a project with multiple Dockerfiles, each located in a named directory that I use as the Docker image tag. I need to build, test, and push each a docker image for each Dockerfile. What do I need to do to make something such as the following work with GNU Make?
# BUILDS needs to be a list of directories, not a list of Dockerfiles
BUILDS := $(wildcard */Dockerfile)
VERSION := $(shell git rev-parse --short=12 --verify HEAD)
DOCKER_REPO_URL := quay.io/reponame
define docker_build =
$(1):
#echo "Building $$#"
docker build -t $$# --force-rm $$#
endef
define docker_test =
$(1):
#echo "Testing $$#"
docker inspect $$#
docker run --rm $$# help
endef
define docker_push =
$(1):
#echo "Pushing $$#"
docker tag $$# $(DOCKER_REPO_URL):$$#-$(VERSION)
docker push $(DOCKER_REPO_URL):$$#-$(VERSION)
docker tag $$# $(DOCKER_REPO_URL):$$#
docker push $(DOCKER_REPO_URL):$$#
endef
.PHONY: all build test release clean
all: build test release
build: $(BUILDS)
$(foreach build,$(BUILDS),$(eval $(call docker_build,$(build))))
test: $(BUILDS)
$(foreach test,$(BUILDS),$(eval $(call docker_test,$(test))))
release:
$(foreach image,$(BUILDS),$(eval $(call docker_push,$(image))))
I'm not sure this is what you want, but...
First consider the BUILD variable. If we have three Dockerfiles:
foo/Dockerfile
bar/Dockerfile
baz/Dockerfile
then we want BUILDS to contain foo bar baz
Here are a couple of attempts:
BUILDS := $(wildcard */Dockerfile) # this is foo/Dockerfile bar/Dockerfile baz/Dockerfile
BUILDS := $(dir $(wildcard */Dockerfile)) # this is foo/ bar/ baz/
BUILDS := $(patsubst %/,%, $(dir $(wildcard */Dockerfile))) # this is foo bar baz
Crude but effective.
Now the rules. Ordinarily the target of a rule is the name of a file which the rule builds. In this case we must break this convention, since we don't know what the name of the image file will be. So if the directory is foo/, we could have a rule called build_foo:
build_foo:
#echo "Building foo"
#echo docker build -t foo --force-rm foo
Since we don't want to write a rule for every possible directory, we will use automatic variables and create a pattern rule:
build_%:
#echo "Building $$#"
#echo docker build -t $* --force-rm $*
Now "make build_foowill work correctly. And we could write abuild` rule that builds all of them:
build: $(addprefix build_,$(BUILDS))
But this is not quite the right approach. We want to build, then test, then push each image, in that order. So we'd like something like this:
push_foo: test_foo
test_foo: build_foo
We can do this with pattern rules:
test_%: build_%
...
push_%: test_%
...
release: $(addprefix push_,$(BUILDS))
Now "make release" will do everything. (And if you put release: as the first rule in the makefile, it will be the default rule, and "make" will suffice.)
Like #Beta, I don't see why you want to build all the images, then
test all the images, then push all the images, as opposed to
building, testing and pushing each image; and the latter approach lends itself
to a simpler and more normal makefile.
If you have reasons for having to do it the first way, then you'd need a
makefile something like this:
# Assuming each subdirectory `foobar` containing a Dockerfile
# is where we `docker build` the image `foobar`
IMAGES := $(patsubst %/,%,$(dir $(wildcard */Dockerfile)))
BUILD_TARGS = $(patsubst %,build_%,$(IMAGES))
TEST_TARGS = $(patsubst %,test_%,$(IMAGES))
PUSH_TARGS = $(patsubst %,push_%,$(IMAGES))
VERSION := 1 # $(shell git rev-parse --short=12 --verify HEAD)
DOCKER_REPO_URL := quay.io/reponame
define docker_build =
build_$(1):
#echo "Building $(1)"
#docker build -t $(1) --force-rm $(1)
endef
define docker_test =
test_$(1):
#echo "Testing $(1)"
#docker inspect $(1)
#docker run --rm $(1) help
endef
define docker_push =
push_$(1):
#echo "Pushing $(1)"
#docker tag $(1) $(DOCKER_REPO_URL):$(1)-$(VERSION)
#docker push $(DOCKER_REPO_URL):$(1)-$(VERSION)
#docker tag $$# $(DOCKER_REPO_URL):$(1)
#docker push $(DOCKER_REPO_URL):$(1)
endef
.PHONY: all build test release clean $(IMAGES) $(BUILD_TARGS) $(TEST_TARGS) $(PUSH_TARGS)
all: build test release
build: $(BUILD_TARGS)
test: $(TEST_TARGS)
release: $(PUSH_TARGS)
$(foreach image,$(IMAGES),$(eval $(call docker_build,$(image))))
$(foreach image,$(IMAGES),$(eval $(call docker_test,$(image))))
$(foreach image,$(IMAGES),$(eval $(call docker_push,$(image))))
Obviously, uncomment the docker commands to make them run, and restore the
correct definition of VERSION.
As it stands it will give you the like of:
$ make
Building foo
#docker build -t foo --force-rm foo
Building bar
#docker build -t bar --force-rm bar
Testing foo
#docker inspect foo
#docker run --rm foo help
Testing bar
#docker inspect bar
#docker run --rm bar help
Pushing foo
#docker tag foo quay.io/reponame:foo-1
#docker push quay.io/reponame:foo-1
#docker tag push_foo quay.io/reponame:foo
#docker push quay.io/reponame:foo
Pushing bar
#docker tag bar quay.io/reponame:bar-1
#docker push quay.io/reponame:bar-1
#docker tag push_bar quay.io/reponame:bar
#docker push quay.io/reponame:bar

GNU make: Generate rules for extracting tarballs

I've got a Makefile that extracts a series of tarballs. Right now the rules are written like:
dirname:
tar zxvf file.tar.gz
and other targets that depend on the expanded tarball reference dirname. But, it's kind of cluttery to define a rule like this for every tarball. So, I'm trying to use the eval function to auto generate these rules. My attempt looks like this:
TARFILES = $(wildcard *.tar.gz *.tgz)
define extract_tmpl =
$(shell tar tf $(1) | head -1):
tar zxvf $(1)
endef
$(foreach file, $(TARFILES), $(eval $(call extract_tmpl, $(file))))
But it doesn't seem to work. I'm testing with this tarball (in the same dir):
$ ls Python-2.6.6.tgz
Python-2.6.6.tgz
$ tar tf Python-2.6.6.tgz | head -1
Python-2.6.6/
$ make Python-2.6.6/
make-3.79.1-p7: *** No rule to make target `Python-2.6.6/'. Stop.
It seems like it should work to me, but honestly I'm not even sure how I can see what it expands to. Anything obviously wrong here?
You do not need an = after the define.
There should be a hard tab before the tar zxvf $(1)
Optional, define a rule called default as the first rule in the Makefile, which depends on all the directories that would be created, so that if you just run make, it would extract all the tarballs.
Optional, but good practice is to define default as a PHONY rule since it does not create any files called default.
This is how the Makefile would look like (and tested using 2 tarballs in the current directory):
TARFILES = $(wildcard *.tar.gz *.tgz)
define extract_tmpl
$(shell tar tf $(1) | head -1):
tar zxvf $(1)
# ^
# HARD-TAB
#
endef
TAR_DIRS := $(foreach file, $(TARFILES), $(shell tar tf $(file) | head -1))
default: $(TAR_DIRS)
$(foreach file, $(TARFILES), $(eval $(call extract_tmpl, $(file))))
.PHONY: default
An alternate solution without using eval and just static pattern rules:
Used .extract_file.tar.gz or .extract_file.tgz as empty files to not re-extract the archives if already extracted
No eval, but just static pattern rules, which should make it compatible with older versions of make like v3.79
Added rules for clean
This is the Makefile:
TARFILES = $(wildcard *.tar.gz *.tgz)
EXTRACT_TARGETS := $(addprefix .extract_,$(TARFILES))
CLEAN_TARGETS := $(addprefix .clean_,$(TARFILES))
default: $(EXTRACT_TARGETS)
clean: $(CLEAN_TARGETS)
$(CLEAN_TARGETS): .clean_%: .extract_%
rm -rf "$(shell tar tf $(patsubst .extract_%,%,$<) | head -1)"
rm -f $<
$(EXTRACT_TARGETS): .extract_%: %
tar zxf $<
touch $#
.PHONY: default clean $(CLEAN_TARGETS)

Resources