I have a makefile which I am running through nmake, as opposed to gnumake.
I have some code taken from a makefile intended to gnumake...
# out_repo =
ifndef out_repo
$(error out_repo is not set)
endif
This does not work with nmake. Is there a way in nmake where I can have the same behaviour, where the make process will error out if a variable is not defined?
The equivalent code in NMAKE syntax would be:
# out_repo =
!ifndef out_repo
!error out_repo is not set
!endif
See my answer to https://stackoverflow.com/a/54046754/318716 for a reference to older, more useful, NMAKE documentation.
Related
I am trying to have gprbuild automatically set some variables' values in my source code - one way or another. In particular I want the outputs of certain commands to be accessible from within the code. In C with Makefiles this is easy:
source:
#include <stdio.h>
int main() { printf("%s\n", COMMAND_OUTPUT); return 0; }
make:
result : source.c
$(CC) -DCOMMAND_OUTPUT=`command -with -options`
However I have no idea how to do such a thing with gprbuild and Ada. (Short of ditching gprbuild and just using make - but I rather like gprbuild)
Ada does not use a preprocessor like C does.You cannot expect Ada compilers to modify strings in your code.
Use of such inline editing can easily become a violation of Ada strong typing, which would be very difficult to diagnose and would be completely invisible to source code static analysis.
I solve that by generating an Ada file from the makefile before building.
An example:
HG_STATE_SOURCE = src/mercurial.ads
HG_MODIFIER = `test $$(hg status | wc -c || echo 0) -gt 0 && echo "plus changes" || echo "as committed"`
HG_REVISION = `hg tip --template '{node}' 2>/dev/null || echo N/A_____________________________________`
[...]
$(HG_STATE_SOURCE): Makefile $(REPOSITORY_CONFIG) $(REPOSITORY_STATE) $(PROJECT_ROOT_SOURCE)
#mkdir -p src
#echo 'package 'Mercurial is' > $(HG_STATE_SOURCE)
#echo ' Revision : constant String (1 .. 53) :=' >> $(HG_STATE_SOURCE)
#echo ' "'$(HG_REVISION)' '$(HG_MODIFIER)'";' >> $(HG_STATE_SOURCE)
#echo 'end 'Mercurial;' >> $(HG_STATE_SOURCE)
Yes, the gnatprep preprocessor allows exactly the same as what you have in your C code:
main.adb:
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
begin
Put_Line ($Command_Output);
end Main;
simple_gnatprep.gpr:
project simple_gnatprep is
for Create_Missing_Dirs use "True";
Command_Output := external ("Command_Output");
for Source_Dirs use (".");
for Exec_Dir use ".";
for Main use ("main.adb");
for Object_Dir use "obj/" & "CommandOutput_" & Command_Output;
package Compiler is
for Switches ("Ada") use ("-gnateDCommand_Output=""" & Command_Output & """");
end Compiler;
end simple_gnatprep;
Makefile:
COMMAND_OUTPUT=$(shell echo hello there)
all:
gprbuild -d -p -g -XCommand_Output='${COMMAND_OUTPUT}'
clean:
rm -rf obj/ *.exe
Note I have included the command output in the obj/ directory used, which will fail if the command outputs any symbol that cannot appear in a directory name. However, if you omit it then gprbuild will say that your executable is up-to-date when nothing has changed except the output of the command.
Another option is to always remove the object directory before compiling, but when possible it is better to include the value of any preprocessor symbols in the object path so that switching from one configuration (e.g. Debug / Release) to another and back doesn't throw away intermediate results and slow down your development process.
Gnatprep is only included in the GNAT compiler, because there isn't yet any provision for preprocessing in the Ada standard. For other compilers, you will need to run each file through gnatprep separately in the Makefile, and then pass it to the compiler. In this case there is no need to fiddle with object directory names, as the source file will always be new and the compiler will always have to recompile everything.
I have this code, and it works, but as you see I do the substitute three times, I would like to set a variable to the value, but with no success
$($(PKG)-py-valgrind-tests-status): $($(PKG)-swig-dlib)
$($(PKG)-py-valgrind-tests-status): $(OBJ_OUTPUT_DIR)%.valgrind_passed: %.py
#echo env $(PKG-TEST-HELPER-ENV) valgrind $(VALGRIND-FLAGS) --log-file=$(subst valgrind_passed,valgrind.log,$#) $(PYTHON_BIN) $< -v ; \
env $(PKG-TEST-HELPER-ENV) $(VALGRIND) $(VALGRIND-FLAGS) --log-file=$(subst valgrind_passed,valgrind.log,$#) $(PYTHON_BIN) $< -v \
|| (cat $(subst valgrind_passed,valgrind.log,$#); exit 1)
#touch $#
the problematic line $(subst valgrind_passed,valgrind.log,$#)
I tried:
$($(PKG)-py-valgrind-tests-status): LOG-FILE = $(subst valgrind_passed,valgrind.log,$#)
and
.SECONDEXPANSION:
$($(PKG)-py-valgrind-tests-status): LOG-FILE = $$(subst valgrind_passed,valgrind.log,$#)
and(inside the recipe)
$(eval LOG-FILE = $$(subst valgrind_passed,valgrind.log,$#))
but for all, if I write
--log-file=$(LOG-FILE)
log file come's up empty.
I have no more ideas on how to go forward,
appreciate the help, thanks!
Are you sure you're using GNU make? What version are you using (run make --version)?
There's nothing wrong with your first attempt, using a target-specific variable. If this doesn't work then you've got something wrong or different about your makefile that you haven't explained. Maybe if you showed the actual complete section of the makefile with the target-specific variable being set and used we might see what's wrong.
Your second attempt can't work because secondary expansion applies only the prerequisites, not target-specific variables (but, as above, it's not needed anyway).
Your third attempt might work but again, without seeing exactly what you do with the eval we can't say for sure. You don't need to double the $ before the subst function; it can be expanded first and it will still work.
I have a Makefile that I run with multithreading (-j8 specifically).
I want to force a make clean and make all operation if I'm missing a specific marker file identifying the version compiled.
(This file should be introduced when the make is completed after the second make all.)
I can't seem to make this work properly. I either get stuck in loops or it just doesn't happen at all.
(This is part of a huge system so I can't just change any paradigms and I have to work with what I have)
Here's the relevant section I have so far. This wasn't the original plan but I shifted so many things around this is the current situation:
VERSION = 2.8
.DEFAULT_GOAL := all
.PHONY : all
all : {some targets} | marker_file
###########################
.PHONY : marker_file
marker_file : build/$(VERSION).marker
.PHONY : check_marker
check_marker :
ifeq (,$(wildcard build/$(VERSION).marker))
#echo -e "\e[41mYOU ARE ON NEW PREREQUISITES $(VERSION)! FORCING MAKE CLEAN BEFORE REBUILDING\e[0m"
$(MAKE) clean
#mkdir -p build
#touch build/$(VERSION).marker
$(MAKE) $(MAKECMDGOALS)
endif
# if the marker file needs generation, force clean and rebuild
build/$(VERSION).marker : check_marker
Can anyone figure out how to properly plan the rules and dependencies so that I can generate the file on the second time?
You definitely don't want to use order-only prerequisites. That forces the prerequisite to always run, but doesn't use the results in determining whether to run the target. That's almost the exact opposite of what you want.
Also you cannot use make preprocessor constructs like ifeq inside a recipe (indented by a TAB). Recipes are passed to the shell, and the shell is not make and does not understand make constructs like ifeq.
You can use make's auto-re-exec feature: if an included file changes then make will re-exec itself. So:
VERSION = 2.8
.DEFAULT_GOAL := all
.PHONY : all
all : {some targets}
###########################
MARKER_FILE = build/$(VERSION).marker
$(MARKER_FILE) :
#echo -e "\e[41mYOU ARE ON NEW PREREQUISITES $(VERSION)! FORCING MAKE CLEAN BEFORE REBUILDING\e[0m"
$(MAKE) clean MARKER_FILE=
#mkdir -p $(#D)
#touch $#
include $(MARKER_FILE)
I am getting the following error running make:
Makefile:168: *** missing separator. Stop.
What is causing this?
As indicated in the online manual, the most common cause for that error is that lines are indented with spaces when make expects tab characters.
Correct
target:
\tcmd
where \t is TAB (U+0009)
Wrong
target:
....cmd
where each . represents a SPACE (U+0020).
Just for grins, and in case somebody else runs into a similar error:
I got the infamous "missing separator" error because I had invoked a rule defining a function as
($eval $(call function,args))
rather than
$(eval $(call function,args))
i.e. ($ rather than $(.
This is a syntax error in your Makefile. It's quite hard to be more specific than that, without seeing the file itself, or relevant portion(s) thereof.
For me, the problem was that I had some end-of-line # ... comments embedded within a define ... endef multi-line variable definition. Removing the comments made the problem go away.
My error was on a variable declaration line with a multi-line extension. I have a trailing space after the "\" which made that an invalid line continuation.
MY_VAR = \
val1 \ <-- 0x20 there caused the error.
val2
In my case, I was actually missing a tab in between ifeq and the command on the next line. No spaces were there to begin with.
ifeq ($(wildcard $DIR_FILE), )
cd $FOLDER; cp -f $DIR_FILE.tpl $DIR_FILE.xs;
endif
Should have been:
ifeq ($(wildcard $DIR_FILE), )
<tab>cd $FOLDER; cp -f $DIR_FILE.tpl $DIR_FILE.xs;
endif
Note the <tab> is an actual tab character
In my case error caused next. I've tried to execute commands globally i.e outside of any target.
UPD. To run command globally one must be properly formed. For example command
ln -sf ../../user/curl/$SRC_NAME ./$SRC_NAME
would become:
$(shell ln -sf ../../user/curl/$(SRC_NAME) ./$(SRC_NAME))
In my case, this error was caused by the lack of a mere space. I had this if block in my makefile:
if($(METHOD),opt)
CFLAGS=
endif
which should have been:
if ($(METHOD),opt)
CFLAGS=
endif
with a space after if.
In my case, the same error was caused because colon: was missing at end as in staging.deploy:. So note that it can be easy syntax mistake.
I had the missing separator file in Makefiles generated by qmake. I was porting Qt code to a different platform. I didn't have QMAKESPEC nor MAKE set. Here's the link I found the answer:
https://forum.qt.io/topic/3783/missing-separator-error-in-makefile/5
Just to add yet another reason this can show up:
$(eval VALUE)
is not valid and will produce a "missing separator" error.
$(eval IDENTIFIER=VALUE)
is acceptable. This sort of error showed up for me when I had an macro defined with define and tried to do
define SOME_MACRO
... some expression ...
endef
VAR=$(eval $(call SOME_MACRO,arg))
where the macro did not evaluate to an assignment.
I had this because I had no colon after PHONY
Not this,
.PHONY install
install:
install -m0755 bin/ytdl-clean /usr/local/bin
But this (notice the colon)
.PHONY: install
...
Following Makefile code worked:
obj-m = hello.o
all:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
So apparently, all I needed was the "build-essential" package, then to run autoconf first, which made the Makefile.pre.in, then the ./configure then the make which works perfectly...
I have makefile structured in the following way.
.PHONY: DEPTARG1 DEPTARG2
$(info BUILD_AGAIN is $(BUILD_AGAIN))
TARGET: DEPTARG1 DEPTARG2
ifeq ($(BUILD_AGAIN),y)
DEPTARG1:
#echo Building $#
DEPTARG2:
#echo Building $#
endif
I anticipated this makefile to throw an error if BUILD-AGAIN is n, since it wont have the dependencies available.But It didn't. Any thoughts for it are welcome.
As an additional information DETARG1 and DEPTARG2 are phony targets.
Output when BUILD_AGAIN is y
BUILD_AGAIN is y
Building DEPTARG1
Building DEPTARG2
Output when BUILD_AGAIN is n
BUILD_AGAIN is n
make: Nothing to be done for `TARGET'.
Moving the .PHONY: declaration inside your conditional would eliminate this error.
That being said you seem to be going through a lot of shenanigans to re-create something that make has built in. Writing up or not writing up recipes is no the way to run or not run them. Make has a flag for that, make -B will force a rebuild even if the targets are up to date. Replacing the whole setting a BUILD_AGAIN variable shenanigans just running with or without that flag should do the trick.