Merge pull request #11737 from amyspark/amyspark/add-nasm-building-rules-to-xcode
backends: Add Nasm build rules to Xcode
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index af730f8..0db5c79 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -2013,6 +2013,34 @@
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 @@
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 abdac0c..467cd1a 100644
--- a/mesonbuild/backend/xcodebackend.py
+++ b/mesonbuild/backend/xcodebackend.py
@@ -10,9 +10,12 @@
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 @@
'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 @@
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 @@
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 @@
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 @@
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 @@
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 d04fbd2..bfe436b 100644
--- a/mesonbuild/compilers/asm.py
+++ b/mesonbuild/compilers/asm.py
@@ -42,8 +42,10 @@
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 @@
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 @@
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 85ecaf1..fac46a6 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 d025d43..d5a2ba3 100644
--- a/test cases/nasm/2 asm language/meson.build
+++ b/test cases/nasm/2 asm language/meson.build
@@ -5,10 +5,8 @@
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 9777291..18b980d 100644
--- a/test cases/nasm/3 nasm only/meson.build
+++ b/test cases/nasm/3 nasm only/meson.build
@@ -4,6 +4,10 @@
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 0000000..5be150e
--- /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 0000000..8f8eb99
--- /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 0000000..373810f
--- /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',
+)