regenerate.py 7.42 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]:
39
40
41
42
                    if cu_version.startswith("rocm") and btype=="conda":
                        pass
                    else:
                        w += build_workflow_pair(btype, os_type, python_version, cu_version, filter_branch, prefix, upload)
43

44
45
46
47
    if not filter_branch:
        # Build on every pull request, but upload only on nightly and tags
        w += build_doc_job(None)
        w += upload_doc_job('nightly')
48
49
        w += docstring_parameters_sync_job(None)

50

51
52
53
    return indent(indentation, w)


54
55
56
57
58
59
60
61
62
63
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
64
def build_workflow_pair(btype, os_type, python_version, cu_version, filter_branch, prefix='', upload=False):
65
66

    w = []
Nikita Shulga's avatar
Nikita Shulga committed
67
68
    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))
69
70
71

    if upload:

72
        w.append(generate_upload_workflow(base_workflow_name, filter_branch, btype))
73

Nikita Shulga's avatar
Nikita Shulga committed
74
        if filter_branch == 'nightly' and os_type != 'macos':
75
            pydistro = 'pip' if btype == 'wheel' else 'conda'
Nikita Shulga's avatar
Nikita Shulga committed
76
            w.append(generate_smoketest_workflow(pydistro, base_workflow_name, filter_branch, python_version, cu_version, os_type))
77
78
79

    return w

moto's avatar
moto committed
80

81
82
83
84
def build_doc_job(filter_branch):
    job = {
        "name": "build_docs",
        "python_version": "3.8",
Nikita Shulga's avatar
Nikita Shulga committed
85
        "requires": ["binary_linux_wheel_py3.8_cpu", ],
86
87
88
89
90
91
    }

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

moto's avatar
moto committed
92

93
94
95
def upload_doc_job(filter_branch):
    job = {
        "name": "upload_docs",
96
        "context": "org-member",
97
        "python_version": "3.8",
moto's avatar
moto committed
98
        "requires": ["build_docs", ],
99
100
101
102
103
104
    }

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

105

106
107
108
109
def docstring_parameters_sync_job(filter_branch):
    job = {
        "name": "docstring_parameters_sync",
        "python_version": "3.8",
Nikita Shulga's avatar
Nikita Shulga committed
110
        "requires": ["binary_linux_wheel_py3.8_cpu", ],
111
112
113
114
115
116
117
    }

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


Nikita Shulga's avatar
Nikita Shulga committed
118
def generate_base_workflow(base_workflow_name, python_version, cu_version, filter_branch, os_type, btype):
119
120
121
122

    d = {
        "name": base_workflow_name,
        "python_version": python_version,
Nikita Shulga's avatar
Nikita Shulga committed
123
        "cuda_version": cu_version,
124
125
    }

126
127
    if os_type in ['linux', 'macos']:
        d['requires'] = ['download_third_parties_nix']
Nikita Shulga's avatar
Nikita Shulga committed
128
129
    if btype == 'conda':
        d['conda_docker_image'] = f'pytorch/conda-builder:{cu_version.replace("cu1","cuda1")}'
130
    elif cu_version.startswith('cu'):
Nikita Shulga's avatar
Nikita Shulga committed
131
        d['wheel_docker_image'] = f'pytorch/manylinux-{cu_version.replace("cu1","cuda1")}'
132
133
    elif cu_version.startswith('rocm'):
        d["wheel_docker_image"] = f"pytorch/manylinux-rocm:{cu_version[len('rocm'):]}"
134

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

138
    return {f"binary_{os_type}_{btype}": d}
139
140


moto's avatar
moto committed
141
def gen_filter_branch_tree(*branches):
142
143
144
145
146
147
148
149
150
151
    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]+/"
        }
    }
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166


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
167
def generate_smoketest_workflow(pydistro, base_workflow_name, filter_branch, python_version, cu_version, os_type):
168
169
170
171

    required_build_suffix = "_upload"
    required_build_name = base_workflow_name + required_build_suffix

Nikita Shulga's avatar
Nikita Shulga committed
172
    smoke_suffix = f"smoke_test_{pydistro}".format(pydistro=pydistro)
173
    d = {
Nikita Shulga's avatar
Nikita Shulga committed
174
        "name": f"{base_workflow_name}_{smoke_suffix}",
175
176
        "requires": [required_build_name],
        "python_version": python_version,
Nikita Shulga's avatar
Nikita Shulga committed
177
        "cuda_version": cu_version,
178
179
180
181
182
    }

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

Nikita Shulga's avatar
Nikita Shulga committed
183
    return {f"smoke_test_{os_type}_{pydistro}": d}
184
185
186
187
188
189


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


moto's avatar
moto committed
190
def unittest_workflows(indentation=6):
moto's avatar
moto committed
191
    jobs = []
192
    jobs += build_download_job(None)
moto's avatar
moto committed
193
    for os_type in ["linux", "windows", "macos"]:
moto's avatar
moto committed
194
        for device_type in ["cpu", "gpu"]:
moto's avatar
moto committed
195
196
197
            if os_type == "macos" and device_type == "gpu":
                continue

198
            for i, python_version in enumerate(PYTHON_VERSIONS):
moto's avatar
moto committed
199
200
                job = {
                    "name": f"unittest_{os_type}_{device_type}_py{python_version}",
moto's avatar
moto committed
201
                    "python_version": python_version,
Nikita Shulga's avatar
Nikita Shulga committed
202
                    "cuda_version": 'cpu' if device_type=="cpu" else "cu102",
moto's avatar
moto committed
203
                }
moto's avatar
moto committed
204

205
206
207
                if os_type != "windows":
                    job['requires'] = ['download_third_parties_nix']

moto's avatar
moto committed
208
                jobs.append({f"unittest_{os_type}_{device_type}": job})
209
210
211

                if i == 0 and os_type == "linux" and device_type == "cpu":
                    jobs.append({
moto's avatar
moto committed
212
                        "stylecheck": {
213
214
                            "name": f"stylecheck_py{python_version}",
                            "python_version": python_version,
Nikita Shulga's avatar
Nikita Shulga committed
215
                            "cuda_version": 'cpu' if device_type=="cpu" else "cu102",
216
217
                        }
                    })
moto's avatar
moto committed
218
    return indent(indentation, jobs)
moto's avatar
moto committed
219
220


221
222
223
224
225
if __name__ == "__main__":
    d = os.path.dirname(__file__)
    env = jinja2.Environment(
        loader=jinja2.FileSystemLoader(d),
        lstrip_blocks=True,
226
        autoescape=select_autoescape(enabled_extensions=('html', 'xml')),
227
228
229
    )

    with open(os.path.join(d, 'config.yml'), 'w') as f:
moto's avatar
moto committed
230
231
232
233
        f.write(env.get_template('config.yml.in').render(
            build_workflows=build_workflows,
            unittest_workflows=unittest_workflows,
        ))
moto's avatar
moto committed
234
        f.write("\n")