VERSION = 0.102

# Crazy hack to do shell commands
# We do it in Nmakefile because that way we don't have to invoke build through build.cmd
# and we can just do ``nmake /f Nmakefile x86-64-vista`` or similar
# and we still get the git branch, id, etc

!IF [git describe --all > git-describe.tmp] == 0
GIT_DESCRIBE = \
!INCLUDE <git-describe.tmp>
!IF [del git-describe.tmp] == 0
!ENDIF
!ENDIF

!IF [git rev-parse HEAD > git-id.tmp] == 0
GIT_ID = \
!INCLUDE <git-id.tmp>
!IF [del git-id.tmp] == 0
!ENDIF
!ENDIF

!IF [git rev-parse --abbrev-ref HEAD > git-branch.tmp] == 0
GIT_BRANCH = \
!INCLUDE <git-branch.tmp>
!IF [del git-branch.tmp] == 0
!ENDIF
!ENDIF

GIT_LABEL = $(GIT_DESCRIBE)-$(GIT_ID)

# Auto-detect platform from vcvarsall.bat environment
# VSCMD_ARG_TGT_ARCH is set by newer VS versions, Platform by older ones
!IF DEFINED(VSCMD_ARG_TGT_ARCH)
DETECTED_ARCH = $(VSCMD_ARG_TGT_ARCH)
!ELSEIF DEFINED(Platform)
DETECTED_ARCH = $(Platform)
!ELSE
# Default to x64 if no environment is set
DETECTED_ARCH = x64
!ENDIF

LINK_FLAGS = /nologo shell32.lib user32.lib
CL_FLAGS = /nologo /O2 /W4 /std:c++17 /D_CRT_SECURE_NO_WARNINGS /DFACTOR_VERSION=$(VERSION) /DFACTOR_GIT_LABEL=$(GIT_LABEL)

# Check if targeting Vista or higher (can be set via WINVER=VISTA environment variable)
!IF DEFINED(WINVER)
!IF "$(WINVER)" == "VISTA" || "$(WINVER)" == "vista"
CL_FLAGS = $(CL_FLAGS) /D_WIN32_WINNT=0x0600
VISTA_BUILD = 1
!ENDIF
!ENDIF

!IF "$(DETECTED_ARCH)" == "arm64"
PLAF_DLL_OBJS = vm\os-windows-arm.64.obj vm\cpu-arm.64-trampoline.obj vm\cpu-arm.64.obj
!IF DEFINED(VISTA_BUILD)
SUBSYSTEM_COM_FLAGS = console
SUBSYSTEM_EXE_FLAGS = windows
!ELSE
SUBSYSTEM_COM_FLAGS = console,"6.02"
SUBSYSTEM_EXE_FLAGS = windows,"6.02"
!ENDIF
!ELSEIF "$(DETECTED_ARCH)" == "x86" || "$(DETECTED_ARCH)" == "Win32"
LINK_FLAGS = $(LINK_FLAGS) /safeseh /largeaddressaware
PLAF_DLL_OBJS = vm\os-windows-x86.32.obj vm\safeseh.obj vm\cpu-x86.obj
!IF DEFINED(VISTA_BUILD)
SUBSYSTEM_COM_FLAGS = console
SUBSYSTEM_EXE_FLAGS = windows
!ELSE
SUBSYSTEM_COM_FLAGS = console,"5.01"
SUBSYSTEM_EXE_FLAGS = windows,"5.01"
!ENDIF
!ELSE
# Default to x64 for x64, AMD64, or any unknown value
PLAF_DLL_OBJS = vm\os-windows-x86.64.obj vm\cpu-x86.obj
!IF DEFINED(VISTA_BUILD)
SUBSYSTEM_COM_FLAGS = console
SUBSYSTEM_EXE_FLAGS = windows
!ELSE
SUBSYSTEM_COM_FLAGS = console,"5.02"
SUBSYSTEM_EXE_FLAGS = windows,"5.02"
!ENDIF
!ENDIF

!IF DEFINED(DEBUG)
LINK_FLAGS = $(LINK_FLAGS) /DEBUG
CL_FLAGS = $(CL_FLAGS) /Zi /DFACTOR_DEBUG
!ENDIF

!IF DEFINED(REPRODUCIBLE)
CL_FLAGS = $(CL_FLAGS) /DFACTOR_REPRODUCIBLE
!ENDIF

!IF DEFINED(LTO)
LINK_FLAGS = $(LINK_FLAGS) /LTCG /OPT:ICF=2
CL_FLAGS = $(CL_FLAGS) /GL /Gw /Gy /Zc:inline
!ENDIF

ML_FLAGS = /nologo /safeseh

EXE_OBJS = vm\main-windows.obj vm\factor.res

DLL_OBJS = $(PLAF_DLL_OBJS) \
	vm\os-windows.obj \
	vm\aging_collector.obj \
	vm\alien.obj \
	vm\arrays.obj \
	vm\bignum.obj \
	vm\byte_arrays.obj \
	vm\callbacks.obj \
	vm\callstack.obj \
	vm\code_blocks.obj \
	vm\code_heap.obj \
	vm\compaction.obj \
	vm\contexts.obj \
	vm\data_heap.obj \
	vm\data_heap_checker.obj \
	vm\debug.obj \
	vm\dispatch.obj \
	vm\entry_points.obj \
	vm\errors.obj \
	vm\factor.obj \
	vm\full_collector.obj \
	vm\gc.obj \
	vm\image.obj \
	vm\inline_cache.obj \
	vm\instruction_operands.obj \
	vm\io.obj \
	vm\jit.obj \
	vm\math.obj \
	vm\mvm.obj \
	vm\mvm-windows.obj \
	vm\nursery_collector.obj \
	vm\object_start_map.obj \
	vm\objects.obj \
	vm\primitives.obj \
	vm\quotations.obj \
	vm\run.obj \
	vm\safepoints.obj \
	vm\sampling_profiler.obj \
	vm\strings.obj \
	vm\to_tenured_collector.obj \
	vm\tuples.obj \
	vm\utilities.obj \
	vm\vm.obj \
	vm\words.obj \
	vm\zstd.obj

!MESSAGE Building Factor for $(DETECTED_ARCH) architecture...

# Default target must be first
all: factor.com.manifest factor.exe.manifest factor.dll.lib libfactor-ffi-test.dll

# batch mode has ::
.cpp.obj::
	cl /EHsc $(CL_FLAGS) /MP /Fovm/ /c $<

.c.obj::
	cl /EHsc $(CL_FLAGS) /MP /Fovm/ /c $<

vm\cpu-arm.64-trampoline.obj:
	armasm64 -nologo vm\cpu-arm.64-trampoline.asm $@

.asm.obj:
	ml $(ML_FLAGS) /Fo$@ /c $<

.rs.res:
	rc /nologo $<

libfactor-ffi-test.dll: vm/ffi_test.obj
	link $(LINK_FLAGS) /out:libfactor-ffi-test.dll /dll /def:vm\ffi_test.def vm/ffi_test.obj

factor.dll.lib: $(DLL_OBJS)
	link $(LINK_FLAGS) /implib:factor.dll.lib /out:factor.dll /dll $(DLL_OBJS)

factor.com: $(EXE_OBJS) $(DLL_OBJS)
	link $(LINK_FLAGS) /out:factor.com /SUBSYSTEM:$(SUBSYSTEM_COM_FLAGS) $(EXE_OBJS) $(DLL_OBJS)

factor.exe: $(EXE_OBJS) $(DLL_OBJS)
	link $(LINK_FLAGS) /out:factor.exe /SUBSYSTEM:$(SUBSYSTEM_EXE_FLAGS) $(EXE_OBJS) $(DLL_OBJS)

# If we compile factor.exe, run mt.exe, and run factor.exe,
# then Windows caches the manifest. Even on a recompile without applying
# the mt.exe tool, if the factor.exe.manifest file is present, the manifest
# is applied. To avoid this, we delete the .manifest file on clean
# and copy it from a reference file on compilation and mt.exe.
#
factor.com.manifest: factor.com
	copy factor.exe.manifest.in factor.exe.manifest
	mt -nologo -manifest factor.exe.manifest -outputresource:"factor.com;#1"

factor.exe.manifest: factor.exe
	copy factor.exe.manifest.in factor.exe.manifest
	mt -nologo -manifest factor.exe.manifest -outputresource:"factor.exe;#1"

clean:
	del vm\*.obj
	if exist vm\factor.res del vm\factor.res
	if exist factor.lib del factor.lib
	if exist factor.com del factor.com
	if exist factor.exe del factor.exe
	if exist factor.exe.manifest del factor.exe.manifest
	if exist factor.exp del factor.exp
	if exist factor.dll del factor.dll
	if exist factor.dll.lib del factor.dll.lib
	if exist factor.dll.exp del factor.dll.exp
	if exist libfactor-ffi-test.dll del libfactor-ffi-test.dll
	if exist libfactor-ffi-test.exp del libfactor-ffi-test.exp
	if exist libfactor-ffi-test.lib del libfactor-ffi-test.lib

.PHONY: all clean factor.exe.manifest

.SUFFIXES: .rs
