# LWDAQ Makefile for MacOS, Linux, and Windows. 
# =============================================

# This Makefile requires the GNU Pascal Compiler (GPC)
# and the GNU C Compiler (GCC) to be available at the 
# command prompt. The makefile does not support 
# compilation for non-native platforms. Calls to gpc
# ask the pascal compiler only to produce assembler text
# files. In the operating-system options below, we specify
# the assembler we wish to apply to these assembler files.
# We do not use gpc to assemble or link, but instead
# call gcc to do these jobs for static libraries, dynamic
# libraries, and stand-alone executables. This way, we do
# not need to instruct gpc on linking. The linking we take
# care of with gcc using gcc command options. At the end
# of the build, all the intermidiate .s assembler files
# will be removed automatically by Make.
#
# The GPC compiler is 32-bit on Windows and MacOS. We were
# able to build a 64-bit GPC compiler on Linx. So we are
# able to generate only 32-bit versions of the lwdaq shared
# library on MacOS and Windows, while on Linux we can generate
# either 32-bit or 64-bit libraries. This makefile forces
# 32-bit compilation in all cases except 64-bit Linux, when
# it forces 64-bit compilation. Because the lwdaq shared
# library links to the TclTk libraries, these must be of the
# same format, 32-bit or 64-bit. Thus LWDAQ uses 32-bit
# TclTk binaries on MacOS, Windows, and Linux 32-bit, but 
# 64-bit binaries on Linux 64-bit.
#
# (C) 2004-2020, Kevan Hashemi, Brandeis University

# Determine the operating system. Our Default
# operating system is Unix.
OS = Unix
ifneq ($(shell uname -a | grep -i Darwin),)
	OS = MacOS
endif
ifneq ($(shell uname -a | grep -i Windows),)
	OS = Windows
endif
ifneq ($(shell uname -a | grep -i Linux),)
	OS = Linux
endif

# Determine the local architecture. Our default is 32-bit Intel.
ARCH = x86_32
ifneq ($(shell uname -a | grep -i PPC),)
	ARCH = ppc_32
endif
ifneq ($(shell uname -a | grep -i x86_64),)
	ARCH = x86_64
endif

# We can compile a 64-bit binary with GPC only if we have available
# a 64-bit version of the GPC compiler. Right now we have the 64-bit
# compiler available only for Linux. 
ifeq ($(OS),MacOS)
	ifeq ($(ARCH),x86_64)
		ARCH = x86_32
	endif
endif
ifeq ($(OS),Windows)
	ifeq ($(ARCH),x86_64)
		ARCH = x86_32
	endif
endif

# Determine the location of the TCL/TK libraries and the
# GCC libraries. On Unix, we assume the libraries are in one 
# of the default library paths. On MacOS, Linux, and Windows,
# we use the libraries bundled in LWDAQ.app.
TCLTK_LIB = 
GCC_LIB = -lm
GCC_FLAGS = 
GPC_FLAGS = 
GCC = gcc
CPP = g++
GPC = gpc

ifeq ($(OS),MacOS)
	SO = -dynamiclib
	ifeq ($(ARCH),ppc_32)
		TCLTK_LIB = -F../LWDAQ.app/Contents/Frameworks -framework Tcl -framework Tk
		GCC_LIB = -Wl,-syslibroot,/Xcode2.5/SDKs/MacOSX10.4u.sdk/
		GCC_FLAGS = -mmacosx-version-min=10.4
		GPC = /Developer/Pascal/gpc346u4/bin/gpc
	endif
	ifeq ($(ARCH),x86_32)
		TCLTK_LIB = -F../LWDAQ.app/Contents/Frameworks -framework Tcl -framework Tk
		GCC_LIB = -Wl,-syslibroot,/Developer/SDKs/MacOSX10.8.sdk/
		GCC_FLAGS = -mmacosx-version-min=10.8 -m32
		GPC = /Developer/Pascal/gpc346u4/bin/gpc
		AS = as -Q -arch i386
	endif
	ifeq ($(ARCH),x86_64)
		TCLTK_LIB = -F../LWDAQ.app/Contents/Frameworks -framework Tcl -framework Tk
		GCC_LIB = -Wl,-syslibroot,/Developer/SDKs/MacOSX10.8.sdk/
		GCC_FLAGS = -mmacosx-version-min=10.8 -m64
		GPC = /Developer/Pascal/gpc346u4/bin/gpc
		AS = as -Q -arch x86_64
	endif
endif

ifeq ($(OS),Windows)
	SO = -shared
	ifeq ($(ARCH),x86_32)
		TCLTK_LIB = ../LWDAQ.app/Contents/Windows/bin/tcl86.dll \
			../LWDAQ.app/Contents/Windows/bin/tk86.dll'
		GCC_FLAGS = -static-libgcc -static-libstdc++
		LIBRARY_PATH = /c/mingw/lib
		export LIBRARY_PATH
	endif
	ifeq ($(ARCH),x86_64)
		TCLTK_LIB = ../LWDAQ.app/Contents/Windows/bin/tcl86.dll \
			../LWDAQ.app/Contents/Windows/bin/tk86.dll
		GCC_FLAGS = -static-libgcc -static-libstdc++
		LIBRARY_PATH = /c/mingw/lib
		export LIBRARY_PATH
	endif
endif

ifeq ($(OS),Linux)
	SO = -shared
	ifeq ($(ARCH),x86_32)
		TCLTK_LIB = -L../LWDAQ.app/Contents/Linux/x86_32/lib -ltcl8.6 \
			-L../LWDAQ.app/Contents/Linux/x86_32/lib -ltk8.6
		GCC_LIB = -lm
	endif
	ifeq ($(ARCH),x86_64)
		TCLTK_LIB = -L../LWDAQ.app/Contents/Linux/x86_64/lib -ltcl8.6 \
			-L../LWDAQ.app/Contents/Linux/x86_64/lib -ltk8.6
		GCC_LIB = -lm
		GCC_FLAGS = -fPIC
		GPC_FLAGS = -fPIC
	endif
endif

# Specify Pascal compiler options. We recommend -O3 for faster
# code, but the compile takes twice as long. You can use 
# -DUSE_TCL_STUBS to set the USE_TCL_STUBS symbol when you compile 
# lwdaq.o, and so add the TCL and TK stub table initialization 
# routine to the LWDAQ initialization routine. See note below
# on stub libraries, and notes on the same subject in lwdaq.pas.
# In production versions of the code we might choose to disable 
# range checking, so as to avoid unecessary program crashes. Disable 
# range checking with --no-range-checking option. You can force
# the compiler options from the shell that calls "make" by 
# assigning and exporting a non-empty option string.
GPC_OPTIONS = -O3

# Determine the location of the pascal library.
PAS_LIB = $(shell gpc --print-file-name=libgpc.a)

# Name and location of the LWDAQ shared library.
LWDAQ = ../LWDAQ.app/Contents/LWDAQ/lwdaq.so_$(OS)_$(ARCH)

# Name and location of ANALYSIS static library.
ANALYSIS = analysis.a

# Where are the Pascal sources?
SRC_DIR = ../Sources

# Objects required by LWDAQ and ANALYSIS.
OBJA = utils.o images.o transforms.o image_manip.o rasnik.o \
	spot.o bcam.o wps.o electronics.o shadow.o metrics.o
	
# Objects required only by LWDAQ.
OBJB = tcltk.o lwdaq.o

# Objects required only by ANALYSIS.
OBJC = analysis.o

# Define the default products of a call to this makefile.
products: $(LWDAQ)

# Compile the Pascal console program. The console program
# declares the Pascal units that are defined by $(OBJA),
# so GPC needs to see the units and their GPI files during
# compilation. The compiler takes care of the two-level
# names used by the linker, and allows you to call from
# p.pas any routine exported by any OBJA unit by using
# the name under which it is declared in the code. We 
# could, however, use analysis.a as our source of routines.
# In that case, we would have to force GPC to export
# the OBJA routines needed by p.pas under fixed names
# using the "attribute (name=thename)" directive, just as we do 
# for routines needed by c.c and cpp.cpp. An example of such 
# a routine is Dispose_Image in images.pas. In p.pas, we would 
# declare external routines with the "external name 'thename'"
# directive. We could then compile p.pas to an object file
# with "gpc -c -o p.o p.pas", then link p.p to ANALYSIS with
# gcc or gpc. For more details see "Mixing Pascal, Fortran, 
# and C with GCC".
p: p.pas $(OBJA)
	gpc -S $< $(GPC_OPTIONS) $(GPC_FLAGS) -o p.s
	$(AS) -o p.o p.s
	$(GCC) -o p.exe p.o $(OBJA) $(PAS_LIB) $(GCC_LIB) $(GCC_FLAGS)

# Compile the C console program. In c.c we declare 
# external routines that we will call from ANALYSIS. In
# the objects that go into ANALYSIS, we force fixed names
# upon these same routines with Pascal's "attribute 
# (name=thename)" directive. If you want to use a routine
# from the ANALYSIS that has not fixed name, you must
# edit the Pascal source file and fix the name. For example,
# if you want to use error_function from utils.pas, you
# add "attribute (name=Error_Function);" after the initial
# declaration of the routine in utils.pas, and re-compile.
# For more details see "Mixing Pascal, Fortran, and C with GCC".
c: c.c $(ANALYSIS)
	$(GCC) -o c.exe c.c $(ANALYSIS) $(GCC_LIB) $(GCC_FLAGS)

# Compile the C++ console program. For more details see
# comments above for c and p products, and "Mixing Pascal, 
# Fortran, and C with GCC.
cpp: cpp.cpp $(ANALYSIS)
	$(CPP) -o cpp.exe cpp.cpp $(ANALYSIS) $(GCC_LIB) $(GCC_FLAGS)

# Compile the LWDAQ shared library. We instruct gcc
# to create a library that a program can load dynamically
# at run-time (dynamic library). The library will include all the
# routines declared in OBJA, OBJB, the TCLTK libraries and
# the Pascal run-time libraries. The lwdaq.o object, which is
# among those listed in OBJB, is a Pascal main program and
# so includes the Pascal initialization routines for all
# pascal units and the run-time library. If you want
# to use the TCL and TK stubs, you have to tell the linker
# not to export symbols from the TCL and TK stub libraries.
# You add "-Wl,--exclude-libs,tclstub84.lib" if the TCL stub
# library is in the default directory and named tclstub84.lib.
# You add a similar command for the TK stub library. You will
# still get a couple of warnings during the link, but they
# are harmless. The "-Wl" option forwards its arguments to the
# linker. The linker is the "ld" utility.
$(LWDAQ): $(OBJA) $(OBJB)
	$(GCC) $(SO) -o $(LWDAQ) $(OBJA) $(OBJB) $(TCLTK_LIB) $(PAS_LIB) $(GCC_LIB) $(GCC_FLAGS) 

# Construct the ANALYSIS static library. We copy the 
# GPC run-time library archive and add all the OBJA and OBJC
# objects to this archive. 
$(ANALYSIS): $(OBJA) $(OBJC)
	cp $(PAS_LIB) $(ANALYSIS)
	ar -r $(ANALYSIS) $(OBJA) $(OBJC)
	ranlib $(ANALYSIS)
	
# Compile ANALYSIS on its own with the target name "analysis".
analysis: $(ANALYSIS)
	
# Compile analysis.pas. This file is a Pascal main program, so gpc generates
# initializers for the other pascal units used by analysis.pas and places
# them in the object file. By default, gpc creates a routine called _main, 
# and this routine will conflict with the _main genterated by any main program 
# that you try to link with analysis.o. So we rename the main routine as 
# Analysis_Main to avoid this conflict.
analysis.s: $(SRC_DIR)/analysis.pas
	gpc -S -o $*.s $< $(GPC_OPTIONS) $(GPC_FLAGS) --gpc-main=Analysis_Main	

# Compile any other pascal source files. 
%.s: $(SRC_DIR)/%.pas
	gpc -S $< $(GPC_OPTIONS) $(GPC_FLAGS) -o $*.s
	
# Assemble all text assembler files with the selected assembler. Note that
# these files will be deleted automatically at the end of the build because
# every file dependent upon them has already been created.
%.o: %.s
	$(AS) -o $@ $*.s
	
# Clean up the intermediate files.
clean: 
	rm *.s *.o *.gpi *.exe *.a
	
# Remove all existing products.
remove:
	rm $(LWDAQ) $(ANALYSIS)
