1
0
mirror of https://github.com/fluencelabs/musl synced 2025-05-09 13:52:22 +00:00
Sam Clegg 14ffeeb5bb
[WebAssembly] Keep itermediate files ()
In particular we need to keep the generated headers around so they can be installed later.

This behavior mimics more closely what the musl Makefile does now that it supports out-of-tree
building (i.e. it puts all the generated files under obj)
2017-11-30 15:32:18 -08:00

267 lines
9.1 KiB
Python
Executable File

#! /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.
import argparse
import glob
import itertools
import multiprocessing
import os
import shutil
import subprocess
import sys
import tempfile
verbose = False
DIR_BLACKLIST = ['misc', 'ldso']
BLACKLIST = [
'puts.c', # Prefer the JS version for now
'abort.c', # Perfer the JS version for now
'_Exit.c', # Perfer the JS version for now
'__init_tls.c',
]
# Files that contain weak reference which are not suppoered by s2wasm
WEAK_BLACKLIST = [
'exit.c',
'__libc_start_main.c',
]
CFLAGS = ['-std=c99',
'-D_XOPEN_SOURCE=700',
'-Werror',
'-Wno-empty-body',
'-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',
'-Wno-parentheses',
'-Wno-unknown-pragmas']
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)
def change_extension(path, new_extension):
return path[:path.rfind('.')] + new_extension
def create_version(musl, outdir):
"""musl's Makefile creates version.h"""
script = os.path.join(musl, 'tools', 'version.sh')
version = check_output(['sh', script], cwd=musl).strip()
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:
v.write('#define VERSION "%s"\n' % version)
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)
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])
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())
o.write(out)
def musl_sources(musl_root, include_weak):
"""musl sources to be built."""
sources = []
for d in os.listdir(os.path.join(musl_root, 'src')):
if d in DIR_BLACKLIST:
continue
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
if not include_weak and os.path.basename(f) in WEAK_BLACKLIST:
continue
sources.append(f)
return sorted(sources)
def includes(musl, arch, outdir):
"""Include path."""
includes = [
os.path.join(musl, 'arch', arch),
os.path.join(musl, 'arch', 'generic'),
os.path.join(outdir, 'src', 'internal'),
os.path.join(musl, 'src', 'internal'),
os.path.join(outdir, 'include'),
os.path.join(musl, 'include')]
return list(itertools.chain(*zip(['-I'] * len(includes), includes)))
class Compiler(object):
"""Compile source files."""
def __init__(self, out, clang_dir, musl, arch, tmpdir):
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:
self.compiled = sorted([self(source) for source in sources])
else:
pool = multiprocessing.Pool()
self.compiled = sorted(pool.map(self, sources))
pool.close()
pool.join()
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']
compile_cmd += includes(self.musl, self.arch, self.tmpdir)
compile_cmd += CFLAGS
check_output(compile_cmd + [src], cwd=self.tmpdir)
return os.path.basename(src)[:-1] + 'o' # .c -> .o
def binary(self):
if os.path.exists(self.out):
os.remove(self.out)
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']
compile_cmd += includes(self.musl, self.arch, self.tmpdir)
compile_cmd += CFLAGS
check_output(compile_cmd + [src], cwd=self.tmpdir)
return os.path.basename(src)[:-1] + 'll' # .c -> .ll
def binary(self):
max_memory = str(16 * 1024 * 1024)
bytecode = change_extension(self.out, '.bc')
assembly = os.path.join(self.tmpdir, self.outbase + '.s')
check_output([os.path.join(self.clang_dir, 'llvm-link'),
'-o', bytecode] + self.compiled,
cwd=self.tmpdir)
check_output([os.path.join(self.clang_dir, 'llc'),
bytecode, '-o', assembly],
cwd=self.tmpdir)
check_output([os.path.join(self.binaryen_dir, 's2wasm'),
assembly, '--ignore-unknown', '-o', self.out,
'--import-memory', '-m', max_memory],
cwd=self.tmpdir)
if self.sexpr_wasm:
check_output([self.sexpr_wasm,
self.out, '-o', change_extension(self.out, '.wasm')],
cwd=self.tmpdir)
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)
else:
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))
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')
parser.add_argument('--compile-to-wasm', default=False, action='store_true',
help='Use clang to compile directly to wasm')
return parser.parse_args()
def main():
global verbose
args = getargs()
if args.verbose:
verbose = True
return run(args.clang_dir, args.binaryen_dir, args.sexpr_wasm,
args.musl, args.arch, args.out, args.compile_to_wasm)
if __name__ == '__main__':
sys.exit(main())