2016-01-28 11:10:19 -08:00
|
|
|
#! /usr/bin/env python
|
|
|
|
|
|
|
|
# Copyright 2016 WebAssembly Community Group participants
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
|
2017-06-09 16:24:23 -07:00
|
|
|
import argparse
|
2016-01-28 11:10:19 -08:00
|
|
|
import glob
|
|
|
|
import itertools
|
|
|
|
import multiprocessing
|
|
|
|
import os
|
|
|
|
import shutil
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
import tempfile
|
|
|
|
|
|
|
|
|
|
|
|
verbose = False
|
|
|
|
|
2017-11-29 12:12:56 -08:00
|
|
|
DIR_BLACKLIST = ['misc', 'ldso']
|
2016-01-28 11:10:19 -08:00
|
|
|
BLACKLIST = [
|
2017-10-25 16:47:06 -07:00
|
|
|
'puts.c', # Prefer the JS version for now
|
|
|
|
'abort.c', # Perfer the JS version for now
|
|
|
|
'_Exit.c', # Perfer the JS version for now
|
2017-11-29 12:12:56 -08:00
|
|
|
'__init_tls.c',
|
2017-10-25 16:47:06 -07:00
|
|
|
]
|
|
|
|
# Files that contain weak reference which are not suppoered by s2wasm
|
|
|
|
WEAK_BLACKLIST = [
|
|
|
|
'exit.c',
|
|
|
|
'__libc_start_main.c',
|
2016-01-28 11:10:19 -08:00
|
|
|
]
|
2017-10-25 16:47:06 -07:00
|
|
|
CFLAGS = ['-std=c99',
|
|
|
|
'-D_XOPEN_SOURCE=700',
|
|
|
|
'-Werror',
|
2017-11-16 16:26:18 -08:00
|
|
|
'-Wno-empty-body',
|
2017-10-25 16:47:06 -07:00
|
|
|
'-Wno-incompatible-library-redeclaration',
|
|
|
|
'-Wno-shift-op-parentheses',
|
|
|
|
'-Wno-tautological-unsigned-zero-compare',
|
|
|
|
'-Wno-tautological-constant-out-of-range-compare',
|
|
|
|
'-Wno-tautological-unsigned-enum-zero-compare',
|
|
|
|
'-Wno-ignored-attributes',
|
|
|
|
'-Wno-format',
|
|
|
|
'-Wno-bitwise-op-parentheses',
|
|
|
|
'-Wno-logical-op-parentheses',
|
|
|
|
'-Wno-string-plus-int',
|
|
|
|
'-Wno-pointer-sign',
|
|
|
|
'-Wno-dangling-else',
|
|
|
|
'-Wno-absolute-value',
|
2017-11-29 12:12:56 -08:00
|
|
|
'-Wno-parentheses',
|
2017-10-25 16:47:06 -07:00
|
|
|
'-Wno-unknown-pragmas']
|
2016-01-28 11:10:19 -08:00
|
|
|
|
|
|
|
|
|
|
|
def check_output(cmd, **kwargs):
|
|
|
|
cwd = kwargs.get('cwd', os.getcwd())
|
|
|
|
if verbose:
|
|
|
|
c = ' '.join('"' + c + '"' if ' ' in c else c for c in cmd)
|
|
|
|
print ' `%s`, cwd=`%s`' % (c, cwd)
|
|
|
|
return subprocess.check_output(cmd, cwd=cwd)
|
|
|
|
|
|
|
|
|
2016-02-03 02:27:12 -08:00
|
|
|
def change_extension(path, new_extension):
|
|
|
|
return path[:path.rfind('.')] + new_extension
|
|
|
|
|
|
|
|
|
2017-11-29 12:12:56 -08:00
|
|
|
def create_version(musl, outdir):
|
2016-01-28 11:10:19 -08:00
|
|
|
"""musl's Makefile creates version.h"""
|
|
|
|
script = os.path.join(musl, 'tools', 'version.sh')
|
|
|
|
version = check_output(['sh', script], cwd=musl).strip()
|
2017-11-29 12:12:56 -08:00
|
|
|
outroot = os.path.join(outdir, 'src', 'internal')
|
|
|
|
if not os.path.exists(outroot):
|
|
|
|
os.makedirs(outroot)
|
|
|
|
with open(os.path.join(outroot, 'version.h'), 'w') as v:
|
2016-01-28 11:10:19 -08:00
|
|
|
v.write('#define VERSION "%s"\n' % version)
|
|
|
|
|
|
|
|
|
2017-11-29 12:12:56 -08:00
|
|
|
def build_headers(musl, arch, outdir):
|
|
|
|
"""Emulate musl's Makefile build of alltypes.h and syscall.h"""
|
|
|
|
outroot = os.path.join(outdir, 'include', 'bits')
|
|
|
|
if not os.path.exists(outroot):
|
|
|
|
os.makedirs(outroot)
|
2016-01-28 11:10:19 -08:00
|
|
|
mkalltypes = os.path.join(musl, 'tools', 'mkalltypes.sed')
|
|
|
|
inbits = os.path.join(musl, 'arch', arch, 'bits', 'alltypes.h.in')
|
|
|
|
intypes = os.path.join(musl, 'include', 'alltypes.h.in')
|
|
|
|
out = check_output(['sed', '-f', mkalltypes, inbits, intypes])
|
2017-11-29 12:12:56 -08:00
|
|
|
with open(os.path.join(outroot, 'alltypes.h'), 'w') as o:
|
|
|
|
o.write(out)
|
|
|
|
|
|
|
|
insyscall = os.path.join(musl, 'arch', arch, 'bits', 'syscall.h.in')
|
|
|
|
out = check_output(['sed', '-n', '-e', 's/__NR_/SYS_/p', insyscall])
|
|
|
|
with open(os.path.join(outroot, 'syscall.h'), 'w') as o:
|
|
|
|
o.write(open(insyscall).read())
|
2016-01-28 11:10:19 -08:00
|
|
|
o.write(out)
|
|
|
|
|
|
|
|
|
2017-10-25 16:47:06 -07:00
|
|
|
def musl_sources(musl_root, include_weak):
|
2016-01-28 11:10:19 -08:00
|
|
|
"""musl sources to be built."""
|
|
|
|
sources = []
|
2017-10-25 16:47:06 -07:00
|
|
|
for d in os.listdir(os.path.join(musl_root, 'src')):
|
|
|
|
if d in DIR_BLACKLIST:
|
|
|
|
continue
|
2016-01-28 11:10:19 -08:00
|
|
|
base = os.path.join(musl_root, 'src', d)
|
|
|
|
pattern = os.path.join(base, '*.c')
|
|
|
|
for f in glob.glob(pattern):
|
|
|
|
if os.path.basename(f) in BLACKLIST:
|
|
|
|
continue
|
2017-10-25 16:47:06 -07:00
|
|
|
if not include_weak and os.path.basename(f) in WEAK_BLACKLIST:
|
|
|
|
continue
|
2017-10-25 16:47:06 -07:00
|
|
|
sources.append(f)
|
2016-01-28 11:10:19 -08:00
|
|
|
return sorted(sources)
|
|
|
|
|
|
|
|
|
2017-11-29 12:12:56 -08:00
|
|
|
def includes(musl, arch, outdir):
|
2016-01-28 11:10:19 -08:00
|
|
|
"""Include path."""
|
2017-10-25 16:47:06 -07:00
|
|
|
includes = [
|
|
|
|
os.path.join(musl, 'arch', arch),
|
2017-11-29 12:12:56 -08:00
|
|
|
os.path.join(musl, 'arch', 'generic'),
|
|
|
|
os.path.join(outdir, 'src', 'internal'),
|
2016-01-28 11:10:19 -08:00
|
|
|
os.path.join(musl, 'src', 'internal'),
|
2017-11-29 12:12:56 -08:00
|
|
|
os.path.join(outdir, 'include'),
|
2017-10-25 16:47:06 -07:00
|
|
|
os.path.join(musl, 'include')]
|
2016-01-28 11:10:19 -08:00
|
|
|
return list(itertools.chain(*zip(['-I'] * len(includes), includes)))
|
|
|
|
|
|
|
|
|
|
|
|
class Compiler(object):
|
|
|
|
"""Compile source files."""
|
2017-06-09 16:24:23 -07:00
|
|
|
def __init__(self, out, clang_dir, musl, arch, tmpdir):
|
2016-01-28 11:10:19 -08:00
|
|
|
self.out = out
|
|
|
|
self.outbase = os.path.basename(self.out)
|
|
|
|
self.clang_dir = clang_dir
|
|
|
|
self.musl = musl
|
|
|
|
self.arch = arch
|
|
|
|
self.tmpdir = tmpdir
|
|
|
|
self.compiled = []
|
|
|
|
|
|
|
|
def compile(self, sources):
|
|
|
|
if verbose:
|
2016-01-30 02:50:24 -08:00
|
|
|
self.compiled = sorted([self(source) for source in sources])
|
2016-01-28 11:10:19 -08:00
|
|
|
else:
|
|
|
|
pool = multiprocessing.Pool()
|
|
|
|
self.compiled = sorted(pool.map(self, sources))
|
|
|
|
pool.close()
|
|
|
|
pool.join()
|
|
|
|
|
2017-06-09 16:24:23 -07:00
|
|
|
|
|
|
|
class ObjCompiler(Compiler):
|
|
|
|
def __init__(self, out, clang_dir, musl, arch, tmpdir):
|
|
|
|
super(ObjCompiler, self).__init__(out, clang_dir, musl, arch, tmpdir)
|
|
|
|
|
|
|
|
def __call__(self, src):
|
|
|
|
target = 'wasm32-unknown-unknown-wasm'
|
|
|
|
compile_cmd = [os.path.join(self.clang_dir, 'clang'), '-target', target,
|
|
|
|
'-Os', '-c', '-nostdinc']
|
2017-11-29 12:12:56 -08:00
|
|
|
compile_cmd += includes(self.musl, self.arch, self.tmpdir)
|
2017-10-25 16:47:06 -07:00
|
|
|
compile_cmd += CFLAGS
|
2017-06-09 16:24:23 -07:00
|
|
|
check_output(compile_cmd + [src], cwd=self.tmpdir)
|
|
|
|
return os.path.basename(src)[:-1] + 'o' # .c -> .o
|
|
|
|
|
|
|
|
def binary(self):
|
2017-10-26 19:19:48 -07:00
|
|
|
if os.path.exists(self.out):
|
|
|
|
os.remove(self.out)
|
2017-06-09 16:24:23 -07:00
|
|
|
check_output([os.path.join(self.clang_dir, 'llvm-ar'), 'rcs', self.out] + self.compiled,
|
|
|
|
cwd=self.tmpdir)
|
|
|
|
|
|
|
|
|
|
|
|
class AsmCompiler(Compiler):
|
|
|
|
def __init__(self, out, clang_dir, musl, arch, tmpdir, binaryen_dir,
|
|
|
|
sexpr_wasm):
|
|
|
|
super(AsmCompiler, self).__init__(out, clang_dir, musl, arch, tmpdir)
|
|
|
|
self.binaryen_dir = binaryen_dir
|
|
|
|
self.sexpr_wasm = sexpr_wasm
|
|
|
|
|
|
|
|
def __call__(self, src):
|
|
|
|
target = 'wasm32-unknown-unknown'
|
|
|
|
compile_cmd = [os.path.join(self.clang_dir, 'clang'), '-target', target,
|
|
|
|
'-Os', '-emit-llvm', '-S', '-nostdinc']
|
2017-11-29 12:12:56 -08:00
|
|
|
compile_cmd += includes(self.musl, self.arch, self.tmpdir)
|
2017-10-25 16:47:06 -07:00
|
|
|
compile_cmd += CFLAGS
|
2017-06-09 16:24:23 -07:00
|
|
|
check_output(compile_cmd + [src], cwd=self.tmpdir)
|
|
|
|
return os.path.basename(src)[:-1] + 'll' # .c -> .ll
|
|
|
|
|
|
|
|
def binary(self):
|
2017-11-06 14:18:53 -08:00
|
|
|
max_memory = str(16 * 1024 * 1024)
|
2016-02-03 02:27:12 -08:00
|
|
|
bytecode = change_extension(self.out, '.bc')
|
|
|
|
assembly = os.path.join(self.tmpdir, self.outbase + '.s')
|
2016-01-28 11:10:19 -08:00
|
|
|
check_output([os.path.join(self.clang_dir, 'llvm-link'),
|
2016-02-03 02:27:12 -08:00
|
|
|
'-o', bytecode] + self.compiled,
|
2016-01-28 11:10:19 -08:00
|
|
|
cwd=self.tmpdir)
|
|
|
|
check_output([os.path.join(self.clang_dir, 'llc'),
|
2016-02-03 02:27:12 -08:00
|
|
|
bytecode, '-o', assembly],
|
2016-01-28 11:10:19 -08:00
|
|
|
cwd=self.tmpdir)
|
|
|
|
check_output([os.path.join(self.binaryen_dir, 's2wasm'),
|
2017-11-06 14:18:53 -08:00
|
|
|
assembly, '--ignore-unknown', '-o', self.out,
|
|
|
|
'--import-memory', '-m', max_memory],
|
2016-01-28 11:10:19 -08:00
|
|
|
cwd=self.tmpdir)
|
|
|
|
|
|
|
|
if self.sexpr_wasm:
|
|
|
|
check_output([self.sexpr_wasm,
|
2016-02-03 02:27:12 -08:00
|
|
|
self.out, '-o', change_extension(self.out, '.wasm')],
|
2016-01-28 11:10:19 -08:00
|
|
|
cwd=self.tmpdir)
|
|
|
|
|
|
|
|
|
2017-11-30 15:32:18 -08:00
|
|
|
def run(clang_dir, binaryen_dir, sexpr_wasm, musl, arch, out, compile_to_wasm):
|
|
|
|
objdir = os.path.join(os.path.dirname(out), 'obj')
|
|
|
|
if os.path.isdir(objdir):
|
|
|
|
shutil.rmtree(objdir)
|
|
|
|
os.mkdir(objdir)
|
|
|
|
|
|
|
|
create_version(musl, objdir)
|
|
|
|
build_headers(musl, arch, objdir)
|
|
|
|
sources = musl_sources(musl, include_weak=compile_to_wasm)
|
|
|
|
if compile_to_wasm:
|
|
|
|
compiler = ObjCompiler(out, clang_dir, musl, arch, objdir)
|
2016-01-30 02:44:50 -08:00
|
|
|
else:
|
2017-11-30 15:32:18 -08:00
|
|
|
compiler = AsmCompiler(out, clang_dir, musl, arch, objdir, binaryen_dir,
|
|
|
|
sexpr_wasm)
|
|
|
|
compiler.compile(sources)
|
|
|
|
compiler.binary()
|
|
|
|
if compile_to_wasm:
|
|
|
|
compiler.compile([os.path.join(musl, 'crt', 'crt1.c')])
|
|
|
|
shutil.copy(os.path.join(objdir, compiler.compiled[0]),
|
|
|
|
os.path.dirname(out))
|
2016-01-28 11:10:19 -08:00
|
|
|
|
|
|
|
|
|
|
|
def getargs():
|
|
|
|
parser = argparse.ArgumentParser(description='Build a hacky wasm libc.')
|
|
|
|
parser.add_argument('--clang_dir', type=str, required=True,
|
|
|
|
help='Clang binary directory')
|
|
|
|
parser.add_argument('--binaryen_dir', type=str, required=True,
|
|
|
|
help='binaryen binary directory')
|
|
|
|
parser.add_argument('--sexpr_wasm', type=str, required=False,
|
|
|
|
help='sexpr-wasm binary')
|
|
|
|
parser.add_argument('--musl', type=str, required=True,
|
|
|
|
help='musl libc root directory')
|
|
|
|
parser.add_argument('--arch', type=str, default='wasm32',
|
|
|
|
help='architecture to target')
|
|
|
|
parser.add_argument('--out', '-o', type=str,
|
|
|
|
default=os.path.join(os.getcwd(), 'musl.wast'),
|
|
|
|
help='Output file')
|
|
|
|
parser.add_argument('--verbose', default=False, action='store_true',
|
|
|
|
help='Verbose')
|
2017-06-09 16:24:23 -07:00
|
|
|
parser.add_argument('--compile-to-wasm', default=False, action='store_true',
|
|
|
|
help='Use clang to compile directly to wasm')
|
2016-01-28 11:10:19 -08:00
|
|
|
return parser.parse_args()
|
|
|
|
|
|
|
|
|
2017-06-09 16:24:23 -07:00
|
|
|
def main():
|
|
|
|
global verbose
|
2016-01-28 11:10:19 -08:00
|
|
|
args = getargs()
|
|
|
|
if args.verbose:
|
|
|
|
verbose = True
|
2017-06-09 16:24:23 -07:00
|
|
|
return run(args.clang_dir, args.binaryen_dir, args.sexpr_wasm,
|
2017-11-30 15:32:18 -08:00
|
|
|
args.musl, args.arch, args.out, args.compile_to_wasm)
|
2017-06-09 16:24:23 -07:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
sys.exit(main())
|