The purpose of this component is to generate automatically a multi-language
Makefile.

Requirements:
- Be easily extensible to new programming languages
- Handle the dependencies automatically
- Handle different compilers for different languages
- Handle source dependencies automatically
- Provide language-specific compilation/link options
- Provide file-specific compilation options
- Provide conditional sections based on configurable variables
- ??? Handle building and use of libraries

Process:

- The tool will take a GNAT project file as its input,
  and generate a Makefile for each project file

- The Ada dependencies and compilations are handled by gnatmake

- Part of the build process is handled by a generic Makefile, as described
  below

- The dynamic part is handled by a Makefile generated from the GNAT project
  file. The automatic translation is described below in the section entitled
  "Section generated by translating the project file"

############################################
# Section handled by the generic Makefile
############################################

- The list of non Ada objects is computed automatically based on language
  extensions:

ifeq ($(strip $(filter c,$(LANGUAGES))),c)
   ifndef C_SRCS
      C_SRCS := \
        $(foreach name,$(SRC_DIRS),$(notdir $(wildcard $(name)/*$(C_EXT))))
   endif

   OBJECTS += $(C_SRCS:$(C_EXT)=$(OBJ_EXT))
endif

ifeq ($(strip $(filter c++,$(LANGUAGES))),c++)
   ifndef CXX_SRCS
      CXX_SRCS := \
        $(foreach name,$(SRC_DIRS),$(notdir $(wildcard $(name)/*$(CXX_EXT))))
   endif

   OBJECTS += $(CXX_SRCS:$(CXX_EXT)=$(OBJ_EXT))
endif

OBJ_FILES := $(foreach name,$(OBJECTS),$(OBJ_DIR)/$(name))

- The initial idea to handle C/C++ dependencies was to use makedepend
  by adding a special rule in the Makefile:

    SRCS = file1.c file2.c ...
    CFLAGS = -O -DHACK -I../foobar -xyz
    depend:
        makedepend -- $(CFLAGS) -- $(SRCS)

  but makedepend does not know about the compiler specific include directories
  and does not seem to handle all C files properly, so the approach taken
  is to use the output of $(CC) -M (also note the handling of object files):

SRCS = file1.c file2.c ...
ALL_CFLAGS = -O -DHACK -I../foobar -xyz
OBJ_DIR = obj

ifeq ($(CC),gcc)
   DEP_FLAGS := -MM
else
   DEP_FLAGS := -M
endif

DEP_FILES := $(OBJ_FILES:$(OBJ_EXT)=.d)

ifeq ($(strip $(filter-out %sh,$(SHELL))),)
$(OBJ_DIR)/%.d: %.c
	@$(SHELL) -ec '$(CC) $(DEP_FLAGS) $(ALL_CFLAGS) $< \
		| sed '\''s/\($*\)\.o[ :]*/\1.o $(notdir $@) : /g'\'' > $@; \
		[ -s $@ ] || rm -f $@'
else
$(OBJ_DIR)/%.d: %.c
	@$(CC) $(DEP_FLAGS) $(ALL_CFLAGS) $< \
		| sed 's/\($*\)\.o[ :]*/\1.o $(notdir $@) : /g' > $@
endif

-include $(DEP_FILES)

   Another approach when CC is gcc is to use the -MD preprocessor option:

DEP_CFLAGS=-Wp,-MD,$(OBJ_DIR)/$(*F).d

$(OBJ_DIR)/%.o: %.c
	$(CC) -c $(ALL_CFLAGS) $(DEP_CFLAGS) $< -o $(OBJ_DIR)/$@

- The directory/source search is handled using the vpath make pattern:

vpath %.c $(C_SRC_DIRS)
vpath %.o $(OBJ_DIR)

- Static linking is handled by special make rule:

foo : foo.c -lcurses
        $(CC) $^ -o $@

will generate:

$(CC) foo.c /usr/lib/libcurses.a -o foo

- Phony commands will be used for reliability and efficiency instead of the
  "force" rule:

.PHONY : clean all

clean:
	$(RM) $(OBJ_FILES)

distclean: clean
	$(RM) $(DEP_FILES)

- Automatic rules are used to handle default compilation:

%.o : %.c
	$(CC) -c $(ALL_CFLAGS) $< -o $(OBJ_DIR)/$@

- A variable LINKER is computed automatically as the C/C++ linker command, based
  on the languages:

# Set C/C++ linker command & target

ifeq ($(filter c++,$(LANGUAGES)),c++)
   LINKER = $(CXX)

   ifeq ($(filter ada,$(LANGUAGES)),ada)
      # C++ and Ada mixed
      LINKER = $(OBJ_DIR)/c++linker
      LARGS = --LINK=$(LINKER)

      ifeq ($(strip $(filter-out %gcc %g++ %c++,$(CXX))),)
         # Case of GNU C++ and GNAT

$(LINKER): Makefile
	@echo \#!/bin/sh > $(LINKER)
	@echo unset BINUTILS_ROOT >> $(LINKER)
	@echo unset GCC_ROOT >> $(LINKER)
	@echo $(CXX) $$\* >> $(LINKER)
	@chmod +x $(LINKER)

      else
$(LINKER): Makefile
	@echo \#!/bin/sh > $(LINKER)
	@echo $(CXX) $$\* $(shell gcc -print-libgcc-file-name) >> $(LINKER)
	@chmod +x $(LINKER)
      endif
   endif
else
   ifeq ($(strip $(LANGUAGES)),c)
      # Case of C only
      LINKER = $(CC)
   endif
endif

- Linking is handled is a generic manner, based on the value of LANGUAGES,
  MAIN and LINKER:

ifeq ($(strip $(filter-out c c++,$(LANGUAGES))),)
# link with C/C++
link: $(OBJ_DIR)/$(EXEC)
$(OBJ_DIR)/$(EXEC): $(OBJ_FILES)
	$(LINKER) $(OBJ_FILES) -o $(OBJ_DIR)/$(EXEC) $(LDFLAGS)

else
ifeq ($(strip $(filter-out c c++ ada,$(LANGUAGES))),)
# link with Ada/C/C++

ifeq ($(MAIN),ada)
# Ada main
link: $(LINKER) force
	$(GNATMAKE) -P$(PROJECT_FILE) $(ADA_SOURCES) \
		    -largs $(OBJ_FILES) $(LARGS)
else
# C/C++ main
# The trick here is to force gnatmake to bind/link, even if there is no
# Ada main program. To achieve this effect, we use the -z switch, which is
# close enough to our needs, and the usual -n gnatbind switch and --LINK=
# gnatlink switch
link: $(LINKER) force
	$(GNATMAKE) -o $(OBJ_DIR)/$(EXEC) -z -P$(PROJECT_FILE) $(ADA_SOURCES) \
		-bargs -n -largs $(LARGS) \
		$(OBJ_FILES)
endif

else
# unknown set of languages, fail
link:   
        @echo do not know how to link with the following languages: $(LANGUAGES)        exit 1
endif
endif

######################################################
# Section generated by translating the project file
######################################################

- each project file generates one Makefile

- The Makefile is called Makefile.<project> where <project> is the name of the
  project, and is located at the same place the project file is.
  In the following sections, <project> will designate the project name in lower
  case, and <PROJECT> the project name in upper case.

- "extends" projects are fully translated into an expanded, non "extending"
  GNAT project file first (as is done internally when processed by GNAT tools)
  and then translated as a regular project into a Makefile

- each Makefile starts with the following section, used to handle multiple
  inclusions in other Makefiles:

ifeq ($(<PROJECT>_PROJECT),)
<PROJECT>_PROJECT=True

ifeq ($(BASE_DIR),)
   <PROJECT>_ROOT=True
   <PROJECT>_BASE_DIR=$(shell gprcmd pwd)
else
   <PROJECT>_ROOT=False
   <PROJECT>_BASE_DIR=$(BASE_DIR)
endif

The variables BASE_DIR and <PROJECT>_BASE_DIR are used to handle relative
directories.
The variable <PROJECT>_ROOT is used to know whether the current Makefile acts
as the main project or as a sub project.

- project dependencies (with and limited-with clauses, there is no need to
  differenciate them at the Makefile level, since Makefile.generic supports
  mutually dependent inclusion) are handled by the following translation:

   with "../src1/project1"

   is translated as:

   BASE_DIR=$(<PROJECT>_BASE_DIR)/../src1
   include $(BASE_DIR)/Makefile.project1

   or, if the with clause contains an absolute path:

   with "/a/b/project1"

   is translated as:

   BASE_DIR=/a/b
   include $(BASE_DIR)/Makefile.project1

- an optional section grouping all the typed variables of the project file

  Given a type variable Var, using an environment variable ENV,
  it is translated into a make variable
  called <PROJECT>__VAR. This notation is used to avoid conflicts between
  variables of different projects.
  If the typed variable has no default value, a simple assignment is used:

  <PROJECT>__VAR=$(ENV)

  otherwise an "ifeq" test is used:

  ifeq ($(ENV),)
     <PROJECT>__VAR=<default value>
  else
     <PROJECT>__VAR=$(ENV)
  endif

  For example:

  type Build_Type is ("development", "production");
  Build : Build_Type := external ("BUILD_ENV", "development");

  is translated into:

  ifeq ($(BUILD_ENV),)
    PRJ__BUILD=development
  else
    PRJ__BUILD=$(BUILD_ENV)
  endif

- a config section that will contain variables configuring the environment,
  with the following:

   CC = <c compiler>                         optional
   CXX = <c++ compiler>                      optional
   LANGUAGES = <list of project languages>
   AR_EXT = <extension of archive files>
   MAIN = <language of main program>         optional
   OBJ_EXT = <extension of object files>     optional
   C_EXT = <extension of C files>            optional
   CXX_EXT = <extension of C++ files>        optional
   ADA_SPEC = <extension of ada spec files>  optional
   ADA_BODY = <extension of ada body files>  optional

  Here is a detail of these variables:
   - A variable CC, taken from IDE.Compiler_Command ("C")

   - A variable CXX, taken from IDE.Compiler_Command ("C++")

   - A variable LANGUAGES will contain the list of languages for the project:

   LANGUAGES = c ada c++

   which is taken directly from the project attribute Languages

   - A variable MAIN will specify the language of the main program:

   MAIN = c++

   which is taken from the project attribute Main_Language, default is "ada"

   - AR_EXT is the extension of archive files, usually .a:

   AR_EXT = .a

   For now, we can assume .a, since this is always the extension used by 'ar'

   - OBJ_EXT is the extension of object files, .o by default (in which case,
     it is not necessary to specify it):

   OBJ_EXT = .obj

   - C_EXT is the extension of C files, .c by default (no need to specify it)
     and is taken from the Naming package:

   [package Naming is]
      for Specification_Suffix ("C") use (".cfile");

   C_EXT = .cfile

   - CXX_EXT is the extension of C++ files, .cc by default (no need to specify
     it) and is taken from the Naming package:

   CXX_EXT = .cpp

   - ADA_SPEC is the extension of ada spec files, .ads by default, and is taken
     from the Naming package:

   ADA_SPEC = .1.ada

   - ADA_BODY is the extension of ada body files, .adb by default, and is taken
     from the Naming package:

   ADA_BODY = .2.ada

   ADA_SPEC and ADA_BODY variable are needed to handle file specific compilation

- The attribute Source_Dirs is translated as two variables:

   for Source_Dirs use ("a", "/a/b/c");

   <PROJECT>_SRC_DIRS = $(<PROJECT>_BASE_DIR)/a /a/b/c
   SRC_DIRS += $(<PROJECT>_SRC_DIRS

   <PROJECT>_SRC_DIRS contains the direct project source directories
   SRC_DIRS will contain all the source directories from the current project and
   all its dependencies

- Non Ada source list is taken from the attribute Source_Files or
  Source_Files_List if present by taking only the relevant sources (following
  the naming convention), e.g:

   for Source_Files use ("a.adb", "foo.c", "foo1.c" "bar.cc");

   is translated as:

   C_SRCS = foo.c foo1.c
   CXX_SRCS = bar.cc

   and

   for Source_Files_List use "foo.txt";

   is translated as:

   SRCS := $(shell gprcmd cat $(<PROJECT>_BASE_DIR)/foo.txt)
   C_SRCS = $(filter %$(C_EXT) $(SRCS))
   CXX_SRCS = $(filter %$(CXX_EXT) $(SRCS))

- If no Source_Files or Source_Files_List attribute is present, the list is
  computed by the following statement:

C_SRCS = \
  $(foreach name,$(<PROJECT>_SRC_DIRS),$(notdir $(wildcard $(name)/*$(C_EXT))))

Same for CXX_SRCS (C++ sources) if needed (if Language attribute contains "c++")

- If it cannot be determined statically whether there are non Ada sources,
  an additional variable declaration is needed for the handling of archive
  files (see LIBS handling below):

<PROJECT>_SRCS := \
  $(foreach name,$(<PROJECT>_SRC_DIRS),$(notdir $(wildcard $(name)/*$(C_EXT))))\
  $(foreach name,$(<PROJECT>_SRC_DIRS),$(notdir $(wildcard $(name)/*$(CXX_EXT))))

  Each "foreach" line will be generated only if the project supports the
  corresponding language.

  To determine statically, there are two criteria:
  - if the Language include only Ada, there are no C/C++ sources, hence no
    library and no need to define <PROJECT>_SRCS
  - if the list of foreign sources is explicitely set, in which case, there is
    always a library, and there is also no need to define <PROJECT>_SRCS

- The attribute Object_Dir is translated as variables <PROJECT>_OBJ_DIR and
  OBJ_DIR:

   for Object_Dir use "obj";

->

<PROJECT>_OBJ_DIR = $(<PROJECT>_BASE_DIR)/obj
OBJ_DIR = $(<PROJECT>_OBJ_DIR)

or if Object_Dir is an absolute path:

<PROJECT>_OBJ_DIR = /absolute/path/to/obj/dir
OBJ_DIR = $(<PROJECT>_OBJ_DIR)

  The two variables are needed because when including other Makefiles, the
  variable OBJ_DIR will be overridden, while the variable <PROJECT>_OBJ_DIR will
  never be.

- Ada switches are ignored since they are handled by gnat tools directly

- Global (non Ada) switches are translated as blank separated variables:

   Default_Switches ("C") -> CFLAGS
   Default_Switches ("C++") -> CXXFLAGS

   e.g:

   for Default_Switches ("C") use ("-g", "-O2");

   ->

   CFLAGS = -g -O2

- Specific rules are used to handle file specific compilation:

The rule

   [package Compiler is]
      for Switches ("file.c") use ("-g", "-O");

is translated as:

file$(OBJ_EXT):
	@echo $(CC) -c -g -O $< -o $(OBJ_DIR)/$@
ifndef FAKE_COMPILE
	@$(CC) -c -g -O $(C_INCLUDES) $(DEP_CFLAGS) $< -o $(OBJ_DIR)/$@
	@$(post-compile)
endif

C_INCLUDES and DEP_CFLAGS should always be added to the compilation flags,
they are computed automatically and provide the necessary source path and
flags to handle dependencies (they can be used for both C and C++).

The FAKE_COMPILE section is useful for computing which files need to be compiled
without actually compiling them.

The echo line provides a clean and stripped output, similar to the output given
by gnatmake when using project files

- The non-Ada parts of conditional sections (case statements in the project
  file) are handled by using nested "ifeq" statements:

for Switches ("C") use ("-O");

case Build is
   when Debug =>
      for Switches ("C") use Switches ("C") & "-g";

   when Production =>
      for Switches ("C") use Switches ("C") & "-O2";

   when others =>
      for Switches ("C") use ("-O2");
end case;

will be converted into:

CFLAGS := -O

ifeq ($(<PROJECT>__BUILD),debug)
   CFLAGS := $(CFLAGS) -g
else
   ifeq ($(<PROJECT>__BUILD),production)
      CFLAGS := $(CFLAGS) -O2
   else
      CFLAGS := -O2
   endif
endif

- The Attribute Main_Sources is translated as a variable ADA_SOURCES:

  for Main use ("main1.adb, "main2.adb");

  ->

  ADA_SOURCES = main1.adb main2.adb

- The name of the current project is specified as a variable PROJECT_FILE:

PROJECT_FILE = <project>

WARNING:
Due to the overriding rules of Makefiles, and to inclusions in other Makefiles,
do not attempt to replace <project> by $(PROJECT_FILE) or any other similar
changes, it won't work.

- Each multi-language project file will generate a library that is added to
  the list of libs using variable LIBS, unless it can be determined statically
  (e.g the list of sources is specified explicitely) that no object files
  will be generated, in which case the following lines should not be generated:

ifneq ($(strip $(<PROJECT>_SRCS)),)
   LIBS := $(<PROJECT>_OBJ_DIR)/lib<project>$(AR_EXT) $(LIBS)
endif

- Each Makefile is terminated by the following section that terminates the
  conditional section started at the beginning of the Makefile, and includes
  the generic Makefile (if project is used as the main project), or
  update the list of dependencies (if project is used as a sub project):

ifeq ($(<PROJECT>_ROOT,True)
   include $(GNAT_ROOT)/share/Makefile.generic
else
   DEPS_PROJECTS += $(<PROJECT>_BASE_DIR/<project>
endif

endif
