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

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

#***************************************************************************************************
# Things To Do :
#
# 2025-08-27 Some schematics in sch/ don't build cleanly
# 2025-08-25 In debug mode, build isn't clean without -Wno-overloaded-virtual compiler option
# 2025-08-25 When build time is less than a second no leading zero is displayed eg. ".41"
# 2025-08-25 Build time for target "tests" doesn't work correctly
#***************************************************************************************************

# Record the time when the build process was started as early as possible
START_TIME := $(shell date +%s.%2N)  # Give resolution of 10 msec

#***************************************************************************************************
# Any or all of the values in this section may be set via the command line when envoking make eg. :
#
#   make GSPICEUI_MULTI=1 GSPICEUI_DEBUG=y GSPICEUI_MSWIN=0
#
# Notes : * Values specified on the command line over-ride those specified in this Makefile.
#         * To enable a flag give it the value y or 1, anything else disables it.
#         * In the above example features GSPICEUI_MULTI & GSPICEUI_DEBUG are enabled and
#           GSPICEUI_MSWIN is disabled.
#***************************************************************************************************

# Flag : Create bin file containing debug info. (ie. for gdb) & add extra build warnings
GSPICEUI_DEBUG := n

# Flag : Specify if the build process should use a single or multiple threads
GSPICEUI_MULTI := n

# Flag : Specify if the host operating system will be MS Windows
GSPICEUI_MSWIN := n

# Specify the toolkit the wxWidgets library is to build against ("gtk3" or "qt")
GSPICEUI_TLKIT := "gtk3"

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

# Specify the compiler (eg. g++ or clang++)
CXX            := g++

# Specify user defined CXXFLAGS (the following block of code allows this to happen)
ifdef CXXFLAGS
  TMP_VAR1 := $(CXXFLAGS)
else
  TMP_VAR1 :=
endif
override undefine CXXFLAGS
CXXFLAGS       := $(TMP_VAR1)

#---------------------------------------------------------------------------------------------------
# Undefine build system control flags that aren't required

     ifeq ($(GSPICEUI_DEBUG), y)
else ifeq ($(GSPICEUI_DEBUG), 1)
else
  override undefine GSPICEUI_DEBUG
endif

     ifeq ($(GSPICEUI_MULTI), y)
else ifeq ($(GSPICEUI_MULTI), 1)
else
  override undefine GSPICEUI_MULTI
endif

     ifeq ($(GSPICEUI_MSWIN), y)
else ifeq ($(GSPICEUI_MSWIN), 1)
else
  override undefine GSPICEUI_MSWIN
endif

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

# Which compiler to envoke (eg. g++ or clang++)
CXX ?= g++

# Manipulate Windows resources
ifdef GSPICEUI_MSWIN
  WINDRES := windres
endif

# Application binary name
PROG := gspiceui

# The wxWidgets configuration utility to use :
ifdef GSPICEUI_DEBUG
  # Explicitly specify the config. utility eg. :
  #   WXCFG := /usr/bin/wx-config                                (use system wide wxWidgets install)
  #   WXCFG := ~/libs/wxWidgets/v3.2.8.1/buildqt/wx-config       (use local build of wxWidgets)
  WXCFG := /usr/bin/wx-config --debug=yes
else
  # Use the system default wxWidgets configuration utility
  WXCFG := wx-config
endif

# Append the wxWidgets port designator if defined
WXCFG += --toolkit=$(GSPICEUI_TLKIT)

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

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

# Specify static linkage for MSWindows
ifdef GSPICEUI_MSWIN
  WXCFG += --static
endif

# Dependency file name
DEPS := Makefile.deps

# Directories
# Project root directory (long winded but unambiguous)
#ROOT    := $(shell cd .. ; pwd)
# Project root directory (short form but possibly ambiguous)
ROOT    := ..
# Source file directory
SRCDIR  := $(ROOT)/src
# Object file directory relative to the source directory
OBJDIR  := obj
# Binary directory
BINDIR  := $(ROOT)/bin
# Specify the install directory
DESTDIR := /usr/local/bin

# Use multiple processes to build the project
ifdef GSPICEUI_MULTI
  CPU_CNT   := $(shell grep -c "^processor" /proc/cpuinfo)
  MAKEFLAGS += -j$(CPU_CNT) -Otarget --no-print-directory
endif

#---------------------------------------------------------------------------------------------------
# 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)

# C++ compiler options
CXXFLAGS += -std=gnu++17
#CXXFLAGS += -fabi-version=14   # ??? 2025-08-25 Don't appear to need this option any longer
ifdef GSPICEUI_DEBUG
  # Options for development
  CXXFLAGS += -pipe -g3 -O0 -Wall -Wextra -Wpedantic
else
  # Options for release (not using -Wall since it's GCC specific)
  CXXFLAGS += -O3
endif

# Compile using the option "-Wall" however tests that break wxWidgets are turned off
ifdef GSPICEUI_DEBUG
  # Suppress some warnings associated with the wxWidgets library
  ifeq ($(GSPICEUI_WXLIB),3.0)
    CXXFLAGS += -Wno-deprecated-copy
  endif
  # The following warning has been disabled because I don't know how to fix it
  CXXFLAGS += -Wno-overloaded-virtual
endif

# Add wxWidgets specific compiler options
CXXFLAGS += $(shell $(WXCFG) --cxxflags)

# Includes file directories
INCLUDES := -I.

# Linker options
ifdef GSPICEUI_MSWIN
  LDFLAGS := -static-libstdc++ -static-libgcc
endif

# Libraries to link against
LIBS := $(shell $(WXCFG) --libs core,base,html)
ifeq ($(GSPICEUI_TLKIT), "qt")
  LIBS += -lcairo  # ??? 2025-08-15 Temporary bug fix where cairo library not automatically included
endif

# ??? 2019-08-07 (The pkg-config stuff was requested by a user, somehow pangox was missing)
#ifdef GSPICEUI_MSWIN
#  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
SRCS := $(wildcard *.cpp) $(wildcard */*.cpp) $(wildcard */*/*.cpp)
ifdef GSPICEUI_MSWIN
  RSRC := $(wildcard *.rc)
endif

# Objects files to be generated
OBJS := $(SRCS)
OBJS := $(filter-out test-apps/%.cpp, $(OBJS))
OBJS := $(notdir $(OBJS))
OBJS := $(patsubst %.cpp, obj/%.o, $(OBJS))
ifdef GSPICEUI_MSWIN
  OBJS := $(OBJS) $(patsubst %.rc, obj/%.o, $(RSRC))
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)

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

all : build_beg $(OBJS) $(PROG) build_end

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

ifdef GSPICEUI_DEBUG
  TMP_VAR1 := "Enabled"
else
  TMP_VAR1 := "Disable"
endif

ifdef GSPICEUI_MULTI
  TMP_VAR2 := "Enabled"
else
  TMP_VAR2 := "Disable"
endif

build_beg :
	@echo
	@echo "Build configuration"
	@echo "-------------------"
	@echo "  gSpiceUI debug mode :" $(TMP_VAR1)
	@echo "  Multi-process build :" $(TMP_VAR2)
	@echo "  Compiler            :" $(CXX)
	@echo "  wxWidgets port      :" $(GSPICEUI_TLKIT) "toolkit"
	@echo "  wxWidgets library   :" "v"$(shell $(WXCFG) --version-full)
	@echo "  wxWidgets based on  :" $(shell $(WXCFG) --selected-config)
	@echo "  wxWidgets cfg. cmd. :" $(WXCFG)
	@echo -e "\n****************************** Build gSpiceUI binary ******************************\n"

build_end : $(PROG)
	@sleep 0.1s
	@echo -e "\n**************************** Build gSpiceUI completed *****************************\n"
	@echo "Build took $$(bc -l <<< '$(shell date +%s.%2N)-$(START_TIME)') seconds"
	@echo

#***************************************************************************************************
# Rules to build 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 (to reveal more of what the linker is doing use "make --debug=j") :
#   -o  specify the output file name
$(BINDIR)/$(PROG) : $(OBJS)
	$(CXX) $(LDFLAGS) -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)
ifdef GSPICEUI_MSWIN
$(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_AppDispCtrl              \
	      test_AppPnlValue test_AppPrcBase test_AppPrcNetLstr

# Add extra compiler option
#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 name of the first dependency
#   $^  is the names of all the prerequisites
#   $@  is the file name of the target

test_% :
	@echo
ifndef GSPICEUI_DEBUG
	@echo "Flag GSPICEUI_DEBUG should enabled when building test utilities"
	@echo
endif
	$(CXX) $(CXXFLAGS) $(INCLUDES) $^ -o $(BINDIR)/$@ $(LIBS)
	@echo
	@echo "Build took $$(($$(date +%s)-$(START_TIME))) seconds"
	@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_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_AppDispCtrl   : AppDispCtrl.cpp PnlValue.cpp UnitsBase.cpp ChoUnits.cpp LblUnits.cpp          \
	                   PnlTxtSpn.cpp PnlLblTxt.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 SysScan.cpp
test_AppPrcNetLstr : AppPrcNetLstr.cpp PrcNetLstr.cpp PrcBase.cpp TypeDefs.cpp TextCtrl.cpp        \
                     StrUtils.cpp Component.cpp CnvtType.cpp SysScan.cpp Config.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)
#
# Notes : 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
#         2. Can also use valgrind as follows :
#              cd <GSPICEUI>/bin
#              valgrind --tool=memcheck ./gspiceui
#         3. Run cppcheck on a single file as follows :
#              cppcheck -q --std=c++17 --enable=warning -I. <Source_File>.cpp
#         4. Run the following command to verify that cppcheck finds all include files it needs :
#              cppcheck -q --check-config -I. -I/usr/lib/wx/include/gtk2-unicode-3.0 -I/usr/include/wx-3.0 .
#         5. Could also try the clang static analyser ie. clang-check.
#***************************************************************************************************
# The checks that have been deliberately disabled are :
#
#  - unusedFunction              : Some functions are added to classes regardless if they're used or not
#  - unsignedLessThanZero        : What if the variable is changed to an integer for some reason?
#  - unknownMacro                : Coursed by wxWidgets for some reason
#  - useInitializationList       : The class initialization list isn't as readable as the constructor body
#  - variableScope               : I like all variables to appear at the top of a function
#  - missingInclude              : Missing include files will be picked up by the compiler
#  - operatorEqVarError          : One instance where an object attribute is deliberately omitted
#  - normalCheckLevelMaxBranches : Limit analysis of branches (too much information)

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

#***************************************************************************************************
# 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 "  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
	@echo -e "Available build flags (only of use for targets \"all\" and \"tests\") :"
	@echo
	@echo -e "  GSPICEUI_DEBUG - Build the binary file/s containing debug information"
	@echo -e "  GSPICEUI_MULTI - The build process should use multiple processes (threads)"
	@echo -e "  GSPICEUI_MSWIN - The host operating system is MS Windows"
	@echo
	@echo -e "  Note : Enable flags by setting value to \"y\" or \"1\", anything else disables flag"
	@echo

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

.PHONY : deps deps0 deps1 install uninstall check clean cleantests cleanall help build_beg build_end

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