regenerate.py 8.61 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.7", "3.8", "3.9", "3.10"]
25
CU_VERSIONS_DICT = {
26
    "linux": ["cpu", "cu116", "cu117", "rocm5.2", "rocm5.3"],
27
    "windows": ["cpu", "cu116", "cu117"],
28
29
    "macos": ["cpu"],
}
Nikita Shulga's avatar
Nikita Shulga committed
30

moto's avatar
moto committed
31

32
DOC_VERSION = ("linux", "3.8")
33

moto's avatar
moto committed
34

35
def build_workflows(prefix="", upload=False, filter_branch=None, indentation=6):
36
    w = []
37
    w += build_download_job(filter_branch)
38
39
    for os_type in ["linux", "macos", "windows"]:
        w += build_ffmpeg_job(os_type, filter_branch)
40
    for btype in ["wheel", "conda"]:
peterjc123's avatar
peterjc123 committed
41
        for os_type in ["linux", "macos", "windows"]:
moto's avatar
moto committed
42
            for python_version in PYTHON_VERSIONS:
Nikita Shulga's avatar
Nikita Shulga committed
43
                for cu_version in CU_VERSIONS_DICT[os_type]:
Matti Picus's avatar
Matti Picus committed
44
                    fb = filter_branch
45
46
47
                    if (cu_version.startswith("rocm") and btype == "conda") or (
                        os_type == "linux" and btype == "wheel"
                    ):
Matti Picus's avatar
Matti Picus committed
48
                        continue
49
50
51
                    if not fb and (
                        os_type == "linux" and btype == "wheel" and python_version == "3.8" and cu_version == "cpu"
                    ):
Matti Picus's avatar
Matti Picus committed
52
                        # the fields must match the build_docs "requires" dependency
53
                        fb = "/.*/"
54

Matti Picus's avatar
Matti Picus committed
55
                    w += build_workflow_pair(btype, os_type, python_version, cu_version, fb, prefix, upload)
56

57
58
    if not filter_branch:
        # Build on every pull request, but upload only on nightly and tags
59
60
        w += build_doc_job("/.*/")
        w += upload_doc_job("nightly")
61

62
63
64
    return indent(indentation, w)


65
66
def build_download_job(filter_branch):
    job = {
67
        "name": "download_third_parties",
68
69
70
71
    }

    if filter_branch:
        job["filters"] = gen_filter_branch_tree(filter_branch)
72
73
74
75
76
77
78
79
80
81
82
83
84
    return [{"download_third_parties": job}]


def build_ffmpeg_job(os_type, filter_branch):
    job = {
        "name": f"build_ffmpeg_{os_type}",
        "requires": ["download_third_parties"],
    }

    if filter_branch:
        job["filters"] = gen_filter_branch_tree(filter_branch)
    job["python_version"] = "foo"
    return [{f"build_ffmpeg_{os_type}": job}]
85
86


87
def build_workflow_pair(btype, os_type, python_version, cu_version, filter_branch, prefix="", upload=False):
88
89

    w = []
Nikita Shulga's avatar
Nikita Shulga committed
90
91
    base_workflow_name = f"{prefix}binary_{os_type}_{btype}_py{python_version}_{cu_version}"
    w.append(generate_base_workflow(base_workflow_name, python_version, cu_version, filter_branch, os_type, btype))
92
93

    if upload:
94
        w.append(generate_upload_workflow(base_workflow_name, filter_branch, os_type, btype, cu_version))
95

moto's avatar
moto committed
96
97
98
99
100
    if os_type != "macos":
        pydistro = "pip" if btype == "wheel" else "conda"
        w.append(
            generate_smoketest_workflow(
                pydistro, base_workflow_name, filter_branch, python_version, cu_version, os_type
101
            )
moto's avatar
moto committed
102
        )
103
104
105

    return w

moto's avatar
moto committed
106

107
108
109
110
def build_doc_job(filter_branch):
    job = {
        "name": "build_docs",
        "python_version": "3.8",
111
        "cuda_version": "cu116",
112
        "requires": [
113
            "binary_linux_conda_py3.8_cu116",
114
        ],
115
116
117
118
119
120
    }

    if filter_branch:
        job["filters"] = gen_filter_branch_tree(filter_branch)
    return [{"build_docs": job}]

moto's avatar
moto committed
121

122
123
124
def upload_doc_job(filter_branch):
    job = {
        "name": "upload_docs",
125
        "context": "org-member",
126
        "python_version": "3.8",
127
128
129
        "requires": [
            "build_docs",
        ],
130
131
132
133
134
135
    }

    if filter_branch:
        job["filters"] = gen_filter_branch_tree(filter_branch)
    return [{"upload_docs": job}]

136

137
138
139
140
def docstring_parameters_sync_job(filter_branch):
    job = {
        "name": "docstring_parameters_sync",
        "python_version": "3.8",
141
142
143
        "requires": [
            "binary_linux_wheel_py3.8_cpu",
        ],
144
145
146
147
148
149
150
    }

    if filter_branch:
        job["filters"] = gen_filter_branch_tree(filter_branch)
    return [{"docstring_parameters_sync": job}]


Nikita Shulga's avatar
Nikita Shulga committed
151
def generate_base_workflow(base_workflow_name, python_version, cu_version, filter_branch, os_type, btype):
152
153
154
155

    d = {
        "name": base_workflow_name,
        "python_version": python_version,
Nikita Shulga's avatar
Nikita Shulga committed
156
        "cuda_version": cu_version,
157
        "requires": [f"build_ffmpeg_{os_type}"],
158
159
    }

160
161
162
163
164
    if btype == "conda":
        d["conda_docker_image"] = f'pytorch/conda-builder:{cu_version.replace("cu1","cuda1")}'
    elif cu_version.startswith("cu"):
        d["wheel_docker_image"] = f'pytorch/manylinux-{cu_version.replace("cu1","cuda1")}'
    elif cu_version.startswith("rocm"):
165
        d["wheel_docker_image"] = f"pytorch/manylinux-rocm:{cu_version[len('rocm'):]}"
166

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

170
    return {f"binary_{os_type}_{btype}": d}
171
172


moto's avatar
moto committed
173
def gen_filter_branch_tree(*branches):
174
175
176
177
178
179
180
181
    return {
        "branches": {
            "only": list(branches),
        },
        "tags": {
            # Using a raw string here to avoid having to escape
            # anything
            "only": r"/v[0-9]+(\.[0-9]+)*-rc[0-9]+/"
182
        },
183
    }
184
185


186
def generate_upload_workflow(base_workflow_name, filter_branch, os_type, btype, cu_version):
187
188
189
190
191
192
    d = {
        "name": "{base_workflow_name}_upload".format(base_workflow_name=base_workflow_name),
        "context": "org-member",
        "requires": [base_workflow_name],
    }

193
194
    if btype == "wheel":
        d["subfolder"] = "" if os_type == "macos" else cu_version + "/"
195

196
197
198
199
200
201
    if filter_branch:
        d["filters"] = gen_filter_branch_tree(filter_branch)

    return {"binary_{btype}_upload".format(btype=btype): d}


Nikita Shulga's avatar
Nikita Shulga committed
202
def generate_smoketest_workflow(pydistro, base_workflow_name, filter_branch, python_version, cu_version, os_type):
203

Nikita Shulga's avatar
Nikita Shulga committed
204
    smoke_suffix = f"smoke_test_{pydistro}".format(pydistro=pydistro)
205
    d = {
Nikita Shulga's avatar
Nikita Shulga committed
206
        "name": f"{base_workflow_name}_{smoke_suffix}",
moto's avatar
moto committed
207
        "requires": [base_workflow_name],
208
        "python_version": python_version,
Nikita Shulga's avatar
Nikita Shulga committed
209
        "cuda_version": cu_version,
210
211
212
213
214
    }

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

Caroline Chen's avatar
Caroline Chen committed
215
    smoke_name = f"smoke_test_{os_type}_{pydistro}"
216
    if pydistro == "conda" and (os_type == "linux" or os_type == "windows") and cu_version != "cpu":
Caroline Chen's avatar
Caroline Chen committed
217
218
        smoke_name += "_gpu"
    return {smoke_name: d}
219
220
221
222
223
224


def indent(indentation, data_list):
    return ("\n" + " " * indentation).join(yaml.dump(data_list).splitlines())


225
226
227
228
229
230
231
232
def unittest_python_versions(os):
    return {
        "windows": PYTHON_VERSIONS[:1],
        "macos": PYTHON_VERSIONS[:1],
        "linux": PYTHON_VERSIONS,
    }.get(os)


moto's avatar
moto committed
233
def unittest_workflows(indentation=6):
moto's avatar
moto committed
234
    jobs = []
235
    jobs += build_download_job(None)
moto's avatar
moto committed
236
    for os_type in ["linux", "windows", "macos"]:
moto's avatar
moto committed
237
        for device_type in ["cpu", "gpu"]:
moto's avatar
moto committed
238
239
240
            if os_type == "macos" and device_type == "gpu":
                continue

241
            for i, python_version in enumerate(unittest_python_versions(os_type)):
moto's avatar
moto committed
242
243
                job = {
                    "name": f"unittest_{os_type}_{device_type}_py{python_version}",
moto's avatar
moto committed
244
                    "python_version": python_version,
245
                    "cuda_version": "cpu" if device_type == "cpu" else "cu116",
246
                    "requires": ["download_third_parties"],
moto's avatar
moto committed
247
                }
moto's avatar
moto committed
248
249

                jobs.append({f"unittest_{os_type}_{device_type}": job})
250
251

                if i == 0 and os_type == "linux" and device_type == "cpu":
252
253
254
255
256
257
258
                    jobs.append(
                        {
                            "stylecheck": {
                                "name": f"stylecheck_py{python_version}",
                                "python_version": python_version,
                                "cuda_version": "cpu",
                            }
259
                        }
260
                    )
moto's avatar
moto committed
261
    return indent(indentation, jobs)
moto's avatar
moto committed
262
263


264
265
266
267
268
if __name__ == "__main__":
    d = os.path.dirname(__file__)
    env = jinja2.Environment(
        loader=jinja2.FileSystemLoader(d),
        lstrip_blocks=True,
269
        autoescape=select_autoescape(enabled_extensions=("html", "xml")),
270
271
    )

272
273
274
275
276
277
278
    with open(os.path.join(d, "config.yml"), "w") as f:
        f.write(
            env.get_template("config.yml.in").render(
                build_workflows=build_workflows,
                unittest_workflows=unittest_workflows,
            )
        )
moto's avatar
moto committed
279
        f.write("\n")