views:

190

answers:

4

One project should be compiled for Windows, Linux and an embedded target. The application has behavior differences when running on host or embedded targets. To summarize my requirements, here is the table:

   Target               Compiler      Behavior
   Windows              MSVC, gcc         A
   Host Linux           gcc               A
   Embedded Linux       cross pltf gcc    B

I wish to create a Makefile that would recognize the compiler and environmental differences automatically (without need to pass parameters/defines manually). Is it possible to resolve such problem by just using conditional compilation at C source file level?
I don't use automake so far.

A: 

gcc will define

 __linux__

when compiling on linux. MSVC defines something similar - I forget which but I am sure the docs will tell you

but really you need to learn how to use configure. Thats what its for. Its a pain to learn but highly useful (automake, autoconf etc)

pm100
+5  A: 

The autotools were made to solve this problem. A Makefile by itself can't do this.

As for conditional compilation, there are several macros available that these compilers define (e.g. _MSC_VER for MSVC++, _WIN32 for Windows and and __linux__ for Linux). Also, gcc provides assertions for handling different CPU targets.

Target         Compiler     Conditional
Windows        MSVC++       #ifdef _MSC_VER
Windows        gcc          #if defined(_WIN32) && defined(__GNUC__)
Linux          gcc          #if defined(__linux__) && defined(__GNUC__)
Platform (x86) gcc          #if #cpu(i386)
Platform (arm) gcc          #if #cpu(arm)

That should be sufficient for your purposes.

Judge Maygarden
Autotools will work for this sort of thing, but it can be an awful lot of overhead for some projects.
Matt McClellan
I agree with Matt. Involving autotools for a project of this kind (and size) that I have is a overhead.
Serge C
You don't have to use Makefiles. There are other options.
Judge Maygarden
Cmake is a good alternative. http://www.cmake.org/cmake/project/statistics.html
supercheetah
Just FYI: #cpu preprocessor directive is a depricated one for new GCC versions. "echo | gcc -E -dM -" shows the list of built-in defines
Serge C
@Serge Is there a new method for determining the target architecture?
Judge Maygarden
+2  A: 

My answer is: probably. It depends on the differences between your environments.

I would generate a list of the macros pre-defined by the preprocessor in gcc for each of your environments. All you need to do is create an empty file, say foo.h, and run the following command: gcc -E -dM foo.h for each environment. I'd redirect the output to a different file for each environment.

Then, compare the predefined macros and look for one that fits the bill. In your question, you indicated that the embedded environment has different behavior from the Windows and Host Linux platforms. Assuming you're using a different chip architecture for the embedded environment, there should be a macro defined that indicates the architecture. For example, on my 64-bit linux box, __x86_64__ is defined.

Once you have that info, use that macro as your conditional value:

#ifdef MACRO_INDICATING_EMBEDDED
// Behavior B
#else
// Behavior A
#endif
Matt McClellan
You don't actually need a dummy file - you can just do it like this:`$ gcc -dM -E - < /dev/null`
Paul R
Thanks, @Paul. Forgot all about reading from standard input. Of course, /dev/null won't work on Windows, though.
Matt McClellan
A: 

You could try a combination of predefined macros for operating systems and architectures; perhaps also interesting: compiler ids.

If you don't want to use a more sophisticated build system, you can parse command line arguments within your makefile. A simple example would be the addition of .exe to executable names on windows. For this I have code like this within the makefile

ifeq ($(target),win32)
    foo_binary := $(foo_binary).exe
endif

and invoke make via a make.bat file containing

@make.exe target=win32 %*
Christoph