From f22376701bdebbda70b8d521a2da8f5376e6fdaa Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Wed, 18 Jun 2014 01:22:55 +0300 Subject: [PATCH] Can compile very basic Rust programs. --- backends.py | 9 +++ build.py | 4 +- environment.py | 75 ++++++++++++++++++++- interpreter.py | 4 ++ ninjabackend.py | 38 ++++++++++- test cases/rust/1 basic/installed_files.txt | 1 + test cases/rust/1 basic/meson.build | 4 ++ test cases/rust/1 basic/prog.rs | 3 + 8 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 test cases/rust/1 basic/installed_files.txt create mode 100644 test cases/rust/1 basic/meson.build create mode 100644 test cases/rust/1 basic/prog.rs diff --git a/backends.py b/backends.py index 5f874a72b..bbc2dabc3 100644 --- a/backends.py +++ b/backends.py @@ -185,6 +185,12 @@ class Backend(): return True return False + def has_rust(self, target): + for s in target.get_sources(): + if s.endswith('.rs'): + return True + return False + def generate_target(self, target, outfile): if isinstance(target, build.CustomTarget): self.generate_custom_target(target, outfile) @@ -197,6 +203,9 @@ class Backend(): if isinstance(target, build.Jar): self.generate_jar_target(target, outfile) return + if 'rust' in self.environment.coredata.compilers.keys() and self.has_rust(target): + self.generate_rust_target(target, outfile) + return if 'vala' in self.environment.coredata.compilers.keys() and self.has_vala(target): gen_src_deps += self.generate_vala_compile(target, outfile) # The following deals with C/C++ compilation. diff --git a/build.py b/build.py index 213918715..c1e7acd31 100644 --- a/build.py +++ b/build.py @@ -51,9 +51,7 @@ class Build: return False def add_compiler(self, compiler): - lang = compiler.get_language() - if self.static_linker is None and lang != 'java'\ - and lang != 'vala': + if self.static_linker is None and compiler.needs_static_linker(): self.static_linker = self.environment.detect_static_linker(compiler) if self.has_language(compiler.get_language()): return diff --git a/environment.py b/environment.py index 2eb913c8d..3f7fc790b 100644 --- a/environment.py +++ b/environment.py @@ -75,6 +75,9 @@ class CCompiler(): else: self.exe_wrapper = exe_wrapper + def needs_static_linker(self): + return True # When compiling static libraries, so yes. + def get_always_flags(self): return [] @@ -524,6 +527,9 @@ class JavaCompiler(): if pe.returncode != 0: raise EnvironmentException('Executables created by Java compiler %s are not runnable.' % self.name_string()) + def needs_static_linker(self): + return False + def has_header(self, hname): raise EnvironmentException('Java does not support header checks.') @@ -549,11 +555,14 @@ class ValaCompiler(): elif type(exelist) == type([]): self.exelist = exelist else: - raise TypeError('Unknown argument to JavaCompiler') + raise TypeError('Unknown argument to Vala compiler') self.version = version self.id = 'unknown' self.language = 'vala' + def needs_static_linker(self): + return False # Because compiles into C. + def get_exelist(self): return self.exelist @@ -577,11 +586,56 @@ class ValaCompiler(): def can_compile(self, fname): return fname.endswith('.vala') +class RustCompiler(): + def __init__(self, exelist, version): + if isinstance(exelist, str): + self.exelist = [exelist] + elif type(exelist) == type([]): + self.exelist = exelist + else: + raise TypeError('Unknown argument to Rust compiler') + self.version = version + self.id = 'unknown' + self.language = 'rust' + + def needs_static_linker(self): + return False + + def get_exelist(self): + return self.exelist + + def get_id(self): + return self.id + + def get_language(self): + return self.language + + def sanity_check(self, work_dir): + source_name = os.path.join(work_dir, 'sanity.rs') + output_name = os.path.join(work_dir, 'rusttest') + ofile = open(source_name, 'w') + ofile.write('''fn main() { +} +''') + ofile.close() + pc = subprocess.Popen(self.exelist + ['-o', output_name, source_name], cwd=work_dir) + pc.wait() + if pc.returncode != 0: + raise EnvironmentException('Rust compiler %s can not compile programs.' % self.name_string()) + if subprocess.call(output_name) != 0: + raise EnvironmentException('Executables created by Rust compiler %s are not runnable.' % self.name_string()) + + def can_compile(self, fname): + return fname.endswith('.rs') + + def get_dependency_gen_flags(self, outfile): + return ['--dep-info', outfile] + class VisualStudioCCompiler(CCompiler): std_warn_flags = ['/W3'] std_opt_flags= ['/O2'] always_flags = ['/nologo', '/showIncludes'] - + def __init__(self, exelist, version, is_cross, exe_wrap): CCompiler.__init__(self, exelist, version, is_cross, exe_wrap) self.id = 'msvc' @@ -1310,6 +1364,23 @@ class Environment(): return ValaCompiler(exelist, version) raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') + def detect_rust_compiler(self): + exelist = ['rustc'] + try: + p = subprocess.Popen(exelist + ['--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except OSError: + raise EnvironmentException('Could not execute Rust compiler "%s"' % ' '.join(exelist)) + (out, _) = p.communicate() + out = out.decode() + vmatch = re.search(Environment.version_regex, out) + if vmatch: + version = vmatch.group(0) + else: + version = 'unknown version' + if 'rustc' in out: + return RustCompiler(exelist, version) + raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') + def detect_static_linker(self, compiler): if compiler.is_cross: linker = self.cross_info['ar'] diff --git a/interpreter.py b/interpreter.py index 3cf521fa3..4d40fa2f3 100644 --- a/interpreter.py +++ b/interpreter.py @@ -953,6 +953,10 @@ class Interpreter(): comp = self.environment.detect_vala_compiler() if is_cross: cross_comp = comp # Vala is too (I think). + elif lang == 'rust': + comp = self.environment.detect_rust_compiler() + if is_cross: + cross_comp = comp # FIXME, probably not correct. else: raise InvalidCode('Tried to use unknown language "%s".' % lang) comp.sanity_check(self.environment.get_scratch_dir()) diff --git a/ninjabackend.py b/ninjabackend.py index 00389c853..8cde99d4e 100644 --- a/ninjabackend.py +++ b/ninjabackend.py @@ -495,6 +495,24 @@ class NinjaBackend(backends.Backend): element.write(outfile) return generated_c + def generate_rust_target(self, target, outfile): + rustc = self.environment.coredata.compilers['rust'] + relsrc = [] + for i in target.get_sources(): + if not rustc.can_compile(i): + raise InvalidArguments('Rust target %s contains a non-rust source file.' % target.get_basename()) + relsrc.append(os.path.join(self.build_to_src, i)) + target_name = os.path.join(target.subdir, target.get_filename()) + flags = ['--crate-type'] + if isinstance(target, build.Executable): + flags.append('bin') + else: + raise InvalidArguments('Unknown target type for rustc.') + flags += ['--out-dir', target.subdir, '-o', target.get_basename()] + element = NinjaBuildElement(target_name, 'rust_COMPILER', relsrc) + element.add_item('FLAGS', flags) + element.write(outfile) + def generate_static_link_rules(self, is_cross, outfile): if self.build.has_language('java'): if not is_cross: @@ -521,7 +539,7 @@ class NinjaBackend(backends.Backend): for (complist, is_cross) in ctypes: for compiler in complist: langname = compiler.get_language() - if langname == 'java' or langname == 'vala': + if langname == 'java' or langname == 'vala' or langname == 'rust': continue crstr = '' if is_cross: @@ -576,6 +594,20 @@ class NinjaBackend(backends.Backend): outfile.write(depstyle) outfile.write('\n') + def generate_rust_compile_rules(self, compiler, outfile): + rule = 'rule %s_COMPILER\n' % compiler.get_language() + invoc = ' '.join([ninja_quote(i) for i in compiler.get_exelist()]) + command = ' command = %s $FLAGS $in\n' % invoc + description = ' description = Compiling Rust source $in.\n' + depfile = ' depfile = $out.d\n' + depstyle = ' deps = gcc\n' + outfile.write(rule) + outfile.write(command) + outfile.write(description) + outfile.write(depfile) + outfile.write(depstyle) + outfile.write('\n') + def generate_compile_rule_for(self, langname, compiler, qstr, is_cross, outfile): if langname == 'java': if not is_cross: @@ -585,6 +617,10 @@ class NinjaBackend(backends.Backend): if not is_cross: self.generate_vala_compile_rules(compiler, outfile) return + if langname == 'rust': + if not is_cross: + self.generate_rust_compile_rules(compiler, outfile) + return if is_cross: crstr = '_CROSS' else: diff --git a/test cases/rust/1 basic/installed_files.txt b/test cases/rust/1 basic/installed_files.txt new file mode 100644 index 000000000..88164df9e --- /dev/null +++ b/test cases/rust/1 basic/installed_files.txt @@ -0,0 +1 @@ +bin/rustprog diff --git a/test cases/rust/1 basic/meson.build b/test cases/rust/1 basic/meson.build new file mode 100644 index 000000000..1c2f9260c --- /dev/null +++ b/test cases/rust/1 basic/meson.build @@ -0,0 +1,4 @@ +project('rustprog', 'rust') + +e = executable('rustprog', 'prog.rs', install : true) +test('rusttest', e) diff --git a/test cases/rust/1 basic/prog.rs b/test cases/rust/1 basic/prog.rs new file mode 100644 index 000000000..b171a80c2 --- /dev/null +++ b/test cases/rust/1 basic/prog.rs @@ -0,0 +1,3 @@ +fn main() { + println!("rust compiler is working"); +}