Add a new subproject 'win-flex-bison-binaries'
[platform/upstream/gstreamer.git] / msys2_setup.py
1 #!/usr/bin/env python3
2 """Setup meson based GStreamer uninstalled environment based on msys2."""
3
4 import argparse
5 import itertools
6 import os
7 import re
8 import sys
9 import shlex
10 import shutil
11 import subprocess
12 import tempfile
13
14 from common import git
15 from setup import GstBuildConfigurer
16
17
18 PROJECTNAME = "GStreamer build"
19
20 ROOTDIR = os.path.abspath(os.path.dirname(__file__))
21
22
23 class Msys2Configurer(GstBuildConfigurer):
24     MESON_GIT = 'https://github.com/mesonbuild/meson.git'
25     DEPENDENCIES = ['git',
26                     'bison',
27                     'mingw-w64-x86_64-pkg-config',
28                     'mingw-w64-x86_64-ninja',
29                     'mingw-w64-x86_64-libxml2',
30                     'mingw-w64-x86_64-ffmpeg',
31                     'mingw-w64-x86_64-python3',
32                     'mingw-w64-x86_64-json-glib']
33     LIBNAME_EXCEPTIONS = {
34         r'^zlib1.lib$': 'z.lib',
35         r'^nettle-.*': 'nettle.lib',
36         r'^hogweed-.*': 'hogweed.lib',
37         # Fancy, but it seems to be the correct way to do it
38         r'^eay32.lib$': 'crypto.lib',
39         r'^ssleay32.lib$': 'ssl.lib',
40     }
41
42     def get_libname(self, dll_name):
43         lib_name = re.sub(r'(?:lib)?(.*?)(?:-\d+)?\.dll', r'\1.lib', dll_name)
44
45         for exception_name, exception_libname in self.LIBNAME_EXCEPTIONS.items():
46             if re.findall(exception_name, lib_name):
47                 return exception_libname
48         return lib_name
49
50     def make_lib(self, lib, dll, dll_name):
51         print('%s... ' % os.path.basename(lib), end='', flush=True)
52         try:
53             os.remove(lib)
54         except FileNotFoundError:
55             pass
56
57         dumpbin = subprocess.check_output(['dumpbin', '/exports', dll])
58         lines = dumpbin.decode().splitlines()
59         export_start = [i for i in enumerate(
60             lines) if i[1].find('ordinal hint') != -1][0][0] + 2
61         exports = itertools.takewhile(lambda x: x != '', lines[export_start:])
62         exports = [i.split() for i in exports]
63         def_file = tempfile.NamedTemporaryFile(
64             suffix='.def', delete=False, mode='w')
65         def_file.write('LIBRARY ' + dll_name + '\r\n')
66         def_file.write('EXPORTS\r\n')
67         for tmp in exports:
68             ordinal, name = tmp[0], tmp[3]
69             def_file.write(name + ' @' + ordinal + '\r\n')
70         def_file.close()
71         subprocess.check_output(['lib', '/def:' + def_file.name,
72                                  '/out:' + lib])
73         os.remove(def_file.name)
74
75     def make_lib_if_needed(self, dll):
76         if not dll.endswith('.dll'):
77             return
78
79         lib_dir, dll_name = os.path.split(dll)
80         if lib_dir.endswith('bin'):
81             lib_dir = lib_dir[:-3] + 'lib'
82
83         lib_name = self.get_libname(dll_name)
84         lib = os.path.join(lib_dir, lib_name)
85         if os.path.exists(lib) and os.stat(dll).st_mtime_ns < os.stat(lib).st_mtime_ns:
86             return
87
88         print('Generating .lib file for %s ...' % os.path.basename(dll), end='', flush=True)
89         self.make_lib(lib, dll, dll_name)
90         print('DONE', flush=True)
91
92     def make_libs(self):
93         base = os.path.join(self.options.msys2_path, 'mingw64', 'bin')
94         for f in os.listdir(base):
95             if f.endswith('.dll'):
96                 self.make_lib_if_needed(os.path.join(base, f))
97
98     def get_configs(self):
99         return GstBuildConfigurer.get_configs(self) + [
100             '-D' + m + ':disable_introspection=true' for m in [
101                 'gst-devtools', 'gstreamer', 'gst-plugins-base',
102                 'gst-editing-services']]
103
104     def setup(self, args):
105         if not os.path.exists(self.options.msys2_path):
106             print("msys2 not found in %s. Please make sure to install"
107                   " (from http://msys2.github.io/) specify --msys2-path"
108                   " if you did not install in the default directory.", flush=True)
109             return False
110
111         for path in ['mingw64/bin', 'bin', 'usr/bin']:
112             os.environ['PATH'] = os.environ.get(
113                 'PATH', '') + os.pathsep + os.path.normpath(os.path.join(self.options.msys2_path, path))
114         os.environ['PATH'] = os.environ['PATH'].replace(';;', ';')
115         os.environ['PKG_CONFIG_PATH'] = os.environ.get(
116             'PKG_CONFIG_PATH', '') + ':/mingw64/lib/pkgconfig:/mingw64/share/pkgconfig'
117
118         subprocess.check_call(['pacman', '-S', '--needed', '--noconfirm'] + self.DEPENDENCIES)
119         source_path = os.path.abspath(os.path.curdir)
120
121         print('Making sure meson is present in root folder... ', end='', flush=True)
122         if not os.path.isdir(os.path.join(source_path, 'meson')):
123             print('\nCloning meson', flush=True)
124             git('clone', self.MESON_GIT, repository_path=source_path)
125         else:
126             print('\nDONE', flush=True)
127
128         print("Making libs", flush=True)
129         self.make_libs()
130         print("Done making .lib files.", flush=True)
131         if not os.path.exists(os.path.join(source_path, 'build', 'build.ninja')) or \
132                 self.options.reconfigure:
133             print("Running meson", flush=True)
134             if not self.configure_meson():
135                 return False
136
137         try:
138             if not args:
139                 print("Getting into msys2 environment", flush=True)
140                 subprocess.check_call([sys.executable,
141                                 os.path.join(source_path, 'gst-uninstalled.py'),
142                                 '--builddir', os.path.join(source_path, 'build')])
143             else:
144                 print("Running %s" ' '.join(args), flush=True)
145                 res = subprocess.check_call(args)
146         except subprocess.CalledProcessError as e:
147             return False
148
149         return True
150
151
152 if __name__ == "__main__":
153     parser = argparse.ArgumentParser(description='Process some integers.')
154     parser.add_argument("--no-error", action='store_true',
155                         default=False, help="Do not error out on warnings")
156     parser.add_argument("--reconfigure", action='store_true',
157                         default=False, help='Force a full reconfiguration'
158                         ' meaning the build/ folder is removed.'
159                         ' You can also use `ninja reconfigure` to just'
160                         ' make sure meson is rerun but the build folder'
161                         ' is kept.')
162     if os.name != 'nt':
163         print("Using this script outside windows does not make sense.", flush=True)
164         exit(1)
165
166     parser.add_argument("-m", "--msys2-path", dest="msys2_path",
167                         help="Where to find msys2 root directory."
168                         "(deactivates msys if unset)",
169                         default="C:\msys64")
170
171     parser.add_argument("-c", "--command", dest="command",
172                         help="Command to run instead of entering environment.",
173                         default="")
174     options, args = parser.parse_known_args()
175
176     if not shutil.which('cl'):
177         print("Can not find MSVC on windows,"
178                 " make sure you are in a 'Visual Studio"
179                 " Native Tools Command Prompt'", flush=True)
180         exit(1)
181
182     configurer = Msys2Configurer(options, args)
183
184     exit(not configurer.setup(shlex.split(options.command)))