1import os
2import stat
3import logging
4
5download_dir = "prebuilt_downloads"
6
7def download_sha1_unzip(url, checksum, save_to_directory, unzip=True):
8    """ This
9    - downloads a url,
10    - sha1 checksum check,
11    - save_to_directory,
12    - then unzips it.
13
14    Does not download again if the file is there.
15    Does not unzip again if the file is there.
16    """
17    # requests does connection retrying, but people might not have it installed.
18    use_requests = True
19
20    try:
21        import requests
22    except ImportError:
23        use_requests = False
24
25    try:
26        import urllib.request as urllib
27    except ImportError:
28        import urllib2 as urllib
29    import hashlib
30    import zipfile
31
32    filename = os.path.split(url)[-1]
33    save_to = os.path.join(save_to_directory, filename)
34
35    # skip download?
36    skip_download = os.path.exists(save_to)
37    if skip_download:
38        with open(save_to, 'rb') as the_file:
39            data = the_file.read()
40            cont_checksum = hashlib.sha1(data).hexdigest()
41            if cont_checksum == checksum:
42                print("Skipping download url:%s: save_to:%s:" % (url, save_to))
43    else:
44        print("Downloading...", url, checksum)
45
46        if use_requests:
47            response = requests.get(url)
48            cont_checksum = hashlib.sha1(response.content).hexdigest()
49        else:
50
51            headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, '
52                                     'like Gecko) Chrome/35.0.1916.47 Safari/537.36'}
53            request = urllib.Request(url, headers=headers)
54            response = urllib.urlopen(request).read()
55            cont_checksum = hashlib.sha1(response).hexdigest()
56
57        if checksum != cont_checksum:
58            raise ValueError(
59                'url:%s should have checksum:%s: Has:%s: ' % (url, checksum, cont_checksum)
60            )
61        with open(save_to, 'wb') as f:
62            if use_requests:
63                f.write(response.content)
64            else:
65                f.write(response)
66
67    if unzip and filename.endswith('.zip'):
68        print("Unzipping :%s:" % save_to)
69        with zipfile.ZipFile(save_to, 'r') as zip_ref:
70            zip_dir = os.path.join(
71                save_to_directory,
72                filename.replace('.zip', '')
73            )
74            if os.path.exists(zip_dir):
75                print("Skipping unzip to zip_dir exists:%s:" % zip_dir)
76            else:
77                os.mkdir(zip_dir)
78                zip_ref.extractall(zip_dir)
79
80def get_urls(x86=True, x64=True):
81    url_sha1 = []
82    url_sha1.extend([
83        [
84        'https://www.libsdl.org/release/SDL2-devel-2.0.16-VC.zip',
85        '13d952c333f3c2ebe9b7bc0075b4ad2f784e7584',
86        ],
87        [
88        'https://www.libsdl.org/projects/SDL_image/release/SDL2_image-devel-2.0.5-VC.zip',
89        '137f86474691f4e12e76e07d58d5920c8d844d5b',
90        ],
91        [
92        'https://www.libsdl.org/projects/SDL_ttf/release/SDL2_ttf-devel-2.0.15-VC.zip',
93        '1436df41ebc47ac36e02ec9bda5699e80ff9bd27',
94        ],
95        [
96        'https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-devel-2.0.4-VC.zip',
97        '9097148f4529cf19f805ccd007618dec280f0ecc',
98        ],
99        [
100        # 'https://www.ijg.org/files/jpegsr9d.zip',
101        'https://www.pygame.org/ftp/jpegsr9d.zip',
102        'ed10aa2b5a0fcfe74f8a6f7611aeb346b06a1f99',
103        ],
104    ])
105    if x86:
106        url_sha1.append([
107         'https://pygame.org/ftp/prebuilt-x86-pygame-1.9.2-20150922.zip',
108         'dbce1d5ea27b3da17273e047826d172e1c34b478'
109        ])
110    if x64:
111        url_sha1.append([
112         'https://pygame.org/ftp/prebuilt-x64-pygame-1.9.2-20150922.zip',
113         '3a5af3427b3aa13a0aaf5c4cb08daaed341613ed'
114        ])
115    return url_sha1
116
117def download_prebuilts(temp_dir, x86=True, x64=True):
118    """ For downloading prebuilt dependencies.
119    """
120    if not os.path.exists(temp_dir):
121        print("Making dir :%s:" % temp_dir)
122        os.makedirs(temp_dir)
123    for url, checksum in get_urls(x86=x86, x64=x64):
124        download_sha1_unzip(url, checksum, temp_dir, 1)
125
126def create_ignore_target_fnc(x64=False, x86=False):
127    if not x64 and not x86:
128        return None
129    strs = []
130    if x64:
131        strs.append('x64')
132    if x86:
133        strs.append('x86')
134    def ignore_func(dir, contents):
135        for target in strs:
136            if target in dir:
137                return contents
138        return []
139    return ignore_func
140
141import shutil
142
143def copytree(src, dst, symlinks=False, ignore=None):
144    """like shutil.copytree() but ignores existing files
145    https://stackoverflow.com/a/22331852/1239986
146    """
147    if not os.path.exists(dst):
148        os.makedirs(dst)
149        shutil.copystat(src, dst)
150    lst = os.listdir(src)
151    if ignore:
152        excl = ignore(src, lst)
153        lst = [x for x in lst if x not in excl]
154    for item in lst:
155        s = os.path.join(src, item)
156        d = os.path.join(dst, item)
157        if symlinks and os.path.islink(s):
158            if os.path.lexists(d):
159                os.remove(d)
160            os.symlink(os.readlink(s), d)
161            try:
162                st = os.lstat(s)
163                mode = stat.S_IMODE(st.st_mode)
164                os.lchmod(d, mode)
165            except OSError:
166                pass # lchmod not available
167        elif os.path.isdir(s):
168            copytree(s, d, symlinks, ignore)
169        else:
170            shutil.copy2(s, d)
171
172def place_downloaded_prebuilts(temp_dir, move_to_dir, x86=True, x64=True):
173    """ puts the downloaded prebuilt files into the right place.
174
175    Leaves the files in temp_dir. copies to move_to_dir
176    """
177    prebuilt_x64 = os.path.join(
178        temp_dir,
179        'prebuilt-x64-pygame-1.9.2-20150922',
180        'prebuilt-x64'
181    )
182    prebuilt_x86 = os.path.join(
183        temp_dir,
184        'prebuilt-x86-pygame-1.9.2-20150922',
185        'prebuilt-x86'
186    )
187
188    ignore = None
189    def copy(src, dst):
190        copytree(src, dst, ignore=ignore)
191
192    if x64:
193        copy(prebuilt_x64, os.path.join(move_to_dir, 'prebuilt-x64'))
194    if x86:
195        copy(prebuilt_x86, os.path.join(move_to_dir, 'prebuilt-x86'))
196
197    ignore = create_ignore_target_fnc(x64=not x64, x86=not x86)
198    prebuilt_dirs = []
199    if x86:
200        prebuilt_dirs.append('prebuilt-x86')
201    if x64:
202        prebuilt_dirs.append('prebuilt-x64')
203
204
205    for prebuilt_dir in prebuilt_dirs:
206        path = os.path.join(move_to_dir, prebuilt_dir)
207        print("copying into %s" % path)
208
209        # update jpeg
210        for file in ('jerror.h', 'jmorecfg.h', 'jpeglib.h'):
211            shutil.copyfile(
212                os.path.join(
213                    temp_dir,
214                    'jpegsr9d',
215                    'jpeg-9d',
216                    file
217                ),
218                os.path.join(
219                    move_to_dir,
220                    prebuilt_dir,
221                    'include',
222                    file
223                )
224            )
225
226        copy(
227            os.path.join(
228                temp_dir,
229                'SDL2_image-devel-2.0.5-VC/SDL2_image-2.0.5'
230            ),
231            os.path.join(
232                move_to_dir,
233                prebuilt_dir,
234                'SDL2_image-2.0.5'
235            )
236        )
237        copy(
238            os.path.join(
239                temp_dir,
240                'SDL2_mixer-devel-2.0.4-VC/SDL2_mixer-2.0.4'
241            ),
242            os.path.join(
243                move_to_dir,
244                prebuilt_dir,
245                'SDL2_mixer-2.0.4'
246            )
247        )
248        copy(
249            os.path.join(
250                temp_dir,
251                'SDL2_ttf-devel-2.0.15-VC/SDL2_ttf-2.0.15'
252            ),
253            os.path.join(
254                move_to_dir,
255                prebuilt_dir,
256                'SDL2_ttf-2.0.15'
257            )
258        )
259        copy(
260            os.path.join(
261                temp_dir,
262                'SDL2-devel-2.0.16-VC/SDL2-2.0.16'
263            ),
264            os.path.join(
265                move_to_dir,
266                prebuilt_dir,
267                'SDL2-2.0.16'
268            )
269        )
270
271def update(x86=True, x64=True):
272    move_to_dir = "."
273    download_prebuilts(download_dir, x86=x86, x64=x64)
274    place_downloaded_prebuilts(download_dir, move_to_dir, x86=x86, x64=x64)
275
276def ask(x86=True, x64=True):
277    move_to_dir = "."
278    if x64:
279        dest_str = "\"%s/prebuilt-x64\"" % move_to_dir
280    else:
281        dest_str = ""
282    if x86:
283        if dest_str:
284            dest_str = "%s and " % dest_str
285        dest_str = "%s\"%s/prebuilt-x86\"" % (dest_str, move_to_dir)
286    logging.info('Downloading prebuilts to "%s" and copying to %s.', (download_dir, dest_str))
287    download_prebuilt = True
288
289    if download_prebuilt:
290        update(x86=x86, x64=x64)
291    return download_prebuilt
292
293def cached(x86=True, x64=True):
294    if not os.path.isdir(download_dir):
295        return False
296    for url, check in get_urls(x86=x86, x64=x64):
297        filename = os.path.split(url)[-1]
298        save_to = os.path.join(download_dir, filename)
299        if not os.path.exists(save_to):
300            return False
301    return True
302
303if __name__ == '__main__':
304    ask()
305