regenerate.py 7.77 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","rocm4.1"],
Nikita Shulga's avatar
Nikita Shulga committed
25
26
27
                    "windows": ["cpu", "cu102", "cu111"],
                    "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, btype))
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
161
162
163
164
165
166
167
168
169
170
171
172


def generate_upload_workflow(base_workflow_name, filter_branch, btype):
    d = {
        "name": "{base_workflow_name}_upload".format(base_workflow_name=base_workflow_name),
        "context": "org-member",
        "requires": [base_workflow_name],
    }

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

    required_build_suffix = "_upload"
    required_build_name = base_workflow_name + required_build_suffix

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

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

Nikita Shulga's avatar
Nikita Shulga committed
189
    return {f"smoke_test_{os_type}_{pydistro}": d}
190
191
192
193
194
195


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


moto's avatar
moto committed
196
def unittest_workflows(indentation=6):
moto's avatar
moto committed
197
    jobs = []
198
    jobs += build_download_job(None)
moto's avatar
moto committed
199
    for os_type in ["linux", "windows", "macos"]:
moto's avatar
moto committed
200
        for device_type in ["cpu", "gpu"]:
moto's avatar
moto committed
201
202
203
            if os_type == "macos" and device_type == "gpu":
                continue

204
            for i, python_version in enumerate(PYTHON_VERSIONS):
moto's avatar
moto committed
205
206
                job = {
                    "name": f"unittest_{os_type}_{device_type}_py{python_version}",
moto's avatar
moto committed
207
                    "python_version": python_version,
Nikita Shulga's avatar
Nikita Shulga committed
208
                    "cuda_version": 'cpu' if device_type=="cpu" else "cu102",
moto's avatar
moto committed
209
                }
moto's avatar
moto committed
210

211
212
213
                if os_type != "windows":
                    job['requires'] = ['download_third_parties_nix']

moto's avatar
moto committed
214
                jobs.append({f"unittest_{os_type}_{device_type}": job})
215
216
217

                if i == 0 and os_type == "linux" and device_type == "cpu":
                    jobs.append({
moto's avatar
moto committed
218
                        "stylecheck": {
219
220
                            "name": f"stylecheck_py{python_version}",
                            "python_version": python_version,
Nikita Shulga's avatar
Nikita Shulga committed
221
                            "cuda_version": 'cpu' if device_type=="cpu" else "cu102",
222
223
                        }
                    })
moto's avatar
moto committed
224
    return indent(indentation, jobs)
moto's avatar
moto committed
225
226


227
228
229
230
231
if __name__ == "__main__":
    d = os.path.dirname(__file__)
    env = jinja2.Environment(
        loader=jinja2.FileSystemLoader(d),
        lstrip_blocks=True,
232
        autoescape=select_autoescape(enabled_extensions=('html', 'xml')),
233
234
235
    )

    with open(os.path.join(d, 'config.yml'), 'w') as f:
moto's avatar
moto committed
236
237
238
239
        f.write(env.get_template('config.yml.in').render(
            build_workflows=build_workflows,
            unittest_workflows=unittest_workflows,
        ))
moto's avatar
moto committed
240
        f.write("\n")