#
# Wrap the standard commands so that we can
# produce a standard Makefile for bootstrapping.
#
BOOT = $(dir $(ROOT)/boot)

MAKEFILE_TEXT =
MAKEFILE_LINK =
MAKEFILE_DONT_LINK =
MAKEFILE_EXPLICIT_LINK =

private.slash = $'$(slash)'
private.ext_obj = $'$(EXT_OBJ)'
private.ext_lib = $'$(EXT_LIB)'

########################################################################
# Linking
#

#
# Specify explicit linking.
#
MakeDontLink(files) =
    MAKEFILE_DONT_LINK += $(files)
    export

MakeLinkFile(dst, src) =
    private.cwd = $(in $(BOOT), $(CWD))
    private.cwd = $(split \/, $(private.cwd))
    private.cwd = $(concat $(slash), $(private.cwd))

    private.src = $(split \/, $(src))
    private.src = $(concat $(slash), $(private.src))

    private.srcfile = $"$(cwd)$(slash)$(private.src)"
    MAKEFILE_TEXT += $"""
$(dst): $(srcfile)
	$$(LN) $(srcfile) $(dst)
"""
    MAKEFILE_EXPLICIT_LINK += $(dst)
    export

MakeLinkExternalFile(dst, src) =
    # Record the link
    MakeLinkFile($(dst), $(src))

    # Perform the link
    $(dst): $(src)
        ln-or-cp $< $@

    export

MakeLinkFiles(files) =
    foreach(name => ..., $(files))
        MakeLinkFile($(name), $(name))
        export
    export

MakeLinkExternal(srcdir, files) =
    foreach(name => ..., $(files))
        # Record the link
        MakeLinkFile($(name), $(srcdir)/$(name))

        # Perform the link
        $(name): $(srcdir)/$(name)
            ln-or-cp $< $@

        export
    export

########################################################################
# C
#
MakeStaticCLibrary(name, files) =
    StaticCLibrary($(name), $(files))

    private.src_cfiles = $(addsuffix .c, $(files))
    private.cfiles = $(addprefix c_, $(src_cfiles))
    private.cfiles = $(src_cfiles)
    private.src_hfiles = $(filter-exists $(addsuffix .h, $(files)))
    private.hfiles = $(addprefix c_, $(src_hfiles))
    private.ofiles = $(addprefix c_, $(addsuffix $(ext_obj), $(files)))

    MAKEFILE_TEXT += $"""
OFILES_$(name) = $(ofiles)

$(name)$$(EXT_LIB): $$(OFILES_$(name))
	-$$(RM) $$@
	$$(AR) $$(AROUT)$$@ $$(OFILES_$(name))
"""

    foreach(name => ..., $(src_cfiles))
        dst=c_$(name)
        src=$(name)
        MakeLinkFile($(dst), $(src))
        $(dst): $(src)
            ln-or-cp $< $@
        export

    foreach(name => ..., $(src_hfiles))
        dst=$(name)
        src=$(name)
        MakeLinkFile($(dst), $(src))
        export

    export

########################################################################
# OCaml
#

#
# Generate a program.
#
MakeOCamlProgram(name, files) =
    OCamlProgram($(name), $(files))

    private.mlfiles = $(addsuffix .ml, $(files))
    private.mlifiles = $(filter-exists $(addsuffix .mli, $(files)))
    private.cmofiles = $(addsuffix .cmo, $(files))
    private.cmxfiles = $(addsuffix .cmx, $(files))
    private.ocaml_libs = $(addsuffix .cma, $(basename $(OCAML_LIBS)))
    private.ocaml_libs_opt = $(addsuffix .cmxa, $(basename $(OCAML_LIBS)))
    private.ocaml_other_libs = $(addsuffix .cma, $(OCAML_OTHER_LIBS))
    private.ocaml_other_libs_opt = $(addsuffix .cmxa, $(OCAML_OTHER_LIBS))
    private.ocaml_other_libs = $(set-diff $(ocaml_other_libs), threads.cma)
    private.ocaml_other_libs_opt = $(set-diff $(ocaml_other_libs_opt), threads.cmxa)
    private.ocaml_clibs = $(addsuffix $(ext_lib), $(basename $(OCAML_CLIBS)))
    private.ocaml_cclibs = $(mapprefix -cclib, $(ocaml_clibs))
    MAKEFILE_LINK += $(mlfiles) $(mlifiles)
    MAKEFILE_TEXT += $"""
CMOFILES_$(name) = $(cmofiles)
CMXFILES_$(name) = $(cmxfiles)
OCAML_LIBS_$(name) = $(ocaml_libs)
OCAML_LIBS_OPT_$(name) = $(ocaml_libs_opt)
OCAML_OTHER_LIBS_$(name) = $(ocaml_other_libs)
OCAML_OTHER_LIBS_OPT_$(name) = $(ocaml_other_libs_opt)
OCAML_CLIBS_$(name) = $(ocaml_clibs)
OCAML_CCLIBS_$(name) = $(ocaml_cclibs)

$(name).byte$$(EXE): $$(CMOFILES_$(name)) $$(OCAML_LIBS_$(name)) $$(OCAML_CLIBS_$(name))
	$$(OCAMLC) $$(OCAMLFLAGS) -custom -o $$@ $$(OCAML_CCLIBS_$(name)) $$(OCAML_OTHER_LIBS_$(name)) $$(THREADSLIB) $$(OCAML_LIBS_$(name)) $$(CMOFILES_$(name))
$(name).opt$$(EXE): $$(CMXFILES_$(name)) $$(OCAML_LIBS_OPT_$(name)) $$(OCAML_CLIBS_$(name))
	$$(OCAMLOPT) $$(OCAMLOPTFLAGS) -o $$@ $$(OCAML_CCLIBS_$(name)) $$(OCAML_OTHER_LIBS_OPT_$(name)) $$(THREADSLIB_OPT) $$(OCAML_LIBS_OPT_$(name)) $$(CMXFILES_$(name))
$(name)$$(EXE): $(name)$$(PREFERRED)$$(EXE)
	$$(LN) $(name)$$(PREFERRED)$$(EXE) $$@
"""
    export

#
# Generate a library.
#
MakeOCamlLibrary(name, files) =
    OCamlLibrary($(name), $(files))

    private.mlfiles = $(addsuffix .ml, $(files))
    private.mlifiles = $(filter-exists $(addsuffix .mli, $(files)))
    private.cmofiles = $(addsuffix .cmo, $(files))
    private.cmxfiles = $(addsuffix .cmx, $(files))
    MAKEFILE_LINK += $(mlfiles) $(mlifiles)
    MAKEFILE_TEXT += $"""
CMOFILES_$(name) = $(cmofiles)
CMXFILES_$(name) = $(cmxfiles)
OCAML_LIB_FLAGS_$(name) = $(OCAML_LIB_FLAGS)

$(name).cma: $$(CMOFILES_$(name))
	$$(OCAMLC) $$(OCAMLFLAGS) $$(OCAML_LIB_FLAGS_$(name)) -a -o $$@ $$(CMOFILES_$(name))
$(name).cmxa: $$(CMXFILES_$(name))
	$$(OCAMLOPT) $$(OCAMLOPTFLAGS) $$(OCAML_LIB_FLAGS_$(name)) -a -o $$@ $$(CMXFILES_$(name))
"""
    export

#
# Add some dependencies (the make version of depend is rough)
#
MakeOCamlDepend(files, deps) =
    private.mlifiles = $(filter-exists $(addsuffix .mli, $(files)))
    private.cmofiles = $(addsuffix .cmo, $(files))
    private.cmxfiles = $(addsuffix .cmx, $(files))
    private.cmifiles = $(addsuffix .cmi, $(removesuffix $(mlifiles)))
    MAKEFILE_TEXT += $"""
$(cmxfiles) $(cmofiles) $(cmifiles): $(deps)
"""
    export

#
# Finish generating the Makefile text.
#
MakeText() =
    private.cwd = $(in $(BOOT), $(CWD))
    private.base = $(basename $(cwd))
    private.cwd = $(split \/, $(cwd))
    private.cwd = $(concat $(slash), $(cwd))
    private.links = $(set $(MAKEFILE_LINK))
    private.implicit-links = $(set-diff $(links), $(MAKEFILE_DONT_LINK) $(MAKEFILE_EXPLICIT_LINK))
    private.makefiledep_deps = $(set-diff $(MAKEFILE_DONT_LINK), omake_magic.ml)

    if $(makefiledep_deps)
        MAKEFILE_TEXT += $"""
Makefile.dep: $(makefiledep_deps)
"""
        export

    MAKEFILE_TEXT += $"""
SRC_$(base) = $(cwd)
"""

    #
    # Link all the files
    #
    foreach(name => ..., $(implicit-links))
        srcfile = $"$$(SRC_$(base))$$(slash)$(name)"
        MAKEFILE_TEXT += $"""
$(name): $(srcfile)
	$$(LN) $(srcfile) $(name)
"""
        export

    #
    # Save the link listing
    #
    MAKEFILE_TEXT += $"""
ALLFILES_$(base) = $(implicit-links) $(MAKEFILE_EXPLICIT_LINK)
"""
    return $(MAKEFILE_TEXT)

#
# Write to the Makefile
#
MakeMakefile() =
    private.text = $(MakeText)

    Makefile.tmp: :value: $(text)
        @echo $(text) > $@

########################################################################
# Global Makefile generation
#

private.MAKEFILE_HEAD = $'''#
# !!!THIS IS A GENERATED FILE!!!
# !!!DO NOT MAKE CHANGES HERE, THEY WILL BE LOST!!!
#
include osconfig.mk
'''

private.MAKEFILE_TAIL = $'''
#
# Create a dependency file
#
Makefile.dep: $(ALLFILES)
	$(OCAMLDEP) $(OCAMLINCLUDES) *.ml *.mli > Makefile.dep

#
# Clean up
#
clean:
	$(RM) *.cmo *.cmx *.cma *.cmxa *.o *.obj *.a *.lib *.exe
	$(RM) $(ALLFILES)
	$(RM) omake

include Makefile.dep
'''

MakeRootMakefiles(dirs) =
    MAKEFILES = $(addsuffix /Makefile.tmp, $(dirs))
    ALLFILES_names = $(add-wrapper $'$(ALLFILES_', $')', $(dirs))
    ALLFILES = $"ALLFILES = $(ALLFILES_names)"

    #
    # Fix up output from latex2man so that it does
    # not have trailing whitespace.
    #
    remove-trailing-whitespace(src, dst) =
       stdout = $(fopen $(dst), wb)
       fsubst($(src))
       case $'[ \t]+$'
          value
       close($(stdout))

    tmp1 = Makefile.tmp1
    tmp2 = Makefile.tmp2
    tmp3 = Makefile.tmp3
    tmp4 = Makefile.tmp4

    Makefile: $(MAKEFILES) $(file $(ROOT)/mk/make_gen)
        @echo $(MAKEFILE_HEAD) > $(tmp1)
        @cat $(MAKEFILES) >> $(tmp1)
        @echo $(ALLFILES) >> $(tmp1)
        @echo $(MAKEFILE_TAIL) >> $(tmp1)
	remove-trailing-whitespace($(tmp1), $(tmp2))
	@mv -f $(tmp2) $@
	@chmod 444 $@

    .DEFAULT: Makefile
