# file      : bootstrap.gmake -*- Makefile -*-
# license   : MIT; see accompanying LICENSE file

# This makefile requires GNU make 3.81 or later and can be used to bootstrap
# the build system similar to the bootstrap.sh script. Its main advantages
# over the script are support for parallel compilation and an out of tree
# build. Note also that this makefile does not support incremental updates,
# only from scratch builds (use the clean target to rebuild).
#
# Similar to the script, the makefile expects to find the libbutl/ or
# libbutl-*/ directory either in the current directory (build2 root) or one
# level up. Both in-tree and out-of-tree builds as well as the 'clean' target
# are supported. The result is saved as build2/b-boot.
#
# Typical in-tree build:
#
# cd build2-X.Y.Z
# make -f bootstrap.gmake -j 8 CXX=g++
#
# Typical out-of-tree build:
#
# mkdir build2-boot
# cd build2-boot
# make -f ../build2-X.Y.Z/bootstrap.gmake -j 8 CXX=g++
#
# If used on Windows, then this makefile assumes you are building either in
# the MinGW environment or with Clang targeting MSVC and sets things up
# similar to bootstrap-mingw.bat or bootstrap-clang.bat, respectively (so
# refer to these batch files on the choice of options, etc).
#
# The following standard make variables can be used to customize the build:
#
# CXX
# CPPFLAGS
# CXXFLAGS
# LDFLAGS
# LIBS

exe   :=
host  :=
chost :=

ifeq ($(OS),Windows_NT)
  exe   := .exe

  # Note that while Clang respects -m32/-m64 in its -dumpmachine output, GCC
  # always dumps its default target.
  #
  target := $(shell $(CXX) $(CXXFLAGS) -dumpmachine)

  ifneq ($(filter %-w64-mingw32,$(target)),)
    host  := x86_64-w64-mingw32
    chost := $(host)
    override LIBS += -limagehlp
  else ifneq ($(filter %-windows-msvc,$(target)),)
    host  := x86_64-microsoft-win32-msvc
    chost := $(host)
    override CPPFLAGS += -D_MT -D_CRT_SECURE_NO_WARNINGS
    ifeq ($(filter x86_64-%,$(target)),)
      override CXXFLAGS += -m64
    endif
    override LDFLAGS += -Xlinker /ignore:4217
    override LIBS += -lshell32 -limagehlp
  else
    $(error unsupported target $(target))
  endif
else
  override LIBS += -lpthread
endif

# Remove all the built-in rules, enable second expansion, etc.
#
.SUFFIXES:
ifeq ($(filter -r,$(MAKEFLAGS)),)
MAKEFLAGS += -r
endif

.DELETE_ON_ERROR:
.SECONDEXPANSION:

# We build in CWD and figure out the source directory based on the makefile
# path.
#
out_root := .
src_root := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))

ifeq ($(realpath $(out_root)),$(realpath $(src_root)))
  in_tree := true
else
  in_tree := false
endif

# See if there is libbutl or libbutl-* in src_root or one directory up.
#
libbutl :=
ifeq ($(libbutl),)
  libbutl := $(filter %/,$(wildcard $(src_root)/libbutl/))
  ifeq ($(libbutl),)
    libbutl := $(filter %/,$(wildcard $(src_root)/libbutl-*/))
  endif
endif

ifeq ($(libbutl),)
  libbutl := $(filter %/,$(wildcard $(src_root)/../libbutl/))
  ifeq ($(libbutl),)
    libbutl := $(filter %/,$(wildcard $(src_root)/../libbutl-*/))
  endif
endif

ifeq ($(libbutl),)
  $(error unable to find libbutl, use libbutl=<dir> to specify its location)
endif

ifneq ($(words $(libbutl)),1)
  $(error found multiple libbutl, use libbutl=<dir> to specify its location)
endif

libbutl := $(patsubst %/,%,$(libbutl))

# Figure out libbutl output directory. If we are building in-tree, then build
# libbutl in-tree as well, whether inside or level up. Otherwise -- in the
# libbutl subdirectory.
#
ifeq ($(in_tree),true)
  libbutl_out := $(libbutl)/libbutl
else
  libbutl_out := $(out_root)/libbutl
endif

# Obtain the host triplet.
#
ifeq ($(host),)
  host := $(shell $(src_root)/config.guess)

  ifeq ($(host),)
    $(error unable to guess host triplet, use host=<triplet> to specify)
  endif

  chost := $(host)
else
  ifeq ($(chost),)
    chost := $(shell $(src_root)/config.sub $(host))

    ifeq ($(chost),)
      $(error unable to canonicalize host triplet, use chost=<triplet> to specify)
    endif
  endif
endif

# Figure out the list of source/object files.
#
# Note: list nested subdirectories first (used in clean).
#
libbuild2_sub := \
script           \
build/script     \
config           \
dist             \
test/script      \
test             \
install          \
bin              \
c                \
cc               \
cxx              \
version          \
in

build2_src    := $(wildcard $(src_root)/build2/*.cxx)
libbuild2_src := $(wildcard $(src_root)/libbuild2/*.cxx)
libbuild2_src += $(foreach d,$(libbuild2_sub),$(wildcard $(src_root)/libbuild2/$d/*.cxx))
libbutl_src   := $(wildcard $(libbutl)/libbutl/*.cxx)

# Filter out *.test.cxx sources.
#
build2_src    := $(filter-out %.test.cxx,$(build2_src))
libbuild2_src := $(filter-out %.test.cxx,$(libbuild2_src))
libbutl_src   := $(filter-out %.test.cxx,$(libbutl_src))

# Note that we use the .b.o object file extension to avoid clashing with the
# build2 builds.
#
build2_obj    := $(patsubst $(src_root)/%.cxx,$(out_root)/%.b.o,$(build2_src))
libbuild2_obj := $(patsubst $(src_root)/%.cxx,$(out_root)/%.b.o,$(libbuild2_src))
libbutl_obj   := $(patsubst $(libbutl)/libbutl/%.cxx,$(libbutl_out)/%.b.o,$(libbutl_src))

# Build.
#
$(out_root)/build2/b-boot$(exe): $(build2_obj) $(libbuild2_obj) $(libbutl_obj)
	$(CXX) -std=c++1y $(CXXFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)

$(out_root)/build2/%.b.o: $(src_root)/build2/%.cxx | $$(dir $$@).
	$(CXX) -I$(libbutl) -I$(src_root) -DBUILD2_BOOTSTRAP -DBUILD2_HOST_TRIPLET=\"$(chost)\" $(CPPFLAGS) -std=c++1y $(CXXFLAGS) -o $@ -c $<

$(out_root)/libbuild2/%.b.o: $(src_root)/libbuild2/%.cxx | $$(dir $$@).
	$(CXX) -I$(libbutl) -I$(src_root) -DBUILD2_BOOTSTRAP -DBUILD2_HOST_TRIPLET=\"$(chost)\" $(CPPFLAGS) -std=c++1y $(CXXFLAGS) -o $@ -c $<

$(libbutl_out)/%.b.o: $(libbutl)/libbutl/%.cxx | $$(dir $$@).
	$(CXX) -I$(libbutl) -DBUILD2_BOOTSTRAP $(CPPFLAGS) -std=c++1y $(CXXFLAGS) -o $@ -c $<

.PRECIOUS: %/.
%/. :
	mkdir -p $*

.PHONY: all
all: $(out_root)/build2/b-boot$(exe)

# Clean.
#
.PHONY: clean cleano

cleano:
	rm -f $(build2_obj)
	rm -f $(libbuild2_obj)
	rm -f $(libbutl_obj)

clean: cleano
	rm -f $(out_root)/build2/b-boot$(exe)
ifeq ($(in_tree),false)
	rm -fd $(out_root)/build2 $(foreach d,$(libbuild2_sub),$(out_root)/libbuild2/$d) $(out_root)/libbuild2 $(libbutl_out)
endif