# # 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()) # ifdef __X64__ ifdef __LINUX__ LINUX64=1 endif endif ifdef LINUX64 all: @echo "Not building Python for Linux x64" else PROC=python STRIPOBJS=idaapi.cpp API_CONTENTS=api_contents.txt IDA_INCLUDE=../../include STAGING=./staging ST_SWIG=$(STAGING)/swig ST_SDK=$(STAGING)/idasdk ST_PYW=$(STAGING)/pywraps ST_PARSED_HEADERS=$(STAGING)/parsed_notifications/xml # ATM, 'dist' in in staging/, but that will change since # staging directories will be $(SYSDIR)-dependent DIST=$(STAGING)/dist ifdef __NT__ SYSNAME=win else .NOTPARALLEL: endif ifdef __LINUX__ SYSNAME=linux DEFS=-D__LINUX__ PYTHON32_LIBRARY_PATH?=/usr/lib PYTHON32_LIBRARY_INCLUDE=-L$(PYTHON32_LIBRARY_PATH) endif ifdef __BSD__ SYSNAME=bsd DEFS=-D__BSD__ endif ifdef __MAC__ SYSNAME=mac DEFS=-D__MAC__ endif O1=idaapi ADDITIONAL_GOALS=pyfiles config $(TEST_IDC) __USE_RTTI__=1 include ../plugin.mak # used python version PYTHON_VERSION_MAJOR?=2 PYTHON_VERSION_MINOR?=7 # IDAPython version IDAPYTHON_VERSION_MAJOR=6 IDAPYTHON_VERSION_MINOR=9 IDAPYTHON_VERSION_PATCH=0 PACKAGE_NAME=idapython-$(IDAPYTHON_VERSION_MAJOR).$(IDAPYTHON_VERSION_MINOR).$(IDAPYTHON_VERSION_PATCH)-python$(PYTHON_VERSION_MAJOR).$(PYTHON_VERSION_MINOR)-$(SYSNAME).zip # HIJACK the $(I) variable to point to our staging SDK I=$(ST_SDK)/ ifdef __CODE_CHECKER__ ADDITIONAL_GOALS:=$(filter-out pyfiles config $(TEST_IDC),$(ADDITIONAL_GOALS)) OBJS:=$(filter-out $(OBJ1),$(OBJS)) endif # output directory for python scripts ifeq ($(OUT_OF_TREE_BUILD),) SCRIPTDIR=$(R)python DEPLOY_INIT_PY=$(SCRIPTDIR)/init.py DEPLOY_IDC_PY=$(SCRIPTDIR)/idc.py DEPLOY_IDAUTILS_PY=$(SCRIPTDIR)/idautils.py TEST_IDC=test_idc else SCRIPTDIR=python endif ifdef __NT__ # os and compiler specific flags ifneq ($(UCRT_INCLUDE),) I_UCRT_INCLUDE=/I$(UCRT_INCLUDE) endif PYTHON_ROOT?=c: PYTHON_DIR=$(PYTHON_ROOT)/python$(PYTHON_VERSION_MAJOR)$(PYTHON_VERSION_MINOR) IDAPYTHON_CFLAGS=-w -Z7 /EHsc /bigobj /I$(MSVCDIR)Include $(I_UCRT_INCLUDE) ifdef __X64__ PYTHONLIB=vcx64_python$(PYTHON_VERSION_MAJOR)$(PYTHON_VERSION_MINOR).lib -nodefaultlib:python$(PYTHON_VERSION_MAJOR)$(PYTHON_VERSION_MINOR).lib else PYTHONLIB=$(PYTHON_DIR)/libs/python$(PYTHON_VERSION_MAJOR)$(PYTHON_VERSION_MINOR).lib endif _SWIGFLAGS=-D__NT__ -DWIN32 -D_USRDLL -I$(PYTHON_DIR)/include SWIGINCLUDES?= # nothing OSFLAGS=$(_SWIGFLAGS) -UNO_OBSOLETE_FUNCS else # unix/mac ifdef __LINUX__ # use the precompiled 2.7 ifeq ($(OUT_OF_TREE_BUILD),) PYDIR=$(shell pwd)/precompiled PYLIBDIR=$(PYDIR) # copy these files to IDA's directory PYLIBFILES=$(shell find precompiled/lib -type f) PRECOMPILED_COPY=$(R)$(PYTHONLIBNAME) $(patsubst precompiled/%,$(SCRIPTDIR)/%,$(PYLIBFILES)) else PYDIR=$(IDAPYTHON_PYTHONHOME) PYLIBDIR=$(PYDIR)/lib endif PYTHON32_INCLUDE:=-I$(PYDIR)/include/python$(PYTHON_VERSION_MAJOR).$(PYTHON_VERSION_MINOR) PYTHONLIBNAME=libpython$(PYTHON_VERSION_MAJOR).$(PYTHON_VERSION_MINOR).so.1.0 PYTHONLIB=$(PYLIBDIR)/$(PYTHONLIBNAME) -ldl else PYVER=$(PYTHON_VERSION_MAJOR).$(PYTHON_VERSION_MINOR) PYTHON32_INCLUDE:=$(shell python$(PYVER)-config --includes) PYTHONLIB:=$(shell python$(PYVER)-config --ldflags) MACDEFINES=-DMACSDKVER=$(MACSDKVER) endif IDAPYTHON_CFLAGS=-w -g OSFLAGS=$(SYS) -g $(PYTHON32_INCLUDE) $(ARCH_CFLAGS) $(PIC) -UNO_OBSOLETE_FUNCS # gcc flags _SWIGFLAGS=$(DEFS) SWIGINCLUDES?=-I$(SWIGDIR)share/swig/$(SWIG_VERSION)/python -I$(SWIGDIR)share/swig/$(SWIG_VERSION) endif SWIGFLAGS=$(_SWIGFLAGS) $(SWIGINCLUDES) ADDITIONAL_LIBS=$(PYTHONLIB) .PHONY: pyfiles docs $(TEST_IDC) staging_dirs clean check_python dist dist_dirs dist_cfg package config: $(C)python.cfg clean: rm -rf $(STAGING)/ obj/ pyfiles: $(SCRIPTDIR)/idautils.py \ $(SCRIPTDIR)/idc.py \ $(SCRIPTDIR)/init.py \ $(SCRIPTDIR)/idaapi.py GENHOOKS=tools/genhooks/ $(DEPLOY_INIT_PY): python/init.py $(CP) $? $@ $(DEPLOY_IDC_PY): python/idc.py $(CP) $? $@ $(DEPLOY_IDAUTILS_PY): python/idautils.py $(CP) $? $@ $(SCRIPTDIR)/idaapi.py: $(F)idaapi.py $(PYTHON) inject_pydoc.py swig/ $? $@ $(SCRIPTDIR)/lib/%: precompiled/lib/% mkdir -p $(@D) cp $< $@ @chmod +w $@ $(C)python.cfg: python.cfg $(CP) $? $@ $(R)$(PYTHONLIBNAME): $(PYDIR)/$(PYTHONLIBNAME) $(CP) $? $@ # ------------------------------------------------------------------------- # Hooks generation $(ST_PARSED_HEADERS)/structprocessor__t.xml: $(I)idp.hpp $(GENHOOKS)doxy_gen_notifs.cfg | $(ST_SDK_TARGETS) @$(DOXYGEN_BIN) $(GENHOOKS)doxy_gen_notifs.cfg $(ST_PARSED_HEADERS)/dbg_8hpp.xml: $(I)dbg.hpp $(GENHOOKS)doxy_gen_notifs.cfg | $(ST_SDK_TARGETS) @$(DOXYGEN_BIN) $(GENHOOKS)doxy_gen_notifs.cfg $(ST_PARSED_HEADERS)/kernwin_8hpp.xml: $(I)kernwin.hpp $(GENHOOKS)doxy_gen_notifs.cfg | $(ST_SDK_TARGETS) @$(DOXYGEN_BIN) $(GENHOOKS)doxy_gen_notifs.cfg $(ST_PARSED_HEADERS)/namespaceidb__event.xml: $(I)kernwin.hpp $(GENHOOKS)doxy_gen_notifs.cfg | $(ST_SDK_TARGETS) @$(DOXYGEN_BIN) $(GENHOOKS)doxy_gen_notifs.cfg # staging_dirs: -@if [ ! -d "$(ST_SDK)" ] ; then mkdir -p 2>/dev/null $(ST_SDK) ; fi -@if [ ! -d "$(ST_SWIG)" ] ; then mkdir -p 2>/dev/null $(ST_SWIG) ; fi -@if [ ! -d "$(ST_PYW)" ] ; then mkdir -p 2>/dev/null $(ST_PYW) ; fi -@if [ ! -d "$(ST_PARSED_HEADERS)" ] ; then mkdir -p 2>/dev/null $(ST_PARSED_HEADERS) ; fi # ------------------------------------------------------------------------- # Rules for preparing 'staging/idasdk/*.h[pp]' # SDK_SOURCES=$(wildcard $(IDA_INCLUDE)/*.h) $(wildcard $(IDA_INCLUDE)/*.hpp) ST_SDK_TARGETS=$(SDK_SOURCES:$(IDA_INCLUDE)/%=$(ST_SDK)/%) $(ST_SDK)/%.h: $(IDA_INCLUDE)/%.h | staging_dirs @$(CP) $^ $@ && chmod +rw $@ $(ST_SDK)/%.hpp: $(IDA_INCLUDE)/%.hpp | staging_dirs @$(CP) $^ $@ && chmod +rw $@ # ------------------------------------------------------------------------- # Rules for preparing 'staging/pywraps/*' # PYW_SOURCES=$(wildcard pywraps/*.hpp) $(wildcard pywraps/*.py) ST_PYW_TARGETS=$(PYW_SOURCES:pywraps/%=$(ST_PYW)/%) $(ST_PYW)/%.hpp: pywraps/%.hpp | staging_dirs @$(CP) $^ $@ && chmod +rw $@ $(ST_PYW)/%.py: pywraps/%.py | staging_dirs @$(CP) $^ $@ && chmod +rw $@ ifneq ($(OUT_OF_TREE_BUILD),) # envvar HAS_HEXRAYS must have been set by build.py if needed else # force hexrays bindings HAS_HEXRAYS=1 # 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 \ $(ST_PARSED_HEADERS)/structprocessor__t.xml | staging_dirs $(SDK_SOURCES) @$(PYTHON) $(GENHOOKS)genhooks.py -i $< -o $@ \ -x $(ST_PARSED_HEADERS)/structprocessor__t.xml -e idp_notify \ -r int -n 0 -m hookgenIDP -q "processor_t::" \ -R $(GENHOOKS)recipe_idphooks.py $(ST_PYW)/py_idbhooks.hpp: pywraps/py_idbhooks.hpp \ $(I)idp.hpp \ $(GENHOOKS)genhooks.py \ $(GENHOOKS)recipe_idbhooks.py \ $(ST_PARSED_HEADERS)/namespaceidb__event.xml | staging_dirs $(SDK_SOURCES) @$(PYTHON) $(GENHOOKS)genhooks.py -i $< -o $@ \ -x $(ST_PARSED_HEADERS)/namespaceidb__event.xml -e event_code_t \ -r int -n 0 -m hookgenIDB -q "idb_event::" \ -R $(GENHOOKS)recipe_idbhooks.py $(ST_PYW)/py_dbg.hpp: pywraps/py_dbg.hpp \ $(I)dbg.hpp \ $(GENHOOKS)genhooks.py \ $(GENHOOKS)recipe_dbghooks.py \ $(ST_PARSED_HEADERS)/dbg_8hpp.xml | staging_dirs $(SDK_SOURCES) @$(PYTHON) $(GENHOOKS)genhooks.py -i $< -o $@ \ -x $(ST_PARSED_HEADERS)/dbg_8hpp.xml -e dbg_notification_t \ -r void -n 0 -m hookgenDBG \ -R $(GENHOOKS)recipe_dbghooks.py $(ST_PYW)/py_kernwin.hpp: pywraps/py_kernwin.hpp \ $(I)kernwin.hpp \ $(GENHOOKS)genhooks.py \ $(GENHOOKS)recipe_uihooks.py \ $(ST_PARSED_HEADERS)/kernwin_8hpp.xml | staging_dirs $(SDK_SOURCES) @$(PYTHON) $(GENHOOKS)genhooks.py -i $< -o $@ \ -x $(ST_PARSED_HEADERS)/kernwin_8hpp.xml -e ui_notification_t \ -r void -n 0 -m hookgenUI \ -R $(GENHOOKS)recipe_uihooks.py \ -d "ui_dbg_,ui_obsolete" -D "ui:" -s "ui_" endif # OUT_OF_TREE_BUILD ifneq ($(HAS_HEXRAYS),) WITH_HEXRAYS=-DWITH_HEXRAYS WITH_HEXRAYS_CHKAPI=--with-hexrays endif # ------------------------------------------------------------------------- # Rules for preparing 'staging/swig/*.i' # # Note: At the time we define those rules, we cannot go and look in # staging/pywraps/ just yet, as those will/might not have been deployed at # that point (i.e., when building from scratch.) # Thus, we must find the dependencies in pywraps/, and then re-route them to # staging/pywraps/ SWIG_SOURCES=$(wildcard swig/*.i) ST_SWIG_TARGETS=$(SWIG_SOURCES:swig/%.i=$(ST_SWIG)/%.i) find-pywraps-deps = $(wildcard pywraps/py_$(subst .i,,$(notdir $1)).*) define make-deploy-swig-file-rule $1: $(subst staging/,,$1) $(addprefix staging/,$(call find-pywraps-deps,$1)) | $(ST_PYW_TARGETS) @$(PYTHON) tools/deploy.py --pywraps $(ST_PYW) --template $$(subst staging/,,$$@) --output $$@ --module $$(subst .i,,$$(notdir $$@)) endef $(foreach tgt,$(ST_SWIG_TARGETS),$(eval $(call make-deploy-swig-file-rule,$(tgt)))) # Require a strict SWiG version (other versions might generate different code.) SWIG_VERSION_ACTUAL=$(shell $(SWIG) -version | awk "/SWIG Version [0-9.]+/ { if (match(\$$0, /([0-9.]+)/)) { print substr(\$$0, RSTART, RLENGTH); } }") # idaapi.py is created together with idaapi_include.cpp $(F)idaapi.py: | $(F)idaapi_include.cpp # idaapi_include.h is created together with idaapi_include.cpp patched_idaapi_include_h: patch_directors_cc.py | $(F)idaapi_include.h ifdef __NT__ @$(PYTHON) patch_directors_cc.py -f $(F)idaapi_include.h endif $(F)idaapi_include.h: | $(F)idaapi_include.cpp $(F)idaapi_include.cpp: $(PRECOMPILED_COPY) \ $(ST_SDK_TARGETS) \ $(ST_PYW_TARGETS) \ $(ST_SWIG_TARGETS) \ pywraps.hpp | objdir @ ! (grep __EA64__ swig/* | grep -v typeconv.i) || \ (echo "ERROR: __EA64__ macro is foribidden in swig subdirectory (to ensure the same api for both ida versions)" && false) ifneq ($(SWIG_VERSION_ACTUAL),$(SWIG_VERSION)) $(error Expected SWIG version "$(SWIG_VERSION)", but got "$(SWIG_VERSION_ACTUAL)" (from $(SWIG))) endif $(SWIG) -modern $(WITH_HEXRAYS) -python -threads -c++ -shadow \ $(MACDEFINES) -D__GNUC__ $(SWIGFLAGS) $(SWITCH64) -I$(ST_SWIG) \ -outdir $(F) -o $@ -I$(ST_SDK) $(ST_SWIG)/idaapi.i || \ { $(RM) $(F)idaapi_include.*; exit 1 ;} $(PYTHON) chkapi.py $(WITH_HEXRAYS_CHKAPI) -f $@ -r $(API_CONTENTS).new @(diff $(API_CONTENTS) $(API_CONTENTS).new) > /dev/null || \ (rm -f $@ $(F)idaapi_include.h && \ echo "API CONTENTS CHANGED! update api_contents.txt or fix the API" && \ echo "(New API: $(API_CONTENTS).new) ***" && \ (diff -U 1 -w $(API_CONTENTS) $(API_CONTENTS).new && false)) # Python version CFLAGS= $(CCOPT) $(OSFLAGS) -D__EXPR_SRC -I. -I$(ST_SWIG) -I$(ST_SDK) -I$(F) \ -DVER_MAJOR="1" -DVER_MINOR="7" -DVER_PATCH="0" -D__IDP__ \ -DUSE_STANDARD_FILE_FUNCTIONS $(IDAPYTHON_CFLAGS) \ $(SWITCH64) $(ARCH_CFLAGS) $(WITH_HEXRAYS) # the html files are produced in docs\hr-html directory docs: hrdoc.py hrdoc.cfg hrdoc.css idaq -Shrdoc.py -t # Test that all functions that are present in ftable.cpp # are present in idc.py (and therefore made available by # the idapython). ifdef __NT__ IDA_CMD=TVHEADLESS=1 $(R)idaw$(X64SUFF)$(SUFF64) else IDA_CMD=TVHEADLESS=1 $(R)idal$(X64SUFF)$(SUFF64) endif # the demo version of ida does not have the -B command line option ifeq ($(OUT_OF_TREE_BUILD),) ISDEMO=$(shell grep "define DEMO$$" $(IDA_INCLUDE)/commerc.hpp) ifeq ($(ISDEMO),) BATCH_SWITCH=-B endif endif $(TEST_IDC): $(F)idctest.log $(F)idctest.log: $(RS)idc/idc.idc | $(BINARY) pyfiles $(PRECOMPILED_COPY) ifneq ($(wildcard ../../tests),) @$(RM) $(F)idctest.log @$(IDA_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 dist_dirs: -@if [ ! -d "$(DIST)/plugins" ] ; then mkdir -p 2>/dev/null $(DIST)/plugins ; fi DIST_OTHER_SOURCES=python.cfg README.md AUTHORS.txt CHANGES.txt COPYING.txt STATUS.txt DIST_OTHER_TARGETS=$(DIST_OTHER_SOURCES:%=$(DIST)/%) $(DIST)/%: % | dist_dirs @$(CP) $^ $@ && chmod +rw $@ DIST_BINARY=$(DIST)/plugins/$(PROC)$(PLUGIN) $(DIST_BINARY): $(BINARY) | dist_dirs $(CP) $? $@ ifdef __LINUX__ strip $@ else ifdef __MAC__ strip -x $@ endif endif dist: $(BINARY) pyfiles $(DIST_OTHER_TARGETS) $(DIST_BINARY) dist_cfg | dist_dirs # rsync -a docs $(DIST)/ rsync -a examples $(DIST)/ rsync -a python $(DIST)/ package: (cd $(DIST) && zip -r ../../$(PACKAGE_NAME) *) # MAKEDEP dependency list ------------------ $(F)idaapi$(O) : $(F)idaapi_include.cpp $(I)allins.hpp $(I)area.hpp \ $(I)auto.hpp $(I)bitrange.hpp $(I)bytes.hpp $(I)dbg.hpp \ $(I)diskio.hpp $(I)entry.hpp $(I)enum.hpp $(I)err.h \ $(I)expr.hpp $(I)fixup.hpp $(I)fpro.h $(I)frame.hpp \ $(I)funcs.hpp $(I)gdl.hpp $(I)graph.hpp $(I)ida.hpp \ $(I)idd.hpp $(I)idp.hpp $(I)ieee.h $(I)ints.hpp \ $(I)kernwin.hpp $(I)lines.hpp $(I)llong.hpp \ $(I)loader.hpp $(I)moves.hpp $(I)nalt.hpp $(I)name.hpp \ $(I)netnode.hpp $(I)offset.hpp $(I)pro.h $(I)queue.hpp \ $(I)search.hpp $(I)segment.hpp $(I)sistack.hpp \ $(I)srarea.hpp $(I)strlist.hpp $(I)struct.hpp \ $(I)typeinf.hpp $(I)ua.hpp $(I)xref.hpp \ idaapi.cpp pywraps.hpp | patched_idaapi_include_h $(ST_SDK_TARGETS) $(F)python$(O) : $(I)area.hpp $(I)bitrange.hpp $(I)bytes.hpp \ $(I)diskio.hpp $(I)expr.hpp $(I)fpro.h $(I)funcs.hpp \ $(I)ida.hpp $(I)idp.hpp $(I)kernwin.hpp $(I)lines.hpp \ $(I)llong.hpp $(I)loader.hpp $(I)nalt.hpp \ $(I)netnode.hpp $(I)pro.h $(I)segment.hpp $(I)ua.hpp \ $(I)xref.hpp python.cpp pywraps.hpp | $(ST_SDK_TARGETS) endif