# Copyright Cartesi and individual authors (see AUTHORS)
# SPDX-License-Identifier: LGPL-3.0-or-later
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with this program (see COPYING). If not, see <https://www.gnu.org/licenses/>.
#

TARGET_OS?=$(shell uname)
BUILDDIR?=.

coverage?=no
sanitize?=no
slirp?=yes

COVERAGE_TOOLCHAIN?=gcc

# Mac OS X specific setup
ifeq ($(TARGET_OS),Darwin)
CC=clang
CXX=clang++

ifeq ($(MACOSX_DEPLOYMENT_TARGET),)
export MACOSX_DEPLOYMENT_TARGET := $(shell sw_vers -productVersion | sed -E "s/([[:digit:]]+)\.([[:digit:]]+)\..+/\1.\2.0/")
endif

# Homebrew installation
ifneq (,$(shell which brew))
BREW_PREFIX = $(shell brew --prefix)
BOOST_LIB_DIR=-L$(BREW_PREFIX)/lib
BOOST_INC=-I$(BREW_PREFIX)/include
SLIRP_LIB=-L$(BREW_PREFIX)/lib -lslirp

# Macports installation
else ifneq (,$(shell which port))
PORT_PREFIX = /opt/local
BOOST_LIB_DIR=-L$(PORT_PREFIX)/libexec/boost/1.87/lib
BOOST_INC=-I$(PORT_PREFIX)/libexec/boost/1.87/include
SLIRP_LIB=-L$(PORT_PREFIX)/lib -lslirp
OMP_LIB=-fopenmp -L$(PORT_PREFIX)/lib/libomp -lgomp
else
$(error Neither Homebrew nor MacPorts is installed)
endif

# Linux or some other POSIX platform
else
CC=gcc
CXX=g++
SLIRP_LIB=-lslirp
endif

ifeq ($(coverage),yes)
ifeq ($(COVERAGE_TOOLCHAIN),gcc)
CXX=g++
CXXFLAGS+=-g -Og -fno-dce -fno-inline -DCODE_COVERAGE --coverage
LDFLAGS+=--coverage
else ifeq ($(COVERAGE_TOOLCHAIN),clang)
CXX=clang++
CXXFLAGS+=-g -O0 -DCODE_COVERAGE --coverage -fno-inline
LDFLAGS+=--coverage
else ifneq ($(COVERAGE_TOOLCHAIN),)
$(error invalid value for COVERAGE_TOOLCHAIN: $(COVERAGE_TOOLCHAIN))
endif
endif

ifeq ($(sanitize),yes)
# Enable address and undefined sanitizers
UBFLAGS+=-fsanitize=address,undefined -fno-sanitize-recover=all
else
# Flags to minimize undefined behavior
UBFLAGS+=-fno-strict-aliasing
UBFLAGS+=-fno-strict-overflow
UBFLAGS+=-fno-delete-null-pointer-checks
endif

# We ignore test-machine-c-api.cpp cause it takes too long.
LINTER_SOURCES=

CLANG_TIDY=clang-tidy
CLANG_TIDY_TARGETS=$(patsubst %.cpp,%.clang-tidy,$(LINTER_SOURCES))
CLANG_TIDY_FLAGS=--header-filter='^$(abspath ../..)/'

CLANG_FORMAT=clang-format
CLANG_FORMAT_FILES:=$(wildcard *.cpp) $(wildcard *.h)

INCS=-I../../src -I../../third-party/ankerl -I../../third-party/nlohmann-json -I../../third-party/downloads
WARNS=-Wall -Wpedantic $(BOOST_INC)

CXXFLAGS+=-O2 -g -std=gnu++23 -fopenmp -fvisibility=hidden $(INCS) $(UBFLAGS) $(WARNS)

ifeq ($(slirp),yes)
LIBCARTESI_LIBS+=$(SLIRP_LIB)
endif

all: $(BUILDDIR)/test-machine-c-api $(BUILDDIR)/test-machine-c-jsonrpc-api

../../src/libcartesi.a ../../src/libcartesi_hash_tree.a:
	$(info libcartesi.a and/or libcartesi_hash_tree.a were not found! Build them first.)
	@exit 1

$(BUILDDIR)/test-machine-c-api: test-machine-c-api.o ../../src/libcartesi_jsonrpc.a ../../src/libcartesi.a ../../src/libcartesi_hash_tree.a
	$(CXX) -o $@ $^ $(CXXFLAGS) $(LDFLAGS) $(LIBCARTESI_LIBS) $(OMP_LIB)

$(BUILDDIR)/test-machine-c-jsonrpc-api: test-machine-c-jsonrpc-api.o ../../src/libcartesi_jsonrpc.a ../../src/libcartesi.a ../../src/libcartesi_hash_tree.a
	$(CXX) -o $@ $^ $(CXXFLAGS) $(LDFLAGS) $(LIBCARTESI_LIBS) $(OMP_LIB)

%.o: %.cpp
	$(CXX) $(CXXFLAGS) -c -o $@ $<

%.o: %.c
	$(CC) $(CFLAGS) -c -o $@ $<

%.clang-tidy: %.cpp
	$(CLANG_TIDY) $(CLANG_TIDY_FLAGS) $< -- $(CXXFLAGS) $(BOOST_INC)
	@$(CXX) $(CXXFLAGS) $(BOOST_INC) $< -MM -MT $@ -MF $@.d > /dev/null 2>&1
	@touch $@

compile_flags.txt:
	@echo "$(CXXFLAGS)" "$(BOOST_INC)" "-xc++" | sed -e $$'s/ \{1,\}/\\\n/g' | grep -v "MMD" > $@

lint: $(CLANG_TIDY_TARGETS)

format:
	@$(CLANG_FORMAT) -i $(CLANG_FORMAT_FILES)

check-format:
	@$(CLANG_FORMAT) -Werror --dry-run $(CLANG_FORMAT_FILES)

clean-tidy:
	@rm -f *.clang-tidy

clean-objs:
	@rm -f *.o *.d

clean: clean-tidy clean-objs
	@rm -f $(BUILDDIR)/test-machine-c-api $(BUILDDIR)/test-machine-c-jsonrpc-api

.SUFFIXES:
