-
-
Notifications
You must be signed in to change notification settings - Fork 51
/
prebuild.py
220 lines (179 loc) · 8.19 KB
/
prebuild.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#!python
# The prebuild script is intended to simplify life for developers and dev-ops. It's repsonsible for acquiring
# tools required by the build as well as dependencies on which we rely.
#
# By using this script, we can reduce the requirements for a developer getting started to:
#
# * A working C++ dev environment like visual studio, xcode, gcc, or clang
# * Qt
# * CMake
# * Python 3.x
#
# The function of the build script is to acquire, if not already present, all the other build requirements
# The build script should be idempotent. If you run it with the same arguments multiple times, that should
# have no negative impact on the subsequent build times (i.e. re-running the prebuild script should not
# trigger a header change that causes files to be rebuilt). Subsequent runs after the first run should
# execute quickly, determining that no work is to be done
import hifi_singleton
import hifi_utils
import hifi_android
import hifi_vcpkg
import hifi_qt
import argparse
import concurrent
import hashlib
import importlib
import json
import os
import platform
import shutil
import ssl
import sys
import re
import tempfile
import time
import functools
import subprocess
import logging
from uuid import uuid4
from contextlib import contextmanager
print = functools.partial(print, flush=True)
class TrackableLogger(logging.Logger):
guid = str(uuid4())
def _log(self, msg, *args, **kwargs):
x = {'guid': self.guid}
if 'extra' in kwargs:
kwargs['extra'].update(x)
else:
kwargs['extra'] = x
super()._log(msg, *args, **kwargs)
logging.setLoggerClass(TrackableLogger)
logger = logging.getLogger('prebuild')
@contextmanager
def timer(name):
''' Print the elapsed time a context's execution takes to execute '''
start = time.time()
yield
# Please take care when modifiying this print statement.
# Log parsing logic may depend on it.
logger.info('%s took %.3f secs' % (name, time.time() - start))
def parse_args():
# our custom ports, relative to the script location
defaultPortsPath = hifi_utils.scriptRelative('cmake', 'ports')
from argparse import ArgumentParser
parser = ArgumentParser(description='Prepare build dependencies.')
parser.add_argument('--android', type=str)
parser.add_argument('--debug', action='store_true')
parser.add_argument('--force-bootstrap', action='store_true')
parser.add_argument('--force-build', action='store_true')
parser.add_argument('--release-type', type=str, default="DEV", help="DEV, PR, or PRODUCTION")
parser.add_argument('--vcpkg-root', type=str, help='The location of the vcpkg distribution')
parser.add_argument('--vcpkg-build-type', type=str, help='Could be `release` or `debug`. By default it doesn`t set the build-type')
parser.add_argument('--vcpkg-skip-clean', action='store_true', help='Skip the cleanup of vcpkg downloads and packages folders after vcpkg build completition.')
parser.add_argument('--build-root', required=True, type=str, help='The location of the cmake build')
parser.add_argument('--ports-path', type=str, default=defaultPortsPath)
parser.add_argument('--ci-build', action='store_true', default=os.getenv('CI_BUILD') is not None)
parser.add_argument('--get-vcpkg-id', action='store_true', help='Get the VCPKG ID, the hash path of the full VCPKG path')
parser.add_argument('--get-vcpkg-path', action='store_true', help='Get the full VCPKG path, ID included.')
parser.add_argument('--quiet', action='store_true', default=False, help='Quiet mode with less output')
if True:
args = parser.parse_args()
else:
args = parser.parse_args(['--android', 'questInterface', '--build-root', 'C:/git/overte/android/apps/questInterface/.externalNativeBuild/cmake/debug/arm64-v8a'])
return args
def main():
# Fixup env variables. Leaving `USE_CCACHE` on will cause scribe to fail to build
# VCPKG_ROOT seems to cause confusion on Windows systems that previously used it for
# building OpenSSL
removeEnvVars = ['VCPKG_ROOT', 'USE_CCACHE']
for var in removeEnvVars:
if var in os.environ:
del os.environ[var]
args = parse_args()
if args.get_vcpkg_id or args.get_vcpkg_path:
# These arguments need quiet mode to avoid confusing scripts that use them.
args.quiet = True
if not args.quiet:
print(sys.argv)
if args.ci_build:
logging.basicConfig(datefmt='%H:%M:%S', format='%(asctime)s %(guid)s %(message)s', level=logging.INFO)
logger.info('start')
pm = hifi_vcpkg.VcpkgRepo(args)
if args.get_vcpkg_id:
print(pm.id)
exit(0)
if args.get_vcpkg_path:
print(pm.path)
exit(0)
assets_url = hifi_utils.readEnviromentVariableFromFile(args.build_root, 'EXTERNAL_BUILD_ASSETS')
# OS dependent information
system = platform.system()
if 'Windows' == system and 'CI_BUILD' in os.environ and os.environ["CI_BUILD"] == "Github":
logger.info("Downloading NSIS")
with timer('NSIS'):
hifi_utils.downloadAndExtract(assets_url + '/dependencies/NSIS-hifi-plugins-1.0.tgz', "C:/Program Files (x86)")
qtInstallPath = None
# If not android, install our Qt build
if not args.android:
qt = hifi_qt.QtDownloader(args)
qtInstallPath = qt.cmakePath
if qtInstallPath is not None:
# qtInstallPath is None when we're doing a system Qt build
print("cmake path: " + qtInstallPath)
with hifi_singleton.Singleton(qt.lockFile) as lock:
with timer('Qt'):
qt.installQt()
qt.writeConfig()
else:
if (os.environ["OVERTE_USE_SYSTEM_QT"]):
if not args.quiet:
print("System Qt selected")
else:
raise Exception("Internal error: System Qt not selected, but hifi_qt.py failed to return a cmake path")
if qtInstallPath is not None:
pm.writeVar('QT_CMAKE_PREFIX_PATH', qtInstallPath)
# Only allow one instance of the program to run at a time
if qtInstallPath is not None:
pm.writeVar('QT_CMAKE_PREFIX_PATH', qtInstallPath)
# Only allow one instance of the program to run at a time
with hifi_singleton.Singleton(pm.lockFile) as lock:
with timer('Bootstraping'):
if not pm.upToDate():
pm.bootstrap()
# Always write the tag, even if we changed nothing. This
# allows vcpkg to reclaim disk space by identifying directories with
# tags that haven't been touched in a long time
pm.writeTag()
# Grab our required dependencies:
# * build host tools, like spirv-cross and scribe
# * build client dependencies like openssl and nvtt
with timer('Setting up dependencies'):
pm.setupDependencies(qt=qtInstallPath)
# wipe out the build directories (after writing the tag, since failure
# here shouldn't invalidate the vcpkg install)
if not args.vcpkg_skip_clean:
with timer('Cleaning builds'):
pm.cleanBuilds()
# If we're running in android mode, we also need to grab a bunch of additional binaries
# (this logic is all migrated from the old setupDependencies tasks in gradle)
if args.android:
# Find the target location
appPath = hifi_utils.scriptRelative('android/apps/' + args.android)
# Copy the non-Qt libraries specified in the config in hifi_android.py
hifi_android.copyAndroidLibs(pm.androidPackagePath, appPath)
# Determine the Qt package path
qtPath = os.path.join(pm.androidPackagePath, 'qt')
hifi_android.QtPackager(appPath, qtPath).bundle()
# Fixup the vcpkg cmake to not reset VCPKG_TARGET_TRIPLET
pm.fixupCmakeScript()
if not args.vcpkg_skip_clean:
# Cleanup downloads and packages folders in vcpkg to make it smaller for CI
pm.cleanupDevelopmentFiles()
# Write the vcpkg config to the build directory last
with timer('Writing configuration'):
pm.writeConfig()
logger.info('end')
try:
main()
except hifi_utils.SilentFatalError as fatal_ex:
sys.exit(fatal_ex.exit_code)