@@ -48,7 +48,7 @@ def _get_ssl_context():
48
48
return ssl .create_default_context (cafile = certifi .where ())
49
49
50
50
51
- def download_or_cache (url , sha ):
51
+ def get_from_cache_or_download (url , sha ):
52
52
"""
53
53
Get bytes from the given url or local cache.
54
54
@@ -86,7 +86,7 @@ def download_or_cache(url, sha):
86
86
file_sha = _get_hash (data )
87
87
if file_sha != sha :
88
88
raise Exception (
89
- f"The download file does not match the expected sha. { url } was "
89
+ f"The downloaded file does not match the expected sha. { url } was "
90
90
f"expected to have { sha } but it had { file_sha } " )
91
91
92
92
if cache_dir is not None : # Try to cache the downloaded file.
@@ -100,6 +100,45 @@ def download_or_cache(url, sha):
100
100
return BytesIO (data )
101
101
102
102
103
+ def get_and_extract_tarball (urls , sha , dirname ):
104
+ """
105
+ Obtain a tarball (from cache or download) and extract it.
106
+
107
+ Parameters
108
+ ----------
109
+ urls : list[str]
110
+ URLs from which download is attempted (in order of attempt), if the
111
+ tarball is not in the cache yet.
112
+ sha : str
113
+ SHA256 hash of the tarball; used both as a cache key (by
114
+ `get_from_cache_or_download`) and to validate a downloaded tarball.
115
+ dirname : path-like
116
+ Directory where the tarball is extracted.
117
+ """
118
+ toplevel = Path ("build" , dirname )
119
+ if not toplevel .exists (): # Download it or load it from cache.
120
+ Path ("build" ).mkdir (exist_ok = True )
121
+ for url in urls :
122
+ try :
123
+ tar_contents = get_from_cache_or_download (url , sha )
124
+ break
125
+ except Exception :
126
+ pass
127
+ else :
128
+ raise IOError (
129
+ f"Failed to download any of the following: { urls } . "
130
+ f"Please download one of these urls and extract it into "
131
+ f"'build/' at the top-level of the source repository." )
132
+ print ("Extracting {}" .format (urllib .parse .urlparse (url ).path ))
133
+ with tarfile .open (fileobj = tar_contents , mode = "r:gz" ) as tgz :
134
+ if os .path .commonpath (tgz .getnames ()) != dirname :
135
+ raise IOError (
136
+ f"The downloaded tgz file was expected to have { dirname } "
137
+ f"as sole top-level directory, but that is not the case" )
138
+ tgz .extractall ("build" )
139
+ return toplevel
140
+
141
+
103
142
# SHA256 hashes of the FreeType tarballs
104
143
_freetype_hashes = {
105
144
'2.6.1' :
@@ -515,7 +554,7 @@ def add_qhull_flags(ext):
515
554
if options .get ("system_qhull" ):
516
555
ext .libraries .append ("qhull" )
517
556
else :
518
- qhull_path = Path (f'extern /qhull-{ LOCAL_QHULL_VERSION } /src' )
557
+ qhull_path = Path (f'build /qhull-{ LOCAL_QHULL_VERSION } /src' )
519
558
ext .include_dirs .insert (0 , str (qhull_path ))
520
559
ext .sources .extend (map (str , sorted (qhull_path .glob ('libqhull_r/*.c' ))))
521
560
if sysconfig .get_config_var ("LIBM" ) == "-lm" :
@@ -560,46 +599,24 @@ def do_custom_build(self, env):
560
599
if options .get ('system_freetype' ):
561
600
return
562
601
563
- src_path = Path ('build' , f'freetype-{ LOCAL_FREETYPE_VERSION } ' )
602
+ tarball = f'freetype-{ LOCAL_FREETYPE_VERSION } .tar.gz'
603
+ src_path = get_and_extract_tarball (
604
+ urls = [
605
+ (f'https://downloads.sourceforge.net/project/freetype'
606
+ f'/freetype2/{ LOCAL_FREETYPE_VERSION } /{ tarball } ' ),
607
+ (f'https://download.savannah.gnu.org/releases/freetype'
608
+ f'/{ tarball } ' )
609
+ ],
610
+ sha = LOCAL_FREETYPE_HASH ,
611
+ dirname = f'freetype-{ LOCAL_FREETYPE_VERSION } ' ,
612
+ )
564
613
565
- # We've already built freetype
566
614
if sys .platform == 'win32' :
567
615
libfreetype = 'libfreetype.lib'
568
616
else :
569
617
libfreetype = 'libfreetype.a'
570
-
571
- # bailing because it is already built
572
618
if (src_path / 'objs' / '.libs' / libfreetype ).is_file ():
573
- return
574
-
575
- # do we need to download / load the source from cache?
576
- if not src_path .exists ():
577
- os .makedirs ('build' , exist_ok = True )
578
-
579
- tarball = f'freetype-{ LOCAL_FREETYPE_VERSION } .tar.gz'
580
- target_urls = [
581
- (f'https://downloads.sourceforge.net/project/freetype'
582
- f'/freetype2/{ LOCAL_FREETYPE_VERSION } /{ tarball } ' ),
583
- (f'https://download.savannah.gnu.org/releases/freetype'
584
- f'/{ tarball } ' )
585
- ]
586
-
587
- for tarball_url in target_urls :
588
- try :
589
- tar_contents = download_or_cache (tarball_url ,
590
- LOCAL_FREETYPE_HASH )
591
- break
592
- except Exception :
593
- pass
594
- else :
595
- raise IOError (
596
- f"Failed to download FreeType. Please download one of "
597
- f"{ target_urls } and extract it into { src_path } at the "
598
- f"top-level of the source repository." )
599
-
600
- print (f"Extracting { tarball } " )
601
- with tarfile .open (fileobj = tar_contents , mode = "r:gz" ) as tgz :
602
- tgz .extractall ("build" )
619
+ return # Bail out because we have already built FreeType.
603
620
604
621
print (f"Building freetype in { src_path } " )
605
622
if sys .platform != 'win32' : # compilation on non-windows
0 commit comments