Conditional Automake compilation for ARM A-32? - arm

I have a source file that needs to be compiled for ARM A-32. A-32 includes ARMv4 and ARMv7 (but not Aarch32 or Aarch64). Our GNU makefile has:
IS_ARM32 := $(shell echo "$(HOSTX)" | $(GREP) -i -c -E 'arm|armhf|arm7l|eabihf')
...
ifeq ($(IS_ARM32),1)
AES_ARCH = -march=armv7-a -marm
SRCS += aes-armv4.S
endif
...
ifeq ($(IS_ARM32),1)
aes-armv4.o : aes-armv4.S
$(CC) $(strip $(CXXFLAGS) $(AES_ARCH) -mfloat-abi=$(FP_ABI) -c) $<
endif
According to the Conditional Compilation using Automake Conditionals manual:
An often simpler way to compile source files conditionally is to use
Automake conditionals. For instance, you could use this Makefile.am
construct to build the same hello example:
bin_PROGRAMS = hello
if LINUX
hello_SOURCES = hello-linux.c hello-common.c
else
hello_SOURCES = hello-generic.c hello-common.c
endif
In this case, configure.ac should setup the LINUX conditional using
AM_CONDITIONAL (see Conditionals).
Following the link to conditionals I don't see a list of the conditionals like LINUX as used in the example. It also lacks a discussion of conditional compilation for architectures, like ARM and PowerPC.
What conditional does Automake use for ARM A-32?
Or how does one conditionally compile for ARM A-32?

Following the link to conditionals I don't see a list of the
conditionals like LINUX in the manual's example. It also lacks a
discussion of conditional compilation for platforms, like ARM and
PowerPC.
You appear to have overlooked this text from the quoted excerpt of the manual:
In this case, configure.ac should setup the LINUX conditional using
AM_CONDITIONAL
AM_CONDITIONAL is the Autoconf macro with which you define a predicate for use in an Automake conditional. There are no premade predicates.
What does Automake use for ARM A-32?
Or how does one conditionally compile for ARM A-32?
Given that your existing approach is based on
IS_ARM32 := $(shell echo "$(HOSTX)" | $(GREP) -i -c -E 'arm|armhf|arm7l|eabihf')
you might do this in configure.ac:
AM_CONDITIONAL([ARM32], [echo "$HOSTX" | $GREP -i -c -E 'arm|armhf|arm7l|eabihf'])
This assumes that HOSTX and GREP are autoconf output variables whose values have already been set. If that's not the case for you then I'm sure that it at least provides a model for you to start with.
With the ARM32 predicate defined in your configure.ac, you can then use it in your Makefile.am files much as in the example from the manual:
if ARM32
hello_SOURCES = hello-arm32.c hello-common.c
else
hello_SOURCES = hello-generic.c hello-common.c
endif

Related

How to compile into a binary for each optimization level with gcc and make?

I have a sourcefile benchmark.c and I want to build it with each compiler optimization level.
Is there a way to achieve this automatically instead of providing every single flag manually like this?
all:
gcc benchmark.c -o benchmark-O0 -O0
gcc benchmark.c -o benchmark-O1 -O1
gcc benchmark.c -o benchmark-O2 -O2
gcc benchmark.c -o benchmark-O3 -O3
I'd like to have a more general method.
Thank you in advance.
You're thinking of a for loop?
all:
for level in O0 O1 O2 O3; do \
gcc benchmark.c -$level benchmark-$level \
done
Since you're assuming GNU make, you might as well use GNU features such as functions. Here's one way you could do the job in what seems to me to be in a reasonably make-like idiom:
baseexe := benchmark
levels := 0 1 2 3 s
files := $(foreach level,$(levels),$(baseexe)-O$(level))
all: $(files)
$(files): $(baseexe).c
gcc $(subst $(baseexe),,$#) $< -o $#
There are two or three bits of magic there:
the definition of the files make variable uses GNU's foreach make function to build the list of target files from the base name and list of optimization levels.
(following you) the names of the target files encode the optimization option, so
the build recipe can and does use GNU's subst make function to extract the optimization option from each target name.
You'll have to judge whether that somewhat arcane 8-line (including two blank) makefile is better for your purposes than what you started with. As for me, I'd take your plain and simple starting version.
You can iterate through a list of words, each containing the digit that sets the optimization option, and each time call a canned recipe which invokes the appropriate command:
LIST := 0 1 2 3
define RUN
gcc benchmark.c -o benchmark-O$1 -O$1
endef
all:
$(foreach variable,$(LIST),$(call RUN,$(variable)))

Can I use Preprocessor Directives in .ld file

Can I use Preprocessor Directives in .ld file?
I need to to use one of two sets of .ld file and wants to let Build engine deside that using macro, Can I do that?
Yes, you can. You need to run preprocessor manually for your linker script, like this:
in="your_linker_script.ld"
out="generated_script.ld"
cflags=-Iinclude/
gcc -E -P -x c $cflags $in >$out
Flags:
-E specifies GCC to only run preprocessor
-P prevents preprocessor from generating linemarkers (#line directives)
-x c tells GCC to treat your linker script as C source file (it's needed to run GCC with your LD script)
Or you can simply use cpp tool, which is actually C preprocessor.
After this you will be able to use generated linker script to build your program (e.g. in Makefile).
Example
Here is how I solved this problem in my project:
Here is my linker script uses preprocessor (#include directive and CONFIG_TEXT_BASE constant). Excerpt:
#include <config.h>
. = CONFIG_TEXT_BASE;
Here is script to generate preprocessed linker script. Excerpt:
gcc -E -P -x c -Iinclude $cflags $in >>$out
Here is my Makefile, it's generating preprocessed linker script at $(LDS_GEN) target (line 53) and the this generated script is being used to build result binary (line 42). Excerpt:
$(LDS_GEN): $(LDS)
build/gen-lds.sh $(LDS) $(LDS_GEN) $(CFLAGS)
$(APP).bin: $(OBJS) $(LDS_GEN)
$(LD) $(OBJS) -T $(LDS_GEN) -o $(APP).elf
Small update after long time. This way of pre-processing works until the memory file does not contain lines that are fooling the pre-processor. E.g.:
"KEEP(*path/*.o(.rodata .rodata*))"
The "/*" after path is considered comment start (the line contains what is considered a C multi-line comment start and not a match pattern).
This line is valid for the linker but it is considered comment and the output from C pre-processor will remove everything till a supposed closing comment is found:
"KEEP(*path"
The resulting file is clearly invalid for the linker. I have no solution at the moment.
As a simplification of Sam's answer, I added the below to my makefile and away we go :)
PRE_LD_FILE = $(PROG_NAME).ld
LD_FILE = $(PROG_NAME)_generated.ld
$(LD_FILE) : $(PRE_LD_FILE)
cpp $(PRE_LD_FILE) | grep -v '^#' >>$(LD_FILE)

Linking with another start-up file

I am trying to link a program with my own start-up file by using the STARTUP directive in a LD script:
...
ENTRY(_start)
STARTUP(my_crt1.o)
...
GCC driver is used to link the program (not to bother with library paths like libgcc, etc.):
gcc -T my_script.ld ...
Unfortunately, it only works with a GCC compiled for powerpc targets, while arm or i686 targets don't and still include crt0.o in collect2. For example:
arm-eabi-g++ -v -T my_script.ld ...
gives me:
collect2 ... /opt/lib/gcc/arm-eabi/4.8.0/../../../../arm-eabi/lib/crt0.o ...
and thus:
crt0.S:101: multiple definition of `_start'
It seems the STARTUP directive is totally ignored (the powerpc target uses its default crt0 too unless the STARTUP directive is specified) and there is no way to disable the default crt0.
Is there a portable way to link against another start-up file?
My start-up file uses libgcc functions (to call ctors and dtors) so crtbegin.o, crtend.o, etc. are needed so I would like to avoid the -nostartfiles option which disables crt*.o - I need to disable crt0.o only.
Thank you
I am trying to link a program with my own start-up file ...
GCC driver is used to link the program ...
In that case, you must also supply -nostartfiles flag to GCC.
This limitation indeed forces you to disable the default startup files with -nostartfiles (I prefer -nostdlib). You then need to build by yourself the list of run-time objects. gcc has the option -print-file-name to print the absolute path of libraries it was compiled with (crtbegin.o, crtend.o, libgcc.a...). For example: arm-eabi-g++ <FLAGS> -print-file-name=crtbegin.o
Here is the GNU Make macro I use (providing gcc and cflags):
define m.in/toolchain/gnu/locate =
$(strip
$(shell $(m.in/toolchain/gnu/bin/gcc) $(m.in/toolchain/gnu/cflags) \
-print-file-name=$(m.in/argv/1))
)
endef
crtn := $(call m.in/toolchain/gnu/locate, crtn.o)

Where to add a CFLAG, such as -std=gnu99, into an autotools project

I have a simple Autotools C project (not C++).
CFLAGs (by inspection) seem to be -g -O2.
I want all of the generated make files to also have -std=gnu99 appended to the CFLAGs, because I use for (int i = 0; i < MAX; i++) and similar.
I can obviously hack the Makefile, but this gets overwritten on ./configure.
Where is the correct place to add (or change) CFLAGs which are required by the code (as opposed to those CFLAGs which the user might want to change)?
(Note this is partial duplicate of Where to add a CFLAG, such as -std=gnu99, into an (Eclipse CDT) autotools project as I was getting Eclipse-specific answers which I didn't want.)
#DevSolar's answer has not helped yet. A configure.ac file (below) generates the configure script (also below).
configure.ac:
dnl Process this file with autoconf to produce a configure script.
CFLAGS="$CFLAGS -std=gnu99"
AC_PREREQ(2.59)
AC_INIT(tuntest, 1.0)
AC_CANONICAL_SYSTEM
AM_INIT_AUTOMAKE()
AC_PROG_CC
AC_CONFIG_FILES(Makefile src/Makefile)
AC_OUTPUT
$ grep CFLAGS configure
CFLAGS
CFLAGS
To assign environment variables (e.g., CC, CFLAGS...), specify them as
CFLAGS C compiler flags
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_test_CFLAGS=${CFLAGS+set}
ac_save_CFLAGS=$CFLAGS
CFLAGS="-g"
CFLAGS=""
CFLAGS="-g"
if test "$ac_test_CFLAGS" = set; then
CFLAGS=$ac_save_CFLAGS
CFLAGS="-g -O2"
CFLAGS="-g"
CFLAGS="-O2"
CFLAGS=
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
autoconf has a macro for this:
Just put:
AC_PROG_CC_STDC
after your AC_PROG_CC and everything will be right.
Especially when you use other compilers that do not have -std=gnu99 but operate in C99 mode by default (or have a different option hpcc's -AC99 springs to mind).
I would NOT use CFLAGS for that kind of thing.
From the docs:
-- Macro: AC_PROG_CC_STDC
If the C compiler cannot compile ISO Standard C (currently C99),
try to add an option to output variable `CC' to make it work. If
the compiler does not support C99, fall back to supporting ANSI
C89 (ISO C90).
After calling this macro you can check whether the C compiler has
been set to accept Standard C; if not, the shell variable
`ac_cv_prog_cc_stdc' is set to `no'.
Although using a macro like AC_PROG_CC_STDC is preferable to adding compiler options to CFLAGS, autoconf currently has no macros enabling C11 compiler support -- there is no AC_PROG_CC_C11 yet, and AC_PROG_CC_STDC only adds the -std=gnu99 option for gcc instead of -std=gnu11 or -std=c11.
You can add compiler flags simply by putting
CFLAGS="$CFLAGS -std=c11"
in the configure.ac.
Alternatively, it might be better to check first if the compiler supports the added option, and print a warning otherwise, telling the user to manually add an equivalent option to CFLAGS that works for their compiler:
AX_CHECK_COMPILE_FLAG([-std=c11],
[AX_APPEND_FLAG([-std=c11])],
[AC_MSG_WARN([-std=c11 not supported, you may have to set CFLAGS to enable C11 support.])
])
This uses the AX_CHECK_COMPILE_FLAG and AX_APPEND_FLAG macros found in the GNU Autoconf Archive. To use them, put them into an m4 subdirectory and add AC_CONFIG_MACRO_DIR([m4]) to configure.ac
This topic is covered in the Automake manual, 27.6 Flag Variables Ordering. There an interaction between configure.ac and Makefile.am, and its more than just setting a well known shell variable used in implicit make rules.
The short of it is, you should set it in a new variable called something like mumble_CFLAGS discussed in the Automake manual. mumble is just the name of your program, and it is often foo or bar in other examples. Later, when your makefile is created, the recipe for your program (mumble or foo or bar) will use $(mumble_CFLAGS) $(CFLAGS) to build the target.
Here is an example of how it might look. Instead of using mumble or foo or bar, it uses my_prog as a artifact name.
configure.ac:
# Perform a compile test using -std=gnu99, set has_gnu99
if test "$has_gnu99" -eq "1"; then
AC_SUBST([MY_GNU99], [-std=gnu99])
fi
Makefile.am:
bin_PROGRAMS = my_prog
my_prog_CFLAGS = $(MY_GNU99) $(MY_ANOTHER_FLAG) $(MY_YET_ANOTHER_FLAG) ...
Later, when the makefile is generated, it will have a recipe similar to the following, where $(MY_PROG_CFLAGS) is applied to all the objects that build my_prog:
my_prog :
$(CC) $(CPPFLAGS) $(MY_PROG_CFLAGS) $(CFLAGS) -c $< -o $#
The extra indirections of my_prog_CFLAGS allows you to have multiple flags for different targets. For example, you could have a my_prog_CFLAGS, a my_archive_CFLAGS and a my_sharedobj_CFLAGS.
And its not limited to my_prog_CFLAGS. You could also have my_prog_CPPFLAGS, my_prog_CXXFLAGS and other variables used implicitly in makefiles.
This is from the Automake manual:
Compile Flag Variables
This section attempts to answer all the above questions. We will
mostly discuss CPPFLAGS in our examples, but actually the answer holds
for all the compile flags used in Automake: CCASFLAGS, CFLAGS,
CPPFLAGS, CXXFLAGS, FCFLAGS, FFLAGS, GCJFLAGS, LDFLAGS, LFLAGS,
LIBTOOLFLAGS, OBJCFLAGS, OBJCXXFLAGS, RFLAGS, UPCFLAGS, and YFLAGS.
CPPFLAGS, AM_CPPFLAGS, and mumble_CPPFLAGS are three variables that
can be used to pass flags to the C preprocessor (actually these
variables are also used for other languages like C++ or preprocessed
Fortran). CPPFLAGS is the user variable (see User Variables),
AM_CPPFLAGS is the Automake variable, and mumble_CPPFLAGS is the
variable specific to the mumble target (we call this a per-target
variable, see Program and Library Variables).
Automake always uses two of these variables when compiling C sources
files. When compiling an object file for the mumble target, the first
variable will be mumble_CPPFLAGS if it is defined, or AM_CPPFLAGS
otherwise. The second variable is always CPPFLAGS.
In the following example,
bin_PROGRAMS = foo bar
foo_SOURCES = xyz.c
bar_SOURCES = main.c
foo_CPPFLAGS = -DFOO
AM_CPPFLAGS = -DBAZ
xyz.o will be compiled with ‘$(foo_CPPFLAGS) $(CPPFLAGS)’, (because
xyz.o is part of the foo target), while main.o will be compiled with
‘$(AM_CPPFLAGS) $(CPPFLAGS)’ (because there is no per-target variable
for target bar).
The difference between mumble_CPPFLAGS and AM_CPPFLAGS being clear
enough, let’s focus on CPPFLAGS. CPPFLAGS is a user variable, i.e., a
variable that users are entitled to modify in order to compile the
package. This variable, like many others, is documented at the end of
the output of ‘configure --help’.
For instance, someone who needs to add /home/my/usr/include to the C
compiler’s search path would configure a package with
./configure CPPFLAGS='-I /home/my/usr/include'
and this flag would be propagated to the compile rules of all
Makefiles.

Use the same makefile for make (Linux) and nmake (Windows)

I have a simple C program (one source file) which I want to compile on Linux and on Windows via make and nmake, respectively. Is there a possibility to accomplish this with a single makefile?
I thought about something like
ifeq($(MAKE), nmake)
// nmake code here
else
// make code here
endif
Unfortunately nmake seems not to understand ifeq, so I cannot use that. I have a working makefile, but that produces very ugly results:
hello: hello.c
$(CC) hello.c
That works on both systems. The problem is that the outcome depends on the default behaviors of the respective compilers. Under Linux I get an executeable named 'a.out' rather than 'hello'. Under Windows I get 'hello.exe' but there is also 'hello.obj' which I do not want to have.
Is there an alternative way? Or is what I'm trying absolutely impossible?
It's probably not impossible, but most likely so hard that it would be easier to write two makefiles anyway.
Both GNU make (used in Linux) and nmake have include directives though, so some common things can be put in a common makefile that is included by the main makefile.
You should look at using CMake for this. With one source file, it should be quite easy. Here is how you could set up a simple project:
cmake_minimum_required(VERSION 3.10)
# set the project name
project(Hello)
# add the executable
add_executable(Hello hello.c)
To build the simple project, you would do the following (this assumes your source and CMakeLists.txt files are in the same directory as the source file hello.c:
mkdir build
cd build
cmake ..
cmake --build .
I wanted to use the same makefile include to be used by Make and NMAKE. Since make recognises line continuation on comment lines, but NMAKE doesn't, this means that we can have separate instructions for Make and NMAKE. For example:
# NMAKE code here \
!ifndef 0 # \
MV=move # \
RM=del # \
CP=copy # \
!else
# Make code here
MV=mv -f
RM=rm -f
CP=cp -f
# \
!endif
You just have to make sure that NMAKE-specific code is encompassed by # \.
I am not able to find a way to use a common makefile to work for both GNU Make and Microsoft NMAKE, mainly because they have an incompatible syntax for "include" and/or "if" directives. Microsoft NMAKE requires to use ! prefix for directives. For example, !if, !include, etc...
If it is allowed to have separate macros, however, it could be tricked around. Here I presents the best way I found so far for making a makefile compatible for both GNU Make and Microsoft NMAKE by observing the followings:
Microsoft NMAKE reads TOOLS.ini file for default macros.
The Microsoft suite uses .obj as the object file extension.
GNU Make reads files defined in a MAKEFILES environment variable.
The GNU suite use .o as the object file extension.
GNU make need not give an executable extension .exe for a target.
Note: The following has been tested using Microsoft Visual Studio 2015 and MINGW32.
Step 1: create a following DOS batch file and let it run whenever the CMD prompt is invoked.
set MAKEFILES=TOOLS.gcc
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
Step 2: Create a TOOLS.ini file under your working directory as below: (this file is independent of your project dependencies except the libraries possibly)
[NMAKE]
LDLIBS =
CDEBUG = /Zi
LDEBUG = /debug:full
WDFLAGS = /wd4996 /wd4774 /wd4018 /wd4710 /wd4820
CFLAGS = /nologo $(CDEBUG) /EHsc /Wall $(WDFLAGS)
LDFLAGS = /nologo $(LDEBUG)
RM = del /F /Q
LINK = "$(VCINSTALLDIR)bin\link" $(LDFLAGS)
CP = copy
CC = cl
CPP = $(CC) /P
X = .exe
O = .obj
.obj.exe:
$(LINK) $** $(LOADLIBES) $(LDLIBS) /Out:$#
Step 3: Create a TOOLS.gcc under your working directory as below: (this file is independent of your project dependencies except the libraries possibly)
LD_LIBS =
LDLIBS =
CDEBUG = -g
LDEBUG = -g
CFLAGS = $(CDEBUG)
LDFLAGS = $(LDEBUG)
RM = rm -f
LINK = gcc $(LDFLAGS)
CP = cp
CC = gcc
CPP = $(CC) -E
X =
O = .o
%: %.o
$(LINK) $^ $(LOADLIBES) $(LDLIBS) -o $#
Step 4: Edit your makefile as below (note $(X) and $(O)) where only dependencies are specified.
SHELL = /usr/bin/sh
app: app1$(X) app2$(X)
app1$(X): app1$(O)
app2$(X): app2$(O)
clean:
$(RM) *.exe *.o *.obj *.ilk *.pdb *.tmp *.i *~
Step 5: Enjoy GNU Make and Microsoft NMAKE with the same makefile
$ nmake
$ make clean
$ nmake clean
$ make
My solution is to use two different filenames. (since the Makefile name searching priority in different OSes will not be the same)
For Windows, I use normal "Makefile."
For Linux, I use the special "GNUmakefile" according to this article.
So that nmake (Win) will find "Makefile," and make (Linux) will find "GNUmakefile."
Solution: https://github.com/jaykrell/w3/blob/master/Makefile
# This one Makefile works with Microsoft nmake and GNU make.
# They use different conditional syntax, but each can be
# nested and inverted within the other.
all: default
ifdef MAKEDIR: # gmake: false; nmake: unused target
!ifdef MAKEDIR # gmake: not seen; nmake: true
#
# Microsoft nmake.
#
!else # and now the other
else
#
# GNU (Posix?) make.
#
endif # gmake: close condition; nmake: not seen
!endif : # gmake: unused target; nmake close conditional
default: # default target for both
I just thought of something completely different.
If you stick to your extremely simple Makefile, which, you say, works, and just put the 'standard' variables CC and CFLAGS in your respective environments, say
export CC=gcc
respectively
set CC=CL.EXE
and
export CFLAGS=-o myexecutable
respectively
set CFLAGS=/out:myexecutable.exe
it might just work.
Be aware, I'm not firm in the exact options to use, you'll have to figure them out yourself. But AFAIK both make variants recognize the same set of flags. You may even set those on the respective command lines (but not in the makefile, since NMAKE uses a different 'ifeq' syntax...)
Yes, you can do this with a single Makefile. The best source for this material is the O'Reilly book:
Managing Projects with GNU Make, Third Edition By Robert Mecklenburg
See chapter 7: Portable Makefiles.
In summary, the technique is to test the environment variable ComSpec which says if the Windows command interpreter is present:
ifdef COMSPEC
MV ?= move
RM ?= del
else
MV ?= mv -f
RM ?= rm -f
endif
I wrap this with a portable shell script which uses sed to edit the makefile for Nmake or GNU make...
I've recently experimented with using the C preprocessor to generate a portable Makefile from a template Makefile.cc containing preprocessor symbols. So far it's worked surprisingly well. The first observation is that NMAKE will prescan a Tools.ini file, which I provide in the same directory as
[NMAKE]
MAKECONFIG=-D_NMAKE
Then I have a 'true' Makefile next to it which is written in only the common sub language of GNU Make and NMAKE.
MAKEFILE=Makefile.mk
TEMPLATE=Makefile.cc
all: $(MAKEFILE)
$(MAKE) -f $(MAKEFILE)
clean: $(MAKEFILE)
$(MAKE) -f $(MAKEFILE) clean
$(MAKEFILE): $(TEMPLATE)
$(CXX) $(MAKECONFIG) -E $(TEMPLATE) > $(MAKEFILE)
Note that the -E switch is pretty common for compilers (at least the big three I work with: GCC, Clang, and CL) for only preprocessing the file. With GNU Make the $(MAKECONFIG) expands to nothing, but in NMAKE it provides the preprocessor variable declaring itself. Since your template Makefile.cc can check it with #ifdef, as well as check for common variables with which the compiler declares itself, you can customize your Makefile.mk quite a bit for both the 'make' program, your operating system, and the compiler you're using.
If you have any 'make' you probably already have a C compiler too; there's no need to install additional software like CMake or autotools. It uses mechanisms that are old and so likely to work in a lot of environments. And from what I've been able to tell so far, it's really fast. Faster at least than running a configuration step in autotools. The only disadvantage I've faced is that it limits the style of your Make rules to being on the same line, because the preprocessor changes the indentation of the code. Also the preprocessor spits out lines with # tags, but since these start a comment in a Makefile, they get ignored anyway.
A have a somewhat small C++ project with a Makefile.cc that looks like the following snippet. It compiles on GNU Make or NMAKE with either GCC, Clang, or CL and on either Windows or in a POSIX environment. I've yet to support BSD Make or test any other compiler though.
// Make Version
#ifdef _NMAKE
# define ifdef !ifdef
# define ifndef !ifndef
# define else !else
# define endif !endif
# define err(x) !error x
# define cat(x, y) x=$(x) y
#else // GNU Make
# define err(x) $(error x)
# define cat(x, y) x += y
#endif
// System Commands
ifdef SHELL
RM=rm -f
else
ifdef COMSPEC
RM=del /f
else
err("Cannot determine your system commands.")
endif // COMSPEC
endif // SHELL
// Project Variables
STD=c++17
SRC=test.cpp dbg.cpp dir.cpp dll.cpp env.cpp err.cpp fifo.cpp file.cpp shm.cpp sig.cpp socket.cpp sys.cpp xdg.cpp
BIN=test
.SUFFIXES: .cpp .hpp .o .d .obj .pdb .lib .exp .ilk .log .i .db
// Operating system
#ifdef _WIN32
cat(CFLAGS, -D_WIN32)
EXE=$(BIN).exe
#else
cat(CFLAGS, -D_POSIX_C_SOURCE)
cat(LDFLAGS, -ldl -lrt -lpthread)
EXE=$(BIN)
#endif
// Make Targets
all: $(EXE)
clean: ; $(RM) $(EXE) *.o *.d *.obj *.pdb *.lib *.exp *.ilk *.log *.i
// Compiler Options
#ifdef _MSC_VER
cat(CFLAGS, -nologo -std:$(STD) -W4 -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS -EHsc -permissive-)
ifndef NDEBUG
cat(CFLAGS, -Zi)
endif
cat(LDFLAGS, -nologo)
OBJ=$(SRC:.cpp=.obj)
$(EXE): $(OBJ); $(CXX) $(LDFLAGS) $(OBJ) -Fe$#
.cpp.obj: ; $(CXX) $(CFLAGS) -c $<
#elif defined(__GNUC__) || defined(__llvm__) || defined(__clang__)
cat(CFLAGS, -std=$(STD) -Wall -Wextra -Wpedantic -MP -MMD)
ifndef NDEBUG
cat(CFALGS, -g)
endif
cat(LDFLAGS, -rdynamic)
OBJ=$(SRC:.cpp=.o)
$(EXE): $(OBJ); $(CXX) $(LDFLAGS) $(OBJ) -o $#
.cpp.o: ; $(CXX) $(CFLAGS) -c $<
# ifndef _NMAKE
-include $(SRC:.cpp=.d)
# endif
#else
# error "Cannot determine your compiler."
#endif
Wouldn't it be possible to use a script in gnu sed, or perl to translate the host's Makefile to a Microsoft compatible NMakefile? The Makefiles, after all, are text files to provide input for whichever helper tool you're using. Sed and Perl both exist for Linux and Windows.

Resources