~jfb-tempo-consulting/unifield-web/US-9592

4680.3.4 by Xavier ALT
[AIO-45] win32: modidify package to not compile .py files
1
#!/usr/bin/python
2
# -*- coding: utf-8 -*-
3
#
4
# Usage for setup.py:
5
#
6
# from distutils.core import setup
7
# import py2exe
8
# from setup_py2exe_custom import custom_py2exe
9
#
10
# setup(...,
11
#       cmdclass={'py2exe': custom_py2exe},
12
#       options={'py2exe': {
13
#           skip_archive=1,   # mandatory overwith will be opt-out
14
#           compressed=0,     # do not compress - mandatory when skip_archive=1
15
#           bundle_files=3,   # keep pythonXX.dll out of exe
16
#           optimize=0,       # do not compile .py files
17
#           collected_libs_dir='libs',  # move all collected libs into this director
18
#           collected_libs_data_relocate='babel,pytz',  # force moving collected libs data into collected libs dir
19
#       }},
20
#       data_files=fixup_data_pytz_zoneinfo() # to add pytz zoneinfo files
21
#      )
22
#
23
#
24
25
import os
26
import tempfile
4831.1.1 by Jeff Allen
[IMP] US-2374 Make setup.py usable from Linux
27
4836.1.1 by Jeff Allen
Fixed failure while making AIOs.
28
if os.name == 'nt':
5019 by jf
WIP py2exe
29
    from py2exe import build_exe
30
    from py2exe.distutils_buildexe import py2exe, fancy_split
4831.1.1 by Jeff Allen
[IMP] US-2374 Make setup.py usable from Linux
31
else:
32
    # fake it for non-Windows, so that setup.py can be run for
33
    # installing dependencies.
34
    class _be(dict):
35
        def __init__(self, arg1,arg2,arg3):
36
            pass
37
        def __dir__(self):
38
            return tuple(self)
39
        def __getattribute__(self, name):
40
            if name == 'user_options':
41
                return []
42
            else:
43
                raise AttributeError(name)
44
    build_exe = _be(1, 2, 3)
45
    fancy_split = None
4680.3.4 by Xavier ALT
[AIO-45] win32: modidify package to not compile .py files
46
47
def fixup_data_pytz_zoneinfo():
48
    r = {}
49
    import pytz
4850.3.1 by Jeff Allen
[IMP] Fix character encoding of installer. Other cleanup as well.
50
    tzdir = os.path.relpath(os.path.dirname(pytz.__file__))
4680.3.4 by Xavier ALT
[AIO-45] win32: modidify package to not compile .py files
51
    for root, _, filenames in os.walk(os.path.join(tzdir, "zoneinfo")):
52
        base = os.path.join('pytz', root[len(tzdir) + 1:])
53
        r[base] = [os.path.join(root, f) for f in filenames]
5010 by jf
2to3
54
    return list(r.items())
4680.3.4 by Xavier ALT
[AIO-45] win32: modidify package to not compile .py files
55
56
def byte_compile_noop(py_files, optimize=0, force=0,
4831.1.1 by Jeff Allen
[IMP] US-2374 Make setup.py usable from Linux
57
                      target_dir=None, verbose=1, dry_run=0,
58
                      direct=None):
4680.3.4 by Xavier ALT
[AIO-45] win32: modidify package to not compile .py files
59
60
    compiled_files = []
61
    from distutils.dir_util import mkpath
62
    from distutils.dep_util import newer
63
    from distutils.file_util import copy_file
64
65
    for file in py_files:
66
        # Terminology from the py_compile module:
67
        #   cfile - byte-compiled file
68
        #   dfile - purported source filename (same as 'file' by default)
69
        cfile = file.__name__.replace('.', '\\')
70
71
        if file.__path__:
72
            dfile = cfile + '\\__init__.py'
73
        else:
74
            dfile = cfile + '.py'
75
        if target_dir:
76
            cfile = os.path.join(target_dir, dfile)
77
78
        if force or newer(file.__file__, cfile):
79
            if verbose:
5010 by jf
2to3
80
                print("fake-byte-compiling %s to %s" % (file.__file__, dfile))
4680.3.4 by Xavier ALT
[AIO-45] win32: modidify package to not compile .py files
81
            if not dry_run:
82
                mkpath(os.path.dirname(cfile))
83
                copy_file(file.__file__, cfile, preserve_mode=0)
84
        else:
85
            if verbose:
5010 by jf
2to3
86
                print("skipping byte-compilation of %s to %s" % \
87
                      (file.__file__, dfile))
4680.3.4 by Xavier ALT
[AIO-45] win32: modidify package to not compile .py files
88
        compiled_files.append(dfile)
89
    return compiled_files
90
91
# byte_compile()
92
5019 by jf
WIP py2exe
93
class custom_py2exe(py2exe):
94
    user_options = py2exe.user_options + [
4680.3.4 by Xavier ALT
[AIO-45] win32: modidify package to not compile .py files
95
        ("collected-libs-dir", None,
96
         "Place all collected libs under a specific sub-directory"),
97
        ("collected-libs-data-relocate", None,
98
         "List of prefix to rellocate under collected-libs-dir directory"),
99
        ("package-build-extra-dirs", None,
100
         "List extra packages dirs to check for - moving them to exe root dir"),
101
    ]
102
103
    def initialize_options(self):
104
        build_exe.initialize_options(self)
105
        self.collected_libs_dir = '.'
106
        self.collected_libs_data_relocate = []
107
        self.package_build_extra_dirs = []
108
109
    def finalize_options(self):
110
        build_exe.finalize_options(self)
111
        self.collected_libs_data_relocate = fancy_split(self.collected_libs_data_relocate)
112
        self.package_build_extra_dirs = fancy_split(self.package_build_extra_dirs)
113
114
    def create_directories(self):
115
        build_exe.create_directories(self)
116
        self.lib_dir = os.path.join(self.lib_dir, self.collected_libs_dir)
117
        self.mkpath(self.lib_dir)
118
        self.boot_tmp_dir = tempfile.mkdtemp()
119
120
    def get_boot_script(self, boot_type):
121
        py2exe_boot_file = build_exe.get_boot_script(self, boot_type)
122
        custom_boot_file = os.path.join(self.boot_tmp_dir, os.path.basename(py2exe_boot_file))
123
        if not os.path.exists(custom_boot_file):
124
            cbootfile = open(custom_boot_file, 'wb')
125
            obootfile = open(py2exe_boot_file, 'rb')
126
127
            # copy original file
128
            cbootfile.write(obootfile.read())
129
            obootfile.close()
130
131
            # write special custom handlers
132
            if boot_type in ['common', 'service']:
133
                cbootfile.write("""
134
import sys, os
135
136
if hasattr(sys, 'frozen'):
137
    # executable is frozen, add executable directory to sys.path
138
    sys.path.append(os.path.dirname(sys.executable))
139
140
""")
141
            elif boot_type == 'service':
142
                pass
143
144
            cbootfile.close()
145
        return custom_boot_file
146
147
    def create_binaries(self, py_files, extensions, dlls):
148
        dist = self.distribution
149
5019 by jf
WIP py2exe
150
        # Do not try compiling .py files for 'packages', we
4680.3.4 by Xavier ALT
[AIO-45] win32: modidify package to not compile .py files
151
        # want them into the exe directory - and only collected
152
        # dependencies with 'collected libbs dir'
153
        src_build_cmd = dist.get_command_obj('build')
154
        src_build_cmd.ensure_finalized()
155
        build_lib = getattr(src_build_cmd, 'build_lib', None)
156
        if build_lib is None:
157
            raise Exception("Could not continue with no 'build_lib' set")
158
159
        dist_packages_py_files = []
160
        def is_forced_packages_files(m):
161
            if m.__file__:
162
                for build_dir in [build_lib] + self.package_build_extra_dirs:
163
                    if m.__file__.startswith(build_dir):
164
                        dist_packages_py_files.append((m.__file__, m.__file__[len(build_dir)+1:]))
165
                        return True
166
            return False
167
        py_files = [ m for m in py_files if not is_forced_packages_files(m) ]
168
169
        if dist_packages_py_files:
170
            print("*** copy package's python file to root directory ***")
171
            for (srcfile, relfile) in dist_packages_py_files:
172
                dstfile = os.path.join(self.exe_dir, *os.path.split(relfile))
173
                dstfile_dir = os.path.dirname(dstfile)
174
                self.mkpath(dstfile_dir)
175
                self.copy_file(srcfile, dstfile, preserve_mode=0)
176
177
        # Run fake compilation - just copy raw .py file into their
178
        # destination directory
179
        self.no_compiled_files = byte_compile_noop(py_files,
4831.1.1 by Jeff Allen
[IMP] US-2374 Make setup.py usable from Linux
180
                                                   target_dir=self.collect_dir,
181
                                                   optimize=self.optimize,
182
                                                   force=0,
183
                                                   verbose=self.verbose,
184
                                                   dry_run=self.dry_run)
4680.3.4 by Xavier ALT
[AIO-45] win32: modidify package to not compile .py files
185
186
        # Force relocate of specific packages data within collected libs dir
187
        def fixup_location(l):
188
            if isinstance(l, tuple) and any([ l[0].startswith(reloc_prefix)
189
                                              for reloc_prefix in self.collected_libs_data_relocate ]):
190
                return (os.path.join(self.collected_libs_dir, l[0]), l[1])
191
            return l
192
        if dist.has_data_files():
193
            dist.data_files = [ fixup_location(f) for f in dist.data_files ]
194
5019 by jf
WIP py2exe
195
        # Call parent create_binaries() without any py_files, so that py2exe
4680.3.4 by Xavier ALT
[AIO-45] win32: modidify package to not compile .py files
196
        # do no force their compilations
197
        return build_exe.create_binaries(self, [], extensions, dlls)
198
199
    def make_lib_archive(self, zip_filename, base_dir, files,
200
                         verbose=0, dry_run=0):
201
        allfiles = files + self.no_compiled_files
202
        return build_exe.make_lib_archive(self, zip_filename, base_dir, allfiles, verbose=verbose, dry_run=dry_run)
203