From 13f4254cccd2d182633d1e7616d72d5db77c76ce Mon Sep 17 00:00:00 2001 From: Arnaud Diederen Date: Tue, 1 Sep 2015 11:56:33 +0200 Subject: [PATCH] Revert "build.py: use argparse to provide 1) better/more options, 2) help about those options" This reverts commit 02e130299d38b02d4bdfc895f0017eeb61b4ad92. --- build.py | 599 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 598 insertions(+), 1 deletion(-) mode change 120000 => 100644 build.py diff --git a/build.py b/build.py deleted file mode 120000 index bcf214d..0000000 --- a/build.py +++ /dev/null @@ -1 +0,0 @@ -/home/aundro/src/idapython-github/src/build.py \ No newline at end of file diff --git a/build.py b/build.py new file mode 100644 index 0000000..46ac3ec --- /dev/null +++ b/build.py @@ -0,0 +1,598 @@ +#!/usr/bin/env python +#--------------------------------------------------------------------- +# IDAPython - Python plugin for Interactive Disassembler +# +# (c) The IDAPython Team +# +# All rights reserved. +# +# For detailed copyright information see the file COPYING in +# the root of the distribution archive. +#--------------------------------------------------------------------- +# build.py - Custom build script +#--------------------------------------------------------------------- +import os +import platform +import shutil +import sys +import types +import zipfile +import glob +from distutils import sysconfig + +# Start of user configurable options +VERBOSE = True + +IDA_MAJOR_VERSION = 6 +IDA_MINOR_VERSION = 8 + +if 'IDA' in os.environ: + IDA_SDK = os.environ['IDA'] +else: + IDA_SDK = os.path.join("..", "..", "include") + if not os.path.exists(IDA_SDK): + IDA_SDK = os.path.join("..", "swigsdk-versions", ("%d.%d" % (IDA_MAJOR_VERSION, IDA_MINOR_VERSION))) + assert os.path.exists(IDA_SDK), "Could not find IDA SDK include path" + + +# End of user configurable options + +# IDAPython version +VERSION_MAJOR = 1 +VERSION_MINOR = 7 +VERSION_PATCH = 0 + +# Determine Python version +PYTHON_MAJOR_VERSION = int(platform.python_version()[0]) +PYTHON_MINOR_VERSION = int(platform.python_version()[2]) + +# Find Python headers +PYTHON_INCLUDE_DIRECTORY = sysconfig.get_config_var('INCLUDEPY') + +S_EA64 = 'ea64' +S_WITH_HEXRAYS = 'with-hexrays' +S_NO_OPT = 'no-opt' + +# Swig command-line parameters +SWIG_OPTIONS = '-modern -python -threads -c++ -w451 -shadow -D__GNUC__' + +# Common macros for all compilations +COMMON_MACROS = [ + ("VER_MAJOR", "%d" % VERSION_MAJOR), + ("VER_MINOR", "%d" % VERSION_MINOR), + ("VER_PATCH", "%d" % VERSION_PATCH), + "__IDP__", + ("MAXSTR", "1024"), + "USE_DANGEROUS_FUNCTIONS", + "USE_STANDARD_FILE_FUNCTIONS" ] + +# Common includes for all compilations +COMMON_INCLUDES = [ ".", "swig" ] + +# ----------------------------------------------------------------------- +# List files for the binary distribution +BINDIST_MANIFEST = [ + "README.txt", + "COPYING.txt", + "CHANGES.txt", + "AUTHORS.txt", + "STATUS.txt", + "python.cfg", + "docs/notes.txt", + "examples/chooser.py", + "examples/colours.py", + "examples/ex_idphook_asm.py", + "examples/ex_uirequests.py", + "examples/debughook.py", + "examples/ex_cli.py", + "examples/ex1.idc", + "examples/ex_custdata.py", + "examples/ex1_idaapi.py", + "examples/ex1_idautils.py", + "examples/hotkey.py", + "examples/structure.py", + "examples/ex_gdl_qflow_chart.py", + "examples/ex_strings.py", + "examples/ex_actions.py", + "examples/ex_func_chooser.py", + "examples/ex_choose2.py", + "examples/ex_debug_names.py", + "examples/ex_graph.py", + "examples/ex_hotkey.py", + "examples/ex_patch.py", + "examples/ex_expr.py", + "examples/ex_timer.py", + "examples/ex_dbg.py", + "examples/ex_custview.py", + "examples/ex_prefix_plugin.py", + "examples/ex_pyside.py", + "examples/ex_pyqt.py", + "examples/ex_askusingform.py", + "examples/ex_uihook.py", + "examples/ex_idphook_asm.py", + "examples/ex_imports.py" +] + +# ----------------------------------------------------------------------- +# List files for the source distribution (appended to binary list) +SRCDIST_MANIFEST = [ + "BUILDING.txt", + "python.cpp", + "basetsd.h", + "build.py", + "python.cfg", + "swig/allins.i", + "swig/area.i", + "swig/auto.i", + "swig/bytes.i", + "swig/dbg.i", + "swig/diskio.i", + "swig/entry.i", + "swig/enum.i", + "swig/expr.i", + "swig/fixup.i", + "swig/frame.i", + "swig/funcs.i", + "swig/gdl.i", + "swig/ida.i", + "swig/idaapi.i", + "swig/idd.i", + "swig/idp.i", + "swig/ints.i", + "swig/kernwin.i", + "swig/lines.i", + "swig/loader.i", + "swig/moves.i", + "swig/nalt.i", + "swig/name.i", + "swig/netnode.i", + "swig/offset.i", + "swig/pro.i", + "swig/queue.i", + "swig/search.i", + "swig/segment.i", + "swig/srarea.i", + "swig/strlist.i", + "swig/struct.i", + "swig/typeconv.i", + "swig/typeinf.i", + "swig/ua.i", + "swig/xref.i", + "swig/graph.i", + "swig/fpro.i", + "swig/hexrays.i", +] + +# ----------------------------------------------------------------------- +def parse_options(args): + """Parse arguments and returned a dictionary of options""" + + no_opt = '--' + S_NO_OPT in sys.argv + ea64 = '--' + S_EA64 in sys.argv + with_hexrays = '--' + S_WITH_HEXRAYS in sys.argv + + return { + S_EA64: ea64, + S_WITH_HEXRAYS: with_hexrays, + S_NO_OPT: no_opt + } + +# ----------------------------------------------------------------------- +class BuilderBase: + """ Base class for builders """ + def __init__(self): + pass + + + def compile(self, source, objectname=None, includes=[], macros=[]): + """ + Compile the source file + """ + allmacros = [] + allmacros.extend(COMMON_MACROS) + allmacros.extend(self.basemacros) + allmacros.extend(macros) + macrostring = self._build_command_string(allmacros, self.macro_delimiter) + + allincludes = [] + allincludes.extend(COMMON_INCLUDES) + allincludes.extend(includes) + includestring = self._build_command_string(allincludes, self.include_delimiter) + + if not objectname: + objectname = source + self.object_extension + + cmdstring = "%s %s %s %s %s %s" % (self.compiler, + self.compiler_parameters, + self.compiler_out_string(objectname), + self.compiler_in_string(source + self.source_extension), + includestring, + macrostring) + + if VERBOSE: + print cmdstring + return os.system(cmdstring) + + + def link(self, objects, outfile, libpaths=[], libraries=[], extra_parameters=None): + """ Link the binary from objects and libraries """ + cmdstring = "%s %s %s" % (self.linker, + self.linker_parameters, + self.linker_out_string(outfile)) + + for objectfile in objects: + cmdstring = "%s %s" % (cmdstring, objectfile + self.object_extension) + for libpath in libpaths: + cmdstring = "%s %s%s" % (cmdstring, self.libpath_delimiter, libpath) + for library in libraries: + cmdstring = "%s %s" % (cmdstring, library) + if extra_parameters: + cmdstring = "%s %s" % (cmdstring, extra_parameters) + + if VERBOSE: print cmdstring + return os.system(cmdstring) + + def _build_command_string(self, macros, argument_delimiter): + macrostring = "" + + for item in macros: + if type(item) == types.TupleType: + macrostring += '%s%s="%s" ' % (argument_delimiter, item[0], item[1]) + else: + macrostring += '%s%s ' % (argument_delimiter, item) + + return macrostring + + +# ----------------------------------------------------------------------- +class GCCBuilder(BuilderBase): + """ Generic GCC compiler class """ + def __init__(self): + self.include_delimiter = "-I" + self.macro_delimiter = "-D" + self.libpath_delimiter = "-L" + self.compiler_parameters = "-fpermissive -Wno-write-strings" + self.linker_parameters = "-shared" + self.basemacros = [ ] + self.compiler = "g++ -m32" + self.linker = "g++ -m32" + self.source_extension = ".cpp" + self.object_extension = ".o" + + def compiler_in_string(self, filename): + return "-c %s" % filename + + def compiler_out_string(self, filename): + return "-o %s" % filename + + def linker_out_string(self, filename): + return "-o %s" % filename + + +# ----------------------------------------------------------------------- +class MSVCBuilder(BuilderBase): + """ Generic Visual C compiler class """ + def __init__(self): + self.include_delimiter = "/I" + self.macro_delimiter = "/D" + self.libpath_delimiter = "/LIBPATH:" + self.compiler_parameters = "/nologo /EHsc" + self.linker_parameters = "/nologo /dll /export:PLUGIN" + self.basemacros = [ "WIN32", + "_USRDLL", + "__NT__" ] + self.compiler = "cl" + self.linker = "link" + self.source_extension = ".cpp" + self.object_extension = ".obj" + + def compiler_in_string(self, filename): + return "/c %s" % filename + + def compiler_out_string(self, filename): + return "/Fo%s" % filename + + def linker_out_string(self, filename): + return "/out:%s" % filename + +# ----------------------------------------------------------------------- +def build_distribution(manifest, distrootdir, ea64, nukeold): + """ Create a distibution to a directory and a ZIP file """ + # (Re)create the output directory + if os.path.exists(distrootdir): + if nukeold: + shutil.rmtree(distrootdir) + os.makedirs(distrootdir) + else: + os.makedirs(distrootdir) + + # Also make a ZIP archive of the build + zippath = distrootdir + ".zip" + zip = zipfile.ZipFile(zippath, nukeold and "w" or "a", zipfile.ZIP_DEFLATED) + + # Copy files, one by one + for f in manifest: + if type(f) == types.TupleType: + srcfilepath = f[0] + srcfilename = os.path.basename(srcfilepath) + dstdir = os.path.join(distrootdir, f[1]) + dstfilepath = os.path.join(dstdir, srcfilename) + else: + srcfilepath = f + srcfilename = os.path.basename(f) + srcdir = os.path.dirname(f) + if srcdir == "": + dstdir = distrootdir + else: + dstdir = os.path.join(distrootdir, srcdir) + + if not os.path.exists(dstdir): + os.makedirs(dstdir) + + dstfilepath = os.path.join(dstdir, srcfilename) + shutil.copyfile(srcfilepath, dstfilepath) + zip.write(dstfilepath) + + zip.close() + + +# ----------------------------------------------------------------------- +def build_plugin( + platform, + idasdkdir, + plugin_name, + options): + """ Build the plugin from the SWIG wrapper and plugin main source """ + + global SWIG_OPTIONS + + # Get the arguments + ea64 = options[S_EA64] + with_hexrays = options[S_WITH_HEXRAYS] + + # Path to the IDA SDK headers + ida_include_directory = os.path.join(idasdkdir, "include") + + builder = None + # Platform-specific settings for the Linux build + if platform == "linux": + builder = GCCBuilder() + platform_macros = [ "__LINUX__" ] + python_libpath = os.path.join(sysconfig.EXEC_PREFIX, "lib") + python_library = "-Bdynamic -lpython%d.%d" % (PYTHON_MAJOR_VERSION, PYTHON_MINOR_VERSION) + ida_libpath = os.path.join(idasdkdir, "lib", ea64 and "x86_linux_gcc_64" or "x86_linux_gcc_32") + ida_lib = "" + extra_link_parameters = " -s" + builder.compiler_parameters += " -O2" + # Platform-specific settings for the Windows build + elif platform == "win32": + builder = MSVCBuilder() + platform_macros = [ "__NT__" ] + python_libpath = os.path.join(sysconfig.EXEC_PREFIX, "libs") + python_library = "python%d%d.lib" % (PYTHON_MAJOR_VERSION, PYTHON_MINOR_VERSION) + ida_libpath = os.path.join(idasdkdir, "lib", ea64 and "x86_win_vc_64" or "x86_win_vc_32") + ida_lib = "ida.lib" + SWIG_OPTIONS += " -D__NT__ " + extra_link_parameters = "" + if not options[S_NO_OPT]: + builder.compiler_parameters += " -Ox" + # Platform-specific settings for the Mac OS X build + elif platform == "macosx": + builder = GCCBuilder() + builder.linker_parameters = "-dynamiclib" + platform_macros = [ "__MAC__" ] + python_libpath = "." + python_library = "-framework Python" + ida_libpath = os.path.join(idasdkdir, "lib", ea64 and "x86_mac_gcc_64" or "x86_mac_gcc_32") + ida_lib = ea64 and "-lida64" or "-lida" + extra_link_parameters = " -s" + builder.compiler_parameters += " -O3" + + assert builder, "Unknown platform! No idea how to build here..." + + # Enable EA64 for the compiler if necessary + if ea64: + platform_macros.append("__EA64__") + + # Build with Hex-Rays decompiler + if with_hexrays: + platform_macros.append("WITH_HEXRAYS") + SWIG_OPTIONS += ' -DWITH_HEXRAYS ' + + platform_macros.append("NDEBUG") + + if not '--no-early-load' in sys.argv: + platform_macros.append("PLUGINFIX") + + # Turn off obsolete functions + #platform_macros.append("NO_OBSOLETE_FUNCS") + + # Build the wrapper from the interface files + ea64flag = ea64 and "-D__EA64__" or "" + swigcmd = "swig %s -Iswig -o idaapi.cpp %s -I%s idaapi.i" % (SWIG_OPTIONS, ea64flag, ida_include_directory) + if VERBOSE: print swigcmd + res = os.system(swigcmd) + assert res == 0, "Failed to build the wrapper with SWIG" + + # If we are running on windows, we have to patch some directors' + # virtual methods, so they have the right calling convention. + # Without that, compilation just won't succeed. + if platform == "win32": + res = os.system("python patch_directors_cc.py -f idaapi.h") + assert res == 0, "Failed to patch directors' calling conventions" + + # Compile the wrapper + res = builder.compile("idaapi", + includes=[ PYTHON_INCLUDE_DIRECTORY, ida_include_directory ], + macros=platform_macros) + assert res == 0, "Failed to build the wrapper module" + + # Compile the main plugin source + res = builder.compile("python", + includes=[ PYTHON_INCLUDE_DIRECTORY, ida_include_directory ], + macros=platform_macros) + assert res == 0, "Failed to build the main plugin object" + + # Link the final binary + res = builder.link( ["idaapi", "python"], + plugin_name, + [ python_libpath, ida_libpath ], + [ python_library, ida_lib ], + extra_link_parameters) + assert res == 0, "Failed to link the plugin binary" + +# ----------------------------------------------------------------------- +def detect_platform(ea64): + # Detect the platform + system = platform.system() + + if system == "Windows" or system == "Microsoft": + system = "Windows" + platform_string = "win32" + plugin_name = ea64 and "python.p64" or "python.plw" + + elif system == "Linux": + platform_string = "linux" + plugin_name = ea64 and "python.plx64" or "python.plx" + + elif system == "Darwin": + platform_string = "macosx" + plugin_name = ea64 and "python.pmc64" or "python.pmc" + else: + print "Unknown platform!" + sys.exit(-1) + + return (system, platform_string, plugin_name) + +# ----------------------------------------------------------------------- +def build_binary_package(options, nukeold): + ea64 = options[S_EA64] + system, platform_string, plugin_name = detect_platform(ea64) + BINDISTDIR = "idapython-%d.%d.%d_ida%d.%d_py%d.%d_%s" % (VERSION_MAJOR, + VERSION_MINOR, + VERSION_PATCH, + IDA_MAJOR_VERSION, + IDA_MINOR_VERSION, + PYTHON_MAJOR_VERSION, + PYTHON_MINOR_VERSION, + platform_string) + # Build the plugin + build_plugin(platform_string, IDA_SDK, plugin_name, options) + + # Build the binary distribution + binmanifest = [] + if nukeold: + binmanifest.extend(BINDIST_MANIFEST) + + if not ea64 or nukeold: + binmanifest.extend([(x, "python") for x in "python/init.py", "python/idc.py", "python/idautils.py", "idaapi.py"]) + + binmanifest.append((plugin_name, "plugins")) + + build_distribution(binmanifest, BINDISTDIR, ea64, nukeold) + + +# ----------------------------------------------------------------------- +def build_source_package(): + """ Build a directory and a ZIP file with all the sources """ + SRCDISTDIR = "idapython-%d.%d.%d" % (VERSION_MAJOR, + VERSION_MINOR, + VERSION_PATCH) + # Build the source distribution + srcmanifest = [] + srcmanifest.extend(BINDIST_MANIFEST) + srcmanifest.extend(SRCDIST_MANIFEST) + srcmanifest.extend([(x, "python") for x in "python/init.py", "python/idc.py", "python/idautils.py"]) + build_distribution(srcmanifest, SRCDISTDIR, ea64=False, nukeold=True) + +# ----------------------------------------------------------------------- +def gen_docs(z = False): + print "Generating documentation....." + old_dir = os.getcwd() + try: + curdir = os.getcwd() + os.sep + docdir = 'idapython-reference-%d.%d.%d' % (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH) + sys.path.append(curdir + 'python') + sys.path.append(curdir + 'tools') + sys.path.append(curdir + 'docs') + import epydoc.cli + import swigdocs + os.chdir('docs') + PYWRAPS_FN = 'pywraps' + swigdocs.gen_docs(outfn = PYWRAPS_FN + '.py') + epydoc.cli.optparse.sys.argv = [ 'epydoc', + '--no-sourcecode', + '-u', 'http://code.google.com/p/idapython/', + '--navlink', 'IDAPython Reference', + '--no-private', + '--simple-term', + '-o', docdir, + '--html', + 'idc', 'idautils', PYWRAPS_FN, 'idaapi'] + # Generate the documentation + epydoc.cli.cli() + + print "Documentation generated!" + + # Clean temp files + for f in [PYWRAPS_FN + '.py', PYWRAPS_FN + '.pyc']: + if os.path.exists(f): + os.unlink(f) + + if z: + z = docdir + '-doc.zip' + zip = zipfile.ZipFile(z, "w", zipfile.ZIP_DEFLATED) + for fn in glob.glob(docdir + os.sep + '*'): + zip.write(fn) + zip.close() + print "Documentation compressed to", z + except Exception, e: + print 'Failed to generate documentation:', e + finally: + os.chdir(old_dir) + return + +# ----------------------------------------------------------------------- +def usage(): + print """IDAPython build script. + +Available switches: + --doc: + Generate documentation into the 'docs' directory + --zip: + Used with '--doc' switch. It will compress the generated documentation + --ea64: + Builds also the 64bit version of the plugin + --with-hexrays: + Build with the Hex-Rays Decompiler wrappings + --no-early-load: + The plugin will be compiled as normal plugin + This switch disables processor, plugin and loader scripts +""" + +# ----------------------------------------------------------------------- +def main(): + if '--help' in sys.argv: + return usage() + elif '--doc' in sys.argv: + return gen_docs(z = '--zip' in sys.argv) + + # Parse options + options = parse_options(sys.argv) + ea64 = options[S_EA64] + + # Always build the non __EA64__ version + options[S_EA64] = False + build_binary_package(options, nukeold=True) + + # Rebuild package with __EA64__ if needed + if ea64: + options[S_EA64] = True + build_binary_package(options, nukeold=False) + + # Always build the source package + build_source_package() + +# ----------------------------------------------------------------------- +if __name__ == "__main__": + main()