diff --git a/cross/ownstdlib.txt b/cross/ownstdlib.txt new file mode 100644 index 000000000..46e99f7a6 --- /dev/null +++ b/cross/ownstdlib.txt @@ -0,0 +1,13 @@ +# This is a setup for compiling a program that runs natively +# but uses a custom std lib. This test will only work on +# x86_64. + +[target_machine] +system = 'linux' +cpu_family = 'x86_64' +cpu = 'x86_64' +endian = 'little' + +[properties] + +c_stdlib = ['mylibc', 'mylibc_dep'] # Subproject name, dependency name diff --git a/manual tests/9 nostdlib/meson.build b/manual tests/9 nostdlib/meson.build new file mode 100644 index 000000000..3ef743ebf --- /dev/null +++ b/manual tests/9 nostdlib/meson.build @@ -0,0 +1,10 @@ +project('own libc', 'c') + +# A simple project that uses its own libc. + +# Note that we don't need to specify anything, the flags to use +# stdlib come from the cross file. + +exe = executable('selfcontained', 'prog.c') + +test('standalone test', exe) diff --git a/manual tests/9 nostdlib/prog.c b/manual tests/9 nostdlib/prog.c new file mode 100644 index 000000000..9414bcea2 --- /dev/null +++ b/manual tests/9 nostdlib/prog.c @@ -0,0 +1,7 @@ + +#include + +int main() { + const char *message = "Hello without stdlib.\n"; + return simple_print(message, simple_strlen(message)); +} diff --git a/manual tests/9 nostdlib/subprojects/mylibc/libc.c b/manual tests/9 nostdlib/subprojects/mylibc/libc.c new file mode 100644 index 000000000..67261cb1d --- /dev/null +++ b/manual tests/9 nostdlib/subprojects/mylibc/libc.c @@ -0,0 +1,35 @@ +/* Do not use this as the basis of your own libc. + * The code is probably unoptimal or wonky, as I + * had no prior experience with this, but instead + * just fiddled with the code until it worked. + */ + +#include + +#define STDOUT 1 +#define SYS_WRITE 4 + +int simple_print(const char *msg, const long bufsize) { + int count; + long total_written = 0; + while(total_written < bufsize) { + asm( + "int $0x80\n\t" + : "=a"(count) + : "0"(SYS_WRITE), "b"(STDOUT), "c"(msg+total_written), "d"(bufsize-total_written) + :); + if(count == 0) { + return 1; + } + total_written += count; + } + return 0; +} + +int simple_strlen(const char *str) { + int len = 0; + while(str[len] != '\0') { + len++; + } + return len; +} diff --git a/manual tests/9 nostdlib/subprojects/mylibc/meson.build b/manual tests/9 nostdlib/subprojects/mylibc/meson.build new file mode 100644 index 000000000..aa0184e11 --- /dev/null +++ b/manual tests/9 nostdlib/subprojects/mylibc/meson.build @@ -0,0 +1,11 @@ +project('own libc', 'c') + +# A very simple libc implementation + +# Do not specify -nostdlib & co. They come from cross specifications. + +libc = static_library('c', 'libc.c', 'stubstart.s') + +mylibc_dep = declare_dependency(link_with : libc, + include_directories : include_directories('.') +) diff --git a/manual tests/9 nostdlib/subprojects/mylibc/stdio.h b/manual tests/9 nostdlib/subprojects/mylibc/stdio.h new file mode 100644 index 000000000..c3f8f56ca --- /dev/null +++ b/manual tests/9 nostdlib/subprojects/mylibc/stdio.h @@ -0,0 +1,5 @@ +#pragma once + +int simple_print(const char *msg, const long bufsize); + +int simple_strlen(const char *str); diff --git a/manual tests/9 nostdlib/subprojects/mylibc/stubstart.s b/manual tests/9 nostdlib/subprojects/mylibc/stubstart.s new file mode 100644 index 000000000..0a6d9729a --- /dev/null +++ b/manual tests/9 nostdlib/subprojects/mylibc/stubstart.s @@ -0,0 +1,8 @@ +.globl _start + +_start: + + call main + movl %eax, %ebx + movl $1, %eax + int $0x80 diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 7c6caa626..d4a0f9975 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -286,6 +286,7 @@ class Backend(): def generate_basic_compiler_args(self, target, compiler): commands = [] + commands += self.get_cross_stdlib_args(target, compiler) commands += compiler.get_always_args() commands += compiler.get_warn_args(self.environment.coredata.get_builtin_option('warning_level')) commands += compiler.get_option_compile_args(self.environment.coredata.compiler_options) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index bd6f4dbc8..4b85565a3 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1394,6 +1394,13 @@ rule FORTRAN_DEP_HACK mod_files.append(os.path.join(dirname, mod_name)) return mod_files + def get_cross_stdlib_args(self, target, compiler): + if not target.is_cross: + return [] + if self.environment.cross_info.has_stdlib(compiler.language): + return [] + return compiler.get_no_stdinc_args() + def generate_single_compile(self, target, outfile, src, is_generated=False, header_deps=[], order_deps=[]): if(isinstance(src, str) and src.endswith('.h')): raise RuntimeError('Fug') @@ -1599,6 +1606,13 @@ rule FORTRAN_DEP_HACK elem.add_item('CROSS', '--cross-host=' + self.environment.cross_info.config['host_machine']['system']) elem.write(outfile) + def get_cross_stdlib_link_args(self, target, linker): + if isinstance(target, build.StaticLibrary) or not target.is_cross: + return [] + if not self.environment.cross_info.has_stdlib(linker.language): + return [] + return linker.get_no_stdlib_link_args() + def generate_link(self, target, outfile, outname, obj_list, linker, extra_args=[]): if isinstance(target, build.StaticLibrary): linker_base = 'STATIC' @@ -1612,6 +1626,7 @@ rule FORTRAN_DEP_HACK linker_rule = linker_base + crstr + '_LINKER' abspath = os.path.join(self.environment.get_build_dir(), target.subdir) commands = [] + commands += self.get_cross_stdlib_link_args(target, linker) commands += linker.get_linker_always_args() if not isinstance(target, build.StaticLibrary): commands += compilers.get_base_link_args(self.environment.coredata.base_options, diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 1e9a1bb3f..9de556c48 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -101,6 +101,7 @@ class Build: self.install_dirs = [] self.dep_manifest_name = None self.dep_manifest = {} + self.cross_stdlibs = {} def has_language(self, language): for i in self.compilers: diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py index 27a49a760..241192e38 100644 --- a/mesonbuild/compilers.py +++ b/mesonbuild/compilers.py @@ -311,6 +311,12 @@ class CCompiler(Compiler): def get_always_args(self): return [] + def get_no_stdinc_args(self): + return ['-nostdinc'] + + def get_no_stdlib_link_args(self): + return ['-nostdlib'] + def get_warn_args(self, level): return self.warn_args[level] diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 41e8531f3..5096320e5 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -759,6 +759,12 @@ class CrossBuildInfo(): def has_target(self): return 'target_machine' in self.config + def has_stdlib(self, language): + return language + '_stdlib' in self.config['properties'] + + def get_stdlib(self, language): + return self.config['properties'][language + '_stdlib'] + # Wehn compiling a cross compiler we use the native compiler for everything. # But not when cross compiling a cross compiler. def need_cross_compiler(self): diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index fb3e601a9..f269c2fb2 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -1078,6 +1078,22 @@ class Interpreter(): if not isinstance(first, mparser.FunctionNode) or first.func_name != 'project': raise InvalidCode('First statement must be a call to project') + def check_cross_stdlibs(self): + if self.build.environment.is_cross_build(): + cross_info = self.build.environment.cross_info + for c in self.build.cross_compilers: + l = c.language + try: + di = mesonlib.stringlistify(cross_info.get_stdlib(l)) + if len(di) != 2: + raise InterpreterException('Stdlib definition for %s should have exactly two elements.' \ + % l) + projname, depname = di + subproj = self.do_subproject(projname, {}) + self.build.cross_stdlibs[l] = subproj.get_variable_method([depname], {}) + except KeyError as e: + pass + def run(self): self.evaluate_codeblock(self.ast) mlog.log('Build targets in project:', mlog.bold(str(len(self.build.targets)))) @@ -1412,6 +1428,8 @@ class Interpreter(): if 'vala' in langs: if not 'c' in langs: raise InterpreterException('Compiling Vala requires C. Add C to your project languages and rerun Meson.') + if not self.is_subproject(): + self.check_cross_stdlibs() @stringArgs def func_add_languages(self, node, args, kwargs): @@ -1983,11 +2001,28 @@ class Interpreter(): mlog.debug('Unknown target type:', str(targetholder)) raise RuntimeError('Unreachable code') target = targetclass(name, self.subdir, self.subproject, is_cross, sources, objs, self.environment, kwargs) + if is_cross: + self.add_cross_stdlib_info(target) l = targetholder(target, self) self.add_target(name, l.held_object) self.global_args_frozen = True return l + def get_used_languages(self, target): + result = {} + for i in target.sources: + for c in self.build.compilers: + if c.can_compile(i): + result[c.language] = True + break + return result + + def add_cross_stdlib_info(self, target): + for l in self.get_used_languages(target): + if self.environment.cross_info.has_stdlib(l) and \ + self.subproject != self.environment.cross_info.get_stdlib(l)[0]: + target.add_external_deps(self.build.cross_stdlibs[l]) + def check_sources_exist(self, subdir, sources): for s in sources: if not isinstance(s, str):