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