summaryrefslogtreecommitdiff
path: root/support/gladtex/create_windows_distributions.py
blob: 3c7ca2c6cf0b351655aa528a6b20a56d8ab9dab0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
"""
This file builds windows distributions, zip files with GladTeX and all other
files."""
import os
import shutil
import stat
import sys
import zipfile
import gleetex

def exec_setup_py(arg_string):
    """Execute `python setup.py` as a subprocess. Use Wine, if necessary."""
    ret = None
    if sys.platform.startswith('win'):
        ret = os.system('python setup.py ' + arg_string)
    else:
        if not shutil.which('wine'):
            print("Error: Wine is not installed, aborting…")
            sys.exit(5)
        ret = os.system('wine python setup.py ' + arg_string)
    if ret:
        if sys.platform.startswith('win'):
            print("Aborting at command `python setup.py %s`." % arg_string)
        else:
            print("Aborting at command `wine python setup.py %s`." % arg_string)
        sys.exit(7)

def get_python_version():
    """Return the python version as a string."""
    import re, subprocess
    args = ['python', '--version']
    if not sys.platform.startswith('win'):
        args = ['wine'] + args
    proc = subprocess.Popen(args, stdout=subprocess.PIPE)
    stdout = proc.communicate()[0].decode(sys.getdefaultencoding())
    if proc.wait():
        raise TypeError("Abnormal subprocess termination while querying python version.")
    return re.search(r'.*?(\d+\.\d+\.\d+)', stdout).groups()[0]

def get_executable_name(label):
    """Construct the name of an executable"""
    return 'gladtex-win64-%s-py_%s-%s.zip' % (gleetex.VERSION, get_python_version(),
            label)

def bundle_files(src, output_name):
    """Bundle the compiled binary files with README, ChangeLog and COPYING."""
    if os.path.exists(output_name):
        shutil.rmtree(output_name)
    os.rename(src, output_name)
    # add README.first
    with open(os.path.join(output_name, 'README.first.txt'), 'w') as f:
        f.write('GladTeX for Windows\r\n===================\r\n\r\n')
        f.write('This program has been compiled with python 3.4.4. If you want to embedd it in binary form with your binary python application, the version numbers HAVE TO match.\r\n')
        f.write('\r\nFor more information, see the file README.md or http://humenda.github.io/GladTeX\r\n')

    # copy README and other files
    for file in ['README.md', 'COPYING', 'ChangeLog']:
        dest = os.path.join(output_name, file)
        # check whether file ending exists
        if not '.' in dest[-5:]:
            dest += '.txt'
        shutil.copy(file, dest)

    files = [os.path.join(root, file)
            for root, _, files in os.walk(output_name)  for file in files]
    with zipfile.ZipFile(output_name + '.zip', 'w', zipfile.ZIP_DEFLATED) as z:
        for file in files:
            z.write(file)
    shutil.rmtree(output_name)

class TemporaryBuildDirectory():
    """Context handler to guard the build process.
    Upon entering the context, the source is copied to a temporary directory and
    the program changes to this directory. After all build actions have been
    done, the output file is copied back to the original directory, the program
    resets the current working directory and deletes the temporary directory."""
    def __init__(self, output_file_name):
        self.orig_cwd = os.getcwd()
        self.tmpdir = None
        self.output_file_name = output_file_name

    def __enter__(self):
        self.tmpdir = self.get_temp_directory()
        shutil.copytree(os.getcwd(), self.tmpdir)
        os.chdir(self.tmpdir)
        return self

    def __exit__(self, _a, _b, _c):
        os.chdir(self.orig_cwd)
        shutil.copy(os.path.join(self.tmpdir, self.output_file_name),
                self.output_file_name)
        shutil.rmtree(self.tmpdir, onerror=self.__onerror)

    def get_temp_directory(self):
        """Find a temporary directory to work in. The checks are done to find a
        directory which does not reside within the user's path, because py2exe
        includes absolute paths for python scripts (in their tracebacks). It is not
        desirable to show the whole world the directory layout of the computer where
        the source code was built on."""
        tmp_base = None
        if os.path.exists('/tmp'):
            tmp_base = '/tmp'
        elif os.path.exists('\\temp'):
            tmp_base = '\\temp'
        elif os.path.exists('\\windows\\temp'):
            tmp_base = '\\windows\\temp'
        else:
            import tempfile
            tmp_base = tempfile.gettempdir()
        tmpdir = os.path.join(tmp_base, 'gladtex.build')
        if os.path.exists(tmpdir):
            shutil.rmtree(tmpdir, onerror=self.__onerror)
        return tmpdir

    def __onerror(self, func, path, exc_info):
        """
        Error handler for ``shutil.rmtree``.
	If the error is due to an access error (read only file) it attempts to
	add write permission and then retries.  If the error is for another reason it re-raises the error.
        Usage : ``shutil.rmtree(path, onerror=onerror)``."""
        if not os.access(path, os.W_OK):
            # Is the error an access error ?
            os.chmod(path, stat.S_IWUSR)
            func(path)
        else:
            raise exc_info


if __name__ == '__main__':
    with TemporaryBuildDirectory(get_executable_name('embeddable')) as tb:
        # build embeddable release, where all files are separate DLL's; if somebody
        # distributes a python app, these DLL files can be shared
        exec_setup_py('py2exe -c -O 2 -i gleetex --bundle-files 3')
        bundle_files('dist', os.path.splitext(tb.output_file_name)[0])

    # create a stand-alone version of GladTeX
    with TemporaryBuildDirectory(get_executable_name('standalone')) as tb:
        exec_setup_py('py2exe -i gleetex -c -O 2 --bundle-files 1')
        bundle_files('dist', os.path.splitext(tb.output_file_name)[0])