compilers: add support for SDCC

This commit is contained in:
Karolis Stasaitis authored and Paolo Bonzini committed 2026-09-08 12:05:55 +02:00
1 parent 4094f1bb21
commit 2b650f4845
11 files changed
+231 -3

No files matched your search

+21
View File
@@ -0,0 +1,21 @@
# This file assumes that path to the SDCC toolchain is added
# to the environment(PATH) variable, so that Meson can find
# sdcc and sdar while building.
[binaries]
c = 'sdcc'
ar = 'sdar'
[host_machine]
system = 'bare metal'
cpu_family = 'mcs51'
cpu = '8051'
endian = 'little'
[properties]
needs_exe_wrapper = true
[built-in options]
# SDCC selects the target port with '-m'; it must be passed when
# compiling and when linking.
c_args = ['-mmcs51']
c_link_args = ['-mmcs51']
+12
View File
@@ -36,6 +36,7 @@ These are return values of the `get_id` (Compiler family) and
| pathscale | The Pathscale Fortran compiler | |
| pgi | Portland PGI C/C++/Fortran compilers | |
| rustc | Rust compiler | |
| sdcc | Small Device C Compiler | |
| sun | Sun Fortran compiler | |
| c2000 | Texas Instruments C/C++ Compiler (C2000) | |
| c6000 | Texas Instruments C/C++ Compiler (C6000) | |
@@ -86,6 +87,7 @@ These are return values of the `get_linker_id` method in a compiler object.
| mwldarm | The Metrowerks Linker with the ARM interface, used with mwccarm only |
| mwldeppc | The Metrowerks Linker with the PowerPC interface, used with mwcceppc only |
| tasking | TASKING VX-toolset |
| sdcc | SDCC used as the linker driver |
For languages that don't have separate dynamic linkers such as C# and Java, the
`get_linker_id` will return the compiler name.
@@ -119,19 +121,25 @@ set in the cross file.
| csky | 32 bit CSky processor |
| dspic | 16 bit Microchip dsPIC |
| e2k | MCST Elbrus processor |
| f8 | 8 bit SDCC F8 |
| ft32 | 32 bit Bridgetek MCU |
| hc08 | 8 bit Motorola HC08/S08 |
| ia64 | Itanium processor |
| loongarch64 | 64 bit Loongson processor|
| m68k | Motorola 68000 processor |
| mcs51 | 8 bit Intel MCS-51 |
| microblaze | MicroBlaze processor |
| mips | 32 bit MIPS processor |
| mips64 | 64 bit MIPS processor |
| mos6502 | 8 bit MOS 6502/65C02 |
| msp430 | 16 bit MSP430 processor |
| padauk | 8 bit Padauk PDK |
| parisc | HP PA-RISC processor |
| pic24 | 16 bit Microchip PIC24 |
| pic32 | 32 bit Microchip PIC32 |
| ppc | 32 bit PPC processors |
| ppc64 | 64 bit PPC processors |
| rabbit | 8 bit Rabbit 2000/3000 |
| riscv32 | 32 bit RISC-V Open ISA |
| riscv64 | 64 bit RISC-V Open ISA |
| rl78 | Renesas RL78 |
@@ -139,13 +147,17 @@ set in the cross file.
| s390 | IBM zSystem s390 |
| s390x | IBM zSystem s390x |
| sh4 | SuperH SH-4 |
| sm83 | 8 bit Sharp SM83 |
| sparc | 32 bit SPARC |
| sparc64 | SPARC v9 processor |
| stm8 | 8 bit STMicro STM8 |
| sw_64 | 64 bit sunway processor |
| tlcs90 | 8 bit Toshiba TLCS-90 |
| wasm32 | 32 bit Webassembly |
| wasm64 | 64 bit Webassembly |
| x86 | 32 bit x86 processor |
| x86_64 | 64 bit x86 processor |
| z80 | 8 bit Zilog Z80 family |
| tricore | Tricore 32 bit processor |
+1 -2
View File
@@ -943,8 +943,7 @@ class Backend:
else:
gen_source = os.path.relpath(os.path.join(build_dir, rel_src),
os.path.join(self.environment.get_source_dir(), target.get_subdir()))
machine = self.environment.machines[target.for_machine]
object_suffix = machine.get_object_suffix()
object_suffix = compiler.get_object_suffix()
# For the TASKING compiler, in case of LTO or prelinking the object suffix has to be .mil
if compiler.get_id() == 'tasking':
use_lto = self.get_target_option(target, 'b_lto')
+1 -1
View File
@@ -3093,7 +3093,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
src_filename = src
obj_basename = self.canonicalize_filename(src_filename)
rel_obj = os.path.join(self.get_target_private_dir(target), obj_basename)
rel_obj += '.' + self.environment.machines[target.for_machine].get_object_suffix()
rel_obj += '.' + compiler.get_object_suffix()
commands += self.get_compile_debugfile_args(compiler, target, rel_obj)
if isinstance(src, File):
if src.is_built:
+4
View File
@@ -2242,6 +2242,8 @@ class Executable(BuildTarget, LinkableTarget):
self.suffix = 'nef'
elif ('c' in self.compilers and self.compilers['c'].get_id() == 'tasking'):
self.suffix = 'elf'
elif ('c' in self.compilers and self.compilers['c'].get_id() == 'sdcc'):
self.suffix = 'ihx'
else:
self.suffix = machine.get_exe_suffix()
self.filename = self.name
@@ -2407,6 +2409,8 @@ class StaticLibrary(BuildTarget, LinkableTarget):
suffix = 'a'
elif self.environment.machines[self.for_machine].is_os2() and self.environment.coredata.optstore.get_value_for(OptionKey('os2_emxomf')):
suffix = 'lib'
elif 'c' in self.compilers and self.compilers['c'].get_id() == 'sdcc':
suffix = 'lib'
else:
suffix = 'a'
if 'c' in self.compilers and self.compilers['c'].get_id() == 'tasking' and not self.prelink:
+28
View File
@@ -17,6 +17,7 @@ from .mixins.clike import CLikeCompiler
from .mixins.ccrx import CcrxCompiler
from .mixins.microchip import Xc16Compiler, Xc32Compiler, Xc32CStds
from .mixins.compcert import CompCertCompiler
from .mixins.sdcc import SdccCompiler
from .mixins.ti import TICompiler
from .mixins.arm import ArmCompiler, ArmclangCompiler
from .mixins.visualstudio import MSVCCompiler, ClangClCompiler
@@ -680,6 +681,33 @@ class CompCertCCompiler(CompCertCompiler, CCompiler):
path = '.'
return ['-I' + path]
class SdccCCompiler(SdccCompiler, CCompiler):
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
env: Environment,
linker: T.Optional['DynamicLinker'] = None,
full_version: T.Optional[str] = None):
CCompiler.__init__(self, ccache, exelist, version, for_machine,
env, linker=linker, full_version=full_version)
SdccCompiler.__init__(self)
def get_options(self) -> 'MutableKeyedOptionDictType':
opts = super().get_options()
key = self.form_compileropt_key('std')
std_opt = opts[key]
assert isinstance(std_opt, options.UserStdOption), 'for mypy'
std_opt.set_versions(['c89', 'c90', 'c99', 'c11', 'c17', 'c23', 'c2y'])
return opts
def get_option_std_args(self, target: BuildTarget, subproject: T.Optional[str] = None) -> T.List[str]:
args = []
std = self.get_compileropt_value('std', target, subproject)
assert isinstance(std, str)
if std != 'none':
args.append('--std-' + std)
return args
class TICCompiler(TICompiler, CCompiler):
def __init__(self, ccache: T.List[str], exelist: T.List[str], version: str, for_machine: MachineChoice,
env: Environment,
+4
View File
@@ -1549,6 +1549,10 @@ class Compiler(HoldableObject, metaclass=SimpleABC):
def get_depfile_suffix(self) -> str:
raise EnvironmentException(f'{self.id} does not implement get_depfile_suffix')
def get_object_suffix(self) -> str:
"""Suffix for object files."""
return self.environment.machines[self.for_machine].get_object_suffix()
def get_no_stdinc_args(self) -> T.List[str]:
"""Arguments to turn off default inclusion of standard libraries."""
return []
+15
View File
@@ -82,6 +82,7 @@ defaults['cuda_static_linker'] = ['nvlink']
defaults['gcc_static_linker'] = ['gcc-ar']
defaults['clang_static_linker'] = ['llvm-ar']
defaults['emxomf_static_linker'] = ['emxomfar']
defaults['sdcc_static_linker'] = ['sdar']
defaults['nasm'] = ['nasm', 'yasm']
@@ -212,6 +213,8 @@ def detect_static_linker(env: 'Environment', compiler: Compiler) -> StaticLinker
trials = [defaults['clang_cl_static_linker']] + trials
if 'vs_static_linker' in defaults:
trials = [defaults['vs_static_linker']] + trials
elif compiler.id == 'sdcc':
trials = [defaults['sdcc_static_linker']] + trials
popen_exceptions = {}
for linker in trials:
@@ -254,6 +257,8 @@ def detect_static_linker(env: 'Environment', compiler: Compiler) -> StaticLinker
return linkers.Xc16Linker(linker, env)
elif 'xc32-ar' in linker_name:
return linkers.Xc32ArLinker(compiler.for_machine, linker, env)
elif 'sdar' in linker_name:
return linkers.SdccLinker(linker, env)
if 'Texas Instruments Incorporated' in out:
if 'ar2000' in linker_name:
return linkers.C2000Linker(linker, env)
@@ -628,6 +633,16 @@ def _detect_c_or_cpp_compiler(env: 'Environment', lang: str, for_machine: Machin
ccache, compiler, version, for_machine, env,
full_version=full_version, linker=linker)
if out.startswith('SDCC'):
if lang != 'c':
raise EnvironmentException(f'SDCC does not support {lang}')
cls = c.SdccCCompiler
env.add_lang_args(cls.language, cls, for_machine)
linker = linkers.SdccDynamicLinker(compiler, env, for_machine, version=version)
return cls(
ccache, compiler, version, for_machine, env,
full_version=full_version, linker=linker)
if 'Metrowerks C/C++' in out or 'Freescale C/C++' in out:
if 'ARM' in out:
cls = c.MetrowerksCCompilerARM if lang == 'c' else cpp.MetrowerksCPPCompilerARM
+96
View File
@@ -0,0 +1,96 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright 2025 The Meson development team
from __future__ import annotations
"""Representations specific to the Small Device C Compiler (SDCC)."""
import os
import typing as T
if T.TYPE_CHECKING:
from ...compilers.compilers import Compiler
else:
Compiler = object
sdcc_optimization_args: T.Dict[str, T.List[str]] = {
'plain': [],
'0': [],
'g': [],
'1': [],
'2': ['--opt-code-speed'],
'3': ['--opt-code-speed'],
's': ['--opt-code-size'],
}
sdcc_debug_args: T.Dict[bool, T.List[str]] = {
False: [],
True: ['--debug'],
}
class SdccCompiler(Compiler):
id = 'sdcc'
def __init__(self) -> None:
self.warn_args: T.Dict[str, T.List[str]] = {
'0': [],
'1': [],
'2': [],
'3': [],
'everything': [],
}
def get_always_args(self) -> T.List[str]:
return []
def get_pic_args(self) -> T.List[str]:
return []
def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
# SDCC's own -M flags suppress codegen; -Wp routes them to sdcpp, which also compiles.
return ['-Wp,-MD', '-Wp,-MF', '-Wp,' + outfile, '-Wp,-MQ', '-Wp,' + outtarget]
def get_no_optimization_args(self) -> T.List[str]:
return []
def get_object_suffix(self) -> str:
return 'rel'
def _sanity_check_filenames(self) -> T.Tuple[str, T.Optional[str], str]:
cross_or_not = '_cross' if self.is_cross else ''
return ('sanity_check_for_c.c', None, f'sanity_check_for_c{cross_or_not}.ihx')
def get_optimization_args(self, optimization_level: str) -> T.List[str]:
return sdcc_optimization_args[optimization_level]
def get_debug_args(self, is_debug: bool) -> T.List[str]:
return sdcc_debug_args[is_debug]
def get_preprocess_only_args(self) -> T.List[str]:
return ['-E']
def get_werror_args(self) -> T.List[str]:
return ['--Werror']
def get_no_stdinc_args(self) -> T.List[str]:
return ['--nostdinc']
def get_no_stdlib_link_args(self) -> T.List[str]:
return ['--nostdlib']
def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
if not path:
path = '.'
return ['-I' + path]
def thread_flags(self) -> T.List[str]:
return []
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] in {'-I', '-L'}:
parameter_list[idx] = i[:2] + os.path.normpath(os.path.join(build_dir, i[2:]))
return parameter_list
+10
View File
@@ -47,19 +47,25 @@ known_cpu_families = (
'csky',
'dspic',
'e2k',
'f8',
'ft32',
'hc08',
'ia64',
'loongarch64',
'm68k',
'mcs51',
'microblaze',
'mips',
'mips64',
'mos6502',
'msp430',
'padauk',
'parisc',
'pic24',
'pic32',
'ppc',
'ppc64',
'rabbit',
'riscv32',
'riscv64',
'rl78',
@@ -67,13 +73,17 @@ known_cpu_families = (
's390',
's390x',
'sh4',
'sm83',
'sparc',
'sparc64',
'stm8',
'sw_64',
'tlcs90',
'wasm32',
'wasm64',
'x86',
'x86_64',
'z80',
'tricore'
)
+39
View File
@@ -499,6 +499,22 @@ class Xc16Linker(StaticLinker):
return ['rcs']
class SdccLinker(StaticLinker):
"""Static linker for the Small Device C Compiler."""
id = 'sdar'
def can_linker_accept_rsp(self) -> bool:
return False
def get_output_args(self, target: str) -> T.List[str]:
return [target]
def get_linker_always_args(self) -> T.List[str]:
return ['-rc']
class Xc32ArLinker(ArLinker):
"""Static linker for Microchip XC32 compiler."""
@@ -1215,6 +1231,29 @@ class Xc32DynamicLinker(GnuDynamicLinker):
return []
class SdccDynamicLinker(DynamicLinker):
"""Linker for the Small Device C Compiler."""
id = 'sdcc'
def __init__(self, exelist: T.List[str], env: Environment, for_machine: mesonlib.MachineChoice,
*, version: str = 'unknown version'):
super().__init__(exelist, env, for_machine, None, [], version=version)
def get_accepts_rsp(self) -> bool:
return False
def get_allow_undefined_args(self) -> T.List[str]:
return []
def get_output_args(self, outputname: str) -> T.List[str]:
return ['-o', outputname]
def get_search_args(self, dirname: str) -> T.List[str]:
return ['-L' + dirname]
class CompCertDynamicLinker(DynamicLinker):
"""Linker for CompCert C compiler."""