regenerate.py 8.72 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", "cu102", "cu116", "cu117", "rocm5.1.1", "rocm5.2"],
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"]:
42
43
44
            # linux wheel are no longer done in circleci
            if os_type == "linux" and btype == "wheel":
                continue
moto's avatar
moto committed
45
            for python_version in PYTHON_VERSIONS:
Nikita Shulga's avatar
Nikita Shulga committed
46
                for cu_version in CU_VERSIONS_DICT[os_type]:
Matti Picus's avatar
Matti Picus committed
47
                    fb = filter_branch
48
                    if cu_version.startswith("rocm") and btype == "conda":
Matti Picus's avatar
Matti Picus committed
49
                        continue
50
51
52
                    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
53
                        # the fields must match the build_docs "requires" dependency
54
                        fb = "/.*/"
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
        # w += docstring_parameters_sync_job(None)
62

63
64
65
    return indent(indentation, w)


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

    if filter_branch:
        job["filters"] = gen_filter_branch_tree(filter_branch)
73
74
75
76
77
78
79
80
81
82
83
84
85
    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}]
86
87


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

    w = []
Nikita Shulga's avatar
Nikita Shulga committed
91
92
    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))
93
94
95

    if upload:

96
        w.append(generate_upload_workflow(base_workflow_name, filter_branch, os_type, btype, cu_version))
97

moto's avatar
moto committed
98
99
100
101
102
    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
103
            )
moto's avatar
moto committed
104
        )
105
106
107

    return w

moto's avatar
moto committed
108

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

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

moto's avatar
moto committed
123

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

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

138

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

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


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

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

162
163
164
165
166
    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"):
167
        d["wheel_docker_image"] = f"pytorch/manylinux-rocm:{cu_version[len('rocm'):]}"
168

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

172
    return {f"binary_{os_type}_{btype}": d}
173
174


moto's avatar
moto committed
175
def gen_filter_branch_tree(*branches):
176
177
178
179
180
181
182
183
    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]+/"
184
        },
185
    }
186
187


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

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

198
199
200
201
202
203
    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
204
def generate_smoketest_workflow(pydistro, base_workflow_name, filter_branch, python_version, cu_version, os_type):
205

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

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

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


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


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


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

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

                jobs.append({f"unittest_{os_type}_{device_type}": job})
252
253

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


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

274
275
276
277
278
279
280
    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
281
        f.write("\n")