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 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
|
#!/usr/bin/env python3
"""Creates the Chromium SQLite amalgamation.
The amalgamation is a single large source file (sqlite3.c) containing all
of the SQLite code. More at https://www.sqlite.org/amalgamation.html.
Usage:
generate_amalgamation.py
"""
import argparse
import os
import stat
import subprocess
import sys
import tempfile
from shutil import copyfile, rmtree
from extract_sqlite_api import ProcessSourceFile, header_line, footer_line
# The Chromium SQLite third party directory (i.e. //third_party/sqlite).
_SQLITE_ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
# The Chromium SQLite source directory (i.e. //third_party/sqlite/src).
_SQLITE_SRC_DIR = os.path.join(_SQLITE_ROOT_DIR, 'src')
# The .gni file (also used by BUILD.gn when building) which contains all
# flags passed to the `configuration` script and also used for the compile.
_COMMON_CONFIGURATION_FLAGS_GNI_FILE = os.path.join(
_SQLITE_ROOT_DIR, 'sqlite_common_configuration_flags.gni')
_CHROMIUM_CONFIGURATION_FLAGS_GNI_FILE = os.path.join(
_SQLITE_ROOT_DIR, 'sqlite_chromium_configuration_flags.gni')
_DEV_CONFIGURATION_FLAGS_GNI_FILE = os.path.join(
_SQLITE_ROOT_DIR, 'sqlite_dev_configuration_flags.gni')
# The temporary directory where `make configure` and the amalgamation
# is temporarily created.
_TEMP_CONFIG_DIR = tempfile.mkdtemp()
# Set to True to generate a configuration which is compatible for
# running the SQLite tests.
_CONFIGURE_FOR_TESTING = False
def get_amalgamation_dir(config_name):
if config_name == 'chromium':
return os.path.join(_SQLITE_SRC_DIR, 'amalgamation')
elif config_name == 'dev':
return os.path.join(_SQLITE_SRC_DIR, 'amalgamation_dev')
else:
assert False
def _icu_cpp_flags():
"""Return the libicu C++ flags."""
cmd = ['icu-config', '--cppflags']
try:
return subprocess.check_output(cmd)
except Exception:
return ''
def _icu_ld_flags():
"""Return the libicu linker flags."""
cmd = ['icu-config', '--ldflags']
try:
return subprocess.check_output(cmd)
except Exception:
return ''
def _strip_flags_for_testing(flags):
"""Accepts the default configure/build flags and strips out those
incompatible with the SQLite tests.
When configuring SQLite to run tests this script uses a configuration
as close to what Chromium ships as possible. Some flags need to be
omitted for the tests to link and run correct. See comments below.
"""
test_flags = []
for flag in flags:
# Omitting features can cause tests to hang/crash/fail because the
# SQLite tests don't seem to detect feature omission. Keep them enabled.
if flag.startswith('SQLITE_OMIT_'):
continue
# Some tests compile with specific SQLITE_DEFAULT_PAGE_SIZE so do
# not hard-code.
if flag.startswith('SQLITE_DEFAULT_PAGE_SIZE='):
continue
# Some tests compile with specific SQLITE_DEFAULT_MEMSTATUS so do
# not hard-code.
if flag.startswith('SQLITE_DEFAULT_MEMSTATUS='):
continue
# If enabled then get undefined reference to `uregex_open_63' and
# other *_64 functions.
if flag == 'SQLITE_ENABLE_ICU':
continue
# If defined then the fts4umlaut tests fail with the following error:
#
# Error: unknown tokenizer: unicode61
if flag == 'SQLITE_DISABLE_FTS3_UNICODE':
continue
test_flags.append(flag)
return test_flags
def _read_flags(file_name, param_name):
config_globals = dict()
with open(file_name) as input_file:
code = compile(input_file.read(), file_name, 'exec')
exec (code, config_globals)
return config_globals[param_name]
def _read_configuration_values(config_name):
"""Read the configuration flags and return them in an array.
|config_name| is one of "chromium" or "dev".
"""
common_flags = _read_flags(_COMMON_CONFIGURATION_FLAGS_GNI_FILE,
'sqlite_common_configuration_flags')
chromium_flags = _read_flags(_CHROMIUM_CONFIGURATION_FLAGS_GNI_FILE,
'sqlite_chromium_configuration_flags')
dev_flags = _read_flags(_DEV_CONFIGURATION_FLAGS_GNI_FILE,
'sqlite_dev_configuration_flags')
if config_name == 'chromium':
flags = common_flags + chromium_flags
elif config_name == 'dev':
flags = common_flags + dev_flags
else:
print('Incorrect config "%s"' % config_name, file=sys.stderr)
sys.exit(1)
if _CONFIGURE_FOR_TESTING:
flags = _strip_flags_for_testing(flags)
return flags
def _do_configure(config_name):
"""Run the configure script for the SQLite source."""
configure = os.path.join(_SQLITE_SRC_DIR, 'configure')
build_flags = ' '.join(
['-D' + f for f in _read_configuration_values(config_name)])
cflags = '-Os {} {}'.format(build_flags, _icu_cpp_flags())
ldflags = _icu_ld_flags()
cmd = [
configure,
'CFLAGS={}'.format(cflags),
'LDFLAGS={}'.format(ldflags),
'--disable-load-extension',
'--enable-amalgamation',
'--enable-threadsafe',
]
subprocess.check_call(cmd)
if _CONFIGURE_FOR_TESTING:
# Copy the files necessary for building/running tests back
#into the source directory.
files = ['Makefile', 'sqlite_cfg.h', 'libtool']
for file_name in files:
copyfile(
os.path.join(_TEMP_CONFIG_DIR, file_name),
os.path.join(_SQLITE_SRC_DIR, file_name))
file_name = os.path.join(_SQLITE_SRC_DIR, 'libtool')
st = os.stat(file_name)
os.chmod(file_name, st.st_mode | stat.S_IEXEC)
def make_aggregate(config_name):
"""Generate the aggregate source files."""
if not os.path.exists(_TEMP_CONFIG_DIR):
os.mkdir(_TEMP_CONFIG_DIR)
try:
os.chdir(_TEMP_CONFIG_DIR)
_do_configure(config_name)
# Chromium compiles 'sqlite3r.c' and 'sqlite3r.h' to use the built-in
# corruption recovery module. These files are then mapped to the standard
# 'sqlite3.c' and 'sqlite3.h' files below. This mapping is required if
# the "SQLITE_HAVE_SQLITE3R" configuration option is specified.
cmd = ['make', 'shell.c', 'sqlite3r.h', 'sqlite3r.c']
subprocess.check_call(cmd)
amalgamation_dir = get_amalgamation_dir(config_name)
if not os.path.exists(amalgamation_dir):
os.mkdir(amalgamation_dir)
readme_dst = os.path.join(amalgamation_dir, 'README.md')
if not os.path.exists(readme_dst):
readme_src = os.path.join(_SQLITE_ROOT_DIR, 'scripts',
'README_amalgamation.md')
copyfile(readme_src, readme_dst)
copyfile(os.path.join(_TEMP_CONFIG_DIR, 'sqlite3r.c'),
os.path.join(amalgamation_dir, 'sqlite3.c'))
copyfile(os.path.join(_TEMP_CONFIG_DIR, 'sqlite3r.h'),
os.path.join(amalgamation_dir, 'sqlite3.h'))
# shell.c must be placed in a different directory from sqlite3.h,
# because it contains an '#include "sqlite3.h"' that we want to resolve
# to our custom //third_party/sqlite/sqlite3.h, not to the sqlite3.h
# produced here.
shell_dir = os.path.join(amalgamation_dir, 'shell')
if not os.path.exists(shell_dir):
os.mkdir(shell_dir)
copyfile(
os.path.join(_TEMP_CONFIG_DIR, 'shell.c'),
os.path.join(shell_dir, 'shell.c'))
finally:
rmtree(_TEMP_CONFIG_DIR)
def extract_sqlite_api(config_name):
amalgamation_dir = get_amalgamation_dir(config_name)
input_file = os.path.join(amalgamation_dir, 'sqlite3.h')
output_file = os.path.join(amalgamation_dir, 'rename_exports.h')
ProcessSourceFile(
api_export_macro='SQLITE_API',
symbol_prefix='chrome_',
header_line=header_line,
footer_line=footer_line,
input_file=input_file,
output_file=output_file)
if __name__ == '__main__':
desc = \
('Create the SQLite amalgamation. The SQLite amalgamation is documented at '
'https://www.sqlite.org/amalgamation.html and is a single large file '
'containing the SQLite source code. Chromium generates the amalgamation with'
' this script to ensure that the configuration parameters are identical to '
'those in the Ninja build file.')
parser = argparse.ArgumentParser(description=desc)
parser.add_argument(
'-t',
'--testing',
action='store_true',
help='Generate an amalgamation for testing (default: false)')
namespace = parser.parse_args()
if namespace.testing:
_CONFIGURE_FOR_TESTING = True
print('Running configure for testing.')
for config_name in ['chromium', 'dev']:
make_aggregate(config_name)
extract_sqlite_api(config_name)
|