regenerate.py 8.64 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env python3

"""
This script should use a very simple, functional programming style.
Avoid Jinja macros in favor of native Python functions.

Don't go overboard on code generation; use Python only to generate
content that can't be easily declared statically using CircleCI's YAML API.

Data declarations (e.g. the nested loops for defining the configuration matrix)
should be at the top of the file for easy updating.

See this comment for design rationale:
https://github.com/pytorch/vision/pull/1321#issuecomment-531033978
"""
16

17
18
import os.path

19
import jinja2
20
import yaml
21
from jinja2 import select_autoescape
22

23

24
PYTHON_VERSIONS = ["3.8", "3.9", "3.10", "3.11"]
25

26
27
RC_PATTERN = r"/v[0-9]+(\.[0-9]+)*-rc[0-9]+/"

28

29
def build_workflows(prefix="", filter_branch=None, upload=False, indentation=6, windows_latest_only=False):
30
    w = []
31
32
33
34

    # Don't generate anything for build workflow
    return indent(indentation, w)

35
    for btype in ["wheel", "conda"]:
36
        for os_type in ["linux", "macos", "win"]:
37
            python_versions = PYTHON_VERSIONS
38
            cu_versions_dict = {
ptrblck's avatar
ptrblck committed
39
40
                "linux": ["cpu", "cu117", "cu118", "cu121", "rocm5.2", "rocm5.3"],
                "win": ["cpu", "cu117", "cu118", "cu121"],
41
42
                "macos": ["cpu"],
            }
43
            cu_versions = cu_versions_dict[os_type]
44
45
            for python_version in python_versions:
                for cu_version in cu_versions:
Jeff Daily's avatar
Jeff Daily committed
46
                    # ROCm conda packages not yet supported
47
                    if cu_version.startswith("rocm") and btype == "conda":
Jeff Daily's avatar
Jeff Daily committed
48
                        continue
Nikita Shulga's avatar
Nikita Shulga committed
49
                    for unicode in [False]:
50
                        fb = filter_branch
51
52
53
54
55
56
57
58
59
                        if (
                            windows_latest_only
                            and os_type == "win"
                            and filter_branch is None
                            and (
                                python_version != python_versions[-1]
                                or (cu_version not in [cu_versions[0], cu_versions[-1]])
                            )
                        ):
60
                            fb = "main"
61
                        if not fb and (
62
                            os_type == "linux" and cu_version == "cpu" and btype == "wheel" and python_version == "3.8"
63
                        ):
64
                            # the fields must match the build_docs "requires" dependency
65
                            fb = "/.*/"
66
67

                        # Disable all Linux Wheels Workflows from CircleCI
68
                        if os_type == "linux" and btype == "wheel":
69
                            continue
70

71
72
73
74
                        # Disable all Macos Wheels Workflows from CircleCI.
                        if os_type == "macos" and btype == "wheel":
                            continue

75
76
77
78
                        # Disable all non-Windows Conda workflows
                        if os_type != "win" and btype == "conda":
                            continue

79
                        w += workflow_pair(
80
81
                            btype, os_type, python_version, cu_version, unicode, prefix, upload, filter_branch=fb
                        )
82
83
84
85

    return indent(indentation, w)


86
def workflow_pair(btype, os_type, python_version, cu_version, unicode, prefix="", upload=False, *, filter_branch=None):
87
88
89
90
91

    w = []
    unicode_suffix = "u" if unicode else ""
    base_workflow_name = f"{prefix}binary_{os_type}_{btype}_py{python_version}{unicode_suffix}_{cu_version}"

92
93
94
95
96
    w.append(
        generate_base_workflow(
            base_workflow_name, python_version, cu_version, unicode, os_type, btype, filter_branch=filter_branch
        )
    )
97

98
    # For the remaining py3.8 Linux Wheels job left around for the docs build,
99
100
101
102
    # we'll disable uploads.
    if os_type == "linux" and btype == "wheel":
        upload = False

103
    if upload:
104
        w.append(generate_upload_workflow(base_workflow_name, os_type, btype, cu_version, filter_branch=filter_branch))
105
106
107
108
        # disable smoke tests, they are broken and needs to be fixed
        # if filter_branch == "nightly" and os_type in ["linux", "win"]:
        #     pydistro = "pip" if btype == "wheel" else "conda"
        #     w.append(generate_smoketest_workflow(pydistro, base_workflow_name, filter_branch, python_version, os_type))
109
110
111
112

    return w


113
manylinux_images = {
ptrblck's avatar
ptrblck committed
114
    "cu117": "pytorch/manylinux-cuda117",
Andrey Talman's avatar
Andrey Talman committed
115
    "cu118": "pytorch/manylinux-cuda118",
ptrblck's avatar
ptrblck committed
116
    "cu121": "pytorch/manylinux-cuda121",
117
118
119
120
}


def get_manylinux_image(cu_version):
Jeff Daily's avatar
Jeff Daily committed
121
    if cu_version == "cpu":
122
        return "pytorch/manylinux-cpu"
123
124
    elif cu_version.startswith("cu"):
        cu_suffix = cu_version[len("cu") :]
Jeff Daily's avatar
Jeff Daily committed
125
        return f"pytorch/manylinux-cuda{cu_suffix}"
126
127
    elif cu_version.startswith("rocm"):
        rocm_suffix = cu_version[len("rocm") :]
Jeff Daily's avatar
Jeff Daily committed
128
        return f"pytorch/manylinux-rocm:{rocm_suffix}"
129
130


131
132
133
def get_conda_image(cu_version):
    if cu_version == "cpu":
        return "pytorch/conda-builder:cpu"
134
135
    elif cu_version.startswith("cu"):
        cu_suffix = cu_version[len("cu") :]
Jeff Daily's avatar
Jeff Daily committed
136
        return f"pytorch/conda-builder:cuda{cu_suffix}"
137
138


139
140
141
def generate_base_workflow(
    base_workflow_name, python_version, cu_version, unicode, os_type, btype, *, filter_branch=None
):
142
143
144
145

    d = {
        "name": base_workflow_name,
        "python_version": python_version,
146
        "cu_version": cu_version,
147
148
    }

149
    if os_type != "win" and unicode:
150
        d["unicode_abi"] = "1"
151

152
153
    if os_type != "win":
        d["wheel_docker_image"] = get_manylinux_image(cu_version)
Jeff Daily's avatar
Jeff Daily committed
154
155
156
        # ROCm conda packages not yet supported
        if "rocm" not in cu_version:
            d["conda_docker_image"] = get_conda_image(cu_version)
157

158
    if filter_branch is not None:
159
        d["filters"] = {
160
            "branches": {"only": filter_branch},
161
162
163
164
            "tags": {
                # Using a raw string here to avoid having to escape
                # anything
                "only": r"/v[0-9]+(\.[0-9]+)*-rc[0-9]+/"
165
            },
166
        }
167

168
    w = f"binary_{os_type}_{btype}"
169
    return {w: d}
170
171


172
173
174
175
176
def gen_filter_branch_tree(*branches, tags_list=None):
    filter_dict = {"branches": {"only": [b for b in branches]}}
    if tags_list is not None:
        filter_dict["tags"] = {"only": tags_list}
    return filter_dict
177
178


179
def generate_upload_workflow(base_workflow_name, os_type, btype, cu_version, *, filter_branch=None):
180
181
182
183
184
185
    d = {
        "name": f"{base_workflow_name}_upload",
        "context": "org-member",
        "requires": [base_workflow_name],
    }

186
187
    if btype == "wheel":
        d["subfolder"] = "" if os_type == "macos" else cu_version + "/"
188

189
    if filter_branch is not None:
190
        d["filters"] = {
191
            "branches": {"only": filter_branch},
192
193
194
195
            "tags": {
                # Using a raw string here to avoid having to escape
                # anything
                "only": r"/v[0-9]+(\.[0-9]+)*-rc[0-9]+/"
196
            },
197
        }
198

199
200
201
    return {f"binary_{btype}_upload": d}


guyang3532's avatar
guyang3532 committed
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
def generate_smoketest_workflow(pydistro, base_workflow_name, filter_branch, python_version, os_type):

    required_build_suffix = "_upload"
    required_build_name = base_workflow_name + required_build_suffix

    smoke_suffix = f"smoke_test_{pydistro}"
    d = {
        "name": f"{base_workflow_name}_{smoke_suffix}",
        "requires": [required_build_name],
        "python_version": python_version,
    }

    if filter_branch:
        d["filters"] = gen_filter_branch_tree(filter_branch)

Nikita Shulga's avatar
Nikita Shulga committed
217
    return {f"smoke_test_{os_type}_{pydistro}": d}
guyang3532's avatar
guyang3532 committed
218
219


220
def indent(indentation, data_list):
221
    return ("\n" + " " * indentation).join(yaml.dump(data_list, default_flow_style=False).splitlines())
222
223


224
225
def cmake_workflows(indentation=6):
    jobs = []
226
227
    python_version = "3.8"
    for os_type in ["linux", "windows", "macos"]:
228
        # Skip OSX CUDA
229
        device_types = ["cpu", "gpu"] if os_type != "macos" else ["cpu"]
230
        for device in device_types:
231
            job = {"name": f"cmake_{os_type}_{device}", "python_version": python_version}
232

233
            job["cu_version"] = "cu117" if device == "gpu" else "cpu"
234
            if device == "gpu" and os_type == "linux":
235
                job["wheel_docker_image"] = "pytorch/manylinux-cuda117"
236
            jobs.append({f"cmake_{os_type}_{device}": job})
237
238
239
    return indent(indentation, jobs)


240
241
242
243
244
if __name__ == "__main__":
    d = os.path.dirname(__file__)
    env = jinja2.Environment(
        loader=jinja2.FileSystemLoader(d),
        lstrip_blocks=True,
245
        autoescape=select_autoescape(enabled_extensions=("html", "xml")),
246
        keep_trailing_newline=True,
247
248
    )

249
250
251
252
253
254
255
    with open(os.path.join(d, "config.yml"), "w") as f:
        f.write(
            env.get_template("config.yml.in").render(
                build_workflows=build_workflows,
                cmake_workflows=cmake_workflows,
            )
        )