Set enviroment variable with y/n in makefile - makefile

I'm trying to implement a question in my makefile. So how can I ask the user whether he wants to build the binary with or without the LEDs on/off.
When the user starts "make" in the shell there should be a question like "Do you want to build your Binary with LED implemented? y/n"
If there is a "n" my preprocessor should ignore all LED implementations in my c program. The part in c I already did. But I'm new with makefiles.
Could I set a env.variable via my makefile? And HOW could I do this?

How about separate targets, so you could have the user utter
$ make with-led
or
$ make no-led
Then your targets could use the appropriate preprocessor flags with ease.

I wouldn't claim it's a good idea. But it is possible:
# For GNU Make
ifndef LEDS
LEDS := $(shell bash -c 'read -p "Use LEDS? [y/n] " && echo $$REPLY')
endif
print:
#echo LEDS=$(LEDS)
I really recommend that you re-think whether that's what you want. On of the principles of Make is that it's not interactive, and users of your build will likely not thank you for violating that. For example, automated compilation on a build server, or simply in an Emacs compile buffer.
A better strategy is to default one way or the other in a Make variable - this can be overridden when invoking Make:
make install LED=0
You could possibly insist that a value is specified:
# GNU Make, again
ifndef LEDS
$(error LEDS must be specified for this build)
endif

Related

How do I configure a permanent shell in GNU Make running on Windows?

I am maintaining a cross-platform project (Linux/Windows/32/64) with hundreds of C and C ++ files. For this I am using a single makefile using GNU Make.
My problem happens when I compile in Windows because the Visual Studio (cl) compiler forces me to set an enviroment by calling vcvarsall.bat [platform_type] and this process is executed every time I compile a file with which the total process is extremely expensive in time.
Partial makefile
# C object file generation rule
../int/$(PROJECT)/%.o: ../%.c
ifeq ($(OS),Windows_NT)
#vcvarsall.bat x86 & cl $(CFLAGS) -c $(subst /,\,$<)
else
#gcc $(CFLAGS) -c $<
endif
The sentence #vcvarsall.bat x86 is executed whenever each file is compiled. Is there a way where this statement is only executed once and that all successive shells inherit this ?
Thanks !
This is one of the truly annoying things about development on Windows, for sure.
It's not possible for a shell to "pass on" its values after it exits, to the next shell. It's also not possible for make to start a single shell then run all the rules' recipes in that one shell.
All you can do is set the values once in the parent shell, then invoke make from there. The simplest way to do this is have a .bat file that does the proper thing, then invokes make. You can have a similarly-named shell script on POSIX systems that doesn't do anything except invoke make.
If you really don't want to do that, then the only option is to use recursive make invocations where the top-level make will run the vcvarsall script then invoke a sub-make which will compile all the things.

Recursive Make passes incorrect -j argument

I'm running make (GNU Make 3.82) with a recursive Makefile.
I'm running make -j2 in order to spawn only 2 processes in parallel.
The internal Makefile is called with $(MAKE).
However, it looks like the internal Makefile (which was started by the main Makefile) spawns processes infinitely as if it was given -j and not -j2.
Trying to verify this, I dumped the environment variables of the child "make":
# pgrep -a make
17218 make -j2
17227 make -C obj_dir/ -f Vf1_package.mk ...
# strings /proc/17227/environ
...
MAKEFLAGS= --jobserver-fds=3,4 -j
...
MAKEFLAGS is not set explicitly anywhere, and -j is only provided in the command line and doesn't appear anywhere in the makefiles. So it seems like "make" itself decided to strip the "2" from the -j argument when composing the MAKEFLAGS for the child "make".
Any idea what could cause "make" to set MAKEFLAGS to -j instead of -j2?
Update 1
I've identified the problem, but I still don't understand why it happens and how to fix that.
The problem is that the job server doesn't work well when the sub-make is running under SCL context.
This is required because I need the sub-make to use specific gcc toolchain.
SCL = scl enable devtoolset-8
...
sub_make:
$(SCL) "$(MAKE) -C $(SUB_MAKE_DIR) ... "
When running like this, the sub-make spawns infinite number of jobs. When SCL is removed, it works as expected.
Why does SCL interfere with make's job server?
How can I solve this? I know I can enable SCL before running the external Makefile, but I would like to control the toolset from within the Makefile.
Update 2
It seems to be related to the fact that SCL changes PATH environment variable. On the new PATH, "make" is newer ("GNU Make 4.2.1").
So it seems that make job server fails if the top level make is running old GNU Make 3.82 and the sub make is running newer 4.2.1 make, maybe something changed between these versions in the way make communicates with the sub-make.
There's nothing wrong here. The top-level make knows how many total jobs there are and it arranges for all the sub-makes to share those jobs through the jobserver (that's what the --jobserver-fds entry in MAKEFLAGS is for). The sub-makes don't need to know how many total jobs there are, they just need to know how to ask if they can start a new job.
In the very old version of GNU make you are using there is no way, from a sub-make, to know what the specific -j number for this build.
Starting with GNU make 4.2, make will add the specific -j value to MAKEFLAGS for informational purposes even though it's still not used.
EDIT
I don't know anything about scl or how it works. But, the GNU make jobserver works by sharing file descriptors across all the sub-makes. If this scl tool is interfering with that, say by forcing all file descriptors to be closed, or running the sub-make inside a docker image where obviously it can't access these shared file descriptors, or some similar thing, then it clearly cannot work with the jobserver feature and you'll have to run the entire make inside the scl.
An option is to not put the -j on the outer make but instead run a single inner make using -j, inside scl.
Can you run make --print-data-base and check if you get proper value of -j.
May be execute a simple test example as shown below where you can test to check if gnu make is able to compile multiple files in parallel to generate object files and is giving correct values of -j:
# .SILENT:
.PHONY:compile objs
TARGET = program.exe
CC=gcc
SOURCES = file_1.c file_2.c file_3.c
OBJ_FILES:= $(SOURCES:.c=.o)
objs: $(OBJ_FILES)
%.o: %.c
$(CC) $(FLAGS) -c $< -o $#
all: test
# Enable parallel compilation
compile:
make -j ${NUMBER_OF_PROCESSORS} objs
link : compile $(TARGET)
$(TARGET): $(OBJ_FILES)
$(CC) $(FLAGS) $(OBJ_FILES) -o $#
test: link
# Execute test script
echo "Executing test script"
Command to execute : make test
This will help you debug and also check if there is issue of gnu-make or some internal bug or make is unable to run in parallel as it did not find anything. I have use ${NUMBER_OF_PROCESSORS} to use all the available processors, you can change it's value and test different runs as per your need.
EDIT
Unfortunately I am not aware about sc1.If scl is the root cause identified, then option would be run the entire make inside sc1. or maye be would be good to test once by explicitly passing -j2 inside the sc1 as maybe global flags are not getting passed to SC1.

Control the output of a make command to be less verbose, don't echo each command

Currently, I'm using a Makefile to keep track of all dependencies and copilation of my project. The problem is that make simply outputs everything it's doing, and that makes it hard to spot (or even read) more important information (such as compiler warnings).
Is there a way to control what information is displayed on the terminal? I know there's a -s option that silences make, but that's not what I want. I need something a little more refined, perhaps showing the compilation target without showing the entire compilation command.
Is there any way to control that?
Note: There's a similar question regarding the automake and autoconf commands. But I don't use those, and I'm specifically looking for something on make.
Well there's the usual business
target: dependency1 dependency2
#echo Making $#
#$(CC) -o $# $(OPTIONS) $^
The leading #'s suppress the usual behavior of echoing the action without suppressing its output.
The output of various actions can be suppressed by redirecting it to /dev/null. Remember to grad the standard error too if you want a line to be really silent.
The standard Unix answer (`make`` is a Unix tool, after all):
make (...) | grep (whatever you want to see)
Why is that not an appropriate solution here?
You could also put filtering within the Makefile itself, e.g. by tweaking the SHELL variable
or adding a target that calls $(MAKE) | grep.
The main idea is to allow the filtering to be switched on and off as the caller pleases.
(Too late, Adding just for Googlers landing here)
This works for me. On your Makefile you can control verbosity for each command using something like:
BRIEF = CC HOSTCC HOSTLD AS YASM AR LD
SILENT = DEPCC DEPHOSTCC DEPAS DEPYASM RANLIB RM STRIP

How can I ctreate a simple makefile for minGW + gfortran

I am absolutely new in gfortran+minGW.
I need to create makefile.
When I run
$ gfortran -c q.f
All is ok!
But how can I run makefile like this?
CC = gfortran
q.o : q.f
$(CC) -c q2.o q2.f
I receive error “CC: command not found”.
(OS – Win 7 (64))
Tanks!!!
It kind of looks like you're trying to run the makefile as a regular script. Try
$ make
or
$ make -f mymakefilename
if you named the file something other than "makefile" or "Makefile".
You can potentially just execute the makefile, but if so you need a "shebang" line, something like
#!/usr/bin/make
at the top of the file, but frankly hardly anyone uses that option. Just use the make(1) command.
Update
It's because they're in the wrong order. Makefiles process (by default) the first target in the file. When you run make it sees the rule to make, q.o from q.f, it compiles it, and says, "Okay, I'm done."
If you put the q.exe target first, it says "Hmmm, I want to build q.exe and to do that I need a q.o. Do I have a q.o? No? Okay, hen I'll build a q.o. I have a rule for that -- I can build a q.o from q.f. okay, that's done. Now can I build q.exe? Oh, yes, I can. I'll build q.exe. Anything? Nope, I'm done."
If you were to use the commend
$ make q.exe
then you'd explicitly tell make to make q.exe, which would cause the same thing to happen, but better you should reorder your makefile and get used to the way they work.

How to make a failing $(shell) command interrupt Make

I have a Makefile that starts by running a tool before applying the build rules (which this tool writes for me). If this tool, which is a python script, exits with a non-null status code, I want GNU Make to stop right there and not go on with building the program.
Currently, I do something like this (top level, i.e. column 1):
$(info Generating build rules...)
$(shell python collect_sources.py)
include BuildRules.mk
But this does not stop make if collect_sources.py exits with a status code of 1. This also captures the standard output of collect_sources.py but does not print it out, so I have the feeling I'm looking in the wrong direction.
If at all possible, the solution should even work when a simple MS-DOS shell is the standard system shell.
Any suggestion?
There might be a better way, but I tried the following and it works:
$(if $(shell if your_command; then echo ok; fi), , $(error your_command failed))
Here I did assume that your_command does not give any output, but it shouldn't be hard to work around such a situation.
Edit: To make it work with the default Windows shell (and probably any decent shell) you could write your_command && echo ok instead of the if within the shell function. I do not think this is possible for (older) DOS shells. For these you probably want to adapt your_command or write a wrapper script to print something on error (or success).
Ok, here's my own solution, which is unfortunately not based on the status code of the collect_sources.py script, but which Works For Me (TM) and lets me see any output that the script produces:
SHELL_OUTPUT := $(shell python collect_sources.py 2>&1)
ifeq ($(filter error: [Errno %],$(SHELL_OUTPUT)),)
$(info $(SHELL_OUTPUT))
else
$(error $(SHELL_OUTPUT))
endif
The script is written so that any error produces an output beginning with "collect_sources: error:". Additionally, if python cannot find or execute the given script, it outputs an error message containing the message "[Errno 2]" or similar. So this little piece of code just captures the output (redirecting stderr to stdout) and searches for error messages. If none is found, it simply uses $(info) to print the output, otherwise it uses $(error), which effectively makes Make stop.
Note that the indentation in the ifeq ... endif is done with spaces. If tabs are used, Make thinks you're trying to invoke a command and complains about it.
You should use a regular target to create BuildRules.mk:
BuildRules.mk: collect_sources.py
python $< >$#
include BuildRules.mk
This is the standard trick to use when automatically generating dependencies.
Fixing https://stackoverflow.com/a/226974/192373
.PHONY: BuildRules.mk
BuildRules.mk: collect_sources.py
echo Generating build rules...)
python $< >$#
$(MAKE) -f BuildRules.mk
Make sure you're not invoking make/gmake with the -k option.

Resources