include ../../allmake.mak

#----------------------------------------------------------------------
# WARNING: Many rules in this file use pattern matching, where 'make'
# first considers rules as simple strings, not paths. Consequently,
# it is necessary that we don't end up with 'some_dir//some_file'.
# Thus, we have to settle for a makefile-wide policy of either:
# - terminating dir paths with '/', in which case we have to write:
#   $(SOMEDIR)somefile, or
# - not terminating dir paths with '/', in which case it becomes:
#   $(SOMEDIR)/somefile
# In other makefiles sitting in IDA's source tree, we use the first approach,
# but this one demands the second: not only is this more natural for
# non-hexrays people looking at the file, it also allows us to work in
# a more natural manner with other tools (such as the build.py wrapper, that
# uses os.path.join())

#----------------------------------------------------------------------
# default goals
.PHONY: configs modules pyfiles deployed_modules idapython_modules api_check api_contents qt_bindings bins public_tree test_idc docs docs_md tbd examples examples_index test_pywraps
GOALS = check-env configs modules deployed_modules qt_bindings bins examples examples_index test_pywraps # public_tree test_idc docs docs_md
ifndef __CODE_CHECKER__
	GOALS += pyfiles idapython_modules api_check api_contents
endif
all: $(GOALS)

check-env:
ifndef SWIG
	$(error You must define the SWIG variable that points swig executable to build IDAPython)
endif

# Commands
PYTHONCMD = $(TIMEWRAP-1) $(PYTHON)
CATCMD = $(TIMEWRAP-1) cat
DIFFCMD = $(TIMEWRAP-1) diff
MKDIRCMD = $(MKDIR) -p
TOUCHMD = $(TIMEWRAP-1) touch

ifeq ($(OUT_OF_TREE_BUILD),)
  IDAPYSWITCH:=$(R)idapyswitch$(B)
  IDAPYSWITCH_DEP:=$(IDAPYSWITCH)
  IDAPYSWITCH_PATH:=$(IDAPYSWITCH)
  TEST_PYWRAPS_RESULT_FNAME:=test_pywraps$(ADRSIZE).txt
  ifdef __NT__
    # On Windows, we cannot afford to use `test_pywraps` during a
    # debug build: since `test_pywraps.exe` relies on `python3.dll`
    # (and corresponding headers), Python will force optimized
    # iterators resulting in errors such as:
    #    dumb.obj : error LNK2038: mismatch detected for '_ITERATOR_DEBUG_LEVEL': value '2' doesn't match value '0' in test_pywraps.obj

    # And as it turns out, doing it during an optimized build
    # is problematic as well, because it'll typically not find
    # `python3.dll`. I tried to support it by patching `PATH`,
    # but now we're dealing with cygwin confusion...
    ifdef NDEBUG
      ifndef CROSS_COMPILING
        HAS_TEST_PYWRAPS:=1
      endif
      PYTHON_ROOT_CYGPATH:=$(shell cygpath $(PYTHON_ROOT))
      TEST_PYWRAPS_ENV:=PATH="$$PATH:$(PYTHON_ROOT_CYGPATH)"
    endif
  else
    ifndef CROSS_COMPILING
      HAS_TEST_PYWRAPS:=1
    endif
  endif
  ifeq ($(HAS_TEST_PYWRAPS),1)
    TEST_PYWRAPS:=$(F)$(TEST_PYWRAPS_RESULT_FNAME).marker
  endif
  BINS += $(IDAPYSWITCH)
else
  ifdef __NT__
    IDAPYSWITCH_PATH:=$(IDA_INSTALL)/idapyswitch.exe
  else
    IDAPYSWITCH_PATH:=$(IDA_INSTALL)/idapyswitch
  endif
endif

BINS += $(IDAPYSWITCH_DEP)
bins: $(BINS)

# it became part of the plugins in the SDK, so it is relative by default
SDK_DIR ?= ../..
ST_SDK = $(SDK_DIR)/include
ST_SDK_EXTRA = $(F)idasdk
II = ../../include/

ifndef __MKDEP__
  I = $(ST_SDK)/
endif

# HACK HIJACK the $(LIBDIR) variable to point to our staging SDK
ifneq ($(OUT_OF_TREE_BUILD),)
  LIBDIR = $(IDA)lib/$(TARGET_PROCESSOR_NAME)_$(SYSNAME)_$(COMPILER_NAME)_$(ADRSIZE)$(EXTRASUF)
endif

# HACK for mkdep to add dependencies for $(F)idapython$(O)
ifdef __MKDEP__
  OBJS += $(F)idapython$(O)
  OBJS += $(F)test_pywraps$(O)
  OBJS += $(F)idapyswitch$(O)
endif

# allmake.mak defines 'CP' as 'qcp.sh' which is an internal tool providing
# support for the '-u' flag on OSX. However, since this makefile is part
# of the public release of IDAPython, we cannot rely on it (we do not use
# that flag in IDAPython anyway)
# using ditto(1) instead of cp(1) to avoid copying in place which could
# lead to code-signing crashes
# see: https://developer.apple.com/documentation/security/updating-mac-software?language=objc
ifdef __MAC__
  CP = $(TIMEWRAP-1) ditto
endif

#----------------------------------------------------------------------
# the 'configs' target is in $(IDA)module.mak
CONFIGS += idapython.cfg

#----------------------------------------------------------------------
# the 'modules' target is in $(IDA)module.mak
ifdef __EA64__
  MODULE_NAME_STEM:=idapython3
else
  MODULE_NAME_STEM:=idapython3_
endif
MODULE = $(call module_dll,$(MODULE_NAME_STEM))
MODULES += $(MODULE)

#----------------------------------------------------------------------
# we don't link to libpython anymore so allow to have undefined symbols
ifdef __LINUX__
  NO_UNDEFS=
else ifdef __MAC__
  NO_UNDEFS=-Wl,-undefined,dynamic_lookup
endif

#----------------------------------------------------------------------
# we explicitly added our module targets
NO_DEFAULT_TARGETS = 1
DONT_ERASE_LIB = 1

# NOTE: all MODULES must be defined before including plugin.mak.
include ../plugin.mak
include ../pyplg.mak
# NOTE: target-specific rules and dependencies that use variable
#       expansion to name the target (such as "$(MODULE): [...]") must
#       come after including plugin.mak

#----------------------------------------------------------------------
PYTHON_OBJS += $(F)idapython$(O)
$(MODULE): MODULE_OBJS += $(PYTHON_OBJS)
$(MODULE): $(PYTHON_OBJS) $(IDAPYSWITCH_MODULE_DEP)
ifdef __NT__
  $(MODULE): OUTDLL = /DLL
  $(MODULE): LDFLAGS += /DEF:$(IDAPYTHON_IMPLIB_DEF) /IMPLIB:$(IDAPYTHON_IMPLIB_PATH)
endif

# TODO these should apply only to $(MODULE)
DEFFILE = idapython.script
INSTALL_NAME = @rpath/plugins/$(notdir $(MODULE))
ifdef __LINUX__
  LDFLAGS_MOD = -Wl,-soname,$(notdir $(MODULE))
  LDFLAGS_EXE = -l$(PYTHON_VERNAME) -Wl,--no-undefined
endif

PATCH_CONST=$(Q)$(PYTHONCMD) tools/patch_constants.py -i $(1) -o $(2)

#----------------------------------------------------------------------
# TODO move this below, but it might be necessary before the defines-*
ifdef DO_IDAMAKE_SIMPLIFY
# No need to pollute linter
ifndef __CODE_CHECKER__
  QCHKAPI = @echo $(call qcolor,chkapi) && #
  QDUMPAPI = @echo $(call qcolor,dumpapi) && #
  QDEPLOY = @echo $(call qcolor,deploy) $$@ && #
  QGENDOXYCFG = @echo $(call qcolor,gendoxycfg) $@ && #
  QGENHOOKS = @echo $(call qcolor,genhooks) $< && #
  QGENIDAAPI = @echo $(call qcolor,genidaapi) $< && #
  QGENSWIGHEADER = @echo $(call qcolor,genswigheader) $< && #
  QINJECT_PYDOC = @echo $(call qcolor,inject_pydoc) $$< && #
  QPATCH_CODEGEN = @echo $(call qcolor,patch_codegen) $$< && #
  QPATCH_H_CODEGEN = @echo $(call qcolor,patch_h_codegen) $$< && #
  QPATCH_PYTHON_CODEGEN = @echo $(call qcolor,patch_python_codegen) $$< && #
  QPATCH_PYSIDE6_INIT = @echo $(call qcolor,patch_pyside6_init) $$< && #
  QSWIG = @echo $(call qcolor,swig) $$< && #
  QUPDATE_SDK = @echo $(call qcolor,update_sdk) $< && #
  QGEN_EXAMPLES_INDEX = @echo $(call qcolor,gen_examples_index) $@ && #
endif
endif

#----------------------------------------------------------------------
ST_SWIG=$(F)swig
ST_PYW=$(F)pywraps
ST_WRAP=$(F)wrappers
ST_PARSED_HEADERS_NOXML=$(F)parsed_notifications
ST_PARSED_HEADERS=$(ST_PARSED_HEADERS_NOXML)/xml
ifeq ($(OUT_OF_TREE_BUILD),)
  ST_PARSED_HEADERS_CONFIG=$(ST_PARSED_HEADERS_NOXML)/doxy_gen_notifs.cfg
endif

# output directory for python scripts
DEPLOY_PYDIR=$(R)python
DEPLOY_INIT_PY=$(DEPLOY_PYDIR)/init.py
DEPLOY_IDC_PY=$(DEPLOY_PYDIR)/idc.py
DEPLOY_IDAUTILS_PY=$(DEPLOY_PYDIR)/idautils.py
DEPLOY_IDAAPI_PY=$(DEPLOY_PYDIR)/idaapi.py
DEPLOY_IDADEX_PY=$(DEPLOY_PYDIR)/idadex.py
DEPLOY_LUMINA_MODEL_PY=$(DEPLOY_PYDIR)/lumina_model.py
ifeq ($(OUT_OF_TREE_BUILD),)
  TEST_IDC=test_idc
  IDAT_PATH?=$(R)/idat
else
  IDAT_PATH?=$(IDA_INSTALL)/idat
endif

ifeq ($(OUT_OF_TREE_BUILD),)
  IDAT_CMD=TVHEADLESS=1 "$(IDAT_PATH)$(SUFF32)"
else
  IDAT_CMD=TVHEADLESS=1 IDAPYTHON_DYNLOAD_BASE=$(R) "$(IDAT_PATH)$(SUFF32)"
endif

ifeq ($(LOCAL_LICENSE),1)
  IDAT_CMD := $(IDAT_CMD) -Olicense:keyfile
endif

ifdef LOG_FILE
  IDAT_CMD := $(IDAT_CMD) -L$(LOG_FILE)
endif

# determine SDK_SOURCES
SDK_SOURCES=$(wildcard $(ST_SDK)/*.h) $(wildcard $(ST_SDK)/*.hpp)

_SWIGPY3FLAG := -DPY3=1
ST_INCLUDES = -I$(ST_SDK)
CC_DEFS += PY3=1
CC_DEFS += Py_LIMITED_API=0x03040000 # we should make sure we use the same version as SWiG

# We want the idapython shared object to runtime link against all python symbols.
$(F)idapython$(O): CC_DEFS += INDIRECT_PYTHON_API


DEPLOY_LIBDIR=$(DEPLOY_PYDIR)/lib-dynload

ifdef __NT__
  PYDLL_EXT = .pyd
else
  PYDLL_EXT = .so
endif

#----------------------------------------------------------------------
# Do not change the order if doesn't wan to update api_contents
MODULES_NAMES += hexrays
MODULES_NAMES += allins
MODULES_NAMES += auto
MODULES_NAMES += bitrange
MODULES_NAMES += bytes
MODULES_NAMES += dbg
MODULES_NAMES += diskio
MODULES_NAMES += dirtree
MODULES_NAMES += entry
MODULES_NAMES += expr
MODULES_NAMES += fixup
MODULES_NAMES += fpro
MODULES_NAMES += frame
MODULES_NAMES += funcs
MODULES_NAMES += gdl
MODULES_NAMES += graph
MODULES_NAMES += ida
MODULES_NAMES += idaapi
MODULES_NAMES += idc
MODULES_NAMES += idd
MODULES_NAMES += idp
MODULES_NAMES += ieee
MODULES_NAMES += kernwin
MODULES_NAMES += libfuncs
MODULES_NAMES += lines
MODULES_NAMES += loader
MODULES_NAMES += lumina
ifdef TESTABLE_BUILD
  MODULES_NAMES += indexer
  SDK_SOURCES+= $(ST_SDK_EXTRA)/indexer.hpp
  CC_INCP += $(ST_SDK_EXTRA)
  ST_INCLUDES += -I$(ST_SDK_EXTRA)

$(ST_SDK_EXTRA)/indexer.hpp: $(II)/indexer.hpp
	$(Q)$(CP) $? $@
endif
MODULES_NAMES += moves
MODULES_NAMES += nalt
MODULES_NAMES += name
MODULES_NAMES += netnode
MODULES_NAMES += offset
MODULES_NAMES += pro
MODULES_NAMES += problems
MODULES_NAMES += range
MODULES_NAMES += registry
MODULES_NAMES += regfinder
MODULES_NAMES += search
MODULES_NAMES += segment
MODULES_NAMES += segregs
MODULES_NAMES += srclang
MODULES_NAMES += strlist
MODULES_NAMES += tryblks
MODULES_NAMES += typeinf
MODULES_NAMES += ua
MODULES_NAMES += xref
MODULES_NAMES += mergemod
MODULES_NAMES += merge
MODULES_NAMES += undo

ALL_ST_WRAP_CPP = $(foreach mod,$(MODULES_NAMES),$(ST_WRAP)/$(mod).cpp)
ALL_ST_WRAP_PY = $(foreach mod,$(MODULES_NAMES),$(ST_WRAP)/ida_$(mod).py)
ALL_ST_WRAP_PY_FINAL = $(foreach mod,$(MODULES_NAMES),$(ST_WRAP)/ida_$(mod).py.final)
DEPLOYED_MODULES = $(foreach mod,$(MODULES_NAMES),$(DEPLOY_LIBDIR)/_ida_$(mod)$(PYDLL_EXT))
IDAPYTHON_MODULES = $(foreach mod,$(MODULES_NAMES),$(DEPLOY_PYDIR)/ida_$(mod).py)
PYTHON_BINARY_MODULES = $(foreach mod,$(MODULES_NAMES),$(DEPLOY_LIBDIR)/_ida_$(mod)$(PYDLL_EXT))

#----------------------------------------------------------------------
idapython_modules: $(IDAPYTHON_MODULES)
deployed_modules: $(DEPLOYED_MODULES)

ifdef __NT__
  IDAPYTHON_IMPLIB_DEF=idapython_implib.def
  IDAPYTHON_IMPLIB_PATH=$(F)idapython.lib
  LINKIDAPYTHON = $(IDAPYTHON_IMPLIB_PATH)
else
  LINKIDAPYTHON = $(MODULE)
endif

ifdef __NT__                   # os and compiler specific flags
  _SWIGFLAGS = -D__NT__ -DWIN32 -D_USRDLL -I"$(PYTHON_ROOT)/include"
  CFLAGS += /bigobj $(_SWIGFLAGS) $(ST_INCLUDES) /Zc:strictStrings-
else # unix/mac
  ifdef __LINUX__
    PYTHON_LDFLAGS_RPATH_MODULE=-Wl,-rpath='$$ORIGIN/../../..'
    _SWIGFLAGS = -D__LINUX__
  else ifdef __MAC__
    PYTHON_LDFLAGS_RPATH_MODULE=-Wl,-rpath,@loader_path/../../..
    _SWIGFLAGS = -D__MAC__
  endif
endif
# Apparently that's not needed, but I don't understand why ATM, since doc says:
#  ...Then, only modules compiled with SWIG_TYPE_TABLE set to myprojectname
#  will share type information. So if your project has three modules, all three
#  should be compiled with -DSWIG_TYPE_TABLE=myprojectname, and then these
#  three modules will share type information. But any other project's
#  types will not interfere or clash with the types in your module.
DEF_TYPE_TABLE = SWIG_TYPE_TABLE=idaapi
SWIGFLAGS=$(_SWIGFLAGS) -Itools/typemaps-supplement $(SWIG_INCLUDES) $(addprefix -D,$(DEF_TYPE_TABLE)) -DMISSED_BC695

pyfiles: $(DEPLOY_IDAUTILS_PY)  \
         $(DEPLOY_IDC_PY)       \
         $(DEPLOY_INIT_PY)      \
         $(DEPLOY_IDAAPI_PY)    \
         $(DEPLOY_IDADEX_PY)    \
         $(DEPLOY_LUMINA_MODEL_PY)

ifeq ($(OUT_OF_TREE_BUILD),)
  ifndef NDEBUG
    SRC_PYQT_BUNDLE:=$(PYQT5_DEBUG)
  else
    SRC_PYQT_BUNDLE:=$(PYQT5_RELEASE)
  endif
  DEST_PYQT_DIR:=$(R)python/PyQt5
  $(DEST_PYQT_DIR):
	-$(Q)$(MKDIRCMD) $(DEST_PYQT_DIR)

  DEST_PYQT5_SHIMS += $(DEST_PYQT_DIR)/__init__.py
  DEST_PYQT5_SHIMS += $(DEST_PYQT_DIR)/sip.py
  DEST_PYQT5_SHIMS += $(DEST_PYQT_DIR)/QtCore.py
  DEST_PYQT5_SHIMS += $(DEST_PYQT_DIR)/QtGui.py
  DEST_PYQT5_SHIMS += $(DEST_PYQT_DIR)/QtWidgets.py
  DEST_PYQT5_SHIMS += $(DEST_PYQT_DIR)/Qt.py
  DEST_PYQT5_SHIMS += $(DEST_PYQT_DIR)/utils.py

  #
  # PySide6: We're looking at producing the following structure:
  #
  # python/PySide6
  #         ├── libpyside6.abi3.so.6.8
  #         ├── __init__.py
  #         ├── QtCore.abi3.so
  #         ├── QtCore.pyi
  #         ├── QtGui.abi3.so
  #         ├── QtGui.pyi
  #         ├── QtWidgets.abi3.so
  #         └── ...
  # python/shiboken6
  #         ├── libshiboken6.abi3.so.6.8
  #         ├── __init__.py
  #         ├── Shiboken.abi3.so
  #         └── ...


  ifdef __LINUX__
    PYSIDE6_DLL_VERSUFFIX=so.6.8
    PYSIDE6_SO_SUFFIX=so
    PYSIDE6_SO_PREFIX=lib
    PYSIDE6_PYD=abi3.$(PYSIDE6_SO_SUFFIX)
  else
    ifdef __MAC__
      PYSIDE6_DLL_VERSUFFIX=6.8.dylib
      PYSIDE6_SO_SUFFIX=so
      PYSIDE6_SO_PREFIX=lib
      PYSIDE6_PYD=abi3.$(PYSIDE6_SO_SUFFIX)
    else
      PYSIDE6_DLL_VERSUFFIX=dll
      PYSIDE6_SO_SUFFIX=dll
      PYSIDE6_SO_PREFIX=
      PYSIDE6_PYD=pyd
    endif
  endif

  define make-pyside6-rules

  DEST_SHIBOKEN6_DEPS += $(2)/shiboken6/$(PYSIDE6_SO_PREFIX)shiboken6.abi3.$(PYSIDE6_DLL_VERSUFFIX)
  DEST_SHIBOKEN6_DEPS += $(2)/shiboken6/__init__.py
  DEST_SHIBOKEN6_DEPS += $(2)/shiboken6/Shiboken.$(PYSIDE6_PYD)
  DEST_SHIBOKEN6_DEPS += $(2)/shiboken6/Shiboken.pyi

  DEST_PYSIDE6_DEPS += $(2)/PySide6/$(PYSIDE6_SO_PREFIX)pyside6.abi3.$(PYSIDE6_DLL_VERSUFFIX)
  DEST_PYSIDE6_DEPS += $(2)/PySide6/__init__.py
  DEST_PYSIDE6_DEPS += $(2)/PySide6/QtCore.$(PYSIDE6_PYD)
  DEST_PYSIDE6_DEPS += $(2)/PySide6/QtCore.pyi
  DEST_PYSIDE6_DEPS += $(2)/PySide6/QtGui.$(PYSIDE6_PYD)
  DEST_PYSIDE6_DEPS += $(2)/PySide6/QtGui.pyi
  DEST_PYSIDE6_DEPS += $(2)/PySide6/QtWidgets.$(PYSIDE6_PYD)
  DEST_PYSIDE6_DEPS += $(2)/PySide6/QtWidgets.pyi
  DEST_PYSIDE6_DEPS += $(2)/PySide6/QtXml.$(PYSIDE6_PYD)
  DEST_PYSIDE6_DEPS += $(2)/PySide6/QtXml.pyi
  DEST_PYSIDE6_DEPS += $(2)/PySide6/QtSvg.$(PYSIDE6_PYD)
  DEST_PYSIDE6_DEPS += $(2)/PySide6/QtSvg.pyi
  DEST_PYSIDE6_DEPS += $(2)/PySide6/QtPrintSupport.$(PYSIDE6_PYD)
  DEST_PYSIDE6_DEPS += $(2)/PySide6/QtPrintSupport.pyi
  DEST_PYSIDE6_DEPS += $(2)/PySide6/QtOpenGL.$(PYSIDE6_PYD)
  DEST_PYSIDE6_DEPS += $(2)/PySide6/QtOpenGL.pyi
  DEST_PYSIDE6_DEPS += $(2)/PySide6/QtOpenGLWidgets.$(PYSIDE6_PYD)
  DEST_PYSIDE6_DEPS += $(2)/PySide6/QtOpenGLWidgets.pyi

  ifndef __NT__
    DEST_PYSIDE6_DEPS += $(2)/PySide6/QtDBus.$(PYSIDE6_PYD)
    DEST_PYSIDE6_DEPS += $(2)/PySide6/QtDBus.pyi
  endif

  DEST_PYSIDE6_PYTHON3$(1)_DIRS += $(2)/shiboken6
  DEST_PYSIDE6_PYTHON3$(1)_DIRS += $(2)/PySide6

  $(2)/shiboken6:
	-$(Q)$(MKDIRCMD) $$@
  $(2)/PySide6:
	-$(Q)$(MKDIRCMD) $$@

    ifndef __NT__
    $(2)/PySide6/libpyside6.abi3.$(PYSIDE6_DLL_VERSUFFIX): $(3)/libpyside6.abi3.$(PYSIDE6_DLL_VERSUFFIX) | $$(DEST_PYSIDE6_PYTHON3$(1)_DIRS)
	$(Q)$(CP) $$? $$@
      ifdef __LINUX__
	$(TIMEWRAP-1) patchelf --set-rpath '$$$$ORIGIN:$$$$ORIGIN/../..' $$@
      endif
    $(2)/shiboken6/libshiboken6.abi3.$(PYSIDE6_DLL_VERSUFFIX): $(3)/libshiboken6.abi3.$(PYSIDE6_DLL_VERSUFFIX) | $$(DEST_PYSIDE6_PYTHON3$(1)_DIRS)
	$(Q)$(CP) $$? $$@
    $(2)/shiboken6/%: $(3)/python3.$(1)/site-packages/shiboken6/% | $$(DEST_PYSIDE6_PYTHON3$(1)_DIRS)
	$(Q)$(CP) $$? $$@
    $(2)/PySide6/%: $(3)/python3.$(1)/site-packages/PySide6/% | $$(DEST_PYSIDE6_PYTHON3$(1)_DIRS)
	$(Q)$(CP) $$? $$@
    $(2)/PySide6/__init__.py: $(3)/python3.$(1)/site-packages/PySide6/__init__.py tools/patch_pyside6_init.py | $(2)/PySide6
	$(QPATCH_PYSIDE6_INIT)$(PYTHONCMD) tools/patch_pyside6_init.py \
                --input $$< \
                --output $$@
    else
    $(2)/shiboken6/%: $(3)/shiboken6/% | $(2)/shiboken6
	$(Q)$(CP) $$? $$@
    $(2)/PySide6/%: $(3)/PySide6/% | $(2)/PySide6
	$(Q)$(CP) $$? $$@
    $(2)/PySide6/__init__.py: $(3)/PySide6/__init__.py tools/patch_pyside6_init.py | $(2)/PySide6
	$(QPATCH_PYSIDE6_INIT)$(PYTHONCMD) tools/patch_pyside6_init.py \
                --input $$< \
                --output $$@
    endif
  endef

  ifndef __NT__
  $(eval $(call make-pyside6-rules,9,$(R)python,$(PYSIDE6_DIR)/PySide6-python3.9))
  else
  $(eval $(call make-pyside6-rules,9,$(R)python,$(PYSIDE6_DIR)/PySide6-python3.9))
  endif

endif


ifeq ($(QT_VERSION_MAJOR),6)
  QT_BINDINGS_DEPS += $(DEST_PYQT5_SHIMS) $(DEST_SHIBOKEN6_DEPS) $(DEST_PYSIDE6_DEPS)
  $(DEST_PYQT_DIR)/%: PyQt5-shims/% | $(DEST_PYQT_DIR)
	$(CP) $? $@
else
  QT_BINDINGS_DEPS += $(DEST_PYQT)
  QT_BINDINGS_DEPS += $(DEST_SIP)
endif

qt_bindings: $(QT_BINDINGS_DEPS)

GENHOOKS=tools/genhooks/

$(DEPLOY_INIT_PY): python/init.py tools/genidaapi.py $(IDAPYTHON_MODULES)
	$(QGENIDAAPI)$(PYTHONCMD) tools/genidaapi.py -i $< -o $@ -m $(subst $(space),$(comma),$(MODULES_NAMES))

$(DEPLOY_IDC_PY): python/idc.py
	$(CP) $? $@

$(DEPLOY_IDAUTILS_PY): python/idautils.py
	$(CP) $? $@

$(DEPLOY_IDAAPI_PY): python/idaapi.py tools/genidaapi.py $(IDAPYTHON_MODULES)
	$(QGENIDAAPI)$(PYTHONCMD) tools/genidaapi.py -i $< -o $@ -m $(subst $(space),$(comma),$(MODULES_NAMES))

$(DEPLOY_IDADEX_PY): python/idadex.py
	$(CP) $? $@

$(DEPLOY_LUMINA_MODEL_PY): python/lumina_model.py
	$(CP) $? $@

$(DEPLOY_PYDIR)/lib/%: precompiled/lib/%
	cp $< $@
	$(Q)chmod +w $@

#----------------------------------------------------------------------
# Hooks generation
# http://stackoverflow.com/questions/11032280/specify-doxygen-parameters-through-command-line
$(ST_PARSED_HEADERS_CONFIG): $(GENHOOKS)doxy_gen_notifs.cfg.in $(SDK_SOURCES) $(GENHOOKS)gendoxycfg.py
	$(QGENDOXYCFG)$(PYTHONCMD) $(GENHOOKS)gendoxycfg.py -i $< -o $@ --includes $(subst $(space),$(comma),$(SDK_SOURCES))

PARSED_HEADERS_MARKER=$(ST_PARSED_HEADERS)/headers_generated.marker
$(PARSED_HEADERS_MARKER): $(SDK_SOURCES) $(ST_PARSED_HEADERS_CONFIG) $(SDK_SOURCES)
ifeq ($(OUT_OF_TREE_BUILD),)
	$(Q) ( $(CATCMD) $(ST_PARSED_HEADERS_CONFIG); echo "OUTPUT_DIRECTORY=$(ST_PARSED_HEADERS_NOXML)" ) | $(DOXYGEN_BIN) - >/dev/null
else
	(cd $(F) && unzip ../../out_of_tree/parsed_notifications.zip)
endif
	$(Q)$(TOUCHMD) $@

#----------------------------------------------------------------------
# Create directories in the first phase of makefile parsing.
DIRLIST += $(DEPLOY_LIBDIR)
DIRLIST += $(DEPLOY_PYDIR)
DIRLIST += $(ST_PARSED_HEADERS)
DIRLIST += $(ST_PYW)
DIRLIST += $(ST_SDK)
DIRLIST += $(ST_SDK_EXTRA)
DIRLIST += $(ST_SWIG)
DIRLIST += $(ST_WRAP)
$(foreach d,$(sort $(DIRLIST)),$(if $(wildcard $(d)),,$(shell mkdir -p $(d))))

#----------------------------------------------------------------------
# obj/.../pywraps/*
$(ST_PYW)/%.hpp: pywraps/%.hpp
	$(Q)$(CP) $^ $@ && chmod +rw $@
$(ST_PYW)/%.py: pywraps/%.py
	$(Q)$(CP) $^ $@ && chmod +rw $@

# These require special care, as they will have to be injected w/ hooks -- this
# only happens if we are sitting in the hexrays source tree; when published to
# the outside world, the pywraps must already contain the injected code.
$(ST_PYW)/py_idp.hpp: pywraps/py_idp.hpp \
        $(I)idp.hpp \
        $(GENHOOKS)genhooks.py \
        $(GENHOOKS)recipe_idphooks.py \
        $(PARSED_HEADERS_MARKER) $(MAKEFILE_DEP) | $(SDK_SOURCES)
	$(QGENHOOKS)$(PYTHONCMD) $(GENHOOKS)genhooks.py -i $< -o $@ \
                -c IDP_Hooks \
                -x $(ST_PARSED_HEADERS) \
                -m hookgenIDP \
                -q "processor_t::"
$(ST_PYW)/py_idp_idbhooks.hpp: pywraps/py_idp_idbhooks.hpp \
        $(I)idp.hpp \
        $(GENHOOKS)recipe_idbhooks.py \
        $(GENHOOKS)genhooks.py $(PARSED_HEADERS_MARKER) $(MAKEFILE_DEP) | $(SDK_SOURCES)
	$(QGENHOOKS)$(PYTHONCMD) $(GENHOOKS)genhooks.py -i $< -o $@ \
                -c IDB_Hooks \
                -x $(ST_PARSED_HEADERS) \
                -m hookgenIDB \
                -q "idb_event::"
$(ST_PYW)/py_dbg.hpp: pywraps/py_dbg.hpp \
        $(I)dbg.hpp \
        $(GENHOOKS)recipe_dbghooks.py \
        $(GENHOOKS)genhooks.py $(PARSED_HEADERS_MARKER) $(MAKEFILE_DEP) | $(SDK_SOURCES)
	$(QGENHOOKS)$(PYTHONCMD) $(GENHOOKS)genhooks.py -i $< -o $@ \
                -c DBG_Hooks \
                -x $(ST_PARSED_HEADERS) \
                -m hookgenDBG \
                -q "dbg_notification_t::"
$(ST_PYW)/py_kernwin.hpp: pywraps/py_kernwin.hpp \
        $(I)kernwin.hpp \
        $(GENHOOKS)recipe_uihooks.py \
        $(GENHOOKS)genhooks.py $(PARSED_HEADERS_MARKER) $(MAKEFILE_DEP) | $(SDK_SOURCES)
	$(QGENHOOKS)$(PYTHONCMD) $(GENHOOKS)genhooks.py -i $< -o $@ \
                -c UI_Hooks \
                -x $(ST_PARSED_HEADERS) \
                -q "ui_notification_t::" \
                -m hookgenUI
$(ST_PYW)/py_kernwin_viewhooks.hpp: pywraps/py_kernwin_viewhooks.hpp \
        $(I)kernwin.hpp \
        $(GENHOOKS)recipe_viewhooks.py \
        $(GENHOOKS)genhooks.py $(PARSED_HEADERS_MARKER) $(MAKEFILE_DEP) | $(SDK_SOURCES)
	$(QGENHOOKS)$(PYTHONCMD) $(GENHOOKS)genhooks.py -i $< -o $@ \
                -c View_Hooks \
                -x $(ST_PARSED_HEADERS) \
                -q "view_notification_t::" \
                -m hookgenVIEW
$(ST_PYW)/py_hexrays_hooks.hpp: pywraps/py_hexrays_hooks.hpp \
        $(I)/hexrays.hpp \
        $(GENHOOKS)recipe_hexrays.py \
        $(GENHOOKS)genhooks.py $(PARSED_HEADERS_MARKER) $(MAKEFILE_DEP) | $(SDK_SOURCES)
	$(QGENHOOKS)$(PYTHONCMD) $(GENHOOKS)genhooks.py -i $< -o $@ \
                -c Hexrays_Hooks \
                -x $(ST_PARSED_HEADERS) \
                -q "hexrays_event_t::" \
                -m hookgenHEXRAYS


#----------------------------------------------------------------------
CFLAGS += $(PYTHON_CFLAGS)
CC_DEFS += MISSED_BC695
CC_DEFS += $(DEF_TYPE_TABLE)
CC_DEFS += USE_STANDARD_FILE_FUNCTIONS
CC_DEFS += IDAVER_MAJOR=$(IDAVER_MAJOR)
CC_DEFS += IDAVER_MINOR=$(IDAVER_MINOR)
CC_DEFS += IDAVER_PATCH=$(IDAVER_PATCH)
CC_DEFS += __EXPR_SRC
CC_INCP += $(F)
CC_INCP += $(ST_SWIG)
CC_INCP += .

ifdef __UNIX__
  # suppress some warnings
  # see https://github.com/swig/swig/pull/801
  # FIXME: remove these once swig is fixed
  CC_WNO += -Wno-shadow
  CC_WNO += -Wno-unused-parameter
  # FIXME: these should be fixed and removed
  CC_WNO += -Wno-attributes
  CC_WNO += -Wno-delete-non-virtual-dtor
  CC_WNO += -Wno-deprecated-declarations
  CC_WNO += -Wno-format-nonliteral
  CC_WNO += -Wno-write-strings
  ifdef __MAC__
    # additional switches for clang
    CC_WNO += -Wno-deprecated-register
  endif
endif

# disable -pthread in CFLAGS
PTHR_SWITCH =

# disable -DNO_OBSOLETE_FUNCS in CFLAGS
NO_OBSOLETE_FUNCS =

#----------------------------------------------------------------------
ifdef TESTABLE_BUILD
  SWIGFLAGS+=-DTESTABLE_BUILD
endif

ST_SWIG_HEADER = $(ST_SWIG)/header.i
$(ST_SWIG)/header.i: tools/deploy/header.i.in tools/genswigheader.py $(SDK_SOURCES)
	$(QGENSWIGHEADER)$(PYTHONCMD) tools/genswigheader.py -i $< -o $@ -s $(ST_SDK)

ifdef __NT__
  PATCH_DIRECTORS_SCRIPT = tools/patch_directors_cc.py
endif

find-pywraps-deps = $(wildcard pywraps/py_$(subst .i,,$(notdir $(1)))*.hpp) $(wildcard pywraps/py_$(subst .i,,$(notdir $(1)))*.py)
find-patch-codegen-deps = $(wildcard tools/patch_codegen/*$(1)*.py)

# Some .i files depend on some other .i files in order to be parseable by SWiG
# (e.g., segregs.i imports range.i). Declare the list of such dependencies here
# so they will be picked by the auto-generated rules.
SWIG_IFACE_bytes=range
SWIG_IFACE_dbg=idd
SWIG_IFACE_frame=range
SWIG_IFACE_funcs=range
SWIG_IFACE_gdl=range
SWIG_IFACE_graph=gdl
SWIG_IFACE_hexrays=pro typeinf xref gdl
SWIG_IFACE_idd=range
SWIG_IFACE_idp=bitrange
SWIG_IFACE_segment=range
SWIG_IFACE_segregs=range
SWIG_IFACE_typeinf=idp
SWIG_IFACE_tryblks=range

MODULE_LIFECYCLE_bytes=--lifecycle-aware
MODULE_LIFECYCLE_hexrays=--lifecycle-aware
MODULE_LIFECYCLE_idaapi=--lifecycle-aware
MODULE_LIFECYCLE_kernwin=--lifecycle-aware
MODULE_LIFECYCLE_typeinf=--lifecycle-aware

define make-module-rules

    # Note: apparently make cannot work well when a given recipe generates multiple files
    # http://stackoverflow.com/questions/19822435/multiple-targets-from-one-recipe-and-parallel-execution
    # Consequently, rules such as this:
    #
    #   $(ST_WRAP)/ida_$(1).py: $(ST_WRAP)/$(1).cpp
    #
    # i.e., that do nothing but rely on the generation of another file,
    # will not work in // execution. Thus, we will rely exclusively on
    # the presence of the generated .cpp file, and not other generated
    # files.

    # ../../bin/x86_linux_gcc/python/ida_$(1).py
    $(DEPLOY_PYDIR)/ida_$(1).py: $(ST_WRAP)/ida_$(1).py.final
	$(Q)$(CP) $$< $$@

    # obj/x86_linux_gcc/wrappers/ida_X.py.final (note: dep. on .cpp. See note above.)
    $(ST_WRAP)/ida_$(1).py.final: $(ST_WRAP)/$(1).cpp $(wildcard tools/pypasses/*.py) $(wildcard tools/pypasses/*/*.py) $(wildcard apidoc/ida_$(1).py) $(PARSED_HEADERS_MARKER) $(call find-patch-codegen-deps,$(1))
	$(QINJECT_PYDOC)$(PYTHONCMD) tools/pypasses.py \
                --input $(ST_WRAP)/ida_$(1).py \
                --idapython-module-name ida_$(1) \
                --doxygen-xml $(ST_PARSED_HEADERS) \
                --cpp-wrapper $(ST_WRAP)/$(1).cpp \
	        --passes inject_doxygen,return_type_from_cpp_wrapper,pydoc_overrides,proto_overrides,argsify_for_dispatching_cpp,fix_known_types \
                --pydoc-overrides apidoc/ida_$(1).py \
                --output $$@ \
                --debug > $(ST_WRAP)/ida_$(1).pydoc_injection

    # obj/x86_linux_gcc/swig/X.i
    $(ST_SWIG)/$(1).i: $(addprefix $(F),$(call find-pywraps-deps,$(1))) swig/$(1).i $(ST_SWIG_HEADER) $(SWIG_IFACE_$(1):%=$(ST_SWIG)/%.i) $(ST_SWIG_HEADER) tools/deploy.py $(PARSED_HEADERS_MARKER)
	$(Q)$(QDEPLOY)$(PYTHONCMD) tools/deploy.py \
                --pywraps $(ST_PYW) \
                --template $$(subst $(F),,$$@) \
                --output $$@ \
                --module $$(subst .i,,$$(notdir $$@)) \
                $(MODULE_LIFECYCLE_$(1)) \
                --interface-dependencies=$(subst $(space),$(comma),$(SWIG_IFACE_$(1))) \
		--xml-doc-directory $(ST_PARSED_HEADERS)

    # creating obj/x86_linux_gcc/wrappers/X.cpp and friends
    # 1. SWIG generates x.cpp.in1, x.h and x.py from swig/x.i
    # 2. patch_const.py generates x.cpp.in2 from x.cpp.in1
    # 3. patch_codegen.py generates x.cpp from x.cpp.in2
    # 4. patch_h_codegen.py patches x.h in place
    # 5. patch_python_codegen.py patches ida_x.py in place using helpers in tools/patch_codegen
    # 6. on windows, patch_directors_cc.py patches x.h in place again
    $(ST_WRAP)/$(1).cpp: $(ST_SWIG)/$(1).i tools/patch_codegen.py tools/patch_python_codegen.py $(PATCH_DIRECTORS_SCRIPT) $(PARSED_HEADERS_MARKER) tools/chkapi.py tools/wrapper_utils.py
	$(QSWIG)$(SWIG_CMD) -python $(_SWIGPY3FLAG) -threads -c++ -shadow \
          -D__GNUC__ -DSWIG_PYTHON_LEGACY_BOOL=1 $(SWIGFLAGS) $(addprefix -D,$(EASIZE_DEF)) -I$(ST_SWIG) \
          -outdir $(ST_WRAP) -o $$@.in1 -oh $(ST_WRAP)/$(1).h $(ST_INCLUDES) -DIDAPYTHON_MODULE_$(1)=1 $$<
	$(call PATCH_CONST,$(ST_WRAP)/$(1).cpp.in1,$(ST_WRAP)/$(1).cpp.in2)
	$(QPATCH_CODEGEN)$(PYTHONCMD) tools/patch_codegen.py \
                --input $(ST_WRAP)/$(1).cpp.in2 \
	        --output $(ST_WRAP)/$(1).cpp \
                --module $(1) \
                --xml-doc-directory $(ST_PARSED_HEADERS) \
                --patches tools/patch_codegen/$(1).py \
		--batch-patches tools/patch_codegen/$(1)_batch.py
	$(QPATCH_H_CODEGEN)$(PYTHONCMD) tools/patch_h_codegen.py \
                --file $(ST_WRAP)/$(1).h \
                --module $(1) \
                --patches tools/patch_codegen/$(1)_h.py
	$(QPATCH_PYTHON_CODEGEN)$(PYTHONCMD) tools/patch_python_codegen.py \
                --file $(ST_WRAP)/ida_$(1).py \
                --module $(1) \
                --patches tools/patch_codegen/ida_$(1).py
    ifdef __NT__
	$(PYTHONCMD) $(PATCH_DIRECTORS_SCRIPT) --file $(ST_WRAP)/$(1).h
    endif
    # The copying of the .py will preserve attributes (including timestamps).
    # And, since we have patched $(1).cpp, it'll be more recent than ida_$(1).py,
    # and make would keep copying the .py file at each invocation.
    # To prevent that, let's make the source .py file more recent than .cpp.
	$(Q)$(TOUCHMD) $(ST_WRAP)/ida_$(1).py
endef
$(foreach mod,$(MODULES_NAMES),$(eval $(call make-module-rules,$(mod))))

# obj/x86_linux_gcc/X.o
X_O = $(call objs,$(MODULES_NAMES))
vpath %.cpp $(ST_WRAP)
ifdef __NT__
  # remove warnings from generated code:
  # warning C4100: '<thing>': unreferenced formal parameter
  # warning C4125: decimal digit terminates octal escape sequence
  # error C4296: '<': expression is always false
  # warning C4647: behavior change: __is_pod(type) has different value in previous versions
  # warning C4700: uninitialized local variable 'c_result' used
  # warning C4706: assignment within conditional expression
  # warning C4800: Implicit conversion from 'int' to bool. Possible information loss
  # warning C5205: delete of an abstract class '' that has a non-virtual destructor results in undefined behavior
  # warning C4996: was declared deprecated
  $(X_O): CFLAGS += /wd4100 /wd4125 /wd4296 /wd4647 /wd4700 /wd4706 /wd4800 /wd4996 /wd5205
ifndef NDEBUG
  # Use debug wrappers with the Python release dll
  $(X_O): CC_DEFS += SWIG_PYTHON_INTERPRETER_NO_DEBUG
endif
endif
# disable -fno-rtti
$(X_O): pywraps.hpp
$(X_O): NORTTI =
$(X_O): CC_DEFS += PLUGIN_SUBMODULE
$(X_O): CC_DEFS += SWIG_DIRECTOR_NORTTI
ifdef __CODE_CHECKER__
$(X_O):
	$(Q)$(TOUCHMD) $@
endif

# obj/x86_linux_gcc/_ida_X.so
_IDA_X_SO = $(addprefix $(F)_ida_,$(addsuffix $(PYDLL_EXT),$(MODULES_NAMES)))
ifdef __NT__
  $(_IDA_X_SO): STDLIBS += user32.lib
endif
# Note: On Windows, IDAPython's python.lib must come *after* python3x.lib
#       in the linking command line, otherwise Python will misdetect
#       IDAPython's python.dll as the main "python" DLL, and IDAPython
#       will fail to load with the following error:
#         "Module use of python.dll conflicts with this version of Python."
#       To achieve this, we add IDAPython's python.lib to STDLIBS, which
#       is at the end of the link command.
#       See Python's dynload_win.c:GetPythonImport() for more details.
$(_IDA_X_SO): STDLIBS += $(LINKIDAPYTHON)
$(_IDA_X_SO): LDFLAGS += $(PYTHON_LDFLAGS) $(PYTHON_LDFLAGS_RPATH_MODULE) $(OUTMAP)$(F)$(@F).map
$(F)_ida_%$(PYDLL_EXT): $(F)%$(O) $(MODULE) $(IDAPYTHON_IMPLIB_DEF) $(IDAPYSWITCH_MODULE_DEP)
	$(call link_dll, $<, $(L)int128$(A) $(LINKIDA))
ifdef __NT__
	$(Q)$(RM) $(@:$(PYDLL_EXT)=.exp) $(@:$(PYDLL_EXT)=.lib)
endif

# ../../bin/x64_linux_gcc/python/ida_32/_ida_X.so
$(DEPLOY_LIBDIR)/_ida_%$(PYDLL_EXT): $(F)_ida_%$(PYDLL_EXT)
	$(Q)$(CP) $< $@
ifdef __LINUX__
  ifneq (,$(POSTACTION_IDA_X_SO))
	$(Q)$(POSTACTION_IDA_X_SO) $@
  endif
endif

#----------------------------------------------------------------------

API_CONTENTS_BRIEF = api_contents$(EXTRASUF1).brief
API_CONTENTS_FULL  = api_contents$(EXTRASUF1).full

ST_API_CONTENTS_BRIEF = $(F)$(API_CONTENTS_BRIEF)
ST_API_CONTENTS_FULL  = $(F)$(API_CONTENTS_FULL)
ST_API_CONTENTS_BRIEF_SUCCESS = $(ST_API_CONTENTS_BRIEF).success
ST_API_CONTENTS_FULL_SUCCESS  = $(ST_API_CONTENTS_FULL).success

.PRECIOUS: $(ST_API_CONTENTS_BRIEF) $(ST_API_CONTENTS_FULL)

ifdef TESTABLE_BUILD
  DEFAULT_API_CONTENTS = $(API_CONTENTS_BRIEF)
else
  DEFAULT_API_CONTENTS = $(API_CONTENTS_FULL)
endif

api_contents: $(DEFAULT_API_CONTENTS)

define make-api-contents-rule
  $(1): $(3)
  ifeq ($(or $(__CODE_CHECKER__),$(NO_CMP_API),$(__ASAN__),$(IDAHOME),$(IDACLASS),$(IDAFREE)),)
  $(3): $(ALL_ST_WRAP_PY_FINAL) tools/py_scanner.py
	$(QDUMPAPI)$(PYTHONCMD) tools/py_scanner.py $(4) --paths $(subst $(space),$(comma),$(ALL_ST_WRAP_PY_FINAL)) > $(2)
    ifeq ($(OUT_OF_TREE_BUILD),)
	$(Q)(($(DIFFCMD) -w $(1) $(2)) > /dev/null && $(TOUCHMD) $$@) || \
          (echo "API CONTENTS CHANGED! update $(1) or fix the API" && \
           echo "(New API: $(2)) ***" && \
           ($(DIFFCMD) -U 1 -w $(1) $(2); true))
    else
	$(Q)$(TOUCHMD) $$@
    endif
  else
  $(3): $(ALL_ST_WRAP_PY_FINAL) tools/py_scanner.py
	$(Q)$(TOUCHMD) $$@
  endif
endef

$(eval $(call make-api-contents-rule,$(API_CONTENTS_BRIEF),$(ST_API_CONTENTS_BRIEF),$(ST_API_CONTENTS_BRIEF_SUCCESS),))
$(eval $(call make-api-contents-rule,$(API_CONTENTS_FULL),$(ST_API_CONTENTS_FULL),$(ST_API_CONTENTS_FULL_SUCCESS),--dump-doc))

#-------------------------------------------------------------------------
ST_API_CHECK_SUCCESS := $(F)api_check.success
api_check: $(ST_API_CHECK_SUCCESS)
$(ST_API_CHECK_SUCCESS): $(ALL_ST_WRAP_CPP)
ifeq ($(or $(__CODE_CHECKER__),$(NO_CMP_API),$(__ASAN__),$(IDAHOME),$(IDACLASS),$(IDAFREE)),)
	$(QCHKAPI)$(PYTHONCMD) tools/chkapi.py -i $(subst $(space),$(comma),$(ALL_ST_WRAP_CPP)) -p $(subst $(space),$(comma),$(ALL_ST_WRAP_PY))
endif
	$(Q)$(TOUCHMD) $@

ifdef __EA64__
  DUMPDOC_IS_64:=True
else
  DUMPDOC_IS_64:=False
endif

#----------------------------------------------------------------------
DOCS_DEPS=tools/docs/hrdoc.py tools/docs/hrdoc.css
DOCS_MODULES=$(foreach mod,$(MODULES_NAMES),ida_$(mod))
SORTED_DOCS_MODULES=$(sort $(DOCS_MODULES))
DOC_OUT_HTML?=docs/hr-html
DOC_OUT_MD?=docs/hr-md
docs_html: $(DOCS_DEPS)
ifndef __NT__
	$(IDAT_CMD) $(BATCH_SWITCH) -S"tools/docs/hrdoc.py -o $(DOC_OUT_HTML) -m $(subst $(space),$(comma),$(SORTED_DOCS_MODULES)),idc,idautils -s idc,idautils -x ida_allins" -t > /dev/null
else
	$(R)ida -S"tools/docs/hrdoc.py -o $(DOC_OUT_HTML) -m $(subst $(space),$(comma),$(SORTED_DOCS_MODULES)),idc,idautils -s idc,idautils -x ida_allins" -t
endif

docs_md: $(DOCS_DEPS)
ifndef __NT__
	$(IDAT_CMD) $(BATCH_SWITCH) -S"tools/docs/hrdoc.py -o $(DOC_OUT_MD) -M -m $(subst $(space),$(comma),$(SORTED_DOCS_MODULES)),idc,idautils -s idc,idautils -x ida_allins" -t > /dev/null
else
	$(R)ida -Stools/docs/hrdoc.py -t
endif

docs: docs_html docs_md
	@echo "copy MARKDOWN files from docs/hr-md ../../../../www/www.hex-rays.com/public_html/hex-rays/cgi-bin/themer/static/products/ida/support/idapython_docs_md/"
	@echo "copy HTML files from docs/hr-html ../../../../www/www.hex-rays.com/public_html/hex-rays/cgi-bin/themer/static/products/ida/support/idapython_docs/"
	@echo ""
	@read -p "Do you want to copy files to P4 repo? [y/N] " ans && ans=$${ans:-N} ; \
	if [ $${ans} = y ] || [ $${ans} = Y ]; then \
	    echo "Starting to copy html & md files" ; \
	    ./copy_docs_to_p4.sh MARKDOWN docs/hr-md ../../../www/www.hex-rays.com/public_html/hex-rays/cgi-bin/themer/static/products/ida/support/idapython_docs_md/; \
	    ./copy_docs_to_p4.sh HTML docs/hr-html ../../../www/www.hex-rays.com/public_html/hex-rays/cgi-bin/themer/static/products/ida/support/idapython_docs/; \
	else \
	    echo "NO file copied" ; \
	fi

	@echo "\033[0;32mFiles prepared in P4, you need to manually submit them\033[0m"
# the demo version of ida does not have the -B command line option
ifeq ($(OUT_OF_TREE_BUILD),)
  ifndef IDAFREE
    BATCH_SWITCH=-B
  endif
endif

#----------------------------------------------------------------------
# Test that all functions that are present in ftable.cpp
# are present in idc.py (and therefore made available by
# the idapython).
test_idc: $(TEST_IDC)
$(TEST_IDC): $(F)idctest.log
$(F)idctest.log: $(RS)idc/idc.idc | $(MODULE) pyfiles
ifneq ($(wildcard ../../tests),)
	$(Q)$(RM) $(F)idctest.log
	$(Q)$(IDAT_CMD) $(BATCH_SWITCH) -S"test_idc.py $^" -t -L$(F)idctest.log >/dev/null || \
          (echo "ERROR: The IDAPython IDC interface is incomplete. IDA log:" && cat $(F)idctest.log && false)
endif

#----------------------------------------------------------------------
PUBTREE_DIR=$(F)public_tree
public_tree: all
ifeq ($(OUT_OF_TREE_BUILD),)
	-$(Q)$(MKDIRCMD) $(PUBTREE_DIR)/out_of_tree
	rsync -a --exclude=obj/ \
                --exclude=precompiled/ \
                --exclude=repl.py \
                --exclude=test_idc.py \
                --exclude=RELEASE.md \
                --exclude=docs/hr-html/ \
                --exclude=**/*~ \
                --exclude=examples-internal \
                . $(PUBTREE_DIR)
	(cd $(F) && zip -r ../../$(PUBTREE_DIR)/out_of_tree/parsed_notifications.zip parsed_notifications)
endif

#----------------------------------------------------------------------
IDAPYSWITCH_OBJS += $(F)idapyswitch$(O)
ifdef __NT__
  ifneq ($(OUT_OF_TREE_BUILD),)
     # SDK provides only MT libraries
     $(F)idapyswitch$(O): RUNTIME_LIBSW=/MT
  else
    ifndef NDEBUG
      $(F)idapyswitch$(O): CFLAGS := $(filter-out /U_DEBUG,$(CFLAGS))
    endif
  endif
endif
$(R)idapyswitch$(B): $(QT_BINDINGS_DEPS) $(call dumb_target, pro int128 compress, $(IDAPYSWITCH_OBJS))

#----------------------------------------------------------------------
TEST_PYWRAPS_OBJS += $(F)test_pywraps$(O)
TEST_PYWRAPS_DEPS := pywraps.cpp pywraps.hpp extapi.cpp extapi.hpp
$(F)test_pywraps$(O): $(PARSED_HEADERS_MARKER) $(TEST_PYWRAPS_DEPS)
ifdef __NT__
  ifneq ($(OUT_OF_TREE_BUILD),)
     # SDK provides only MT libraries
     $(F)test_pywraps$(O): RUNTIME_LIBSW=/MT
  endif
endif

ifdef __MAC__
  LDFLAGS_EXE += -Wl,-rpath,@executable_path
  LDFLAGS_EXE += -Wl,-weak-l$(PYTHON_VERNAME) -flat_namespace
endif
$(R)test_pywraps$(B): $(call dumb_target, json idc unicode pro compress, $(TEST_PYWRAPS_OBJS)) $(PYTHON_LDFLAGS) $(LDFLAGS_EXE)

$(F)$(TEST_PYWRAPS_RESULT_FNAME).marker: $(R)test_pywraps$(B)
	$(Q)$(TEST_PYWRAPS_ENV) $(TIMEWRAP-1) $(R)test_pywraps$(B) > $(F)$(TEST_PYWRAPS_RESULT_FNAME)
  ifeq ($(OUT_OF_TREE_BUILD),)
	$(Q)(($(DIFFCMD) -w $(TEST_PYWRAPS_RESULT_FNAME) $(F)$(TEST_PYWRAPS_RESULT_FNAME)) > /dev/null && $(TOUCHMD) $@) || \
          (echo "$(TEST_PYWRAPS_RESULT_FNAME) changed" && \
           ($(DIFFCMD) -U 1 -w $(TEST_PYWRAPS_RESULT_FNAME) $(F)$(TEST_PYWRAPS_RESULT_FNAME); true))
  else
	$(Q)$(TOUCHMD) $@
  endif

ifdef __CODE_CHECKER__
  test_pywraps: ;
else
test_pywraps: $(TEST_PYWRAPS)
endif

#----------------------------------------------------------------------
# the 'echo_modules' target must be called explicitly
# Note: used by ida/build/pkgbin.py
echo_modules:
	@echo $(MODULES_NAMES)

#----------------------------------------------------------------------
clean::
	rm -rf obj/

ifdef __CODE_CHECKER__
  examples_index: ;
  examples: ;
else
  EXAMPLES          := $(wildcard examples/*/*.py examples/*/*/*.py examples/*/*/*/*.py)
  GEN_EXAMPLES_TOOL := tools/gen_examples_index.py
  GEN_EXAMPLES_CFG  := tools/gen_examples_index.cfg

  EXAMPLES_INDEX_MD   := examples/index.md
  EXAMPLES_README     := examples/README.md

  ST_EXAMPLES_INDEX_MD = $(F)$(EXAMPLES_INDEX_MD)
  ST_EXAMPLES_INDEX_MD_SUCCESS = $(ST_EXAMPLES_INDEX_MD).success

  .PRECIOUS: $(ST_EXAMPLES_INDEX_MD)

  define make-examples-index-rules

    $(eval EXAMPLES_INDEX_CMD := $(PYTHONCMD) $(GEN_EXAMPLES_TOOL) write \
	-t $(4) \
	-e examples \
	-o $(2) \
	   )

    $(1): $(3)
    $(3): $(GEN_EXAMPLES_TOOL) $(GEN_EXAMPLES_CFG) $(4) $(EXAMPLES)
	-$(Q)$(MKDIRCMD) $(F)examples
	$(QGEN_EXAMPLES_INDEX)$(EXAMPLES_INDEX_CMD) \
	  || echo FAILED: $(EXAMPLES_INDEX_CMD)
	$(Q)$(DIFFCMD) -w $(1) $(2) > /dev/null && $(TOUCHMD) $$@ \
	  || (echo "EXAMPLES INDEX CHANGED! update $(1)" \
	   && echo "(New examples: $(2)) ***" \
	   && $(DIFFCMD) -U 1 -w $(1) $(2); true)
  endef

  $(eval $(call make-examples-index-rules,$(EXAMPLES_INDEX_MD),$(ST_EXAMPLES_INDEX_MD),$(ST_EXAMPLES_INDEX_MD_SUCCESS),tools/examples_index_template.md,md))

  examples_index: $(EXAMPLES_INDEX_MD)

  EXAMPLE_FILES := $(addprefix $(R)python/,$(EXAMPLES) $(EXAMPLES_INDEX_MD) $(EXAMPLES_README))
  examples: $(EXAMPLE_FILES)
  $(EXAMPLE_FILES): $(R)python/examples/%: examples/%
	$(Q)$(MKDIRCMD) $(dir $@)
	$(Q)$(CP) $< $@

endif

$(MODULE): LDFLAGS += $(PYTHON_LDFLAGS) $(LDFLAGS_MOD)

# MAKEDEP dependency list ------------------
$(F)idapyswitch$(O): $(I)auto.hpp $(I)bitrange.hpp $(I)bytes.hpp            \
                  $(I)config.hpp $(I)diskio.hpp $(I)entry.hpp $(I)err.h     \
                  $(I)exehdr.h $(I)fixup.hpp $(I)fpro.h $(I)funcs.hpp       \
                  $(I)ida.hpp $(I)idp.hpp $(I)ieee.h $(I)kernwin.hpp        \
                  $(I)lines.hpp $(I)llong.hpp $(I)loader.hpp $(I)md5.h      \
                  $(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp                 \
                  $(I)network.hpp $(I)offset.hpp $(I)pro.h $(I)prodir.h     \
                  $(I)range.hpp $(I)registry.hpp $(I)segment.hpp            \
                  $(I)segregs.hpp $(I)ua.hpp $(I)xref.hpp                   \
                  ../../ldr/ar/aixar.hpp ../../ldr/ar/ar.hpp                \
                  ../../ldr/ar/arcmn.cpp ../../ldr/mach-o/../ar/ar.hpp      \
                  ../../ldr/mach-o/../idaldr.h                              \
                  ../../ldr/mach-o/apple_common.h                           \
                  ../../ldr/mach-o/base.cpp ../../ldr/mach-o/common.cpp     \
                  ../../ldr/mach-o/common.h ../../ldr/mach-o/macho_node.h   \
                  ../../ldr/mach-o/strtab_reader_t.h                        \
                  ../../ldr/mach-o/uncompress.cpp ../../ldr/pe/../idaldr.h  \
                  ../../ldr/pe/common.cpp ../../ldr/pe/common.h             \
                  ../../ldr/pe/pe.h ../../pro/registry.cpp idapyswitch.cpp  \
                  idapyswitch_linux.cpp idapyswitch_mac.cpp                 \
                  idapyswitch_win.cpp
$(F)idapython$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp            \
                  $(I)diskio.hpp $(I)err.h $(I)expr.hpp $(I)fpro.h          \
                  $(I)funcs.hpp $(I)gdl.hpp $(I)graph.hpp $(I)hexrays.hpp   \
                  $(I)ida.hpp $(I)ida_highlighter.hpp $(I)idd.hpp           \
                  $(I)idp.hpp $(I)ieee.h $(I)kernwin.hpp $(I)lex.hpp        \
                  $(I)lines.hpp $(I)llong.hpp $(I)loader.hpp                \
                  $(I)make_script_ns.hpp $(I)nalt.hpp $(I)name.hpp          \
                  $(I)netnode.hpp $(I)parsejson.hpp $(I)pro.h               \
                  $(I)qalloc_shim.hpp $(I)qallocator.hpp                    \
                  $(I)qida_alloc_shim.hpp $(I)qiterator.hpp $(I)qmap.hpp    \
                  $(I)qpair.hpp $(I)qset.hpp $(I)qtree.hpp $(I)range.hpp    \
                  $(I)registry.hpp $(I)segment.hpp $(I)typeinf.hpp          \
                  $(I)ua.hpp $(I)xref.hpp extapi.cpp extapi.hpp             \
                  idapython.cpp py-include/Python_dynload.h                 \
                  py-include/Python_types.h pywraps.cpp pywraps.hpp
$(F)test_pywraps$(O): $(I)bitrange.hpp $(I)bytes.hpp $(I)config.hpp         \
                  $(I)err.h $(I)expr.hpp $(I)fpro.h $(I)funcs.hpp           \
                  $(I)gdl.hpp $(I)graph.hpp $(I)hexrays.hpp $(I)ida.hpp     \
                  $(I)idd.hpp $(I)idp.hpp $(I)ieee.h $(I)kernwin.hpp        \
                  $(I)lex.hpp $(I)lines.hpp $(I)llong.hpp $(I)loader.hpp    \
                  $(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp                 \
                  $(I)parsejson.hpp $(I)pro.h $(I)qalloc_shim.hpp           \
                  $(I)qallocator.hpp $(I)qida_alloc_shim.hpp                \
                  $(I)qiterator.hpp $(I)qmap.hpp $(I)qpair.hpp              \
                  $(I)qset.hpp $(I)qtree.hpp $(I)range.hpp                  \
                  $(I)registry.hpp $(I)segment.hpp $(I)typeinf.hpp          \
                  $(I)ua.hpp $(I)xref.hpp extapi.cpp extapi.hpp             \
                  py-include/Python_dynload.h py-include/Python_types.h     \
                  pywraps.cpp pywraps.hpp test_pywraps.cpp
