aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2014-02-08 12:14:56 -0800
committerAlon Zakai <alonzakai@gmail.com>2014-02-08 12:14:56 -0800
commit157f3151a43e02b6c335c312a3f78bac0d0110eb (patch)
tree63116ffd0784a7e1e6e5bc77f9222429f1afbf9c /tools
parent7ec33d39d7a45992688a84dd0cf38ae992c3d710 (diff)
move system libs calculation to its own file
Diffstat (limited to 'tools')
-rw-r--r--tools/system_libs.py384
1 files changed, 384 insertions, 0 deletions
diff --git a/tools/system_libs.py b/tools/system_libs.py
new file mode 100644
index 00000000..42c9d67c
--- /dev/null
+++ b/tools/system_libs.py
@@ -0,0 +1,384 @@
+import os, json, logging
+import shared
+
+def calculate(temp_files):
+ # Check if we need to include some libraries that we compile. (We implement libc ourselves in js, but
+ # compile a malloc implementation and stdlibc++.)
+
+ def read_symbols(path, exclude=None):
+ symbols = map(lambda line: line.strip().split(' ')[1], open(path).readlines())
+ if exclude:
+ symbols = filter(lambda symbol: symbol not in exclude, symbols)
+ return set(symbols)
+
+ lib_opts = ['-O2']
+
+ # XXX We also need to add libc symbols that use malloc, for example strdup. It's very rare to use just them and not
+ # a normal malloc symbol (like free, after calling strdup), so we haven't hit this yet, but it is possible.
+ libc_symbols = read_symbols(shared.path_from_root('system', 'lib', 'libc.symbols'))
+ libcextra_symbols = read_symbols(shared.path_from_root('system', 'lib', 'libcextra.symbols'))
+ libcxx_symbols = read_symbols(shared.path_from_root('system', 'lib', 'libcxx', 'symbols'), exclude=libc_symbols)
+ libcxxabi_symbols = read_symbols(shared.path_from_root('system', 'lib', 'libcxxabi', 'symbols'), exclude=libc_symbols)
+
+ # XXX we should disable EMCC_DEBUG when building libs, just like in the relooper
+
+ def build_libc(lib_filename, files):
+ o_s = []
+ prev_cxx = os.environ.get('EMMAKEN_CXX')
+ if prev_cxx: os.environ['EMMAKEN_CXX'] = ''
+ musl_internal_includes = shared.path_from_root('system', 'lib', 'libc', 'musl', 'src', 'internal')
+ for src in files:
+ o = in_temp(os.path.basename(src) + '.o')
+ execute([shared.PYTHON, shared.EMCC, shared.path_from_root('system', 'lib', src), '-o', o, '-I', musl_internal_includes] + lib_opts, stdout=stdout, stderr=stderr)
+ o_s.append(o)
+ if prev_cxx: os.environ['EMMAKEN_CXX'] = prev_cxx
+ shared.Building.link(o_s, in_temp(lib_filename))
+ return in_temp(lib_filename)
+
+ def build_libcxx(src_dirname, lib_filename, files):
+ o_s = []
+ for src in files:
+ o = in_temp(src + '.o')
+ srcfile = shared.path_from_root(src_dirname, src)
+ execute([shared.PYTHON, shared.EMXX, srcfile, '-o', o, '-std=c++11'] + lib_opts, stdout=stdout, stderr=stderr)
+ o_s.append(o)
+ shared.Building.link(o_s, in_temp(lib_filename))
+ return in_temp(lib_filename)
+
+ # libc
+ def create_libc():
+ logging.debug(' building libc for cache')
+ libc_files = [
+ 'dlmalloc.c',
+ os.path.join('libcxx', 'new.cpp'),
+ ]
+ musl_files = [
+ ['internal', [
+ 'floatscan.c',
+ 'shgetc.c',
+ ]],
+ ['math', [
+ 'scalbn.c',
+ 'scalbnl.c',
+ ]],
+ ['stdio', [
+ '__overflow.c',
+ '__toread.c',
+ '__towrite.c',
+ '__uflow.c',
+ ]],
+ ['stdlib', [
+ 'atof.c',
+ 'strtod.c',
+ ]],
+ ['string', [
+ 'memcmp.c',
+ 'strcasecmp.c',
+ 'strcmp.c',
+ 'strncasecmp.c',
+ 'strncmp.c',
+ ]]
+ ]
+ for directory, sources in musl_files:
+ libc_files += [os.path.join('libc', 'musl', 'src', directory, source) for source in sources]
+ return build_libc('libc.bc', libc_files)
+
+ def apply_libc(need):
+ # libc needs some sign correction. # If we are in mode 0, switch to 2. We will add our lines
+ try:
+ if shared.Settings.CORRECT_SIGNS == 0: raise Exception('we need to change to 2')
+ except: # we fail if equal to 0 - so we need to switch to 2 - or if CORRECT_SIGNS is not even in Settings
+ shared.Settings.CORRECT_SIGNS = 2
+ if shared.Settings.CORRECT_SIGNS == 2:
+ shared.Settings.CORRECT_SIGNS_LINES = [shared.path_from_root('src', 'dlmalloc.c') + ':' + str(i+4) for i in [4816, 4191, 4246, 4199, 4205, 4235, 4227]]
+ # If we are in mode 1, we are correcting everything anyhow. If we are in mode 3, we will be corrected
+ # so all is well anyhow too.
+ return True
+
+ # libcextra
+ def create_libcextra():
+ logging.debug('building libcextra for cache')
+ musl_files = [
+ ['ctype', [
+ 'iswalnum.c',
+ 'iswalpha.c',
+ 'iswblank.c',
+ 'iswcntrl.c',
+ 'iswctype.c',
+ 'iswdigit.c',
+ 'iswgraph.c',
+ 'iswlower.c',
+ 'iswprint.c',
+ 'iswpunct.c',
+ 'iswspace.c',
+ 'iswupper.c',
+ 'iswxdigit.c',
+ 'towctrans.c',
+ 'wcswidth.c',
+ 'wctrans.c',
+ 'wcwidth.c',
+ ]],
+ ['internal', [
+ 'intscan.c',
+ ]],
+ ['legacy', [
+ 'err.c',
+ ]],
+ ['locale', [
+ 'iconv.c',
+ 'iswalnum_l.c',
+ 'iswalpha_l.c',
+ 'iswblank_l.c',
+ 'iswcntrl_l.c',
+ 'iswctype_l.c',
+ 'iswdigit_l.c',
+ 'iswgraph_l.c',
+ 'iswlower_l.c',
+ 'iswprint_l.c',
+ 'iswpunct_l.c',
+ 'iswspace_l.c',
+ 'iswupper_l.c',
+ 'iswxdigit_l.c',
+ 'strcoll.c',
+ 'strcasecmp_l.c',
+ 'strfmon.c',
+ 'strncasecmp_l.c',
+ 'strxfrm.c',
+ 'towctrans_l.c',
+ 'towlower_l.c',
+ 'towupper_l.c',
+ 'wcscoll.c',
+ 'wcscoll_l.c',
+ 'wcsxfrm.c',
+ 'wcsxfrm_l.c',
+ 'wctrans_l.c',
+ 'wctype_l.c',
+ ]],
+ ['math', [
+ '__cos.c',
+ '__cosdf.c',
+ '__sin.c',
+ '__sindf.c',
+ 'ilogb.c',
+ 'ilogbf.c',
+ 'ilogbl.c',
+ 'ldexp.c',
+ 'ldexpf.c',
+ 'ldexpl.c',
+ 'logb.c',
+ 'logbf.c',
+ 'logbl.c',
+ 'lgamma.c',
+ 'lgamma_r.c',
+ 'lgammaf.c',
+ 'lgammaf_r.c',
+ 'lgammal.c',
+ 'scalbnf.c',
+ 'signgam.c',
+ 'tgamma.c',
+ 'tgammaf.c',
+ 'tgammal.c'
+ ]],
+ ['misc', [
+ 'getopt.c',
+ 'getopt_long.c',
+ ]],
+ ['multibyte', [
+ 'btowc.c',
+ 'mblen.c',
+ 'mbrlen.c',
+ 'mbrtowc.c',
+ 'mbsinit.c',
+ 'mbsnrtowcs.c',
+ 'mbsrtowcs.c',
+ 'mbstowcs.c',
+ 'mbtowc.c',
+ 'wcrtomb.c',
+ 'wcsnrtombs.c',
+ 'wcsrtombs.c',
+ 'wcstombs.c',
+ 'wctob.c',
+ 'wctomb.c',
+ ]],
+ ['regex', [
+ 'fnmatch.c',
+ 'regcomp.c',
+ 'regerror.c',
+ 'regexec.c',
+ 'tre-mem.c',
+ ]],
+ ['stdio', [
+ 'fwprintf.c',
+ 'swprintf.c',
+ 'vfwprintf.c',
+ 'vswprintf.c',
+ 'vwprintf.c',
+ 'wprintf.c',
+ 'fputwc.c',
+ 'fputws.c',
+ ]],
+ ['stdlib', [
+ 'ecvt.c',
+ 'fcvt.c',
+ 'gcvt.c',
+ 'wcstod.c',
+ 'wcstol.c',
+ ]],
+ ['string', [
+ 'memccpy.c',
+ 'memmem.c',
+ 'mempcpy.c',
+ 'memrchr.c',
+ 'strcasestr.c',
+ 'strchrnul.c',
+ 'strlcat.c',
+ 'strlcpy.c',
+ 'strsep.c',
+ 'strverscmp.c',
+ 'wcpcpy.c',
+ 'wcpncpy.c',
+ 'wcscasecmp.c',
+ 'wcscasecmp_l.c',
+ 'wcscat.c',
+ 'wcschr.c',
+ 'wcscmp.c',
+ 'wcscpy.c',
+ 'wcscspn.c',
+ 'wcsdup.c',
+ 'wcslen.c',
+ 'wcsncasecmp.c',
+ 'wcsncasecmp_l.c',
+ 'wcsncat.c',
+ 'wcsncmp.c',
+ 'wcsncpy.c',
+ 'wcsnlen.c',
+ 'wcspbrk.c',
+ 'wcsrchr.c',
+ 'wcsspn.c',
+ 'wcsstr.c',
+ 'wcstok.c',
+ 'wcswcs.c',
+ 'wmemchr.c',
+ 'wmemcmp.c',
+ 'wmemcpy.c',
+ 'wmemmove.c',
+ 'wmemset.c',
+ ]]
+ ]
+ libcextra_files = []
+ for directory, sources in musl_files:
+ libcextra_files += [os.path.join('libc', 'musl', 'src', directory, source) for source in sources]
+ return build_libc('libcextra.bc', libcextra_files)
+
+ # libcxx
+ def create_libcxx():
+ logging.debug('building libcxx for cache')
+ libcxx_files = [
+ 'algorithm.cpp',
+ 'condition_variable.cpp',
+ 'future.cpp',
+ 'iostream.cpp',
+ 'memory.cpp',
+ 'random.cpp',
+ 'stdexcept.cpp',
+ 'system_error.cpp',
+ 'utility.cpp',
+ 'bind.cpp',
+ 'debug.cpp',
+ 'hash.cpp',
+ 'mutex.cpp',
+ 'string.cpp',
+ 'thread.cpp',
+ 'valarray.cpp',
+ 'chrono.cpp',
+ 'exception.cpp',
+ 'ios.cpp',
+ 'locale.cpp',
+ 'regex.cpp',
+ 'strstream.cpp'
+ ]
+ return build_libcxx(os.path.join('system', 'lib', 'libcxx'), 'libcxx.bc', libcxx_files)
+
+ def apply_libcxx(need):
+ assert shared.Settings.QUANTUM_SIZE == 4, 'We do not support libc++ with QUANTUM_SIZE == 1'
+ # libcxx might need corrections, so turn them all on. TODO: check which are actually needed
+ shared.Settings.CORRECT_SIGNS = shared.Settings.CORRECT_OVERFLOWS = shared.Settings.CORRECT_ROUNDINGS = 1
+ #logging.info('using libcxx turns on CORRECT_* options')
+ return True
+
+ # libcxxabi - just for dynamic_cast for now
+ def create_libcxxabi():
+ logging.debug('building libcxxabi for cache')
+ libcxxabi_files = [
+ 'typeinfo.cpp',
+ 'private_typeinfo.cpp'
+ ]
+ return build_libcxx(os.path.join('system', 'lib', 'libcxxabi', 'src'), 'libcxxabi.bc', libcxxabi_files)
+
+ def apply_libcxxabi(need):
+ assert shared.Settings.QUANTUM_SIZE == 4, 'We do not support libc++abi with QUANTUM_SIZE == 1'
+ #logging.info('using libcxxabi, this may need CORRECT_* options')
+ #shared.Settings.CORRECT_SIGNS = shared.Settings.CORRECT_OVERFLOWS = shared.Settings.CORRECT_ROUNDINGS = 1
+ return True
+
+ # Settings this in the environment will avoid checking dependencies and make building big projects a little faster
+ # 1 means include everything; otherwise it can be the name of a lib (libcxx, etc.)
+ force = os.environ.get('EMCC_FORCE_STDLIBS')
+ force_all = force == '1'
+
+ # Scan symbols
+ symbolses = map(lambda temp_file: shared.Building.llvm_nm(temp_file), temp_files)
+
+ # Add in some hacks for js libraries. If a js lib depends on a symbol provided by a C library, it must be
+ # added to here, because our deps go only one way (each library here is checked, then we check the next
+ # in order - libcxx, libcxextra, etc. - and then we run the JS compiler and provide extra symbols from
+ # library*.js files. But we cannot then go back to the C libraries if a new dep was added!
+ # TODO: Move all __deps from src/library*.js to deps_info.json, and use that single source of info
+ # both here and in the JS compiler.
+ deps_info = json.loads(open(shared.path_from_root('src', 'deps_info.json')).read())
+ def add_back_deps(need):
+ for ident, deps in deps_info.iteritems():
+ if ident in need.undefs:
+ for dep in deps:
+ need.undefs.add(dep)
+ shared.Settings.EXPORTED_FUNCTIONS.append('_' + dep)
+ for symbols in symbolses:
+ add_back_deps(symbols)
+
+ all_needed = set()
+ for symbols in symbolses:
+ all_needed.update(symbols.undefs)
+ for symbols in symbolses:
+ all_needed.difference_update(symbols.defs)
+
+ # Go over libraries to figure out which we must include
+ # If we have libcxx, we must force inclusion of libc, since libcxx uses new internally. Note: this is kind of hacky.
+ ret = []
+ has = need = None
+ for name, create, apply_, library_symbols in [('libcxx', create_libcxx, apply_libcxx, libcxx_symbols),
+ ('libcextra', create_libcextra, lambda x: True, libcextra_symbols),
+ ('libcxxabi', create_libcxxabi, apply_libcxxabi, libcxxabi_symbols),
+ ('libc', create_libc, apply_libc, libc_symbols)]:
+ force_this = force_all or force == name
+ if not force_this:
+ need = set()
+ has = set()
+ for symbols in symbolses:
+ for library_symbol in library_symbols:
+ if library_symbol in symbols.undefs:
+ need.add(library_symbol)
+ if library_symbol in symbols.defs:
+ has.add(library_symbol)
+ for haz in has: # remove symbols that are supplied by another of the inputs
+ if haz in need:
+ need.remove(haz)
+ if shared.Settings.VERBOSE: logging.debug('considering %s: we need %s and have %s' % (name, str(need), str(has)))
+ if force_this or len(need) > 0:
+ force_all = True
+ if apply_(need):
+ # We need to build and link the library in
+ logging.debug('including %s' % name)
+ libfile = shared.Cache.get(name, create)
+ ret.append(libfile)
+ return ret
+