#***************************************************************************************************
#                                             Makefile                                             *
#                                            ----------                                            *
# Description : Build system for the GNU SPICE GUI project C++ source files.                       *
# Started     : 2003-03-18                                                                         *
# Updated     : 2024-03-15                                                                         *
# Copyright   : (C) 2003-2024 MSWaters                                                             *
# Note        : Enter "make help" to view available Makefile targets.                              *
#***************************************************************************************************

#***************************************************************************************************
#                                                                                                  *
#       This program is free software; you can redistribute it and/or modify it under the          *
#       terms of the GNU General Public License as published by the Free Software Foundation;      *
#       either version 3 of the License, or (at your option) any later version.                    *
#                                                                                                  *
#***************************************************************************************************

#***************************************************************************************************
# Any or all of the values in this section may be set below or on the command line when envoking
# make eg. :
#
#   make GSPICEUI_DEBUG=1 GSPICEUI_WXLIB=3.0 GSPICEUI_MULTI=1 GSPICEUI_MSWIN=1
#
# Note : Values specified on the command line over-ride those specified below.
#***************************************************************************************************

# Create bin file containing debug information (ie. for gdb) and turn on extra build warnings
# (0 -> Disable, 1 -> Enable)
GSPICEUI_DEBUG = 1

# Specify the version of the wxWidgets library to compile against
GSPICEUI_WXLIB = 3.2

# Specify if the build process should use a single or multiple processes (0 -> Disable, 1 -> Enable)
GSPICEUI_MULTI = 1

# Specify if the host operating system will be MS Windows (0 -> No, 1 -> Yes)
GSPICEUI_MSWIN = 0

# Specify the install directory for gspiceui
DESTDIR = /usr/local/bin

#***************************************************************************************************
# Specify string values
#***************************************************************************************************

# Which compiler and linker (eg. g++ or clang++)
CXX = g++
LD  = g++
ifneq ($(GSPICEUI_MSWIN),0)
  LD += -static-libstdc++ -static-libgcc
  WINDRES = windres
endif

# Application name
PROG = gspiceui

# The wxWidgets configuration utility to use :
ifneq ($(GSPICEUI_DEBUG),0)
  # Explicitly specify the config. utility
  # /usr/bin/wx-config      --> wxWidgets built on GTK3 --> Works (looks ordinary) and GTK errors
  # /usr/bin/wx-config-qt   --> wxWidgets built on QT5  --> Linker error
  # /usr/bin/wx-config-gtk2 --> wxWidgets built on GTK2 --> Works (looks ordinary)
  WXCFG = /usr/bin/wx-config
  #WXCFG = /usr/bin/wx-config-qt
  #WXCFG = /usr/bin/wx-config-gtk2
else
  # Use the system default config. utility
  WXCFG = wx-config
endif

# Specify the library version to use
WXCFG += --version=$(GSPICEUI_WXLIB)

# Specify the unicode character set
WXCFG += --unicode=yes

# Specify static linkage for MSWindows
ifneq ($(GSPICEUI_MSWIN),0)
  WXCFG += --static
endif

# Dependency file name
DEPS = Makefile.deps

# Directories
# Long winded but unambiguous
#ROOT := $(shell cd .. ; pwd)
# Short form but possibly ambiguous
ROOT   = ..
SRCDIR = $(ROOT)/src
OBJDIR = obj
BINDIR = $(ROOT)/bin

# Compiler options :
#  -Wall            Enable all optional warnings desirable for normal code
#  -Wextra          Enable extra warning flags not enabled by "-Wall"
#  -pipe            Use pipes rather than temp. files for comms. between various stages of compilation
#  -O0              Reduce compilation time but don't break debugging (this is the default)
#  -O1              Optimize
#  -O2              Optimize even more
#  -O3              Optimize yet more
#  -Ofast           Optimize till it hurts : "-O3" + enable options not valid for all standard-compliants
#  -Os              Optimize for size
#  -Og              Optimize debugging experience but don't break debugging
#  -std=[C++NO]     The C++ standard to use where C++NO is eg. c++98, c++03, c++11, c++14, c++17, etc.
#  -fabi-version=N  Use version N of the C++ ABI (this choice must match the wxWidgets library)

CXXFLAGS := -std=c++17 -fabi-version=14
ifeq ($(GSPICEUI_DEBUG),0)
  # Options for release (not using -Wall since it's GCC specific)
  CXXFLAGS += -O3
else
  # Options for development
  CXXFLAGS += -g3 -O0 -Wall -Wextra -Wpedantic

  # The following warning has been disabled because I don't know how to fix it
  CXXFLAGS += -Wno-overloaded-virtual
endif
CXXFLAGS += -pipe $(shell $(WXCFG) --cxxflags)

# I like to compile using the option "-Wall" etc. however tests that break wxWidgets are turned off
ifneq ($(GSPICEUI_DEBUG),0)
  # Suppress warnings associated with the wxWidgets library
  ifeq ($(GSPICEUI_WXLIB),3.0)
    CXXFLAGS += -Wno-deprecated-copy
  endif
endif

# Includes
INCLUDES = -I.

# Libraries
LIBS := $(shell $(WXCFG) --libs core,base,html)
# (The pkg-config stuff was requested by a user, somehow pangox was missing) 2019-08-07 ???
#ifeq ($(GSPICEUI_MSWIN),0)
#LIBS := $(shell $(WXCFG) --libs core,base,html) # $(shell pkg-config --libs-only-l pangox)
#else
#LIBS := $(shell $(WXCFG) --libs core,base,html) $(shell pkg-config --libs pangowin32)
#endif

# Source files
#HDRS := $(wildcard *.hpp) $(wildcard */*.hpp) $(wildcard */*/*.hpp)
SRCS := $(wildcard *.cpp) $(wildcard */*.cpp) $(wildcard */*/*.cpp)
ifneq ($(GSPICEUI_MSWIN),0)
  RSRC := $(wildcard *.rc)
endif

# Objects
OBJS := $(SRCS)
OBJS := $(filter-out test-apps/%.cpp, $(OBJS))
OBJS := $(notdir $(OBJS))
OBJS := $(patsubst %.cpp, obj/%.o, $(OBJS))
ifneq ($(GSPICEUI_MSWIN),0)
  OBJS := $(OBJS) $(patsubst %.rc, obj/%.o, $(RSRC))
endif

# Multiple build processes
ifneq ($(GSPICEUI_MULTI),0)
  CPU_CNT = $(shell grep -c "^processor" /proc/cpuinfo)
  MAKEFLAGS += -j$(CPU_CNT) -Otarget --no-print-directory
endif

#***************************************************************************************************
# Search paths for source files
#***************************************************************************************************

# Specify list of directories that `make' should search for prerequisite files
VPATH = $(BINDIR)

# Set search paths for the specific file types
vpath   %.cpp    base main netlist process utility test-apps             \
	               ngspice ngspice/commands ngspice/dialogs ngspice/panels \
	               gnucap  gnucap/commands  gnucap/dialogs  gnucap/panels
vpath   %.o      $(OBJDIR)
vpath   test_%   $(BINDIR)

#***************************************************************************************************
# Specify phony targets (ie. targets which are not files)
#***************************************************************************************************

.PHONY : deps install uninstall check clean cleantests help

#***************************************************************************************************
# Make these targets
#***************************************************************************************************

all : bnr_beg $(OBJS) $(PROG) bnr_end

#***************************************************************************************************
# Print banners
#***************************************************************************************************

BANNER1 = "\n****************************** Build gSpiceUI binary ******************************\n"
BANNER2 = "\n**************************** Build gSpiceUI completed *****************************\n"
bnr_beg :
	@echo
	@echo "Build configuration"
	@echo "-------------------"
	@echo "  gSpiceUI debug mode :" $(intcmp $(GSPICEUI_DEBUG),0,"","Disabled","Enabled")
	@echo "  Multi-process build :" $(intcmp $(GSPICEUI_MULTI),0,"","Disabled","Enabled")
	@echo "  wxWidgets library   :" "v"$(shell $(WXCFG) --version-full)
	@echo "  wxWidgets based on  :" $(shell $(WXCFG) --selected-config)
	@echo -e $(BANNER1)
bnr_end : $(PROG)
	@sleep 0.1s
	@echo -e $(BANNER2)

#GSPICEUI_DEBUG
#GSPICEUI_WXLIB
#GSPICEUI_MULTI
#GSPICEUI_MSWIN

#***************************************************************************************************
# Rules to make targets
#***************************************************************************************************

# Compiler Rules :
#   $<  is the name of the first dependency
#   $@  is the file name of the target
#   -c  compile only, don't link, produces object file ie. <name>.o files
#   -o  place output file in $@

$(OBJDIR)/%.o : %.cpp
	$(CXX) -c $(CXXFLAGS) $(INCLUDES) $< -o $@
	@echo

# Linker Rules :
#   -pipe  use pipes rather temporary files for interprocess communications
#   -o     specify the output file name

$(BINDIR)/$(PROG) : $(OBJS)
	$(LD) -pipe -o $(BINDIR)/$(PROG) obj/*.o $(LIBS)
ifeq ($(ROOT)/GSpiceUI.app,$(wildcard $(ROOT)/GSpiceUI.app))
	cp $(BINDIR)/$(PROG) $(ROOT)/GSpiceUI.app/Contents/MacOS/gspiceui
endif

# Windows resource manipulation (windres is part of GNU Binary Utilities)

ifneq ($(GSPICEUI_MSWIN),0)
$(OBJDIR)/%.o : %.rc
	$(WINDRES) -i $< -o $@ $(INCLUDES) $(shell $(WXCFG) --cppflags)
endif

#***************************************************************************************************
# Include the dependencies file
#***************************************************************************************************

ifeq ($(DEPS),$(wildcard $(DEPS)))
  include $(DEPS)
endif

#***************************************************************************************************
# Create the dependencies file
#***************************************************************************************************

# Using a bash "for" loop
deps :
	@echo -e "\n***************************** Build dependencies ******************************\n"
	@truncate --size=0 $(DEPS)
	@for SRCFILE in $(SRCS); do                  \
	   echo -e "Process C++ file :" $$SRCFILE ;  \
	   $(CXX) -MM -I. $$SRCFILE |                \
	   gawk 'BEGIN { printf "$(OBJDIR)/" }       \
	         { printf "%s\n", $$0 }              \
	         END { printf "\n" }' >> $(DEPS);    \
	 done
	@echo -e "\n**************************** Build deps completed *****************************\n"

# Using the make "foreach" function
deps0 :
	$(shell truncate --size=0 $(DEPS))
	$(foreach SRCFILE,$(SRCS),                   \
	   $(shell $(CXX) -MM -I. $(SRCFILE) |       \
	      gawk 'BEGIN { printf "$(OBJDIR)/" }    \
		         { printf "%s\n", $$0 }            \
		         END { printf "\n" }' >> $(DEPS)))
	@echo -e "\nDependency file $(DEPS) has been updated\n"

# Not using any loop but no gap between dependencies in Makefile.deps
deps1 :
	@$(CXX) -MM -I. $(SRCS) | \
	   gawk 'BEGIN {} { printf "%s%s\n", (index($$0," ")!=1 ? "$(OBJDIR)/" : ""), $$0 }' > $(DEPS)
	@echo -e "\nDependency file $(DEPS) has been updated\n"

#***************************************************************************************************
# Build the test utilities
#***************************************************************************************************

tests : test_Component test_NetList test_CnvtType test_CmdNgSpiceOPT test_CmdNgSpicePR           \
	      test_CmdNgSpiceDC test_CmdNgSpiceAC test_CmdNgSpiceTR test_CmdGnuCapOPT test_CmdGnuCapPR \
	      test_CmdGnuCapOP  test_CmdGnuCapDC  test_CmdGnuCapAC  test_CmdGnuCapTR  test_CmdGnuCapFO \
	      test_CmdGnuCapGEN test_StrUtils test_Config test_AppConfig test_AppPnlValue              \
	      test_AppPrcBase test_AppPrcNetList

# Compiler options

test_% : CXXFLAGS  = -Wall -g -pipe $(shell $(WXCFG) --cxxflags)
test_% : CXXFLAGS += -D $(shell echo $@ | tr "[:lower:]" "[:upper:]")
# Libraries
test_% : LIBS = $(shell $(WXCFG) --libs core,base)

# Compiler Rules for test utilities :
#   $^  is the names of all the prerequisites
#   $@  is the file name of the target

test_% : %.cpp
	@echo
	$(CXX) $(CXXFLAGS) $(INCLUDES) $^ -o $(BINDIR)/$@ $(LIBS)
	@echo

test_StrUtils      : StrUtils.cpp Component.cpp CnvtType.cpp
test_CnvtType      : CnvtType.cpp
test_Config        : Config.cpp TypeDefs.cpp StrUtils.cpp Component.cpp CnvtType.cpp
test_Component     : Component.cpp CnvtType.cpp StrUtils.cpp
test_NetList       : NetList.cpp Component.cpp StrUtils.cpp TypeDefs.cpp CnvtType.cpp
test_SysScan       : SysScan.cpp TypeDefs.cpp
#test_Test          : Test.cpp TypeDefs.cpp

test_CmdNgSpiceOPT : CmdNgSpiceOPT.cpp CmdBase.cpp CnvtType.cpp
test_CmdNgSpiceDC  : CmdNgSpiceDC.cpp  CmdBase.cpp CnvtType.cpp
test_CmdNgSpiceAC  : CmdNgSpiceAC.cpp  CmdBase.cpp CnvtType.cpp
test_CmdNgSpiceTR  : CmdNgSpiceTR.cpp  CmdBase.cpp CnvtType.cpp
test_CmdNgSpicePR  : CmdNgSpicePR.cpp  CmdBase.cpp CnvtType.cpp TypeDefs.cpp StrUtils.cpp \
                     Component.cpp NetList.cpp

test_CmdGnuCapOPT  : CmdGnuCapOPT.cpp  CmdBase.cpp CnvtType.cpp
test_CmdGnuCapOP   : CmdGnuCapOP.cpp   CmdBase.cpp CnvtType.cpp
test_CmdGnuCapDC   : CmdGnuCapDC.cpp   CmdBase.cpp CnvtType.cpp
test_CmdGnuCapAC   : CmdGnuCapAC.cpp   CmdBase.cpp CnvtType.cpp
test_CmdGnuCapTR   : CmdGnuCapTR.cpp   CmdBase.cpp CnvtType.cpp
test_CmdGnuCapFO   : CmdGnuCapFO.cpp   CmdBase.cpp CnvtType.cpp
test_CmdGnuCapGEN  : CmdGnuCapGEN.cpp  CmdBase.cpp CnvtType.cpp
test_CmdGnuCapPR   : CmdGnuCapPR.cpp   CmdBase.cpp CnvtType.cpp TypeDefs.cpp StrUtils.cpp \
	                   Component.cpp NetList.cpp

test_AppConfig     : AppConfig.cpp Config.cpp TypeDefs.cpp StrUtils.cpp Component.cpp CnvtType.cpp
test_AppPnlValue   : AppPnlValue.cpp PnlValue.cpp UnitsBase.cpp ChoUnits.cpp LblUnits.cpp \
	                   PnlTxtSpn.cpp PnlLblTxt.cpp CnvtType.cpp
test_AppPrcBase    : AppPrcBase.cpp PrcBase.cpp TypeDefs.cpp TextCtrl.cpp StrUtils.cpp \
                     Component.cpp CnvtType.cpp
test_AppPrcNetList : PrcNetList.cpp PrcBase.cpp TypeDefs.cpp TextCtrl.cpp StrUtils.cpp \
                     Component.cpp CnvtType.cpp

#***************************************************************************************************
# Install the application
#***************************************************************************************************

install :
	mkdir -p $(DESTDIR)/bin
	cp ../bin/$(PROG) $(DESTDIR)/bin

#***************************************************************************************************
# Uninstall the application
#***************************************************************************************************

uninstall :
	rm -f $(DESTDIR)/bin/$(PROG)
#	rmdir --ignore-fail-on-non-empty $(DESTDIR)/bin

#***************************************************************************************************
# Check for bugs using cppcheck (a tool for static C/C++ code analysis)
#
# Note : 1. The check process can be confined to a specific sub-directory as in the following eg. :
#             cd <GSPICEUI>/src/base
#             make -f ../Makefile check
# Note : 2. Can also use valgrind as follows :
#             cd <GSPICEUI>/bin
#             valgrind --tool=memcheck ./gspiceui
# Note : 3. Could also try the clang static analyser.
#***************************************************************************************************
# The checks that have been deliberately disabled are :
#  o unusedFunction        : Some functions are added to classes regardless if they're used or not
#  o unsignedLessThanZero  : What if the variable is changed to an integer for some reason?
#  o unknownMacro          : Coursed by wxWidgets for some reason
#  o useInitializationList : The class initialization list isn't as readable as the constructor body
#  o variableScope         : I like all variables to appear at the top of a function
#  o missingInclude        : Missing include files will be picked up by the compiler
#  o operatorEqVarError    : One instance where an object attribute is deliberately omitted

check :
	@echo -e "\n************************** Perform static bug check ***************************\n"
	@cppcheck -q --std=c++17 --enable=all -I.                 \
	          --suppress=unusedFunction                       \
	          --suppress=unsignedLessThanZero                 \
	          --suppress=unknownMacro                         \
	          --suppress=constParameter                       \
	          --suppress=useInitializationList                \
	          --suppress=variableScope                        \
	          --suppress=missingInclude                       \
	          --suppress=operatorEqVarError:base/SimnBase.cpp \
	          .
	@echo -e "\n************************* Static bug check completed **************************\n"

# Run the following command to verify that cppcheck finds all the include files it needs :
# cppcheck -q --check-config -I. -I/usr/lib/wx/include/gtk2-unicode-3.0 -I/usr/include/wx-3.0 .

#***************************************************************************************************
# Remove old versions of the main application binary and object files
#***************************************************************************************************

clean :
	rm -f $(BINDIR)/$(PROG) $(OBJDIR)/*.o

#***************************************************************************************************
# Remove old versions of test utility binaries
#***************************************************************************************************

cleantests :
	rm -f $(BINDIR)/test_*

#***************************************************************************************************
# Remove old versions of all application and test utility binaries and object files
#***************************************************************************************************

cleanall : clean cleantests

#***************************************************************************************************
# Display a help message
#***************************************************************************************************

help :
	@echo
	@echo -e "gSpiceUI C++ source code build system. "
	@echo
	@echo -e "Available make targets :"
	@echo
	@echo -e "  all        - Build the application binary (default)"
	@echo -e "  deps       - Create the dependencies file"
	@echo -e "  tests      - Build the all test utilities"
	@echo -e "  test_*     - Build a particular test utility"
	@echo -e "  install    - Install   the application binary"
	@echo -e "  uninstall  - Uninstall the application binary"
	@echo -e "  check      - Check for bugs using cppcheck"
	@echo -e "  clean      - Remove the application binary and object files"
	@echo -e "  cleantests - Remove the test utility binaries"
	@echo -e "  cleanall   - Remove the application and test utility binaries and object files"
	@echo -e "  help       - Display this message"
	@echo

#***************************************************************************************************
