1#!/usr/bin/env python3 2"""Generate distribution archives for a given FreeType 2 release.""" 3 4from __future__ import print_function 5 6import argparse 7import atexit 8import os 9import shutil 10import subprocess 11import sys 12import tempfile 13 14_TOP_DIR = os.path.abspath(os.path.join(__file__, "..", "..", "..")) 15_SCRIPT_DIR = os.path.dirname(os.path.join(_TOP_DIR, "builds", "meson", "")) 16 17 18def get_cmd_output(cmd, cwd=None): 19 """Run a command and return its output as a string.""" 20 if cwd is not None: 21 out = subprocess.check_output(cmd, cwd=cwd) 22 else: 23 out = subprocess.check_output(cmd) 24 return out.decode("utf-8").rstrip() 25 26 27def is_git_dir_clean(git_dir): 28 """Return True iff |git_dir| is a git directory in clean state.""" 29 out = get_cmd_output(["git", "status", "--porcelain"], cwd=git_dir) 30 return len(out) == 0 31 32 33def main(): 34 parser = argparse.ArgumentParser(description=__doc__) 35 36 parser.add_argument( 37 "--source_dir", default=_TOP_DIR, help="Source directory path." 38 ) 39 40 parser.add_argument( 41 "--version", 42 help=( 43 "Specify alternate FreeType version (it is otherwise extracted" 44 " from current sources by default)." 45 ), 46 ) 47 48 parser.add_argument( 49 "--gnu-config-dir", 50 help=( 51 "Path of input directory containing recent `config.guess` and" 52 " `config.sub` files from GNU config." 53 ), 54 ) 55 56 parser.add_argument( 57 "--build-dir", 58 help="Specify build directory. Only used for debugging this script.", 59 ) 60 61 parser.add_argument( 62 "--ignore-clean-check", 63 action="store_true", 64 help=( 65 "Do not check for a clean source git repository. Only used for" 66 " debugging this script." 67 ), 68 ) 69 70 parser.add_argument( 71 "output_dir", help="Output directory for generated archives." 72 ) 73 74 args = parser.parse_args() 75 76 git_dir = args.source_dir if args.source_dir else _TOP_DIR 77 if not args.ignore_clean_check and not is_git_dir_clean(git_dir): 78 sys.stderr.write( 79 "ERROR: Your git repository is not in a clean state: %s\n" 80 % git_dir 81 ) 82 return 1 83 84 if args.version: 85 version = args.version 86 else: 87 # Extract FreeType version from sources. 88 version = get_cmd_output( 89 [ 90 sys.executable, 91 os.path.join(_SCRIPT_DIR, "extract_freetype_version.py"), 92 os.path.join(_TOP_DIR, "include", "freetype", "freetype.h"), 93 ] 94 ) 95 96 # Determine the build directory. This will be a temporary file that is 97 # cleaned up on script exit by default, unless --build-dir=DIR is used, 98 # in which case we only create and empty the directory, but never remove 99 # its content on exit. 100 if args.build_dir: 101 build_dir = args.build_dir 102 if not os.path.exists(build_dir): 103 os.makedirs(build_dir) 104 else: 105 # Remove anything from the build directory, if any. 106 for item in os.listdir(build_dir): 107 file_path = os.path.join(build_dir, item) 108 if os.path.isdir(file_path): 109 shutil.rmtree(file_path) 110 else: 111 os.unlink(file_path) 112 else: 113 # Create a temporary directory, and ensure it is removed on exit. 114 build_dir = tempfile.mkdtemp(prefix="freetype-dist-") 115 116 def clean_build_dir(): 117 shutil.rmtree(build_dir) 118 119 atexit.register(clean_build_dir) 120 121 # Copy all source files known to git into $BUILD_DIR/freetype-$VERSION 122 # with the exception of .gitignore and .mailmap files. 123 source_files = [ 124 f 125 for f in get_cmd_output(["git", "ls-files"], cwd=git_dir).split("\n") 126 if os.path.basename(f) not in (".gitignore", ".mailmap") 127 ] 128 129 freetype_dir = "freetype-" + version 130 tmp_src_dir = os.path.join(build_dir, freetype_dir) 131 os.makedirs(tmp_src_dir) 132 133 for src in source_files: 134 dst = os.path.join(tmp_src_dir, src) 135 dst_dir = os.path.dirname(dst) 136 if not os.path.exists(dst_dir): 137 os.makedirs(dst_dir) 138 shutil.copy(os.path.join(git_dir, src), dst) 139 140 # Run autogen.sh in directory. 141 subprocess.check_call(["/bin/sh", "autogen.sh"], cwd=tmp_src_dir) 142 shutil.rmtree( 143 os.path.join(tmp_src_dir, "builds", "unix", "autom4te.cache") 144 ) 145 146 # Copy config.guess and config.sub if possible! 147 if args.gnu_config_dir: 148 for f in ("config.guess", "config.sub"): 149 shutil.copy( 150 os.path.join(args.gnu_config_dir, f), 151 os.path.join(tmp_src_dir, "builds", "unix", f), 152 ) 153 154 # Generate reference documentation under docs/ 155 subprocess.check_call( 156 [ 157 sys.executable, 158 os.path.join(_SCRIPT_DIR, "generate_reference_docs.py"), 159 "--input-dir", 160 tmp_src_dir, 161 "--version", 162 version, 163 "--output-dir", 164 os.path.join(tmp_src_dir, "docs"), 165 ] 166 ) 167 168 shutil.rmtree(os.path.join(tmp_src_dir, "docs", "markdown")) 169 os.unlink(os.path.join(tmp_src_dir, "docs", "mkdocs.yml")) 170 171 # Generate our archives 172 freetype_tar = freetype_dir + ".tar" 173 174 subprocess.check_call( 175 ["tar", "-H", "ustar", "-chf", freetype_tar, freetype_dir], 176 cwd=build_dir, 177 ) 178 179 subprocess.check_call( 180 ["gzip", "-9", "--keep", freetype_tar], cwd=build_dir 181 ) 182 183 subprocess.check_call(["xz", "--keep", freetype_tar], cwd=build_dir) 184 185 ftwinversion = "ft" + "".join(version.split(".")) 186 subprocess.check_call( 187 ["zip", "-qlr9", ftwinversion + ".zip", freetype_dir], cwd=build_dir 188 ) 189 190 # Copy file to output directory now. 191 if not os.path.exists(args.output_dir): 192 os.makedirs(args.output_dir) 193 194 for f in ( 195 freetype_tar + ".gz", 196 freetype_tar + ".xz", 197 ftwinversion + ".zip", 198 ): 199 shutil.copy( 200 os.path.join(build_dir, f), os.path.join(args.output_dir, f) 201 ) 202 203 # Done! 204 return 0 205 206 207if __name__ == "__main__": 208 sys.exit(main()) 209