1# Copyright 2019 The meson development team 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""Provides a mixin for shared code between C and C++ Emscripten compilers.""" 16 17import os.path 18import typing as T 19 20from ... import coredata 21from ... import mesonlib 22from ...mesonlib import OptionKey 23from ...mesonlib import LibType 24 25if T.TYPE_CHECKING: 26 from ...environment import Environment 27 from ...compilers.compilers import Compiler 28 from ...dependencies import Dependency 29else: 30 # This is a bit clever, for mypy we pretend that these mixins descend from 31 # Compiler, so we get all of the methods and attributes defined for us, but 32 # for runtime we make them descend from object (which all classes normally 33 # do). This gives up DRYer type checking, with no runtime impact 34 Compiler = object 35 36 37def wrap_js_includes(args: T.List[str]) -> T.List[str]: 38 final_args = [] 39 for i in args: 40 if i.endswith('.js') and not i.startswith('-'): 41 final_args += ['--js-library', i] 42 else: 43 final_args += [i] 44 return final_args 45 46class EmscriptenMixin(Compiler): 47 48 def _get_compile_output(self, dirname: str, mode: str) -> str: 49 # In pre-processor mode, the output is sent to stdout and discarded 50 if mode == 'preprocess': 51 return None 52 # Unlike sane toolchains, emcc infers the kind of output from its name. 53 # This is the only reason why this method is overridden; compiler tests 54 # do not work well with the default exe/obj suffices. 55 if mode == 'link': 56 suffix = 'js' 57 else: 58 suffix = 'o' 59 return os.path.join(dirname, 'output.' + suffix) 60 61 def thread_flags(self, env: 'Environment') -> T.List[str]: 62 return ['-s', 'USE_PTHREADS=1'] 63 64 def thread_link_flags(self, env: 'Environment') -> T.List[str]: 65 args = ['-s', 'USE_PTHREADS=1'] 66 count: int = env.coredata.options[OptionKey('thread_count', lang=self.language, machine=self.for_machine)].value 67 if count: 68 args.extend(['-s', f'PTHREAD_POOL_SIZE={count}']) 69 return args 70 71 def get_options(self) -> 'coredata.KeyedOptionDictType': 72 opts = super().get_options() 73 key = OptionKey('thread_count', machine=self.for_machine, lang=self.language) 74 opts.update({ 75 key: coredata.UserIntegerOption( 76 'Number of threads to use in web assembly, set to 0 to disable', 77 (0, None, 4), # Default was picked at random 78 ), 79 }) 80 81 return opts 82 83 @classmethod 84 def native_args_to_unix(cls, args: T.List[str]) -> T.List[str]: 85 return wrap_js_includes(super().native_args_to_unix(args)) 86 87 def get_dependency_link_args(self, dep: 'Dependency') -> T.List[str]: 88 return wrap_js_includes(super().get_dependency_link_args(dep)) 89 90 def find_library(self, libname: str, env: 'Environment', extra_dirs: T.List[str], 91 libtype: LibType = LibType.PREFER_SHARED) -> T.Optional[T.List[str]]: 92 if not libname.endswith('.js'): 93 return super().find_library(libname, env, extra_dirs, libtype) 94 if os.path.isabs(libname): 95 if os.path.exists(libname): 96 return [libname] 97 if len(extra_dirs) == 0: 98 raise mesonlib.EnvironmentException('Looking up Emscripten JS libraries requires either an absolute path or specifying extra_dirs.') 99 for d in extra_dirs: 100 abs_path = os.path.join(d, libname) 101 if os.path.exists(abs_path): 102 return [abs_path] 103 return None 104