#
# Required version of omake
#
OMakeVersion(0.9.6, 0.9.6)

########################################################################
# Building C files.
#
# Copyright (C) 2003-2005 Jason Hickey and Mojave Group
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this file, to deal in the File without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the File, and to permit persons to whom the File
# is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the File.
#
# THE FILE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# FILE OR THE USE OR OTHER DEALINGS IN THE FILE.

include $(STDLIB)/build/Common


#
# \begin{doc}
# \section{Building C programs}
#
# \Prog{omake} provides extensive support for building C programs.
#
# \subsection{C configuration variables}
#
# The following variables can be redefined in your project.
#
# \begin{description}
# \item[CC] The name of the C compiler (defaults to \verb+cc+ on \verb+Unix+, and \verb+cl+ on \verb+Win32+).
# \item[CPP] The name of the C preprocessor (defaults to \verb+cpp+ on \verb+Unix+, and \verb+cl /E+ on \verb+Win32+).
# \item[CFLAGS] Compilation flags to pass to the C compiler (default empty on \verb+Unix+, and \verb+/DWIN32+ on \verb+Win32+).
# \item[INCLUDES] Additional directories that specify the search path to the C compiler (default is \verb+.+).
#   The directories are passed to the C compiler with the \verb+-I+ option.
#   The include path with \verb+-I+ prefixes is defined in the \verb+PREFIXED_INCLUDES+ variable.
# \item[LIBS] Additional libraries needed when building a program (default is empty).
# \item[AS] The name of the assembler (defaults to \verb+as+ on \verb+Unix+, and \verb+ml+ on \verb+Win32+).
# \item[ASFLAGS] Flags to pass to the assembler (default is empty on \verb+Unix+, and \verb+/c /coff+ on \verb+Win32+).
# \item[AR] The name of the program to create static libraries (defaults to \verb+ar cq+ on \verb+Unix+,
#     and \verb+lib+ on \verb+Win32+).
# \item[AROUT] The option string that specifies the output file for \verb+AR+.
# \item[LD] The name of the linker (defaults to \verb+ld+ on \verb+Unix+, and \verb+cl+ on \verb+Win32+).
# \item[LDFLAGS] Options to pass to the linker (default is empty).
# \item[YACC] The name of the \verb+yacc+ parser generator (default is \verb+yacc+ on \verb+Unix+, empty on \verb+Win32+).
# \item[LEX] The name of the \verb+lex+ lexer generator (default is \verb+lex+ on \verb+Unix+, empty on \verb+Win32+).
# \end{description}
# \end{doc}
#
if $(equal $(OSTYPE), Win32)
    CC = cl /nologo
    CPP = cl /nologo /E
    CFLAGS = /DWIN32
    AR(name) =
        return(lib /nologo /debugtype:CV /out:$(name))
    RANLIB = echo ranlib
    LD = cl
    INCLUDES[] = .
    INCLUDES_OPT = /I
    YACC = echo yacc
    LEX = echo lex
    LIBS =
    LDFLAGS =

    AS = ml /nologo
    ASOUT = /Fo
    ASFLAGS = /c /coff

    export
else
    CC = cc
    CPP = cpp
    CFLAGS =
    AR(name) =
        return(ar cq $(name))
    RANLIB = ranlib
    LD = ld
    INCLUDES[] = .
    INCLUDES_OPT = -I
    YACC = yacc
    LEX = lex
    LIBS =
    LDFLAGS =

    AS = as
    ASOUT = $(array -o)
    ASFLAGS =

    export

#
# Add the -I option to the includes lazily.
# Don't redefine this variable unless you know what you are doing.
#
PREFIXED_INCLUDES = $`(addprefix $(INCLUDES_OPT), $(INCLUDES))

#
# Special flags for compiling C files for use in OCaml
#
BYTE_CFLAGS =
NATIVE_CFLAGS =

#
# Generic build rules
#
#
%$(EXT_OBJ): %.c :scanner: scan-c-%.c
    $(CC) $(CFLAGS) $(PREFIXED_INCLUDES) -c -o $@ $*.c

%$(EXT_OBJ): %$(EXT_ASM)
    $(AS) $(ASFLAGS) $(PREFIXED_INCLUDES) $(ASOUT)$@ $*$(EXT_ASM)

%.c: %.y
    $(YACC) $*.y

%.c: %.l
    $(LEX) $*.l

#
# Default C scanner
#

#
# Make sure generated files are built before scanning
#
.PHONY: CGeneratedFilesTarget

CGeneratedFiles(files) =
    CGeneratedFilesTarget: $(files)

LocalCGeneratedFiles(files) =
    .SCANNER: scan-c-%: $(files)
    .SCANNER: %$(EXT_OBJ): $(files)
    export

#
# We use digest-path-exists value dependency to make sure the SCANNER is re-run
# whenever the scanned dependencies change.
#
if $(equal $(OSTYPE), Win32)
    #
    # Somehow Win32 doesn't have good dependency analysis,
    # so we try to construct it by extracting the #line
    # directives from the preprocessor.
    #
    Shell. +=
        builtin-cpp-filenames(argv) =
           fsubst($(argv))
           case $'^[^"]*\("[^"]+"\).*'
              return $1

        builtin-cc-depend(argv) =
           filename = $(nth 0, $(argv))
           pipeline =\
              $(CC) $(CFLAGS) $(PREFIXED_INCLUDES) -E $(argv) |\
              grep "^#(line)? [0-9]" |\
              builtin-cpp-filenames |\
              grep "^[^<]"
           if $(VERBOSE)
              eprintln(+ $(string $(pipeline)))
           depends = $(set $(shell $(pipeline)))
           objname = $(rootname $(filename))$(EXT_OBJ)
           println($(string $(objname): $(depends)))

    .SCANNER: scan-c-%.c: %.c CGeneratedFilesTarget :value: $(digest-in-path-optional $(INCLUDES), $&)
        builtin-cc-depend $<

    # Include default rule for backwards-compatibility
    .SCANNER: %$(EXT_OBJ): %.c CGeneratedFilesTarget :value: $(digest-in-path-optional $(INCLUDES), $&)
        builtin-cc-depend $<
    export
else
    .SCANNER: scan-c-%.c: %.c CGeneratedFilesTarget :value: $(digest-in-path-optional $(INCLUDES), $&)
        $(CC) $(CFLAGS) $(PREFIXED_INCLUDES) -MM $<

    # Include default rule for backwards-compatibility
    .SCANNER: %$(EXT_OBJ): %.c CGeneratedFilesTarget :value: $(digest-in-path-optional $(INCLUDES), $&)
        $(CC) $(CFLAGS) $(PREFIXED_INCLUDES) -MM $<
    export

# Define a function to build a C-library
#
# \begin{doc}
# \subsection{StaticCLibrary}
#
# The \verb+StaticCLibrary+ builds a static library.
#
# \verb+StaticCLibrary(<target>, <files>)+
#
# The \verb+<target>+ does \emph{not} include the library suffix, and
# The \verb+<files>+ list does not include the object suffix.  These
# are obtained from the \verb+EXT_LIB+ and \verb+EXT_OBJ+ variables.
#
# The following command builds the library \verb+libfoo.a+ from the
# files \verb+a.o b.o c.o+ on \verb+Unix+, or the library
# \verb+libfoo.lib+ from the files \verb+a.obj b.obj c.obj+
# on \verb+Win32+.
#
# \begin{verbatim}
# StaticCLibrary(libfoo, a b c)
# \end{verbatim}
# \end{doc}
#
StaticCLibrary(name, files) =
    #
    # Generic library that can be used on byte and native-code
    #
    OFILES = $(addsuffix $(EXT_OBJ), $(files))

    #
    # Names of libs
    #
    NORMALLIB = $(file $(name)$(EXT_LIB))

    if $(equal $(OSTYPE), Win32)
        $(NORMALLIB): $(OFILES)
            echo $(OFILES) > $@.tmp
            $(AR $@) @$@.tmp
            rm -f $@.tmp

    else
        $(NORMALLIB): $(OFILES)
            rm -f $@
            $(AR $@) $(OFILES)
            $(RANLIB) $@

#
# Copy to an install directory
#
# \begin{doc}
# \subsection{StaticCLibraryCopy}
#
# The \verb+StaticCLibraryCopy+ function copies the static library
# to an install location.
#
# \verb+StaticCLibraryCopy(<tag>, <dir>, <lib>)+
#
# The \verb+<tag>+ is the name of a target (typically a \verb+.PHONY+ target);
# the \verb+<dir>+ is the installation directory, and \verb+<lib>+ is
# the library to be copied (without the library suffix).
#
# For example, the following code copies the library
# \verb+libfoo.a+ to the \verb+/usr/lib+ directory.
#
# \begin{verbatim}
# .PHONY: install
#
# StaticCLibraryCopy(install, /usr/lib, libfoo)
# \end{verbatim}
# \end{doc}
#
StaticCLibraryCopy(tag, lib, name) =
    #
    # Names of libs
    #
    NORMALLIB = $(file $(name)$(EXT_LIB))
    LIBNORMAL = $(file $(lib)/$(name)$(EXT_LIB))

    #
    # Linking the library into the root lib dir
    #
    $(LIBNORMAL): $(NORMALLIB)
        $(symlink $<, $@)

    #
    # Add dependency to the tag
    #
    $(tag): $(LIBNORMAL)

#
# We often use them together
#
# \begin{doc}
# \subsection{StaticCLibraryInstall}
#
# The \verb+StaticCLibraryInstall+ function builds a library, and
# sets the install location in one step.
#
# \verb+StaticCLibraryInstall(<tag>, <dir>, <libname>, <files>)+
#
# \begin{verbatim}
# StaticCLibraryInstall(install, /usr/lib, libfoo, a b c)
# \end{verbatim}
# \end{doc}
#
StaticCLibraryInstall(tag, lib, name, files) =
    StaticCLibrary($(name), $(files))
    StaticCLibraryCopy($(tag), $(lib), $(name))

#
# Build a .o file.  This is like a library,
# but use the linker instead.
#
# \begin{doc}
# \subsection{StaticCObject, StaticCObjectCopy, StaticCObjectInstall}
#
# These functions mirror the \verb+StaticCLibrary+, \verb+StaticCLibraryCopy+,
# and \verb+StaticCLibraryInstall+ functions, but they build an \emph{object}
# file (a \verb+.o+ file on \verb+Unix+, and a \verb+.obj+ file on \verb+Win32+).
# \end{doc}
#
StaticCObject(name, files) =
    #
    # Generic library that can be used on byte and native-code
    #
    OFILES = $(addsuffix $(EXT_OBJ), $(files))

    #
    # Names of libs
    #
    NORMALLIB = $(file $(name)$(EXT_OBJ))

    $(NORMALLIB): $(OFILES)
        $(LD) $(LDFLAGS) -r -o $@ $(OFILES)

#
# Copy to an install directory
#
StaticCObjectCopy(tag, lib, name) =
    #
    # Names of libs
    #
    NORMALLIB = $(file $(name)$(EXT_OBJ))
    LIBNORMAL = $(file $(lib)/$(name)$(EXT_OBJ))

    #
    # Linking the library into the root lib dir
    #
    $(LIBNORMAL): $(NORMALLIB)
        $(symlink $<, $@)

    #
    # Add dependency to the tag
    #
    $(tag): $(LIBNORMAL)

#
# We often use them together
#
StaticCObjectInstall(tag, lib, name, files) =
    StaticCObject($(name), $(files))
    StaticCObjectCopy($(tag), $(lib), $(name))

#
# Define a function to build a C-program
#
# \begin{doc}
# \subsection{CProgram}
#
# The \verb+CProgram+ function builds a C program from a set
# of object files and libraries.
#
# \verb+CProgram(<name>, <files>)+
#
# The \verb+<name>+ argument specifies the name of the program to be built;
# the \verb+<files>+ argument specifies the files to be linked.
#
# Additional options can be passed through the following variables.
# \begin{description}
# \item[CFLAGS] Flags used by the C compiler during the link step.
# \item[LDFLAGS] Flags to pass to the loader.
# \item[LIBS] Additional libraries to be linked.
# \end{description}
#
# For example, the following code specifies that the program
# \verb+foo+ is to be produced by linking the files \verb+bar.o+
# and \verb+baz.o+ and libraries \verb+libfoo.a+.
#
# \begin{verbatim}
# section
#    LIBS = libfoo$(EXT_LIB)
#    CProgram(foo, bar baz)
# \end{verbatim}
# \end{doc}
#
CProgram(name, files) =
   #
   # Generic program
   #
   OFILES = $(addsuffix $(EXT_OBJ), $(files))
   NAME   = $(file $(name))

   $(NAME): $(OFILES)
        $(CC) $(CFLAGS) -o $@ $,(OFILES) $(LIBS) $(LDFLAGS)

#
# Copy to a bin directory
#
# \begin{doc}
# \subsection{CProgramCopy}
#
# The \verb+CProgramCopy+ function copies a file to an install location.
#
# \verb+CProgramCopy(<tag>, <dir>, <program>)+
#
# \begin{verbatim}
# CProgramCopy(install, /usr/bin, foo)
# \end{verbatim}
# \end{doc}
#
CProgramCopy(tag, bin, name) =
   #
   # Name of the program
   #
   NAME   = $(file $(name)$(EXE))
   BINNAME = $(file $(bin)/$(name)$(EXE))

   #
   # Linking the program into the root bin dir
   #
   $(BINNAME): $(NAME)
      $(symlink $<, $@)

   #
   # Add the dependency to the tag
   #
   $(tag): $(BINNAME)

#
# We often use them together
#
# \begin{doc}
# \subsection{CProgramInstall}
#
# The \verb+CProgramInstall+ function specifies a program to build,
# and a location to install, simultaneously.
#
# \verb+CProgramInstall(<tag>, <dir>, <name>, <files>)+
#
# \begin{verbatim}
# section
#    LIBS = libfoo$(EXT_LIB)
#    CProgramInstall(install, /usr/bin, foo, bar baz)
# \end{verbatim}
# \end{doc}
#
CProgramInstall(tag, bin, name, files) =
   CProgram($(name), $(files))
   CProgramCopy($(tag), $(bin), $(name))

