Merge branch 'blender-v2.83-release'
[blender.git] / build_files / buildbot / slave_pack.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 # Runs on buildbot slave, creating a release package using the build
22 # system and zipping it into buildbot_upload.zip. This is then uploaded
23 # to the master in the next buildbot step.
24
25 import os
26 import sys
27
28 from pathlib import Path
29
30 import buildbot_utils
31
32 def get_package_name(builder, platform=None):
33     info = buildbot_utils.VersionInfo(builder)
34
35     package_name = 'blender-' + info.full_version
36     if platform:
37       package_name += '-' + platform
38     if not (builder.branch == 'master' or builder.is_release_branch):
39         if info.is_development_build:
40             package_name = builder.branch + "-" + package_name
41
42     return package_name
43
44 def sign_file_or_directory(path):
45     from codesign.simple_code_signer import SimpleCodeSigner
46     code_signer = SimpleCodeSigner()
47     code_signer.sign_file_or_directory(Path(path))
48
49
50 def create_buildbot_upload_zip(builder, package_files):
51     import zipfile
52
53     buildbot_upload_zip = os.path.join(builder.upload_dir, "buildbot_upload.zip")
54     if os.path.exists(buildbot_upload_zip):
55         os.remove(buildbot_upload_zip)
56
57     try:
58         z = zipfile.ZipFile(buildbot_upload_zip, "w", compression=zipfile.ZIP_STORED)
59         for filepath, filename in package_files:
60             print("Packaged", filename)
61             z.write(filepath, arcname=filename)
62         z.close()
63     except Exception as ex:
64         sys.stderr.write('Create buildbot_upload.zip failed: ' + str(ex) + '\n')
65         sys.exit(1)
66
67 def create_tar_xz(src, dest, package_name):
68     # One extra to remove leading os.sep when cleaning root for package_root
69     ln = len(src) + 1
70     flist = list()
71
72     # Create list of tuples containing file and archive name
73     for root, dirs, files in os.walk(src):
74         package_root = os.path.join(package_name, root[ln:])
75         flist.extend([(os.path.join(root, file), os.path.join(package_root, file)) for file in files])
76
77     import tarfile
78
79     # Set UID/GID of archived files to 0, otherwise they'd be owned by whatever
80     # user compiled the package. If root then unpacks it to /usr/local/ you get
81     # a security issue.
82     def _fakeroot(tarinfo):
83         tarinfo.gid = 0
84         tarinfo.gname = "root"
85         tarinfo.uid = 0
86         tarinfo.uname = "root"
87         return tarinfo
88
89     package = tarfile.open(dest, 'w:xz', preset=9)
90     for entry in flist:
91         package.add(entry[0], entry[1], recursive=False, filter=_fakeroot)
92     package.close()
93
94 def cleanup_files(dirpath, extension):
95     for f in os.listdir(dirpath):
96         filepath = os.path.join(dirpath, f)
97         if os.path.isfile(filepath) and f.endswith(extension):
98             os.remove(filepath)
99
100
101 def pack_mac(builder):
102     info = buildbot_utils.VersionInfo(builder)
103
104     os.chdir(builder.build_dir)
105     cleanup_files(builder.build_dir, '.dmg')
106
107     package_name = get_package_name(builder, 'macOS')
108     package_filename = package_name + '.dmg'
109     package_filepath = os.path.join(builder.build_dir, package_filename)
110
111     release_dir = os.path.join(builder.blender_dir, 'release', 'darwin')
112     buildbot_dir = os.path.join(builder.blender_dir, 'build_files', 'buildbot')
113     bundle_script = os.path.join(buildbot_dir, 'slave_bundle_dmg.py')
114
115     command = [bundle_script]
116     command += ['--dmg', package_filepath]
117     if info.is_development_build:
118         background_image = os.path.join(release_dir, 'buildbot', 'background.tif')
119         command += ['--background-image', background_image]
120     command += [builder.install_dir]
121     buildbot_utils.call(command)
122
123     create_buildbot_upload_zip(builder, [(package_filepath, package_filename)])
124
125
126 def pack_win(builder):
127     info = buildbot_utils.VersionInfo(builder)
128
129     os.chdir(builder.build_dir)
130     cleanup_files(builder.build_dir, '.zip')
131
132     # CPack will add the platform name
133     cpack_name = get_package_name(builder, None)
134     package_name = get_package_name(builder, 'windows' + str(builder.bits))
135
136     command = ['cmake', '-DCPACK_OVERRIDE_PACKAGENAME:STRING=' + cpack_name, '.']
137     buildbot_utils.call(builder.command_prefix + command)
138     command = ['cpack', '-G', 'ZIP']
139     buildbot_utils.call(builder.command_prefix + command)
140
141     package_filename = package_name + '.zip'
142     package_filepath = os.path.join(builder.build_dir, package_filename)
143     package_files = [(package_filepath, package_filename)]
144
145     if info.version_cycle == 'release':
146         # Installer only for final release builds, otherwise will get
147         # 'this product is already installed' messages.
148         command = ['cpack', '-G', 'WIX']
149         buildbot_utils.call(builder.command_prefix + command)
150
151         package_filename = package_name + '.msi'
152         package_filepath = os.path.join(builder.build_dir, package_filename)
153         sign_file_or_directory(package_filepath)
154
155         package_files += [(package_filepath, package_filename)]
156
157     create_buildbot_upload_zip(builder, package_files)
158
159
160 def pack_linux(builder):
161     blender_executable = os.path.join(builder.install_dir, 'blender')
162
163     info = buildbot_utils.VersionInfo(builder)
164
165     # Strip all unused symbols from the binaries
166     print("Stripping binaries...")
167     buildbot_utils.call(builder.command_prefix + ['strip', '--strip-all', blender_executable])
168
169     print("Stripping python...")
170     py_target = os.path.join(builder.install_dir, info.short_version)
171     buildbot_utils.call(builder.command_prefix + ['find', py_target, '-iname', '*.so', '-exec', 'strip', '-s', '{}', ';'])
172
173     # Construct package name
174     platform_name = 'linux64'
175     package_name = get_package_name(builder, platform_name)
176     package_filename = package_name + ".tar.xz"
177
178     print("Creating .tar.xz archive")
179     package_filepath = builder.install_dir + '.tar.xz'
180     create_tar_xz(builder.install_dir, package_filepath, package_name)
181
182     # Create buildbot_upload.zip
183     create_buildbot_upload_zip(builder, [(package_filepath, package_filename)])
184
185
186 if __name__ == "__main__":
187     builder = buildbot_utils.create_builder_from_arguments()
188
189     # Make sure install directory always exists
190     os.makedirs(builder.install_dir, exist_ok=True)
191
192     if builder.platform == 'mac':
193         pack_mac(builder)
194     elif builder.platform == 'win':
195         pack_win(builder)
196     elif builder.platform == 'linux':
197         pack_linux(builder)