1#!/usr/bin/env python3
2# Copyright 2016 gRPC authors.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15"""Definition of targets to build distribution packages."""
16
17import os.path
18import sys
19
20sys.path.insert(0, os.path.abspath('..'))
21import python_utils.jobset as jobset
22
23
24def create_docker_jobspec(name,
25                          dockerfile_dir,
26                          shell_command,
27                          environ={},
28                          flake_retries=0,
29                          timeout_retries=0):
30    """Creates jobspec for a task running under docker."""
31    environ = environ.copy()
32
33    docker_args = []
34    for k, v in list(environ.items()):
35        docker_args += ['-e', '%s=%s' % (k, v)]
36    docker_env = {
37        'DOCKERFILE_DIR': dockerfile_dir,
38        'DOCKER_RUN_SCRIPT': 'tools/run_tests/dockerize/docker_run.sh',
39        'DOCKER_RUN_SCRIPT_COMMAND': shell_command,
40        'OUTPUT_DIR': 'artifacts'
41    }
42    jobspec = jobset.JobSpec(
43        cmdline=['tools/run_tests/dockerize/build_and_run_docker.sh'] +
44        docker_args,
45        environ=docker_env,
46        shortname='build_package.%s' % (name),
47        timeout_seconds=30 * 60,
48        flake_retries=flake_retries,
49        timeout_retries=timeout_retries)
50    return jobspec
51
52
53def create_jobspec(name,
54                   cmdline,
55                   environ=None,
56                   cwd=None,
57                   shell=False,
58                   flake_retries=0,
59                   timeout_retries=0,
60                   cpu_cost=1.0):
61    """Creates jobspec."""
62    jobspec = jobset.JobSpec(cmdline=cmdline,
63                             environ=environ,
64                             cwd=cwd,
65                             shortname='build_package.%s' % (name),
66                             timeout_seconds=10 * 60,
67                             flake_retries=flake_retries,
68                             timeout_retries=timeout_retries,
69                             cpu_cost=cpu_cost,
70                             shell=shell)
71    return jobspec
72
73
74class CSharpPackage:
75    """Builds C# packages."""
76
77    def __init__(self, platform):
78        self.platform = platform
79        self.labels = ['package', 'csharp', self.platform]
80        self.name = 'csharp_package_nuget_%s' % self.platform
81        self.labels += ['nuget']
82
83    def pre_build_jobspecs(self):
84        return []
85
86    def build_jobspec(self, inner_jobs=None):
87        del inner_jobs  # arg unused as there is little opportunity for parallelizing
88        environ = {
89            'GRPC_CSHARP_BUILD_SINGLE_PLATFORM_NUGET':
90                os.getenv('GRPC_CSHARP_BUILD_SINGLE_PLATFORM_NUGET', '')
91        }
92
93        build_script = 'src/csharp/build_nuget.sh'
94
95        if self.platform == 'linux':
96            return create_docker_jobspec(
97                self.name,
98                'tools/dockerfile/test/csharp_debian11_x64',
99                build_script,
100                environ=environ)
101        else:
102            repo_root = os.path.join(os.path.dirname(os.path.abspath(__file__)),
103                                     '..', '..', '..')
104            environ['EXTERNAL_GIT_ROOT'] = repo_root
105            return create_jobspec(self.name, ['bash', build_script],
106                                  environ=environ)
107
108    def __str__(self):
109        return self.name
110
111
112class RubyPackage:
113    """Collects ruby gems created in the artifact phase"""
114
115    def __init__(self):
116        self.name = 'ruby_package'
117        self.labels = ['package', 'ruby', 'linux']
118
119    def pre_build_jobspecs(self):
120        return []
121
122    def build_jobspec(self, inner_jobs=None):
123        del inner_jobs  # arg unused as this step simply collects preexisting artifacts
124        return create_docker_jobspec(
125            self.name, 'tools/dockerfile/grpc_artifact_centos6_x64',
126            'tools/run_tests/artifacts/build_package_ruby.sh')
127
128
129class PythonPackage:
130    """Collects python eggs and wheels created in the artifact phase"""
131
132    def __init__(self):
133        self.name = 'python_package'
134        self.labels = ['package', 'python', 'linux']
135
136    def pre_build_jobspecs(self):
137        return []
138
139    def build_jobspec(self, inner_jobs=None):
140        del inner_jobs  # arg unused as this step simply collects preexisting artifacts
141        # since the python package build does very little, we can use virtually
142        # any image that has new-enough python, so reusing one of the images used
143        # for artifact building seems natural.
144        return create_docker_jobspec(
145            self.name,
146            'tools/dockerfile/grpc_artifact_python_manylinux2014_x64',
147            'tools/run_tests/artifacts/build_package_python.sh',
148            environ={'PYTHON': '/opt/python/cp39-cp39/bin/python'})
149
150
151class PHPPackage:
152    """Copy PHP PECL package artifact"""
153
154    def __init__(self):
155        self.name = 'php_package'
156        self.labels = ['package', 'php', 'linux']
157
158    def pre_build_jobspecs(self):
159        return []
160
161    def build_jobspec(self, inner_jobs=None):
162        del inner_jobs  # arg unused as this step simply collects preexisting artifacts
163        return create_docker_jobspec(
164            self.name, 'tools/dockerfile/grpc_artifact_centos6_x64',
165            'tools/run_tests/artifacts/build_package_php.sh')
166
167
168def targets():
169    """Gets list of supported targets"""
170    return [
171        CSharpPackage('linux'),
172        CSharpPackage('macos'),
173        CSharpPackage('windows'),
174        RubyPackage(),
175        PythonPackage(),
176        PHPPackage()
177    ]
178