regenerate.py 8.01 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

import jinja2
18
from jinja2 import select_autoescape
19
import yaml
20
21
import os.path

22

Eli Uriegas's avatar
Eli Uriegas committed
23
PYTHON_VERSIONS = ["3.6", "3.7", "3.8", "3.9"]
24
CU_VERSIONS_DICT = {"linux": ["cpu", "cu102", "cu111","cu113", "rocm4.1"],
25
                    "windows": ["cpu", "cu113"],
Nikita Shulga's avatar
Nikita Shulga committed
26
27
                    "macos": ["cpu"]}

moto's avatar
moto committed
28

29
30
DOC_VERSION = ('linux', '3.8')

moto's avatar
moto committed
31
32

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

50
51
    if not filter_branch:
        # Build on every pull request, but upload only on nightly and tags
Matti Picus's avatar
Matti Picus committed
52
        w += build_doc_job('/.*/')
53
        w += upload_doc_job('nightly')
54
55
        w += docstring_parameters_sync_job(None)

56

57
58
59
    return indent(indentation, w)


60
61
62
63
64
65
66
67
68
69
def build_download_job(filter_branch):
    job = {
        "name": "download_third_parties_nix",
    }

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


Nikita Shulga's avatar
Nikita Shulga committed
70
def build_workflow_pair(btype, os_type, python_version, cu_version, filter_branch, prefix='', upload=False):
71
72

    w = []
Nikita Shulga's avatar
Nikita Shulga committed
73
74
    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))
75
76
77

    if upload:

78
        w.append(generate_upload_workflow(base_workflow_name, filter_branch, os_type, btype, cu_version))
79

Nikita Shulga's avatar
Nikita Shulga committed
80
        if filter_branch == 'nightly' and os_type != 'macos':
81
            pydistro = 'pip' if btype == 'wheel' else 'conda'
Nikita Shulga's avatar
Nikita Shulga committed
82
            w.append(generate_smoketest_workflow(pydistro, base_workflow_name, filter_branch, python_version, cu_version, os_type))
83
84
85

    return w

moto's avatar
moto committed
86

87
88
89
90
def build_doc_job(filter_branch):
    job = {
        "name": "build_docs",
        "python_version": "3.8",
Nikita Shulga's avatar
Nikita Shulga committed
91
        "requires": ["binary_linux_wheel_py3.8_cpu", ],
92
93
94
95
96
97
    }

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

moto's avatar
moto committed
98

99
100
101
def upload_doc_job(filter_branch):
    job = {
        "name": "upload_docs",
102
        "context": "org-member",
103
        "python_version": "3.8",
moto's avatar
moto committed
104
        "requires": ["build_docs", ],
105
106
107
108
109
110
    }

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

111

112
113
114
115
def docstring_parameters_sync_job(filter_branch):
    job = {
        "name": "docstring_parameters_sync",
        "python_version": "3.8",
Nikita Shulga's avatar
Nikita Shulga committed
116
        "requires": ["binary_linux_wheel_py3.8_cpu", ],
117
118
119
120
121
122
123
    }

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


Nikita Shulga's avatar
Nikita Shulga committed
124
def generate_base_workflow(base_workflow_name, python_version, cu_version, filter_branch, os_type, btype):
125
126
127
128

    d = {
        "name": base_workflow_name,
        "python_version": python_version,
Nikita Shulga's avatar
Nikita Shulga committed
129
        "cuda_version": cu_version,
130
131
    }

132
133
    if os_type in ['linux', 'macos']:
        d['requires'] = ['download_third_parties_nix']
Nikita Shulga's avatar
Nikita Shulga committed
134
135
    if btype == 'conda':
        d['conda_docker_image'] = f'pytorch/conda-builder:{cu_version.replace("cu1","cuda1")}'
136
    elif cu_version.startswith('cu'):
Nikita Shulga's avatar
Nikita Shulga committed
137
        d['wheel_docker_image'] = f'pytorch/manylinux-{cu_version.replace("cu1","cuda1")}'
138
139
    elif cu_version.startswith('rocm'):
        d["wheel_docker_image"] = f"pytorch/manylinux-rocm:{cu_version[len('rocm'):]}"
140

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

144
    return {f"binary_{os_type}_{btype}": d}
145
146


moto's avatar
moto committed
147
def gen_filter_branch_tree(*branches):
148
149
150
151
152
153
154
155
156
157
    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]+/"
        }
    }
158
159


160
def generate_upload_workflow(base_workflow_name, filter_branch, os_type, btype, cu_version):
161
162
163
164
165
166
    d = {
        "name": "{base_workflow_name}_upload".format(base_workflow_name=base_workflow_name),
        "context": "org-member",
        "requires": [base_workflow_name],
    }

167
168
169
170
    if btype == 'wheel':
        d["subfolder"] = "" if os_type == 'macos' else cu_version + "/"


171
172
173
174
175
176
    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
177
def generate_smoketest_workflow(pydistro, base_workflow_name, filter_branch, python_version, cu_version, os_type):
178
179
180
181

    required_build_suffix = "_upload"
    required_build_name = base_workflow_name + required_build_suffix

Nikita Shulga's avatar
Nikita Shulga committed
182
    smoke_suffix = f"smoke_test_{pydistro}".format(pydistro=pydistro)
183
    d = {
Nikita Shulga's avatar
Nikita Shulga committed
184
        "name": f"{base_workflow_name}_{smoke_suffix}",
185
186
        "requires": [required_build_name],
        "python_version": python_version,
Nikita Shulga's avatar
Nikita Shulga committed
187
        "cuda_version": cu_version,
188
189
190
191
192
    }

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

Caroline Chen's avatar
Caroline Chen committed
193
194
195
196
    smoke_name = f"smoke_test_{os_type}_{pydistro}"
    if pydistro == "conda" and os_type == "linux" and cu_version != "cpu":
        smoke_name += "_gpu"
    return {smoke_name: d}
197
198
199
200
201
202


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


moto's avatar
moto committed
203
def unittest_workflows(indentation=6):
moto's avatar
moto committed
204
    jobs = []
205
    jobs += build_download_job(None)
moto's avatar
moto committed
206
    for os_type in ["linux", "windows", "macos"]:
moto's avatar
moto committed
207
        for device_type in ["cpu", "gpu"]:
moto's avatar
moto committed
208
209
210
            if os_type == "macos" and device_type == "gpu":
                continue

211
            for i, python_version in enumerate(PYTHON_VERSIONS):
moto's avatar
moto committed
212
213
                job = {
                    "name": f"unittest_{os_type}_{device_type}_py{python_version}",
moto's avatar
moto committed
214
                    "python_version": python_version,
nateanl's avatar
nateanl committed
215
                    "cuda_version": 'cpu' if device_type == "cpu" else "cu113",
moto's avatar
moto committed
216
                }
moto's avatar
moto committed
217

218
219
220
                if os_type != "windows":
                    job['requires'] = ['download_third_parties_nix']

moto's avatar
moto committed
221
                jobs.append({f"unittest_{os_type}_{device_type}": job})
222
223
224

                if i == 0 and os_type == "linux" and device_type == "cpu":
                    jobs.append({
moto's avatar
moto committed
225
                        "stylecheck": {
226
227
                            "name": f"stylecheck_py{python_version}",
                            "python_version": python_version,
228
                            "cuda_version": "cpu",
229
230
                        }
                    })
moto's avatar
moto committed
231
    return indent(indentation, jobs)
moto's avatar
moto committed
232
233


234
235
236
237
238
if __name__ == "__main__":
    d = os.path.dirname(__file__)
    env = jinja2.Environment(
        loader=jinja2.FileSystemLoader(d),
        lstrip_blocks=True,
239
        autoescape=select_autoescape(enabled_extensions=('html', 'xml')),
240
241
242
    )

    with open(os.path.join(d, 'config.yml'), 'w') as f:
moto's avatar
moto committed
243
244
245
246
        f.write(env.get_template('config.yml.in').render(
            build_workflows=build_workflows,
            unittest_workflows=unittest_workflows,
        ))
moto's avatar
moto committed
247
        f.write("\n")