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
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