Using nmake with wildcarded targets - makefile

Using nmake I have the following makefile which currently does what I need it to do. mycmd (the program being run) will take a .inp file and produce a .out file. I can make as many .inp files as I need to and the makefile doesn't have to change. It will find them all and make all the relevant .out files.
#####################################################################################
# A SUFFIXES declaration is required in order to later use the rule with target .inp.out
#####################################################################################
.SUFFIXES: .inp
#####################################################################################
# Here, NMAKE will expand *.inp in the prereq list for all, into the list of *.inp
# files in the directory, and then it will start a new NMAKE instance, specifying the
# goals to build all those files.
#####################################################################################
all: *.inp
$(MAKE) $(**:.inp=.out)
#####################################################################################
# $(*B) represents the current target's base name minus the path and the file extension
#####################################################################################
.inp.out:
mycmd -i $(*B).inp -o $(*B).out
My question is, how do I enhance this makefile further so that I could, for example, run it for a set of .inp files, so not *.inp but say, ABC*.inp?

A simple modification to your makefile should work. Add a new $(pattern) macro:
.SUFFIXES: .inp
pattern = * # new macro; defaults to *
all: $(pattern).inp # use it!
#$(MAKE) -nologo $(**:.inp=.out)
.inp.out: # dummy stub for testing
#echo mycmd -i $(*B).inp -o $(*B).out
#type NUL > $(*B).out
Then in your command line, overwrite pattern. For example, nmake -nologo pattern=ABC*.
Update: The command line in your makefile:
$(MAKE) $(**:.inp=.out)
will fail with fatal error U1095: expanded command line ... too long if the string $** is too long. On my system, this happens at roughly 32800 characters.
Adding an exclamation mark ! to the start (see here) doesn't seem to work, probably because there is no simple $**. Two workarounds are to use:
!call set a=$** & call nmake %%a:.inp=.out%%
or:
!for %a in ($**) do nmake -nologo %~na.out
These are both about twice as slow as your original, with a do-nothing mycmd stub. (The for loop here is not really a loop, because $** is just a single item.)

A different solution is to keep your original makefile, and use a DOS command such as:
for %a in (ABC*.inp) do nmake -nologo %~na.out
Here the syntax %~na removes the extension from the variable %a.
This is slightly slower than just using the makefile, but not by much. For example, with 600 inp files, and a mycmd stub, on my system this command takes 20 seconds compared to 15 seconds for the makefile.

Related

Windows Makefiles and TEXINPUTS

Remark: Put this Question on tex.stackexchange and was adviced to also ask here
Under Windows (using make from GnuWin32) i want to set my TEXINPUTS variable in a makefile
My Structure is as follows:
./
|-texmf_project/
|-Package.sty
|-main.tex
|-makefile
I want to be able to use Package.sty in my compilation process. The files look like this:
Package.sty contains:
\ProvidesClass{Package}[]
\NeedsTeXFormat{LaTeX2e}
\RequirePackage{xcolor}
\newcommand{\red}[1]{\textcolor{red}{#1}}
main.tex contains:
\documentclass[11pt,a4paper]{report}
\usepackage{xcolor}
\usepackage{Package}
\begin{document}
Hello World\\
\red{Hello World}
\end{document}
Now i want to set the TEXINPUTS to include what is in ./texmf-project/
Hence my Makefile:
edit:
set TEXINPUTS=./texmf-project//;
pdflatex -synctex=1 -interaction=nonstopmode main.tex
del *.log
del *.aux
However this does not seem to make the location available for compilation.
However if I put the line
set TEXINPUTS=./texmf-project//; directly into the cmd and run make afterwards it works.
I believe I am making a mistake with the set, but I am far from beeing an expert.
Any helps or hints would be greatly appreciated.
Edit: Fixed a spelling mistake
Please remember that every recipe line is executed in a separate shell, so your set is executed in a different shell than pdflatex. Either put those two commands on a single line, or concatenate lines with \ so that lines are executed in a single shell, i.e.:
edit:
set TEXINPUTS=./texmf-project//; \
pdflatex -synctex=1 -interaction=nonstopmode main.tex
Edit:
Since this is Windows, it gets more complicated. Just adding \ and even & is not enough, since make will run it in a single line (through a batch file):
> type Makefile
all:
set TEXINPUTS=./texmf-project// & \
echo %TEXINPUTS%
> make -dr
...
Must remake target 'all'.
Creating temporary batch file C:\Users\user\AppData\Local\Temp\make11444-1.bat
Batch file contents:
#echo off
set TEXINPUTS=./texmf-project// & echo %TEXINPUTS%
...
ECHO is off.
...
Now, cmd.exe uses a line parser that expands variables when the line is parsed, so it first expands %TEXINPUTS% to an empty string (since it was not yet defined) and after that it evaluates the code which sets the variable. Therefore it is crucial that those commands are on separate lines to have variable set before it is used. The easiest way (if you use quite modern make) is including a .ONESHELL directive which would place all recipe into the batch and execute all at once:
> type Makefile
.ONESHELL:
all:
set TEXINPUTS=./texmf-project//
echo %TEXINPUTS%
> make -dr
...
Must remake target 'all'.
Creating temporary batch file C:\Users\user\AppData\Local\Temp\make29908-1.bat
Batch file contents:
#echo off
set TEXINPUTS=./texmf-project//
echo %TEXINPUTS%
...
./texmf-project//
...
Alternatively, if you don't somehow calculate the value, you can just set the variable at the make level (global or target level) and export it to the process, i.e.:
> type Makefile
all: export TEXINPUTS := ./texmf-project//
all:
echo %TEXINPUTS%
> make -dr
...
Must remake target 'all'.
Creating temporary batch file C:\Users\user\AppData\Local\Temp\make25392-1.bat
Batch file contents:
#echo off
echo %TEXINPUTS%
...
./texmf-project//
...

GNU make - transform every prerequisite into target (implicitly)

I have another make-like tool that produces an XML as an artifact after parsing my makefile which I'll then further process with Python.
It'd simplify things for me - a lot - if I could have make consider every single prerequisite to be an actual target because then this other tool
will classify each and every file as a "job".
This is a fragment of my makefile:
.obj/eventlookupmodel.o: C:/Users/User1/Desktop/A/PROJ/src/AL2HMIBridge/LookupModels/eventlookupmodel.cpp C:\Users\User1\Desktop\A\PROJ\src\AL2HMIBridge\LookupModels\eventlookupmodel.h \
C:/Users/User1/Desktop/A/PROJ/qt5binaries/include/QtCore/qabstractitemmodel.h \
C:/Users/User1/Desktop/A/PROJ/qt5binaries/include/QtCore/qvariant.h \
...
I'd want for make to think I have a dummy rule for each prerequisite such as below:
C:/Users/User1/Desktop/A/PROJ/qt5binaries/include/QtCore/qvariant.h:
#echo target pre= $#
C:/Users/User1/Desktop/A/PROJ/qt5binaries/include/QtCore/qabstractitemmodel.h:
#echo target pre=$#
C:/Users/User1/Desktop/A/PROJ/src/AL2HMIBridge/LookupModels/eventlookupmodel.cpp :
#echo target pre=$#
C:\Users\User1\Desktop\A\PROJ\src\AL2HMIBridge\LookupModels\eventlookupmodel.h:
#echo target pre=$#
I don't care about the exact form of the rule just that each file is considered an actual target.
My method of passing in this rule would be by setting the MAKEFILES variable like so
make all MAKEFILES=Dummy.mk
with Dummy.mk containing this rule so that I do not modify the makefiles.
I've tried the following so far.
Dummy.mk:
%.h:
#echo header xyz = $#
%:
#echo other xyz= $#
This partially works.
I run make all --trace --print-data-base MAKEFILES=Dummy.mk and I can see that
make does "bind" the %.h: rule to the header files. In the --print-data-base section, I see that rule being assigned to the header files.
C:/Users/User1/Desktop/A/QNX_SDK/target/qnx6/usr/include/stddef.h:
# Implicit rule search has been done.
# Implicit/static pattern stem: 'C:/Users/User1/Desktop/A/QNX_SDK/target/qnx6/usr/include/stddef'
# Last modified 2016-05-27 12:39:16
# File has been updated.
# Successfully updated.
# recipe to execute (from '#$(QMAKE) top_builddir=C:/Users/User1/Desktop/A/HMI_FORGF/src/../lib/armle-v7/release/ top_srcdir=C:/Users/User1/Desktop/A/HMI_FORGF/ -Wall CONFIG+=release CONFIG+=qnx_build_release_with_symbols CONFIG+=rtc_build -o Makefile C:/Users/User1/Desktop/A/HMI_FORGF/src/HmiLogging/HmiLogging.pro
', line 2):
#echo header xyz = $#
However, I do NOT see the "echo header xyz $#"-rule being executed.
Regarding the %: rule, it is neither executed for the .cpp files nor "bound" to them in the --print-data-base section.
However, it is bound and executed for existing targets which have no suffix i.e.
all: library binary
binary: | library
ifs: | library
For the %: rule, the reason for this behavior is because of 10.5.5 Match-Anything Pattern Rules: If you do not mark the match-anything rule as terminal, then it is non-terminal. A non-terminal match-anything rule cannot apply to a file name that indicates a specific type of data. A file name indicates a specific type of data if some non-match-anything implicit rule target matches it.
If I make it non-terminal - no double colon - then the rule doesn't apply to built-in types like .cppunless I un-define the built-in rules that negate my intended %: rule.
If I make it terminal, "it does not apply unless its prerequisites actually exist". But a .h or .cpp doesn't technically have prerequisites; can I just create a dummy file and have that as its prerequisite?
NOTE: This has NOTHING to do with gcc -M generation. Yes the -M option would help in the specific case of header and source files but this question is for more generic targets and prerequisites that already exist in the makefile when make is launched.
This may take a few iterations. Try:
%.h: null
#echo header xyz = $#
%: null
#echo other xyz= $#
null:
#:
Try generating static pattern rules for the header files. See one of the answers to Make ignoring Prerequisite that doesn't exist.
Static pattern rules only apply to an explicit list of target files like this:
$(OBJECTS): %.o: %.c
*recipe here*
where the variable OBJECTS is defined earlier in the makefile to be a list of target files (separated by spaces), for example:
OBJECTS := src/fileA.c src/fileB.c src/fileC.c
Note that you can use the various make utility functions to build that list of target files. For example, $(wildcard pattern), $(addsuffix), etc.
You should also ensure that the recipe "touches" the header file to change the timestamp.
I've found that using static pattern rules instead of pattern rules fixes problems where make doesn’t build prerequisites that don’t exist, or deletes files that you want.
Here is an example of using wildcard to copy files from one directory to another.
# Copy images to build/images
img_files := $(wildcard src/images/*.png src/images/*.gif src/images/*.jpg \
src/images/*.mp3)
build_images := $(subst src/,$(BUILD_DIR)/,$(img_files))
$(build_images): $(BUILD_DIR)/images/% : src/images/%
mkdir -p $(dir $#)
cp -v -a $< $#
There are other make functions like addprefix that could be used to generate a more complex file specification.

Visual C++ Command Line Compiler (CL.EXE) Redirect OBJ Files

The compiler (CL.EXE) can take multiple source files, but likes to generate all the OBJ files in the directory that it is invoked. I couldn't find the compiler flag to set an output directory but I did find one for an individual OBJ, but it can't take multiple sources.
Without having to specify each file to redirect the output and having lots of targets for NMAKE, is there an easy way to do it through CL?
It turns out the /Fo option actually works, but the directory you specify must end with a backslash. Thus
cl /Fo.\obj\ -c foo.c fee.c
Works but cl /Fo.\obj -c ... would fail.
Just to add to the only answer. In case when the obj path is quoted, the trailing backslash has to be either added after the path closing quote, or escaped if added before the quote.
cl /Fo"quoted path\obj"\ -c foo.c fee.c
OR
cl "/Foquoted path\obj"\ -c foo.c fee.c
OR
cl /Fo"quoted path\obj\\" -c foo.c fee.c
Speaking of NMAKE, similar syntax is expected when passing quoted macro values on NMAKE
command line. The trailing backslash seems to be the crucial bit to watch for.
nmake SOMEDIR="quoted path\obj"\
OR
nmake SOMEDIR="quoted path\obj\\"
OR
nmake "SOMEDIR=quoted path\obj"
NOT
nmake SOMEDIR="quoted path\obj\"
as this would result in an escaped quote \" and would grab whatever else followed on the command line and put it into $(SOMEDIR). Took me a while to diagnose such a behavior, hope this would save time to someone else.

how to get the output of a command when using Microsoft nmake?

With make on linux, we can do things like:
foo=$(shell /bin/bar)
which runs the command bar and assigns the output to foo. This can later be used in the makefile by referencing $(foo).
But now I have a Microsoft project on my hands that is compiled and linked with the Microsoft nmake.exe. Is there an equivalent thing for nmake that would allow me to run a command and assign the output to a variable?
Although this is an old question, there is a method of doing what is asked; its just convoluted, like everything in batch files!
One has to use the combined mechanisms of the fact that make imports environmental variables and that the preprocessor can invoke commands, and then call the Makefile recursively. It assume the Makefile is called Makefile (with no extension, which is the default).
!IFNDEF MAKE
MAKE=NMAKE
!ENDIF
!IFNDEF SHELLVALUE
! IF [echo off && FOR /F "usebackq" %i IN (`hostname`) DO SET SHELLVALUE=%i && $(MAKE) /$(MAKEFLAGS) /nologo /f $(MAKEDIR)\Makefile && exit /b ] == 0
! MESSAGE Make completed
! ELSE
! ERROR Error in nmake
! ENDIF
!ELSE
# $(SHELLVALUE) now contains the string returned from the command USERNAME
!MESSAGE Shellvalue is $(SHELLVALUE)
# Put the parts of the makefile that depend on SHELLVALUE here
!ENDIF
#
# To be a valid makefile it must have some rules to perform
all:
#echo;$(SHELLVALUE)
Yes, I know its horrible, but it does demonstrate how to do the technique, which can be done with any shell command and not just hostname.
I think the answer is "no." There is no equivalent.
I'd recommend that you convert to MSBuild if possible.

How to get absolute paths after vpath matching in make?

I have a makefile that depending on some properties sets vpath and generates a list of source files into one variable. I need to run the makefile without compiling anything (the compilation is actually handled by a different makefile) and just see to which real files the filenames get matched depending on the vpath settings.
Option 1: Let make do its path search:
.PHONY: whichfiles
whichfiles: $(LIST_OF_SOURCE_FILES)
#echo $+
Option 2: Simulate the path search using $(wildcard):
.PHONY: whichfiles
whichfiles:
#echo $(foreach f,$(LIST_OF_SOURCE_FILES),$(firstword $(wildcard $(VPATH:%=%/$f)) not-found:$f))
Either way, "make whichfiles" will print the list of matched files.
If some of the files can't be found, option 1 will fail with "no rule to make" reporting the first file that could not be found. Option 2 will print "not-found:" for each missing file.

Resources