From 942aea230f5e517d5add194240c8943f28d79943 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Fri, 14 Oct 2022 15:32:50 -0400 Subject: Add MASM compiler ml and armasm are Microsoft's Macro Assembler, part of MSVC. --- docs/markdown/Reference-tables.md | 5 +- docs/markdown/snippets/asm.md | 10 ++- mesonbuild/compilers/asm.py | 115 +++++++++++++++++++++++++++++++++ mesonbuild/compilers/compilers.py | 3 +- mesonbuild/compilers/detect.py | 42 ++++++++++++ test cases/windows/21 masm/hello.masm | 33 ++++++++++ test cases/windows/21 masm/meson.build | 14 ++++ 7 files changed, 217 insertions(+), 5 deletions(-) create mode 100644 test cases/windows/21 masm/hello.masm create mode 100644 test cases/windows/21 masm/meson.build diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md index 7e0cf42..fde816d 100644 --- a/docs/markdown/Reference-tables.md +++ b/docs/markdown/Reference-tables.md @@ -38,6 +38,8 @@ These are return values of the `get_id` (Compiler family) and | cython | The Cython compiler | | | nasm | The NASM compiler (Since 0.64.0) | | | yasm | The YASM compiler (Since 0.64.0) | | +| ml | Microsoft Macro Assembler for x86 and x86_64 (Since 0.64.0) | msvc | +| armasm | Microsoft Macro Assembler for ARM and AARCH64 (Since 0.64.0) | | ## Linker ids @@ -169,7 +171,8 @@ These are the parameter names for passing language specific arguments to your bu | Rust | rust_args | rust_link_args | | Vala | vala_args | vala_link_args | | Cython | cython_args | cython_link_args | -| NASM | nasm_args | | +| NASM | nasm_args | N/A | +| MASM | masm_args | N/A | All these `_*` options are specified per machine. See in [specifying options per diff --git a/docs/markdown/snippets/asm.md b/docs/markdown/snippets/asm.md index a68f163..1055b53 100644 --- a/docs/markdown/snippets/asm.md +++ b/docs/markdown/snippets/asm.md @@ -1,11 +1,15 @@ -## New language `nasm` +## New languages: `nasm` and `masm` When the `nasm` language is added to the project, `.asm` files are automatically compiled with NASM. This is only supported for x86 and x86_64 CPU family. `yasm` is used as fallback if `nasm` command is not found. -Note that GNU Assembly files usually have `.s` extension and were already built -using C compiler such as GCC or CLANG. +When the `masm` language is added to the project, `.masm` files are +automatically compiled with Microsoft's Macro Assembler. This is only supported +for x86, x86_64, ARM and AARCH64 CPU families. + +Note that GNU Assembly files usually have `.s` or `.S` extension and were already +built using C compiler such as GCC or CLANG. ```meson project('test', 'nasm') diff --git a/mesonbuild/compilers/asm.py b/mesonbuild/compilers/asm.py index 4135a5b..1bcae2d 100644 --- a/mesonbuild/compilers/asm.py +++ b/mesonbuild/compilers/asm.py @@ -93,3 +93,118 @@ class YasmCompiler(NasmCompiler): def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]: return ['--depfile', outfile] + +# https://learn.microsoft.com/en-us/cpp/assembler/masm/ml-and-ml64-command-line-reference +class MasmCompiler(Compiler): + language = 'masm' + id = 'ml' + + def get_compile_only_args(self) -> T.List[str]: + return ['/c'] + + def get_argument_syntax(self) -> str: + return 'msvc' + + def needs_static_linker(self) -> bool: + return True + + def get_always_args(self) -> T.List[str]: + return ['/nologo'] + + def get_werror_args(self) -> T.List[str]: + return ['/WX'] + + def get_output_args(self, outputname: str) -> T.List[str]: + return ['/Fo', outputname] + + def get_optimization_args(self, optimization_level: str) -> T.List[str]: + return [] + + def get_debug_args(self, is_debug: bool) -> T.List[str]: + if is_debug: + return ['/Zi'] + return [] + + def sanity_check(self, work_dir: str, environment: 'Environment') -> None: + if self.info.cpu_family not in {'x86', 'x86_64'}: + raise EnvironmentException(f'ASM compiler {self.id!r} does not support {self.info.cpu_family} CPU family') + + def get_buildtype_args(self, buildtype: str) -> T.List[str]: + # FIXME: Not implemented + return [] + + def get_pic_args(self) -> T.List[str]: + return [] + + def get_include_args(self, path: str, is_system: bool) -> T.List[str]: + if not path: + path = '.' + return ['-I' + path] + + def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], + build_dir: str) -> T.List[str]: + for idx, i in enumerate(parameter_list): + if i[:2] == '-I' or i[:2] == '/I': + parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) + return parameter_list + + def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]: + return [] + + def depfile_for_object(self, objfile: str) -> T.Optional[str]: + return None + + +# https://learn.microsoft.com/en-us/cpp/assembler/arm/arm-assembler-command-line-reference +class MasmARMCompiler(Compiler): + language = 'masm' + id = 'armasm' + + def needs_static_linker(self) -> bool: + return True + + def get_always_args(self) -> T.List[str]: + return ['-nologo'] + + def get_werror_args(self) -> T.List[str]: + return [] + + def get_output_args(self, outputname: str) -> T.List[str]: + return ['-o', outputname] + + def get_optimization_args(self, optimization_level: str) -> T.List[str]: + return [] + + def get_debug_args(self, is_debug: bool) -> T.List[str]: + if is_debug: + return ['-g'] + return [] + + def sanity_check(self, work_dir: str, environment: 'Environment') -> None: + if self.info.cpu_family not in {'arm', 'aarch64'}: + raise EnvironmentException(f'ASM compiler {self.id!r} does not support {self.info.cpu_family} CPU family') + + def get_buildtype_args(self, buildtype: str) -> T.List[str]: + # FIXME: Not implemented + return [] + + def get_pic_args(self) -> T.List[str]: + return [] + + def get_include_args(self, path: str, is_system: bool) -> T.List[str]: + if not path: + path = '.' + return ['-i' + path] + + def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], + build_dir: str) -> T.List[str]: + for idx, i in enumerate(parameter_list): + if i[:2] == '-I': + parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:])) + return parameter_list + + def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]: + return [] + + def depfile_for_object(self, objfile: str) -> T.Optional[str]: + return None diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index d54d06d..50a2f4e 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -71,12 +71,13 @@ lang_suffixes = { 'java': ('java',), 'cython': ('pyx', ), 'nasm': ('asm',), + 'masm': ('masm',), } all_languages = lang_suffixes.keys() c_cpp_suffixes = {'h'} cpp_suffixes = set(lang_suffixes['cpp']) | c_cpp_suffixes c_suffixes = set(lang_suffixes['c']) | c_cpp_suffixes -assembler_suffixes = {'s', 'S', 'asm'} +assembler_suffixes = {'s', 'S', 'asm', 'masm'} llvm_ir_suffixes = {'ll'} all_suffixes = set(itertools.chain(*lang_suffixes.values(), assembler_suffixes, llvm_ir_suffixes, c_cpp_suffixes)) source_suffixes = all_suffixes - header_suffixes diff --git a/mesonbuild/compilers/detect.py b/mesonbuild/compilers/detect.py index 849024c..64f6276 100644 --- a/mesonbuild/compilers/detect.py +++ b/mesonbuild/compilers/detect.py @@ -107,6 +107,7 @@ def compiler_from_language(env: 'Environment', lang: str, for_machine: MachineCh 'swift': detect_swift_compiler, 'cython': detect_cython_compiler, 'nasm': detect_nasm_compiler, + 'masm': detect_masm_compiler, } return lang_map[lang](env, for_machine) if lang in lang_map else None @@ -1173,6 +1174,47 @@ def detect_nasm_compiler(env: 'Environment', for_machine: MachineChoice) -> Comp _handle_exceptions(popen_exceptions, compilers) raise EnvironmentException('Unreachable code (exception to make mypy happy)') +def detect_masm_compiler(env: 'Environment', for_machine: MachineChoice) -> Compiler: + # We need a C compiler to properly detect the machine info and linker + is_cross = env.is_cross_build(for_machine) + cc = detect_c_compiler(env, for_machine) + if not is_cross: + from ..environment import detect_machine_info + info = detect_machine_info({'c': cc}) + else: + info = env.machines[for_machine] + + from .asm import MasmCompiler, MasmARMCompiler + comp_class: T.Type[Compiler] + if info.cpu_family == 'x86': + comp = ['ml'] + comp_class = MasmCompiler + arg = '/?' + elif info.cpu_family == 'x86_64': + comp = ['ml64'] + comp_class = MasmCompiler + arg = '/?' + elif info.cpu_family == 'arm': + comp = ['armasm'] + comp_class = MasmARMCompiler + arg = '-h' + elif info.cpu_family == 'aarch64': + comp = ['armasm64'] + comp_class = MasmARMCompiler + arg = '-h' + else: + raise EnvironmentException(f'Platform {info.cpu_family} not supported by MASM') + + popen_exceptions: T.Dict[str, Exception] = {} + try: + output = Popen_safe(comp + [arg])[2] + version = search_version(output) + env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env) + return comp_class(comp, version, for_machine, info, cc.linker, is_cross=is_cross) + except OSError as e: + popen_exceptions[' '.join(comp + [arg])] = e + _handle_exceptions(popen_exceptions, [comp]) + raise EnvironmentException('Unreachable code (exception to make mypy happy)') # GNU/Clang defines and version # ============================= diff --git a/test cases/windows/21 masm/hello.masm b/test cases/windows/21 masm/hello.masm new file mode 100644 index 0000000..a47dc96 --- /dev/null +++ b/test cases/windows/21 masm/hello.masm @@ -0,0 +1,33 @@ +; --------------------------------------------- +; Hello World for Win64 Intel x64 Assembly +; +; by fruel (https://github.com/fruel) +; 13 June 2016 +; --------------------------------------------- + +GetStdHandle PROTO +ExitProcess PROTO +WriteConsoleA PROTO + +.data +msg BYTE "Hello World!",0 +bytesWritten DWORD ? + +.code +mainCRTStartup proc + sub rsp, 5 * 8 ; reserve shadow space + + mov rcx, -11 ; nStdHandle (STD_OUTPUT_HANDLE) + call GetStdHandle + + mov rcx, rax ; hConsoleOutput + lea rdx, msg ; *lpBuffer + mov r8, LENGTHOF msg - 1 ; nNumberOfCharsToWrite + lea r9, bytesWritten ; lpNumberOfCharsWritten + mov QWORD PTR [rsp + 4 * SIZEOF QWORD], 0 ; lpReserved + call WriteConsoleA + + mov rcx, 0 ; uExitCode + call ExitProcess +mainCRTStartup endp +END diff --git a/test cases/windows/21 masm/meson.build b/test cases/windows/21 masm/meson.build new file mode 100644 index 0000000..b6b8fbb --- /dev/null +++ b/test cases/windows/21 masm/meson.build @@ -0,0 +1,14 @@ +project('test-masm', 'c') + +if get_option('backend').startswith('vs') + error('MESON_SKIP_TEST: masm is not supported by vs backend') +endif + +cc = meson.get_compiler('c') + +# MASM must be found when using MSVC, otherwise it is optional +if not add_languages('masm', required: cc.get_argument_syntax() == 'msvc') + error('MESON_SKIP_TEST: masm not available') +endif + +executable('app', 'hello.masm') -- cgit v1.1