# 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/>.
#

EMULATOR_MARCHID=21

# Every new emulator release should bump these constants
EMULATOR_VERSION_MAJOR=0
EMULATOR_VERSION_MINOR=21
EMULATOR_VERSION_PATCH=0
EMULATOR_VERSION_LABEL=

TARGET_OS?=$(shell uname)

LUA_INC=$(shell pkg-config --cflags-only-I lua5.4)
LUA_LIB=$(shell pkg-config --libs lua5.4)
LUA_BIN=lua5.4

# Are we on an environment that has the riscv64-cartesi-linux-gnu-* toolchain?
DEV_ENV_HAS_TOOLCHAIN ?= no

# If instead of building the microarchitecture you want to use a pre-built image file, modify this variable to
# indicate the path of the desired file.
UARCH_PRISTINE_RAM_C ?= ../uarch/uarch-pristine-ram.c
UARCH_PRISTINE_HASH_C ?= ../uarch/uarch-pristine-hash.c

# Code instrumentation
release?=no
sanitize?=no
fuzz?=no
coverage?=no
threads?=yes
slirp?=yes

CARTESI_TESTS_PATH?=../tests/build/machine

# So we can use gcc to check if there are unused symbols
ifeq ($(gc),yes)
GCFLAGS=-fno-inline-small-functions -fno-inline -fdata-sections -ffunction-sections
GCLDFLAGS=-Wl,--gc-sections,--print-gc-sections
endif

# Mac OS X specific setup
ifeq ($(TARGET_OS),Darwin)
PICCFLAGS=-fPIC
# We use -dynamiclib instead of the conventional -bundle flag when compiling Lua libraries
# allowing jsonrpc.so to link with cartesi.so, which is necessary because they share symbols
SOLDFLAGS=-dynamiclib -undefined dynamic_lookup
LIBLDFLAGS=-dynamiclib
EXELDFLAGS=
CC=clang
CXX=clang++
COVERAGE_TOOLCHAIN?=clang
AR=libtool -static -o
INCS=

BREW_PREFIX=$(shell which brew)
PORT_PREFIX=$(shell which port)

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

# Homebrew installation
ifneq (,$(BREW_PREFIX))
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
SLIRP_INC=-I$(BREW_PREFIX)/libslirp/include
PTHREAD_CFLAGS=-I$(BREW_PREFIX)/opt/libomp/include -fopenmp
PTHREAD_LDFLAGS=-L$(BREW_PREFIX)/opt/libomp/lib -lomp

# Macports installation
else ifneq (,$(PORT_PREFIX))
INSTALL_PREFIX=/opt/local
BOOST_LIB_DIR=-L$(INSTALL_PREFIX)/libexec/boost/1.87/lib
BOOST_INC=-I$(INSTALL_PREFIX)/libexec/boost/1.87/include
SLIRP_LIB=-L$(INSTALL_PREFIX)/lib -lslirp
SLIRP_INC=-I$(INSTALL_PREFIX)/include
PTHREAD_CFLAGS=-I$(INSTALL_PREFIX)/include/libomp -fopenmp
PTHREAD_LDFLAGS=-L$(INSTALL_PREFIX)/lib/libomp -lgomp

else
$(warning Neither Homebrew nor MacPorts prefix found)
endif

SO_EXT=dylib
LIBCARTESI_LDFLAGS=-install_name '@rpath/$(LIBCARTESI)'
LIBCARTESI_HASH_TREE_LDFLAGS=-install_name '@rpath/$(LIBCARTESI_HASH_TREE)'
LIBCARTESI_JSONRPC_LDFLAGS=-install_name '@rpath/$(LIBCARTESI_JSONRPC)' -Wl,-rpath,@loader_path
LUACARTESI_LDFLAGS=-install_name '@rpath/cartesi.so'
LUACARTESI_JSONRPC_LDFLAGS=-install_name '@rpath/cartesi/jsonrpc.so' -Wl,-rpath,@loader_path/..
PROFILE_DATA=default.profdata

# Linux or some other POSIX platform
else

# Linux specific setup
PICCFLAGS=-fPIC
SOLDFLAGS=-shared $(PICCFLAGS) $(GCLDFLAGS)
LIBLDFLAGS=$(SOLDFLAGS) -Wl,--no-undefined
EXELDFLAGS=$(GCLDFLAGS) -Wl,--no-undefined
PTHREAD_CFLAGS=-fopenmp
PTHREAD_LDFLAGS=-fopenmp
CC=gcc
CXX=g++
COVERAGE_TOOLCHAIN?=gcc
AR=ar rcs
INCS=

BOOST_INC=
SLIRP_INC=
SLIRP_LIB=-lslirp
SO_EXT=so
LIBCARTESI_LDFLAGS=
LIBCARTESI_HASH_TREE_LDFLAGS=
LIBCARTESI_JSONRPC_LDFLAGS=-Wl,-rpath,'$$ORIGIN'
LUACARTESI_LDFLAGS=
LUACARTESI_JSONRPC_LDFLAGS=-Wl,-rpath,'$$ORIGIN/..'
PROFILE_DATA=

endif

LIBCARTESI=libcartesi-$(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR).$(SO_EXT)
LIBCARTESI_HASH_TREE=libcartesi_hash_tree-$(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR).$(SO_EXT)
LIBCARTESI_JSONRPC=libcartesi_jsonrpc-$(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR).$(SO_EXT)

ifeq ($(slirp),yes)
# Workaround for building with macports lua-luarocks installation
machine-address-ranges.o: INCS+=$(SLIRP_INC)
machine-address-ranges.clang-tidy: INCS+=$(SLIRP_INC)
virtio-net-user-address-range.o: INCS+=$(SLIRP_INC)
virtio-net-user-address-range.clang-tidy: INCS+=$(SLIRP_INC)
#INCS+=$(SLIRP_INC)
LIBCARTESI_COMMON_LIBS+=$(SLIRP_LIB)
else
DEFS+=-DNO_SLIRP
endif

LIBCARTESI_LIBS=$(LIBCARTESI_COMMON_LIBS)
LIBCARTESI_HASH_TREE_LIBS=
LIBCARTESI_JSONRPC_LIBS=
LUACARTESI_LIBS=$(LIBCARTESI_COMMON_LIBS)
LUACARTESI_JSONRPC_LIBS=
CARTESI_JSONRPC_MACHINE_LIBS=$(LIBCARTESI_COMMON_LIBS)
CARTESI_HASH_TREE_HASH_LIBS=

C_WARNS=-Wall -Wextra -Wpedantic
# C_WARNS+=-Wshadow -Wconversion -Wsign-conversion -Wsign-promo -Wuseless-cast -Wformat=2
CXX_WARNS=$(C_WARNS)

CLANG_TIDY_WARNS=-Wthread-safety -Wglobal-constructors -Wundef -Wredundant-decls -Wextra-semi

# Place our include directories before the system's
INCS+= \
	-I../third-party/ankerl \
	-I../third-party/llvm-flang-uint128 \
	-I../third-party/nlohmann-json \
	-I../third-party/downloads \
	$(BOOST_INC)

# Use 64-bit offsets for file operations in POSIX APIs
DEFS+=-D_FILE_OFFSET_BITS=64

# Disable json filesystem to support more compilers
DEFS+=-DJSON_HAS_FILESYSTEM=0

ifeq ($(dump),yes)
DUMP_DEFS+=-DDUMP_HASH_TREE_STATS
DUMP_DEFS+=-DDUMP_ILLEGAL_INSN_EXCEPTIONS
DUMP_DEFS+=-DDUMP_EXCEPTIONS
DUMP_DEFS+=-DDUMP_INTERRUPTS
DUMP_DEFS+=-DDUMP_MMU_EXCEPTIONS
DUMP_DEFS+=-DDUMP_INVALID_CSR
DUMP_DEFS+=-DDUMP_REGS
DUMP_DEFS+=-DDUMP_INSN_HIST
DUMP_DEFS+=-DDUMP_STATS
DUMP_DEFS+=-DDUMP_INSN
DUMP_DEFS+=-DDUMP_UARCH_INSN
DUMP_DEFS+=-DDUMP_SCOPED_NOTE
DUMP_DEFS+=-DDUMP_STATE_ACCESS
DUMP_DEFS+=-DDUMP_UARCH_STATE_ACCESS
endif
DEFS += $(DUMP_DEFS)
# Pass down UARCH_DEFS to sub-makefiles
export UARCH_DEFS += $(DUMP_DEFS)

# By default we compile in release with debug information,
# so the emulator is packaged correctly by default.
ifeq (,$(filter yes,$(relwithdebinfo) $(release) $(debug) $(coverage) $(sanitize) $(fuzz)))
relwithdebinfo=yes
endif

ifeq ($(coverage),yes)
OPTFLAGS+=-Og -g -fno-omit-frame-pointer -fno-inline
DEFS+=-DCODE_COVERAGE
else ifeq ($(relwithdebinfo),yes)
OPTFLAGS+=-O2 -g
INTERPRET_CXXFLAGS+=-DNDEBUG # disable asserts only for interpret.cpp
else ifeq ($(release),yes)
OPTFLAGS+=-O2
DEFS+=-DNDEBUG # disable all asserts
DEFS+=-DBOOST_ASIO_DISABLE_ERROR_LOCATION # remove line/function location from error messages
else ifeq ($(debug),yes)
OPTFLAGS+=-Og -g -fno-omit-frame-pointer
OPTFLAGS+=-D_FORTIFY_SOURCE=3 -D_GLIBCXX_ASSERTIONS -fstack-clash-protection -fstack-protector-strong
else ifeq ($(sanitize),yes)
OPTFLAGS+=-O1 -g -fno-omit-frame-pointer
OPTFLAGS+=-D_FORTIFY_SOURCE=3 -D_GLIBCXX_ASSERTIONS -fstack-clash-protection -fstack-protector-strong
else ifeq ($(fuzz),yes)
OPTFLAGS+=-O1 -g -fno-omit-frame-pointer
OPTFLAGS+=-D_GLIBCXX_ASSERTIONS
endif

# Git commit hash (for releases)
ifneq ($(git_commit),)
DEFS+=-DGIT_COMMIT='"$(git_commit)"'
endif

# Hashing libraries have special optimizations flags
HASH_CFLAGS=-O3 -DNDEBUG -funroll-loops -fno-stack-protector

# Optimization flags for the interpreter
ifneq (,$(filter yes,$(relwithdebinfo) $(release)))
# Enable strict aliasing otherwise the optimizer may generate indirections to the jump table
# This is fine, because the interpreter is written to comply with strict aliasing rules
INTERPRET_CXXFLAGS+=-fstrict-aliasing
# Ensure frame pointers are omitted to free an extra register (some distributions enable them by default)
INTERPRET_CXXFLAGS+=-fomit-frame-pointer
ifneq (,$(findstring gcc,$(CC)))
# The following improves computed goto dispatch as stated in GCC manual
INTERPRET_CXXFLAGS+=-fno-gcse
# The following remove extra jumps in the computed goto dispatch
INTERPRET_CXXFLAGS+=-fno-crossjumping
# The interpreter dispatch loop performs better as a big inlined function
INTERPRET_CXXFLAGS+=-finline-limit=1024
# The interpreter hot loop is big and puts pressure on register allocation, this improves register use
INTERPRET_CXXFLAGS+=-frename-registers -fweb
# The interpreter instruction dispatch is big, the following reduces its size minimizing CPU cache pressure
INTERPRET_CXXFLAGS+=-freorder-blocks-algorithm=simple
# Some distributions enables stack protector by default, make sure it's disabled
INTERPRET_CXXFLAGS+=-fno-stack-protector
endif
endif

# Make testing new optimization options easier
INTERPRET_CXXFLAGS+=$(MYINTERPRET_CXXFLAGS)

# Link time optimizations
ifeq ($(lto),yes)
OPTFLAGS+=-flto=auto
endif

ifeq ($(sanitize),yes)
# Enable address and undefined sanitizers
UBCFLAGS+=-fsanitize=address,undefined -fno-sanitize-recover=all
UBLDFLAGS+=-fsanitize=address,undefined -fno-sanitize-recover=all
LIBASAN_SO=$(shell realpath `$(CC) -print-file-name=libasan.so`)
LIBSTDCPP_SO=$(shell realpath `$(CC) -print-file-name=libstdc++.so`)
LD_PRELOAD="$(LIBASAN_SO) $(LIBSTDCPP_SO)"
LD_PRELOAD_PREFIX=LD_PRELOAD=$(LD_PRELOAD)
LUA=$(LD_PRELOAD_PREFIX) $(LUA_BIN)
else ifeq ($(fuzz),yes)
# Enable address, undefined, and fuzzer sanitizers (requires Clang)
UBCFLAGS+=-fsanitize=address,undefined -fno-sanitize-recover=all
# Coverage instrumentation so libFuzzer can track edges inside libcartesi
UBCFLAGS+=-fsanitize=fuzzer-no-link
UBLDFLAGS+=-fsanitize=address,undefined -fno-sanitize-recover=all
LD_PRELOAD_PREFIX=
LUA=$(LUA_BIN)
else
# Flags to minimize undefined behavior
UBCFLAGS+=-fno-strict-aliasing
UBCFLAGS+=-fno-strict-overflow
UBCFLAGS+=-fno-delete-null-pointer-checks
LD_PRELOAD_PREFIX=
LUA=$(LUA_BIN)
endif

# Compile for current system processor
ifeq ($(native),yes)
CC_MARCH=-march=native
else
CC_MARCH=
endif

# Workload to use in PGO
PGO_WORKLOAD=\
	tar c -C / bin | gzip > a.tar.gz && gzip -c a.tar.gz | sha256sum; \
	dhrystone 5000000; \
	whetstone 25000

LINTER_IGNORE_SOURCES=
LINTER_SOURCES=$(filter-out $(LINTER_IGNORE_SOURCES),$(strip $(wildcard *.cpp) $(wildcard *.c)))

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

CLANG_FORMAT=clang-format
CLANG_FORMAT_UARCH_FILES:=$(wildcard ../uarch/*.cpp) $(wildcard ../uarch/*.h) $(wildcard ../uarch/*.hpp)
CLANG_FORMAT_UARCH_FILES:=$(filter-out %uarch-printf%,$(strip $(CLANG_FORMAT_UARCH_FILES)))
CLANG_FORMAT_FILES:=$(wildcard *.cpp) $(wildcard *.c) $(wildcard *.h) $(wildcard *.hpp) $(CLANG_FORMAT_UARCH_FILES)
CLANG_FORMAT_IGNORE_FILES:=interpret-jump-table.hpp
CLANG_FORMAT_FILES:=$(strip $(CLANG_FORMAT_FILES))
CLANG_FORMAT_FILES:=$(filter-out $(CLANG_FORMAT_IGNORE_FILES),$(strip $(CLANG_FORMAT_FILES)))

STYLUA=stylua
STYLUA_FLAGS=--indent-type Spaces --collapse-simple-statement Always --respect-ignores

EMPTY:=
SPACE:=$(EMPTY) $(EMPTY)

ifeq ($(threads),yes)
CFLAGS+=$(PTHREAD_CFLAGS)
CXXFLAGS+=$(PTHREAD_CFLAGS)
LDFLAGS+=$(PTHREAD_LDFLAGS)
else
DEFS+=-DNO_THREADS
C_WARNS+=-Wno-unknown-pragmas
CXX_WARNS+=-Wno-unknown-pragmas
endif

CXXFLAGS+=$(OPTFLAGS) -std=gnu++23 -fvisibility=hidden -MMD $(PICCFLAGS) $(CC_MARCH) $(INCS) $(GCFLAGS) $(UBCFLAGS) $(DEFS) $(CXX_WARNS)
CFLAGS+=$(OPTFLAGS) -std=gnu99 -fvisibility=hidden -MMD $(PICCFLAGS) $(CC_MARCH) $(INCS) $(GCFLAGS) $(UBCFLAGS) $(DEFS) $(C_WARNS)
LDFLAGS+=$(UBLDFLAGS)

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

CXXFLAGS+=$(MYCXXFLAGS) $(MYDEFS)
CFLAGS+=$(MYCFLAGS) $(MYDEFS)
LDFLAGS+=$(MYLDFLAGS)
SOLDFLAGS+=$(MYSOLDFLAGS)
LIBLDFLAGS+=$(MYLIBLDFLAGS)
EXELDFLAGS+=$(MYEXELDFLAGS)

all: libcartesi libcartesi_hash_tree libcartesi_jsonrpc c-api luacartesi cartesi-jsonrpc-machine hash

luacartesi: libluacartesi.a cartesi.so libluacartesi_jsonrpc.a cartesi/jsonrpc.so

jsonrpc: cartesi/jsonrpc.so cartesi-jsonrpc-machine

hash: cartesi-hash-tree-hash

c-api: $(LIBCARTESI) $(LIBCARTESI_HASH_TREE) $(LIBCARTESI_JSONRPC)

.PHONY: all generate use clean lint format format-lua check-format check-format-lua luacartesi hash c-api compile_flags.txt swap-uarch-pristine-coverage swap-uarch-pristine-stock

LIBCARTESI_OBJS:= \
	base64.o \
	clint-address-range.o \
	dtb.o \
	hex.o \
	hash-tree.o \
	htif-address-range.o \
	interpret.o \
	json-util.o \
	cm.o \
	machine-config.o \
	machine.o \
	machine-address-ranges.o \
	machine-console.o \
	memory-address-range.o \
	os.o \
	os-mapped-memory.o \
	os-filesystem.o \
	plic-address-range.o \
	back-merkle-tree.o \
	send-cmio-response.o \
	keccak-256-hasher.o \
	sha-256-hasher.o \
	is-pristine.o \
	uarch-pristine-hash.o \
	uarch-pristine-ram.o \
	uarch-pristine-state-hash.o \
	uarch-reset-state.o \
	uarch-step.o \
	local-machine.o \
	uarch-interpret.o \
	virtio-address-range.o \
	virtio-console-address-range.o \
	virtio-p9fs-address-range.o \
	virtio-net-address-range.o \
	virtio-net-tuntap-address-range.o \
	virtio-net-user-address-range.o

CARTESI_CLUA_OBJS:= \
	clua.o \
	clua-i-machine.o \
	uarch-pristine-ram.o \
	uarch-pristine-state-hash.o \
	uarch-pristine-hash.o

LUACARTESI_OBJS:= \
	clua-cartesi.o \
	$(CARTESI_CLUA_OBJS)

LIBCARTESI_HASH_TREE_OBJS:= \
	keccak-256-hasher.o \
	sha-256-hasher.o \
	is-pristine.o \
	back-merkle-tree.o \

CARTESI_HASH_TREE_HASH_OBJS:= \
	hash-tree-hash.o

LIBCARTESI_JSONRPC_OBJS:= \
	jsonrpc-machine.o \
	os.o \
	cm-jsonrpc.o \
	base64.o \
	hex.o \
	json-util.o

LUACARTESI_JSONRPC_OBJS:= \
	clua-cartesi-jsonrpc.o \
	$(CARTESI_CLUA_OBJS)

CARTESI_JSONRPC_MACHINE_OBJS:= \
	jsonrpc-remote-machine.o \
	jsonrpc-discover.o \
	slog.o

ifeq ($(gperf),yes)
DEFS+=-DGPERF
LIBCARTESI_LIBS+=-lprofiler
LIBCARTESI_HASH_TREE_LIBS+=-lprofiler
LIBCARTESI_JSONRPC_LIBS+=-lprofiler
LUACARTESI_LIBS+=-lprofiler
LUACARTESI_JSONRPC_LIBS+=-lprofiler
endif

version:
	@echo $(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR).$(EMULATOR_VERSION_PATCH)$(EMULATOR_VERSION_LABEL)

so-version:
	@echo $(EMULATOR_VERSION_MAJOR).$(EMULATOR_VERSION_MINOR)

libcartesi: libcartesi.a libcartesi.$(SO_EXT)
libcartesi.$(SO_EXT): $(LIBCARTESI) $(LIBCARTESI_HASH_TREE)
	ln -sf $< $@
libcartesi_hash_tree: libcartesi_hash_tree.a libcartesi_hash_tree.$(SO_EXT)
libcartesi_hash_tree.$(SO_EXT): $(LIBCARTESI_HASH_TREE)
	ln -sf $< $@
libcartesi_jsonrpc: libcartesi_jsonrpc.a libcartesi_jsonrpc.$(SO_EXT)
libcartesi_jsonrpc.$(SO_EXT): $(LIBCARTESI_JSONRPC)
	ln -sf $< $@

libcartesi.a: $(LIBCARTESI_OBJS)
	$(AR) $@ $^

libcartesi_hash_tree.a: $(LIBCARTESI_HASH_TREE_OBJS)
	$(AR) $@ $^

libcartesi_jsonrpc.a: $(LIBCARTESI_JSONRPC_OBJS)
	$(AR) $@ $^

libluacartesi.a: $(LUACARTESI_OBJS)
	$(AR) $@ $^

libluacartesi_jsonrpc.a: $(LUACARTESI_JSONRPC_OBJS)
	$(AR) $@ $^

$(LIBCARTESI_HASH_TREE): $(LIBCARTESI_HASH_TREE_OBJS)
	$(CXX) -o $@ $^ $(LIBCARTESI_HASH_TREE_LIBS) $(LDFLAGS) $(LIBCARTESI_HASH_TREE_LDFLAGS) $(LIBLDFLAGS)

$(LIBCARTESI): $(LIBCARTESI_OBJS)
	$(CXX) -o $@ $^ $(LIBCARTESI_LIBS) $(LDFLAGS) $(LIBCARTESI_LDFLAGS) $(LIBLDFLAGS)

$(LIBCARTESI_JSONRPC): $(LIBCARTESI_JSONRPC_OBJS) $(LIBCARTESI)
	$(CXX) -o $@ $^ $(LIBCARTESI_JSONRPC_LIBS) $(LDFLAGS) $(LIBCARTESI_JSONRPC_LDFLAGS) $(LIBLDFLAGS)

cartesi.so: $(LUACARTESI_OBJS) libcartesi.a
	$(CXX) -o $@ $^ $(LUACARTESI_LIBS) $(LDFLAGS) $(LUACARTESI_LDFLAGS) $(SOLDFLAGS)

# Relink cartesi.so with the coverage uarch embedded as the pristine state, so
# reset_uarch restores the instrumented binary (the one PCs are resolved against)
# for the whole run instead of reverting to the stock uarch after the first
# instruction. Only the pristine ram/hash objects change; no instrumented object
# is recompiled, so the collected gcov data set is unaffected. Used by the
# coverage PC-collection test (tests/Makefile test-coverage-uarch-pcs).
swap-uarch-pristine-coverage:
	@rm -f uarch-pristine-ram.o uarch-pristine-hash.o
	@$(MAKE) cartesi.so coverage=yes \
		UARCH_PRISTINE_RAM_C=../uarch/uarch-pristine-ram-coverage.c \
		UARCH_PRISTINE_HASH_C=../uarch/uarch-pristine-hash-coverage.c

# Undo swap-uarch-pristine-coverage by relinking with the stock pristine, leaving
# the build tree ready for the next run. Only the pristine ram/hash objects
# change, so the collected gcov data set is unaffected.
swap-uarch-pristine-stock:
	@rm -f uarch-pristine-ram.o uarch-pristine-hash.o
	@$(MAKE) cartesi.so coverage=yes

cartesi/jsonrpc.so: $(LUACARTESI_JSONRPC_OBJS) libcartesi_jsonrpc.a cartesi.so
	$(CXX) -o $@ $^ $(LUACARTESI_JSONRPC_LIBS) $(LDFLAGS) $(LUACARTESI_JSONRPC_LDFLAGS) $(SOLDFLAGS)

lint: $(CLANG_TIDY_TARGETS)
	@$(MAKE) --no-print-directory -C ../uarch lint

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

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

format-lua:
	@$(STYLUA) $(STYLUA_FLAGS) .

check-format-lua:
	@$(STYLUA) $(STYLUA_FLAGS) --check .

check-lua:
	luacheck .

fs.ext2: fs/*
	genext2fs -f -i 512 -b 8192 -d fs fs.ext2
	truncate -s %4096 fs.ext2

generate: CXXFLAGS += -fprofile-generate
generate: LDFLAGS += -fprofile-generate
generate: luacartesi

use: CXXFLAGS += -fprofile-use -Wno-missing-profile
use: LDFLAGS += -fprofile-use
use: $(PROFILE_DATA) luacartesi

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

luacartesi-pgo:
	$(MAKE) --no-print-directory generate
	./cartesi-machine.lua -- "$(PGO_WORKLOAD)"
	$(MAKE) clean-libcartesi
	$(MAKE) --no-print-directory use
	$(MAKE) clean-profile

valgrind: luacartesi
	valgrind --leak-check=full --tool=memcheck --track-origins=yes $(LUA_BIN) cartesi-machine-tests.lua --test-path="$(CARTESI_TESTS_PATH)" --test=".*" run
	valgrind --leak-check=full --tool=memcheck --track-origins=yes $(LUA_BIN) cartesi-machine.lua --initial-hash --final-hash -- /bin/true

ifeq ($(TARGET_OS),Darwin)
$(PROFILE_DATA):
	llvm-profdata merge -output=default.profdata default*.profraw
endif

cartesi-hash-tree-hash: $(CARTESI_HASH_TREE_HASH_OBJS) libcartesi_hash_tree.a
	$(CXX) -o $@ $^ $(CARTESI_HASH_TREE_HASH_LIBS) $(LDFLAGS) $(EXELDFLAGS)

cartesi-jsonrpc-machine: $(CARTESI_JSONRPC_MACHINE_OBJS) libcartesi_jsonrpc.a libcartesi.a
	$(CXX) -o $@ $^ $(CARTESI_JSONRPC_MACHINE_LIBS) $(LDFLAGS) $(EXELDFLAGS)

clua-%.o clua.o: CXXFLAGS += $(LUA_INC)

cm-version.h: ../tools/template/cm-version.h.template
	sed "s|EMULATOR_MARCHID|$(EMULATOR_MARCHID)|g;s|EMULATOR_VERSION_MAJOR|$(EMULATOR_VERSION_MAJOR)|g;s|EMULATOR_VERSION_MINOR|$(EMULATOR_VERSION_MINOR)|g;s|EMULATOR_VERSION_PATCH|$(EMULATOR_VERSION_PATCH)|g;s|EMULATOR_VERSION_LABEL|$(EMULATOR_VERSION_LABEL)|g" $< > $@

jsonrpc-discover.cpp: jsonrpc-discover.json
	echo '// This file is auto-generated and should not be modified' > jsonrpc-discover.cpp
	echo 'namespace cartesi {' >> jsonrpc-discover.cpp
	echo '// NOLINTNEXTLINE(clang-diagnostic-overlength-strings)' >> jsonrpc-discover.cpp
	echo '#pragma GCC diagnostic push' >> jsonrpc-discover.cpp
	echo '#pragma GCC diagnostic ignored "-Woverlength-strings"' >> jsonrpc-discover.cpp
	echo 'extern char const *const jsonrpc_discover_json = R"json(' >> jsonrpc-discover.cpp
	cat jsonrpc-discover.json >> jsonrpc-discover.cpp
	echo ')json";' >> jsonrpc-discover.cpp
	echo '#pragma GCC diagnostic pop' >> jsonrpc-discover.cpp
	echo '} // namespace cartesi' >> jsonrpc-discover.cpp

%.clang-tidy: %.cpp cm-version.h interpret-jump-table.hpp
	$(CLANG_TIDY) $(CLANG_TIDY_FLAGS) $< -- $(CXXFLAGS) $(CLANG_TIDY_WARNS) $(LUA_INC) -DCLANG_TIDY_LINT
	@$(CXX) $(CXXFLAGS) $(LUA_INC) $< -MM -MT $@ -MF $@.d > /dev/null 2>&1
	@touch $@

%.clang-tidy: %.c
	$(CLANG_TIDY) $(CLANG_TIDY_FLAGS) $< -- $(CFLAGS) $(CLANG_TIDY_WARNS) -DCLANG_TIDY_LINT
	@$(CC) $(CFLAGS) $< -MM -MT $@ -MF $@.d > /dev/null 2>&1
	@touch $@


uarch-pristine-ram.o: $(UARCH_PRISTINE_RAM_C)
	$(CC) $(CFLAGS) -c -o $@ $<

uarch-pristine-hash.o: $(UARCH_PRISTINE_HASH_C)
	$(CC) $(CFLAGS) -c -o $@ $<

interpret-jump-table.hpp: ../tools/gen-interpret-jump-table.lua
	$< > $@

interpret.o: interpret.cpp cm-version.h interpret-jump-table.hpp
	$(CXX) $(CXXFLAGS) $(INTERPRET_CXXFLAGS) -c -o $@ $<

keccak-256-hasher.o: keccak-256-hasher.cpp
	$(CXX) $(CXXFLAGS) $(HASH_CFLAGS) -c -o $@ $<

sha-256-hasher.o: sha-256-hasher.cpp
	$(CXX) $(CXXFLAGS) $(HASH_CFLAGS) -c -o $@ $<

is-pristine.o: is-pristine.cpp
	$(CXX) $(CXXFLAGS) $(HASH_CFLAGS) -c -o $@ $<

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

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

../uarch/uarch-pristine-ram.c ../uarch/uarch-pristine-hash.c: generate-uarch-pristine

generate-uarch-pristine: cm-version.h interpret-jump-table.hpp
ifeq (,$(wildcard ../uarch/uarch-pristine-hash.c))
	@if [ "$(DEV_ENV_HAS_TOOLCHAIN)" = "yes" ]; then \
		$(MAKE) -C .. uarch coverage=$(coverage); \
	else \
		$(MAKE) -C .. uarch-with-toolchain coverage=$(coverage); \
	fi
endif

clean: clean-auto-generated clean-coverage clean-profile clean-tidy clean-libcartesi clean-executables

clean-auto-generated:
	@rm -f jsonrpc-discover.cpp cm-version.h interpret-jump-table.hpp

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

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

clean-libcartesi: clean-objs
	@rm -f *.so *.a cartesi/*.so *.dylib

clean-executables:
	@rm -f cartesi-jsonrpc-machine cartesi-hash-tree-hash compute-uarch-pristine-hash

clean-coverage:
	@rm -f *.profdata *.profraw *.gcda *.gcov coverage.info coverage.txt

clean-profile:
	@rm -f *.profdata *.profraw *.gcda *.gcno

-include $(wildcard *.d)
