regenerate.py 7.15 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"]
Nikita Shulga's avatar
Nikita Shulga committed
24
25
26
27
CU_VERSIONS_DICT = {"linux": ["cpu", "cu102", "cu111"],
                    "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
39
                for cu_version in CU_VERSIONS_DICT[os_type]:
                    w += build_workflow_pair(btype, os_type, python_version, cu_version, filter_branch, prefix, upload)
40

41
42
43
44
    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')
45
46
        w += docstring_parameters_sync_job(None)

47

48
49
50
    return indent(indentation, w)


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

    w = []
Nikita Shulga's avatar
Nikita Shulga committed
64
65
    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))
66
67
68

    if upload:

69
        w.append(generate_upload_workflow(base_workflow_name, filter_branch, btype))
70

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

    return w

moto's avatar
moto committed
77

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

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

moto's avatar
moto committed
89

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

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

102

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

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


Nikita Shulga's avatar
Nikita Shulga committed
115
def generate_base_workflow(base_workflow_name, python_version, cu_version, filter_branch, os_type, btype):
116
117
118
119

    d = {
        "name": base_workflow_name,
        "python_version": python_version,
Nikita Shulga's avatar
Nikita Shulga committed
120
        "cuda_version": cu_version,
121
122
    }

123
124
    if os_type in ['linux', 'macos']:
        d['requires'] = ['download_third_parties_nix']
Nikita Shulga's avatar
Nikita Shulga committed
125
126
127
128
    if btype == 'conda':
        d['conda_docker_image'] = f'pytorch/conda-builder:{cu_version.replace("cu1","cuda1")}'
    elif cu_version != 'cpu':
        d['wheel_docker_image'] = f'pytorch/manylinux-{cu_version.replace("cu1","cuda1")}'
129

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

133
    return {f"binary_{os_type}_{btype}": d}
134
135


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


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
162
def generate_smoketest_workflow(pydistro, base_workflow_name, filter_branch, python_version, cu_version, os_type):
163
164
165
166

    required_build_suffix = "_upload"
    required_build_name = base_workflow_name + required_build_suffix

Nikita Shulga's avatar
Nikita Shulga committed
167
    smoke_suffix = f"smoke_test_{pydistro}".format(pydistro=pydistro)
168
    d = {
Nikita Shulga's avatar
Nikita Shulga committed
169
        "name": f"{base_workflow_name}_{smoke_suffix}",
170
171
        "requires": [required_build_name],
        "python_version": python_version,
Nikita Shulga's avatar
Nikita Shulga committed
172
        "cuda_version": cu_version,
173
174
175
176
177
    }

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

Nikita Shulga's avatar
Nikita Shulga committed
178
    return {f"smoke_test_{os_type}_{pydistro}": d}
179
180
181
182
183
184


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


moto's avatar
moto committed
185
def unittest_workflows(indentation=6):
moto's avatar
moto committed
186
    jobs = []
187
    jobs += build_download_job(None)
moto's avatar
moto committed
188
    for os_type in ["linux", "windows", "macos"]:
moto's avatar
moto committed
189
        for device_type in ["cpu", "gpu"]:
moto's avatar
moto committed
190
191
192
            if os_type == "macos" and device_type == "gpu":
                continue

193
            for i, python_version in enumerate(PYTHON_VERSIONS):
moto's avatar
moto committed
194
195
                job = {
                    "name": f"unittest_{os_type}_{device_type}_py{python_version}",
moto's avatar
moto committed
196
                    "python_version": python_version,
Nikita Shulga's avatar
Nikita Shulga committed
197
                    "cuda_version": 'cpu' if device_type=="cpu" else "cu102",
moto's avatar
moto committed
198
                }
moto's avatar
moto committed
199

200
201
202
                if os_type != "windows":
                    job['requires'] = ['download_third_parties_nix']

moto's avatar
moto committed
203
                jobs.append({f"unittest_{os_type}_{device_type}": job})
204
205
206

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


216
217
218
219
220
if __name__ == "__main__":
    d = os.path.dirname(__file__)
    env = jinja2.Environment(
        loader=jinja2.FileSystemLoader(d),
        lstrip_blocks=True,
221
        autoescape=select_autoescape(enabled_extensions=('html', 'xml')),
222
223
224
    )

    with open(os.path.join(d, 'config.yml'), 'w') as f:
moto's avatar
moto committed
225
226
227
228
        f.write(env.get_template('config.yml.in').render(
            build_workflows=build_workflows,
            unittest_workflows=unittest_workflows,
        ))
moto's avatar
moto committed
229
        f.write("\n")