#!/usr/bin/python

# zip_script.py
#       --copyright--                   Copyright 2013 (C) Tranzoa, Co. All rights reserved.    Warranty: You're free and on your own here. This code is not necessarily up-to-date or of public quality.
#       --url--                         http://www.tranzoa.net/tzpython/
#       --email--                       pycode is the name to send to. tranzoa.com is the place to send to.
#       --bodstamps--
#       October 1, 2013         bar
#       October 3, 2013         bar     put the files in a subdirectory in the zip file
#       February 25, 2014       bar     add the script's path to the sys path so local modules are found
#       April 4, 2014           bar     allow non-python files to be included on the command line
#       December 25, 2016       bar     confusing --base_dir (must specify full path for output file)
#       January 6, 2018         bar     allow tzlib stuff to go in the main directory with the program
#                                       allow a version to be put in the ZIP file's directory and in the name of the ZIP file
#       --eodstamps--
##      \file
#       \namespace              tzpython.zip_script
#
#
#       Zip up the .py files for a script.
#
#


import  modulefinder
import  os
import  re
import  sys


import  tzlib
import  zipem


def all_modules(py_name) :
    """
        Return the list of Python module file names needed for the given Python source file.

        Fake tzlib to be in a subdirectory, which the program will probably need to append to sys.path to import modules from.

    """

    sys.path.append(os.path.dirname(os.path.abspath(os.path.normpath(py_name))))

    m       = modulefinder.ModuleFinder()
    m.run_script(py_name)
    fnames  = [ re.sub(r'^.*?tzpython', 'tzlib', mm.__file__) for mm in m.modules.values() if mm.__file__ and (not mm.__file__.endswith('__init__.py')) and (mm.__file__.startswith('/home/') or ((not mm.__file__.startswith('/')) and (not re.search(r'^[a-zA-Z]\:\\', mm.__file__))) ) ]
    fns     = {}
    for fn in fnames :
        bfn = os.path.basename(fn)
        fns[bfn]    = fns.get(bfn, fn)
    odn     = os.getcwd()
    fnames  = [ re.sub(r"^\./", "", fn.replace(odn, '.')) for fn in fns.values() if not fn.endswith(py_name) ]
    fnames.sort(lambda a, b : cmp(a.find('/'), b.find('/')) or cmp(a.lower(), b.lower()))
    fnames  = [ py_name, ] + fnames

    return(fnames)


def write_zip_file(file_name, pfnames, in_zip_dir_name = None, tz_dir = None) :
    """ Write a ZIP file containing the given file names, tzlib ones put in a directory given by tz_dir or a ./tzlib directory. """
    if  not file_name :
        raise ValueError("No file name!")

    if  tz_dir  is None :
        tz_dir  = "tzlib"                                           # a sub-directory to put the tzlib files in (I think such a dir requires importing modules to know the dir, or PYTHONPATH to be set)

    tzp     = os.path.dirname(tzlib.__file__) or 'tzlib'            # where to find the tzlib files
    args    = []
    for fn in pfnames :
        ofn = fn
        if  fn.startswith('tzlib/') :
            if  in_zip_dir_name :
                ofn = in_zip_dir_name.rstrip('/\\') + '/' + tz_dir + ((tz_dir and '/') or '') + ((tz_dir and fn) or os.path.basename(fn))
            fn  = "%s <- %s" % ( ofn, os.path.join(tzp, os.path.basename(fn)), )
        else    :
            if  in_zip_dir_name :
                ofn = in_zip_dir_name.rstrip('/\\') + '/' + fn
            fn  = "%s <- %s" % ( ofn, fn, )
        args.append(fn)

    r       = zipem.zipem(file_name, args)
    if  r   :
        raise ValueError(r)
    pass



help_str    = """
%s (options) python_file_name... other_file_names...

    Zips a script file and all the local .py files it needs.

Options:

    --output        file_name       Set the output ZIP file name. (default: inputfile_basename.zip)
    --base_dir      directory_name  Change to the given directory before doing the ZIP so that the names
                                    of the files in the ZIP file are not full path names.
    --version                       Set a version string to put at the end of the basename of the ZIP file
                                    and at the end of the directory all the ZIP file's files are under.
    --tz_dir        directory_name  Set the tzlib directory name.
                                      May be "" to put tzlib modules in the main program directory.
    --verbose                       Increase the verbosity level.

This program puts the non-system Python scripts for a Python script(s) with the script(s) in a ZIP file.
Multiple Python script files can be given to either combine them or to make sure they all are included.

Non-Python files can be included in the list of files to ZIP.

"""



if  __name__ == '__main__' :

    import  TZCommandLineAtFile


    program_name    = sys.argv.pop(0)

    TZCommandLineAtFile.expand_at_sign_command_line_files(sys.argv)


    verbose         = 0
    ofile_name      = None
    base_dir        = None
    tz_dir          = None
    vers            = ""

    while True :
        oi  = tzlib.array_find(sys.argv, [ "--help", "-h", "-?", "/h", "/H", "/?" ] )
        if  oi < 0  :   break
        del sys.argv[oi]
        print help_str % ( os.path.basename(program_name), )
        sys.exit(254)


    while True :
        oi  = tzlib.array_find(sys.argv, [ "--verbose", "-v", ] )
        if  oi < 0  :   break
        del sys.argv[oi]
        verbose    += 1

    while True :
        oi  = tzlib.array_find(sys.argv, [ "--output", "--ofile_name", "--ofile-name", "--ofilename", "-o", ] )
        if  oi < 0  :   break
        del sys.argv[oi]
        ofile_name  = sys.argv.pop(oi)

    while True :
        oi  = tzlib.array_find(sys.argv, [ "--base_dir", "--base-dir", "--basedir", "--base_directory", "--base-directory", "--basedirectory", "--base_folder", "--base-folder", "--basefolder", ] )
        if  oi < 0  :   break
        del sys.argv[oi]
        os.chdir(os.path.abspath(tzlib.expand_user_vars(sys.argv.pop(oi))))

    while True :
        oi  = tzlib.array_find(sys.argv, [ "--version", "--set_version", "--set-version", "--setversion", ] )
        if  oi < 0  :   break
        del sys.argv[oi]
        vers        = sys.argv.pop(oi)

    while True :
        oi  = tzlib.array_find(sys.argv, [ "--tzlib_dir", "--tzlib-dir", "--tzlibdir", "--tz_lib_dir", "--tz-lib-dir", "--tzlibdir", "--tz_dir", "--tz-dir", "--tzdir", "--tzld", "--tzd", "--tz", ] )
        if  oi < 0  :   break
        del sys.argv[oi]
        tz_dir      = sys.argv.pop(oi)

    if  len([ a for a in sys.argv if a.startswith('-') ]) :
        print "I won't do args starting with dashes:", sys.argv
        sys.exit(103)

    dn  = ""
    fns = []
    while len(sys.argv) :
        afn         = sys.argv.pop(0)
        fns        += tzlib.ambiguous_file_list(afn)
        if  (not ofile_name) and (len(fns) != 1) :
            print "The first (Python) file name must be unambiguous if you do not specify an output ZIP file"
            sys.exit(102)
        if  len(fns) == 1 :
            dn      = os.path.splitext(os.path.basename(fns[0]))[0] + vers                  # get the directory everybody goes in from the first file name
        ofile_name  = ofile_name or   (os.path.splitext(fns[0])[0]  + vers + '.zip')        # put the zip file in the directory with the 1st python file name

    if  not len(fns) :
        print "Tell me scripts to ZIP"
        sys.exit(101)

    fns = tzlib.without_dupes(fns)
    fns.sort(lambda a, b : cmp(a.lower(), b.lower()))

    pfnames = []
    for fn in fns :
        if  os.path.splitext(fn)[1].lower() in [ '.py', '.pyc', ] :
            pfnames    += all_modules(fn)
        else            :
            pfnames.append(fn)
        pass
    pfnames             = tzlib.without_dupes(pfnames)

    write_zip_file(ofile_name, pfnames, dn, tz_dir = tz_dir)

#
#
# eof
