Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated support for Zig #14226

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion ci/ciimage/arch/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ pkgs=(
itstool glib2-devel gtk3 java-environment=8 gtk-doc llvm clang sdl2 graphviz
doxygen vulkan-validation-layers openssh mercurial gtk-sharp-2 qt5-tools
libwmf cmake netcdf-fortran openmpi nasm gnustep-base gettext
python-lxml hotdoc rust-bindgen qt6-base qt6-tools qt6-declarative wayland wayland-protocols
python-lxml hotdoc rust-bindgen qt6-base qt6-tools qt6-declarative wayland
wayland-protocols zig
# cuda
)

Expand Down
370 changes: 186 additions & 184 deletions docs/markdown/Reference-tables.md

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions docs/markdown/snippets/zig_language_support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Basic support for the Zig language

Meson now supports the compilation of basic Zig
programs. Support for more advanced scenarios
is not implemented yet.
2 changes: 1 addition & 1 deletion mesonbuild/backend/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class TargetIntrospectionData(TypedDict):
# Languages that can mix with C or C++ but don't support unity builds yet
# because the syntax we use for unity builds is specific to C/++/ObjC/++.
# Assembly files cannot be unitified and neither can LLVM IR files
LANGS_CANT_UNITY = ('d', 'fortran', 'vala')
LANGS_CANT_UNITY = ('d', 'fortran', 'vala', 'zig')

@dataclass(eq=False)
class RegenInfo:
Expand Down
29 changes: 23 additions & 6 deletions mesonbuild/backend/ninjabackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -958,10 +958,9 @@ def generate_target(self, target) -> None:
outname = self.get_target_filename(target)
obj_list = []
is_unity = target.is_unity
header_deps = []
header_deps = self.get_generated_headers(target)
unity_src = []
unity_deps = [] # Generated sources that must be built before compiling a Unity target.
header_deps += self.get_generated_headers(target)

if is_unity:
# Warn about incompatible sources if a unity build is enabled
Expand Down Expand Up @@ -1086,6 +1085,8 @@ def generate_target(self, target) -> None:
if is_compile_target:
# Skip the link stage for this special type of target
return
if target.uses_zig():
pass
linker, stdlib_args = self.determine_linker_and_stdlib_args(target)

if not isinstance(target, build.StaticLibrary):
Expand Down Expand Up @@ -1412,7 +1413,7 @@ def add_build(self, build: NinjaBuildElement) -> None:
if build.rulename in self.ruledict:
build.rule = self.ruledict[build.rulename]
else:
mlog.warning(f"build statement for {build.outfilenames} references nonexistent rule {build.rulename}")
raise MesonBugException(f"build statement for {build.outfilenames} references nonexistent rule {build.rulename}")

def write_rules(self, outfile: T.TextIO) -> None:
for b in self.build_elements:
Expand Down Expand Up @@ -2357,7 +2358,11 @@ def generate_dynamic_link_rules(self) -> None:
continue
rule = '{}_LINKER{}'.format(langname, self.get_rule_suffix(for_machine))
command = compiler.get_linker_exelist()
args = ['$ARGS'] + NinjaCommandArg.list(compiler.get_linker_output_args('$out'), Quoting.none) + ['$in', '$LINK_ARGS']
args: T.List[str] = []
if langname == 'zig':
args.append('$MODE')
args += ['$ARGS']
args += NinjaCommandArg.list(compiler.get_linker_output_args('$out'), Quoting.none) + ['$in', '$LINK_ARGS']
description = 'Linking target $out'
if num_pools > 0:
pool = 'pool = link_pool'
Expand Down Expand Up @@ -2432,6 +2437,13 @@ def generate_rust_compile_rules(self, compiler) -> None:
self.add_rule(NinjaRule(rule, command, [], description, deps=depstyle,
depfile=depfile))

def generate_zig_compile_rules(self, compiler: Compiler) -> None:
rule = self.compiler_to_rule_name(compiler)
command = compiler.get_exelist() + ['$ARGS', '$in']
command += NinjaCommandArg.list(compiler.get_output_args('$out'), Quoting.none)
description = 'Compiling Zig source $in'
self.add_rule(NinjaRule(rule, command, [], description))

def generate_swift_compile_rules(self, compiler) -> None:
rule = self.compiler_to_rule_name(compiler)
full_exe = self.environment.get_build_command() + [
Expand Down Expand Up @@ -2517,6 +2529,9 @@ def generate_compile_rule_for(self, langname: str, compiler: Compiler) -> None:
if langname == 'rust':
self.generate_rust_compile_rules(compiler)
return
if langname == 'zig':
self.generate_zig_compile_rules(compiler)
return
if langname == 'swift':
if self.environment.machines.matches_build_machine(compiler.for_machine):
self.generate_swift_compile_rules(compiler)
Expand Down Expand Up @@ -2983,14 +2998,14 @@ def generate_common_compile_args_per_src_type(self, target: build.BuildTarget) -
src_type_to_args[src_type_str] = commands.to_native()
return src_type_to_args

def generate_single_compile(self, target: build.BuildTarget, src,
def generate_single_compile(self, target: build.BuildTarget, src: mesonlib.FileOrString,
is_generated: bool = False, header_deps=None,
order_deps: T.Optional[T.List[FileOrString]] = None,
extra_args: T.Optional[T.List[str]] = None,
unity_sources: T.Optional[T.List[FileOrString]] = None,
) -> T.Tuple[str, str]:
"""
Compiles C/C++, ObjC/ObjC++, Fortran, and D sources
Compiles C/C++, ObjC/ObjC++, Fortran, D, and Zig sources
"""
header_deps = header_deps if header_deps is not None else []
order_deps = order_deps if order_deps is not None else []
Expand Down Expand Up @@ -3619,6 +3634,8 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T.
elem.add_item('ARGS', compile_args)

elem.add_item('LINK_ARGS', commands)
if linker.id == 'zig':
elem.add_item('MODE', 'build-exe' if isinstance(target, build.Executable) else 'build-lib')
self.create_target_linker_introspection(target, linker, commands)
return elem

Expand Down
3 changes: 3 additions & 0 deletions mesonbuild/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -1662,6 +1662,9 @@ def get_used_stdlib_args(self, link_language: str) -> T.List[str]:
def uses_rust(self) -> bool:
return 'rust' in self.compilers

def uses_zig(self) -> bool:
return 'zig' in self.compilers

def uses_rust_abi(self) -> bool:
return self.uses_rust() and self.rust_crate_type in {'dylib', 'rlib', 'proc-macro'}

Expand Down
2 changes: 2 additions & 0 deletions mesonbuild/compilers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
'detect_rust_compiler',
'detect_d_compiler',
'detect_swift_compiler',
'detect_zig_compiler',
]

# Bring symbols from each module into compilers sub-package namespace
Expand Down Expand Up @@ -83,4 +84,5 @@
detect_rust_compiler,
detect_d_compiler,
detect_swift_compiler,
detect_zig_compiler,
)
6 changes: 4 additions & 2 deletions mesonbuild/compilers/compilers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2012-2022 The Meson development team
# Copyright © 2023-2024 Intel Corporation
# Copyright © 2023-2025 Intel Corporation

from __future__ import annotations

Expand Down Expand Up @@ -70,6 +70,7 @@
'nasm': ('asm', 'nasm',),
'masm': ('masm',),
'linearasm': ('sa',),
'zig': ('zig',),
}
all_languages = lang_suffixes.keys()
c_cpp_suffixes = {'h'}
Expand All @@ -86,7 +87,7 @@
# List of languages that can be linked with C code directly by the linker
# used in build.py:process_compilers() and build.py:get_dynamic_linker()
# This must be sorted, see sort_clink().
clink_langs = ('d', 'cuda') + clib_langs
clink_langs = ('zig', 'd', 'cuda') + clib_langs

SUFFIX_TO_LANG = dict(itertools.chain(*(
[(suffix, lang) for suffix in v] for lang, v in lang_suffixes.items())))
Expand All @@ -110,6 +111,7 @@
'rust': 'RUSTFLAGS',
'cython': 'CYTHONFLAGS',
'cs': 'CSFLAGS', # This one might not be standard.
'zig': 'ZIGFLAGS', # This is not standard
}

# All these are only for C-linkable languages; see `clink_langs` above.
Expand Down
24 changes: 24 additions & 0 deletions mesonbuild/compilers/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
defaults['gcc_static_linker'] = ['gcc-ar']
defaults['clang_static_linker'] = ['llvm-ar']
defaults['nasm'] = ['nasm', 'yasm']
defaults['zig'] = ['zig']


def compiler_from_language(env: 'Environment', lang: str, for_machine: MachineChoice) -> T.Optional[Compiler]:
Expand All @@ -99,6 +100,7 @@ def compiler_from_language(env: 'Environment', lang: str, for_machine: MachineCh
'nasm': detect_nasm_compiler,
'masm': detect_masm_compiler,
'linearasm': detect_linearasm_compiler,
'zig': detect_zig_compiler,
}
return lang_map[lang](env, for_machine) if lang in lang_map else None

Expand Down Expand Up @@ -1401,6 +1403,28 @@ def detect_linearasm_compiler(env: Environment, for_machine: MachineChoice) -> C
_handle_exceptions(popen_exceptions, [comp])
raise EnvironmentException('Unreachable code (exception to make mypy happy)')

def detect_zig_compiler(env: Environment, for_machine: MachineChoice) -> Compiler:
from .zig import ZigCompiler
from ..linkers.linkers import ZigDynamicLinker
exelist = env.lookup_binary_entry(for_machine, 'zig')
is_cross = env.is_cross_build(for_machine)
info = env.machines[for_machine]
if exelist is None:
exelist = defaults['zig']

try:
_, ver, _ = Popen_safe([exelist[0], 'version'])
except OSError:
raise EnvironmentException('Could not execute Zig compiler "{}"'.format(' '.join(exelist)))

version = search_version(ver)
linker = ZigDynamicLinker(exelist, for_machine, '', [], version=version)
comp = ZigCompiler(exelist, version, for_machine, info, linker, env.exe_wrapper, is_cross)
env.coredata.add_lang_args(comp.language, ZigCompiler, for_machine, env)

return comp


# GNU/Clang defines and version
# =============================

Expand Down
4 changes: 4 additions & 0 deletions mesonbuild/compilers/mixins/islinker.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,7 @@ def thread_flags(self, env: 'Environment') -> T.List[str]:

def thread_link_flags(self, env: 'Environment') -> T.List[str]:
return []

def headerpad_args(self) -> T.List[str]:
# Only used by the Apple linker
return []
143 changes: 143 additions & 0 deletions mesonbuild/compilers/zig.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Copyright 2012-2021 The Meson development team

# 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.

from __future__ import annotations

import os
import subprocess
import textwrap
import typing as T

from ..mesonlib import MachineChoice, EnvironmentException, MesonException
from .compilers import Compiler

if T.TYPE_CHECKING:
from .._typing import ImmutableListProtocol
from ..envconfig import MachineInfo
from ..environment import Environment
from ..linkers.linkers import DynamicLinker
from ..programs import ExternalProgram


_OPTIMIZATION_ARGS: T.Mapping[str, ImmutableListProtocol[str]] = {
'0': [],
'g': ['-O', 'Debug'],
'1': ['-O', 'ReleaseSafe'],
'2': ['-O', 'ReleaseSafe'],
'3': ['-O', 'ReleaseFast'],
's': ['-O', 'ReleaseSmall'],
}


class ZigCompiler(Compiler):
language = 'zig'

# TODO: lto
# TODO: threads? That seems more like code sanitizers than actual threading
# TODO: rpath
# TODO: darwin specific things
# TODO: emit header?
# TODO: structured_sources?
# TODO: tests?
# TODO: good solution for the need to link with libc
# TODO: demonstrate linking with rust

def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice, info: 'MachineInfo',
linker: DynamicLinker, exe_wrapper: T.Optional['ExternalProgram'] = None, is_cross: bool = False):
super().__init__([], exelist + ['build-obj'], version, for_machine, info, linker, is_cross=is_cross)
self.id = 'zig'
self.exe_wrapper = exe_wrapper

def depfile_for_object(self, objfile: str) -> T.Optional[str]:
# not implemented currently: https://github.com/ziglang/zig/issues/16850
return None

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] == '-L':
parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:]))

return parameter_list

def get_buildtype_args(self, buildtype: str) -> T.List[str]:
return []

def get_optimization_args(self, optimization_level: str) -> T.List[str]:
return _OPTIMIZATION_ARGS[optimization_level].copy()

def get_output_args(self, outputname: str) -> T.List[str]:
return [f'-femit-bin={outputname}']

def get_pic_args(self) -> T.List[str]:
return ['-fPIC']

def get_win_subsystem_args(self, value: str) -> T.List[str]:
return ['--subsystem', value]

def get_colorout_args(self, colortype: str) -> T.List[str]:
if colortype == 'auto':
return ['--color', 'auto']
elif colortype == 'always':
return ['--color', 'on']
elif colortype == 'never':
return ['--color', 'off']
else:
raise MesonException(f'Invalid color type for zig {colortype}')

def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
if not path:
path = '.'
if is_system:
return ['-isystem' + path]
return ['-I' + path]

def needs_static_linker(self) -> bool:
return True

def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
source_name = os.path.join(work_dir, 'sanity.zig')
output_name = os.path.join(work_dir, 'zigtest')

with open(source_name, 'w', encoding='utf-8') as ofile:
ofile.write(textwrap.dedent(
'''pub fn main() !void {
}
'''))

# Compile the source file to an executable
# Drop the added `build-obj`
pc = subprocess.Popen(self.exelist[:-1] + ['build-exe', source_name] + self.get_output_args(output_name),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=work_dir)
_stdo, _stde = pc.communicate()
stdo = _stdo.decode('utf-8', errors='replace')
stde = _stde.decode('utf-8', errors='replace')

# Check if the build was successful
if pc.returncode != 0:
raise EnvironmentException(f'Zig compiler {self.name_string()} can not compile programs.\n{stdo}\n{stde}')

if self.is_cross:
if self.exe_wrapper is None:
# Can't check if the binaries run so we have to assume they do
return
cmdlist = self.exe_wrapper.get_command() + [output_name]
else:
cmdlist = [output_name]

# Check if the built executable is runnable
pe = subprocess.Popen(cmdlist, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
if pe.wait() != 0:
raise EnvironmentException(f'Executables created by Zig compiler {self.name_string()} are not runnable.')
1 change: 1 addition & 0 deletions mesonbuild/envconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
'rust': 'RUSTC',
'vala': 'VALAC',
'nasm': 'NASM',
'zig': 'ZIGC',

# Linkers
'c_ld': 'CC_LD',
Expand Down
Loading
Loading