diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index af730f88e..0db5c79af 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -2013,6 +2013,34 @@ class Backend: env.prepend('PATH', list(extra_paths)) return env + def compiler_to_generator_args(self, target: build.BuildTarget, + compiler: 'Compiler', output: str = '@OUTPUT@', + depfile: T.Union[str, None] = '@DEPFILE@', + extras: T.Union[T.List[str], None] = None, + input: str = '@INPUT@') -> CompilerArgs: + ''' + The VS and Xcode backends need the full set of arguments for making a + custom build rule. This is a convenience method to convert a Compiler + to its arguments, for later concatenation. + ''' + # FIXME: There are many other args missing + commands = self.generate_basic_compiler_args(target, compiler) + if depfile: + commands += compiler.get_dependency_gen_args(output, depfile) + commands += compiler.get_output_args(output) + commands += self.get_source_dir_include_args(target, compiler) + commands += self.get_build_dir_include_args(target, compiler) + commands += compiler.get_compile_only_args() + # Add per-target compile args, f.ex, `c_args : ['-DFOO']`. We set these + # near the end since these are supposed to override everything else. + commands += self.escape_extra_args(target.get_extra_args(compiler.get_language())) + # Do not escape this one, it is interpreted by the build system + # (Xcode considers these as variables to expand at build time) + if extras is not None: + commands += extras + commands += [input] + return commands + def compiler_to_generator(self, target: build.BuildTarget, compiler: 'Compiler', sources: _ALL_SOURCES_TYPE, @@ -2026,16 +2054,7 @@ class Backend: exelist = compiler.get_exelist() exe = programs.ExternalProgram(exelist[0]) args = exelist[1:] - # FIXME: There are many other args missing - commands = self.generate_basic_compiler_args(target, compiler) - commands += compiler.get_dependency_gen_args('@OUTPUT@', '@DEPFILE@') - commands += compiler.get_output_args('@OUTPUT@') - commands += compiler.get_compile_only_args() + ['@INPUT@'] - commands += self.get_source_dir_include_args(target, compiler) - commands += self.get_build_dir_include_args(target, compiler) - # Add per-target compile args, f.ex, `c_args : ['-DFOO']`. We set these - # near the end since these are supposed to override everything else. - commands += self.escape_extra_args(target.get_extra_args(compiler.get_language())) + commands = self.compiler_to_generator_args(target, compiler) generator = build.Generator(exe, args + commands.to_native(), [output_templ], depfile='@PLAINNAME@.d', depends=depends) diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index abdac0cc6..467cd1a6d 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -10,9 +10,12 @@ from . import backends from .. import build from .. import mesonlib from .. import mlog +from ..arglist import CompilerArgs from ..mesonlib import MesonBugException, MesonException, OptionKey if T.TYPE_CHECKING: + from ..build import BuildTarget + from ..compilers import Compiler from ..interpreter import Interpreter INDENT = '\t' @@ -33,10 +36,12 @@ XCODETYPEMAP = {'c': 'sourcecode.c.c', 'dylib': 'compiled.mach-o.dylib', 'o': 'compiled.mach-o.objfile', 's': 'sourcecode.asm', - 'asm': 'sourcecode.asm', + 'asm': 'sourcecode.nasm', 'metal': 'sourcecode.metal', 'glsl': 'sourcecode.glsl', } +NEEDS_CUSTOM_RULES = {'nasm': 'sourcecode.nasm', + } LANGNAMEMAP = {'c': 'C', 'cpp': 'CPLUSPLUS', 'objc': 'OBJC', @@ -271,6 +276,7 @@ class XCodeBackend(backends.Backend): self.generate_native_target_map() self.generate_native_frameworks_map() self.generate_custom_target_map() + self.generate_native_target_build_rules_map() self.generate_generator_target_map() self.generate_source_phase_map() self.generate_target_dependency_map() @@ -288,6 +294,9 @@ class XCodeBackend(backends.Backend): objects_dict.add_comment(PbxComment('Begin PBXBuildFile section')) self.generate_pbx_build_file(objects_dict) objects_dict.add_comment(PbxComment('End PBXBuildFile section')) + objects_dict.add_comment(PbxComment('Begin PBXBuildRule section')) + self.generate_pbx_build_rule(objects_dict) + objects_dict.add_comment(PbxComment('End PBXBuildRule section')) objects_dict.add_comment(PbxComment('Begin PBXBuildStyle section')) self.generate_pbx_build_style(objects_dict) objects_dict.add_comment(PbxComment('End PBXBuildStyle section')) @@ -401,6 +410,16 @@ class XCodeBackend(backends.Backend): for t in self.build_targets: self.native_targets[t] = self.gen_id() + def generate_native_target_build_rules_map(self) -> None: + self.build_rules = {} + for name, target in self.build_targets.items(): + languages = {} + for language in target.compilers: + if language not in NEEDS_CUSTOM_RULES: + continue + languages[language] = self.gen_id() + self.build_rules[name] = languages + def generate_custom_target_map(self) -> None: self.shell_targets = {} self.custom_target_output_buildfile = {} @@ -720,6 +739,53 @@ class XCodeBackend(backends.Backend): settings_dict.add_item('COPY_PHASE_STRIP', 'NO') styledict.add_item('name', f'"{name}"') + def to_shell_script(self, args: CompilerArgs) -> str: + quoted_cmd = [] + for c in args: + quoted_cmd.append(c.replace('"', chr(92) + '"')) + cmd = ' '.join(quoted_cmd) + return f"\"#!/bin/sh\\n{cmd}\\n\"" + + def generate_pbx_build_rule(self, objects_dict: PbxDict) -> None: + for name, languages in self.build_rules.items(): + target: BuildTarget = self.build_targets[name] + for language, idval in languages.items(): + compiler: Compiler = target.compilers[language] + buildrule = PbxDict() + buildrule.add_item('isa', 'PBXBuildRule') + buildrule.add_item('compilerSpec', 'com.apple.compilers.proxy.script') + if compiler.get_id() != 'yasm': + # Yasm doesn't generate escaped build rules + buildrule.add_item('dependencyFile', '"$(DERIVED_FILE_DIR)/$(INPUT_FILE_BASE).d"') + buildrule.add_item('fileType', NEEDS_CUSTOM_RULES[language]) + inputfiles = PbxArray() + buildrule.add_item('inputFiles', inputfiles) + buildrule.add_item('isEditable', '0') + outputfiles = PbxArray() + outputfiles.add_item('"$(DERIVED_FILE_DIR)/$(INPUT_FILE_BASE).o"') + buildrule.add_item('outputFiles', outputfiles) + # Do NOT use this parameter. Xcode will accept it from the UI, + # but the parser will break down inconsistently upon next + # opening. rdar://FB12144055 + # outputargs = PbxArray() + # args = self.generate_basic_compiler_args(target, compiler) + # outputargs.add_item(self.to_shell_script(args)) + # buildrule.add_item('outputFilesCompilerFlags', outputargs) + commands = CompilerArgs(compiler) + commands += compiler.get_exelist() + if compiler.get_id() == 'yasm': + # Yasm doesn't generate escaped build rules + commands += self.compiler_to_generator_args(target, compiler, output='"$SCRIPT_OUTPUT_FILE_0"', input='"$SCRIPT_INPUT_FILE"', depfile=None) + else: + commands += self.compiler_to_generator_args(target, + compiler, + output='"$SCRIPT_OUTPUT_FILE_0"', + input='"$SCRIPT_INPUT_FILE"', + depfile='"$(dirname "$SCRIPT_OUTPUT_FILE_0")/$(basename "$SCRIPT_OUTPUT_FILE_0" .o).d"', + extras=['$OTHER_INPUT_FILE_FLAGS']) + buildrule.add_item('script', self.to_shell_script(commands)) + objects_dict.add_item(idval, buildrule, 'PBXBuildRule') + def generate_pbx_container_item_proxy(self, objects_dict: PbxDict) -> None: for t in self.build_targets: proxy_dict = PbxDict() @@ -1115,7 +1181,10 @@ class XCodeBackend(backends.Backend): generator_id += 1 for bpname, bpval in t.buildphasemap.items(): buildphases_array.add_item(bpval, f'{bpname} yyy') - ntarget_dict.add_item('buildRules', PbxArray()) + build_rules = PbxArray() + for language, build_rule_idval in self.build_rules[tname].items(): + build_rules.add_item(build_rule_idval, f'{language}') + ntarget_dict.add_item('buildRules', build_rules) dep_array = PbxArray() ntarget_dict.add_item('dependencies', dep_array) dep_array.add_item(self.regen_dependency_id) diff --git a/mesonbuild/compilers/asm.py b/mesonbuild/compilers/asm.py index d04fbd293..bfe436b0b 100644 --- a/mesonbuild/compilers/asm.py +++ b/mesonbuild/compilers/asm.py @@ -42,8 +42,10 @@ class NasmCompiler(Compiler): linker: T.Optional['DynamicLinker'] = None, full_version: T.Optional[str] = None, is_cross: bool = False): super().__init__(ccache, exelist, version, for_machine, info, linker, full_version, is_cross) + self.links_with_msvc = False if 'link' in self.linker.id: self.base_options.add(OptionKey('b_vscrt')) + self.links_with_msvc = True def needs_static_linker(self) -> bool: return True @@ -83,9 +85,7 @@ class NasmCompiler(Compiler): def get_debug_args(self, is_debug: bool) -> T.List[str]: if is_debug: - if self.info.is_windows(): - return [] - return ['-g', '-F', 'dwarf'] + return ['-g'] return [] def get_depfile_suffix(self) -> str: @@ -138,9 +138,12 @@ class YasmCompiler(NasmCompiler): def get_debug_args(self, is_debug: bool) -> T.List[str]: if is_debug: - if self.info.is_windows(): + if self.info.is_windows() and self.links_with_msvc: + return ['-g', 'cv8'] + elif self.info.is_darwin(): return ['-g', 'null'] - return ['-g', 'dwarf2'] + else: + return ['-g', 'dwarf2'] return [] def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]: diff --git a/test cases/nasm/1 configure file/meson.build b/test cases/nasm/1 configure file/meson.build index 85ecaf1c4..fac46a687 100644 --- a/test cases/nasm/1 configure file/meson.build +++ b/test cases/nasm/1 configure file/meson.build @@ -1,15 +1,17 @@ project('nasm config file', 'c') -if host_machine.cpu_family() == 'x86' and host_machine.system() == 'windows' - asm_format = 'win32' -elif host_machine.cpu_family() == 'x86_64' and host_machine.system() == 'windows' - asm_format = 'win64' -elif host_machine.cpu_family() == 'x86' and host_machine.system() == 'linux' +if not host_machine.cpu_family().startswith('x86') + error('MESON_SKIP_TEST: nasm only supported for x86 and x86_64') +endif + +if host_machine.system() != 'linux' + error('MESON_SKIP_TEST: this test asm is made for Linux') +endif + +if host_machine.cpu_family() == 'x86' asm_format = 'elf32' -elif host_machine.cpu_family() == 'x86_64' and host_machine.system() == 'linux' - asm_format = 'elf64' else - error('MESON_SKIP_TEST: skipping test on this platform') + asm_format = 'elf64' endif nasm = find_program('nasm', required: false) diff --git a/test cases/nasm/2 asm language/meson.build b/test cases/nasm/2 asm language/meson.build index d025d43ea..d5a2ba3ea 100644 --- a/test cases/nasm/2 asm language/meson.build +++ b/test cases/nasm/2 asm language/meson.build @@ -5,10 +5,8 @@ if not host_machine.cpu_family().startswith('x86') error('MESON_SKIP_TEST: nasm only supported for x86 and x86_64') endif -if host_machine.system() == 'windows' - error('MESON_SKIP_TEST: this test asm is not made for Windows') -elif host_machine.system() == 'sunos' - error('MESON_SKIP_TEST: this test asm is not made for Solaris or illumos') +if host_machine.system() != 'linux' + error('MESON_SKIP_TEST: this test asm is made for Linux') endif if meson.backend().startswith('vs') diff --git a/test cases/nasm/3 nasm only/meson.build b/test cases/nasm/3 nasm only/meson.build index 9777291f5..18b980da5 100644 --- a/test cases/nasm/3 nasm only/meson.build +++ b/test cases/nasm/3 nasm only/meson.build @@ -4,6 +4,10 @@ if not add_languages('nasm', required: false) error('MESON_SKIP_TEST: nasm not found') endif +if not ['linux', 'windows'].contains(host_machine.system()) + error('MESON_SKIP_TEST: this test asm is made for Windows and Linux') +endif + if meson.backend().startswith('vs') error('MESON_SKIP_TEST: VS backend does not recognise NASM yet') endif diff --git a/test cases/nasm/4 through configure/dummy.asm.in b/test cases/nasm/4 through configure/dummy.asm.in new file mode 100644 index 000000000..5be150e44 --- /dev/null +++ b/test cases/nasm/4 through configure/dummy.asm.in @@ -0,0 +1,4 @@ +global dummy +section .rodata align=16 +dummy: + dd 0x00010203 diff --git a/test cases/nasm/4 through configure/dummy.def b/test cases/nasm/4 through configure/dummy.def new file mode 100644 index 000000000..8f8eb9959 --- /dev/null +++ b/test cases/nasm/4 through configure/dummy.def @@ -0,0 +1,2 @@ +EXPORTS + dummy diff --git a/test cases/nasm/4 through configure/meson.build b/test cases/nasm/4 through configure/meson.build new file mode 100644 index 000000000..373810f5f --- /dev/null +++ b/test cases/nasm/4 through configure/meson.build @@ -0,0 +1,30 @@ +project('through configure') + +if not add_languages('nasm', required: false) + error('MESON_SKIP_TEST: nasm not found') +endif + +if not host_machine.cpu_family().startswith('x86') + assert(not add_languages('nasm', required: false)) + error('MESON_SKIP_TEST: nasm only supported for x86 and x86_64') +endif + +if meson.backend().startswith('vs') + error('MESON_SKIP_TEST: VS backend does not recognise NASM yet') +endif + +section = host_machine.system() == 'macos' ? '.rodata' : '.rdata' + +sources = configure_file( + input: 'dummy.asm.in', + output: 'dummy.asm', + configuration: { + 'section': section + } +) + +dummy = library( + 'dummy', + sources, + vs_module_defs: 'dummy.def', +)