diff --git a/.vscode/launch.json b/.vscode/launch.json index 6d2bdbed4c..f7b831c86d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -24,6 +24,24 @@ "args": [], "cwd": "${workspaceFolder}" }, + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'rustyc' - oscat", + "cargo": { + "args": [ + "build", + "--bin=rustyc", + "--package=rusty" + ], + "filter": { + "name": "rustyc", + "kind": "bin" + } + }, + "args": ["--ir", "/home/ghaith/git/oscat/oscat_single_file.st","/home/ghaith/git/oscat/std_stubs.st", "-o", "/tmp/comp.ll"], + "cwd": "${workspaceFolder}" + }, { "type": "lldb", "request": "launch", @@ -39,7 +57,7 @@ "kind": "bin" } }, - "args": ["--ir", "examples/oscat/*.st"], + "args": ["-c", "/home/ghaith/git/oscat/test.st", "-o", "/tmp/comp.ll"], "cwd": "${workspaceFolder}" }, { diff --git a/src/cli.rs b/src/cli.rs index 0a1660502c..ffdd6656c3 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -3,14 +3,7 @@ use encoding_rs::Encoding; use std::{ffi::OsStr, path::Path}; use structopt::{clap::ArgGroup, StructOpt}; -#[derive(PartialEq, Debug)] -pub enum FormatOption { - Static, - PIC, - Shared, - Bitcode, - IR, -} +use crate::FormatOption; // => Set the default output format here: const DEFAULT_FORMAT: FormatOption = FormatOption::Static; @@ -56,6 +49,13 @@ pub struct CompileParameters { #[structopt(long = "static", group = "format", help = "Emit an object as output")] pub output_obj_code: bool, + #[structopt( + long = "relocatable", + group = "format", + help = "Emit an object as output" + )] + pub output_reloc_code: bool, + #[structopt( long = "bc", group = "format", @@ -126,6 +126,8 @@ impl CompileParameters { Some(FormatOption::Shared) } else if self.output_obj_code { Some(FormatOption::Static) + } else if self.output_reloc_code { + Some(FormatOption::Relocatable) } else { None } @@ -146,6 +148,7 @@ impl CompileParameters { } else { let ending = match out_format { FormatOption::Bitcode => ".bc", + FormatOption::Relocatable => ".o", FormatOption::Static if self.skip_linking => ".o", FormatOption::Static => "", FormatOption::Shared | FormatOption::PIC => ".so", @@ -164,7 +167,8 @@ impl CompileParameters { #[cfg(test)] mod cli_tests { - use super::{CompileParameters, FormatOption, ParameterError}; + use super::{CompileParameters, ParameterError}; + use crate::FormatOption; use pretty_assertions::assert_eq; use structopt::clap::ErrorKind; @@ -206,6 +210,10 @@ mod cli_tests { vec_of_strings!["input.st", "--ir", "--shared", "--pic", "--bc"], ErrorKind::ArgumentConflict, ); + expect_argument_error( + vec_of_strings!["input.st", "--ir", "--relocatable"], + ErrorKind::ArgumentConflict, + ); } #[test] @@ -336,6 +344,7 @@ mod cli_tests { assert_eq!(parameters.output_obj_code, false); assert_eq!(parameters.output_pic_obj, false); assert_eq!(parameters.output_shared_obj, false); + assert_eq!(parameters.output_reloc_code, false); let parameters = CompileParameters::parse(vec_of_strings!("input.st", "--bc")).unwrap(); assert_eq!(parameters.output_ir, false); @@ -343,6 +352,7 @@ mod cli_tests { assert_eq!(parameters.output_obj_code, false); assert_eq!(parameters.output_pic_obj, false); assert_eq!(parameters.output_shared_obj, false); + assert_eq!(parameters.output_reloc_code, false); let parameters = CompileParameters::parse(vec_of_strings!("input.st", "--static")).unwrap(); assert_eq!(parameters.output_ir, false); @@ -350,6 +360,7 @@ mod cli_tests { assert_eq!(parameters.output_obj_code, true); assert_eq!(parameters.output_pic_obj, false); assert_eq!(parameters.output_shared_obj, false); + assert_eq!(parameters.output_reloc_code, false); let parameters = CompileParameters::parse(vec_of_strings!("input.st", "--pic")).unwrap(); assert_eq!(parameters.output_ir, false); @@ -357,6 +368,7 @@ mod cli_tests { assert_eq!(parameters.output_obj_code, false); assert_eq!(parameters.output_pic_obj, true); assert_eq!(parameters.output_shared_obj, false); + assert_eq!(parameters.output_reloc_code, false); let parameters = CompileParameters::parse(vec_of_strings!("input.st", "--shared")).unwrap(); assert_eq!(parameters.output_ir, false); @@ -364,6 +376,16 @@ mod cli_tests { assert_eq!(parameters.output_obj_code, false); assert_eq!(parameters.output_pic_obj, false); assert_eq!(parameters.output_shared_obj, true); + assert_eq!(parameters.output_reloc_code, false); + + let parameters = + CompileParameters::parse(vec_of_strings!("input.st", "--relocatable")).unwrap(); + assert_eq!(parameters.output_ir, false); + assert_eq!(parameters.output_bit_code, false); + assert_eq!(parameters.output_obj_code, false); + assert_eq!(parameters.output_pic_obj, false); + assert_eq!(parameters.output_shared_obj, false); + assert_eq!(parameters.output_reloc_code, true); let parameters = CompileParameters::parse(vec_of_strings!("input.st")).unwrap(); assert_eq!(parameters.output_ir, false); @@ -371,6 +393,7 @@ mod cli_tests { assert_eq!(parameters.output_obj_code, false); assert_eq!(parameters.output_pic_obj, false); assert_eq!(parameters.output_shared_obj, false); + assert_eq!(parameters.output_reloc_code, false); } #[test] diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 5cc5585c6c..f3b6616e0d 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -35,6 +35,7 @@ pub enum ErrNo { //general general__io_err, + general__param_err, //syntax syntax__generic_error, @@ -76,6 +77,9 @@ pub enum ErrNo { codegen__general, codegen__missing_function, codegen__missing_compare_function, + + //linker + linker__generic_error, } impl Diagnostic { @@ -376,6 +380,13 @@ impl Diagnostic { } } + pub fn param_error(reason: &str) -> Diagnostic { + Diagnostic::GeneralError { + message: reason.to_string(), + err_no: ErrNo::general__param_err, + } + } + pub fn llvm_error(file: &str, llvm_error: &LLVMString) -> Diagnostic { Diagnostic::GeneralError { message: format!( @@ -438,6 +449,13 @@ impl Diagnostic { } } + pub fn link_error(error: &str) -> Diagnostic { + Diagnostic::GeneralError { + err_no: ErrNo::linker__generic_error, + message: error.to_string(), + } + } + pub fn get_message(&self) -> &str { match self { Diagnostic::SyntaxError { message, .. } diff --git a/src/lib.rs b/src/lib.rs index 1db2aee429..ea0818edd7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,9 +19,11 @@ //! [`IR`]: https://llvm.org/docs/LangRef.html use std::fs; +use glob::glob; use std::path::Path; use ast::{PouType, SourceRange}; +use cli::CompileParameters; use diagnostics::Diagnostic; use encoding_rs::Encoding; use encoding_rs_io::DecodeReaderBytesBuilder; @@ -44,6 +46,7 @@ mod codegen; pub mod diagnostics; pub mod index; mod lexer; +mod linker; mod parser; mod resolver; mod test_utils; @@ -54,6 +57,28 @@ mod validation; #[cfg(test)] extern crate pretty_assertions; +#[derive(PartialEq, Debug, Clone, Copy)] +pub enum FormatOption { + Static, + PIC, + Shared, + Relocatable, + Bitcode, + IR, +} + +pub struct CompileOptions { + pub format: FormatOption, + pub output: String, + pub target: Option, +} + +pub struct LinkOptions { + pub libraries: Vec, + pub library_pathes: Vec, + pub sysroot: Option, +} + /// SourceContainers offer source-code to be compiled via the load_source function. /// Furthermore it offers a location-String used when reporting diagnostics. pub trait SourceContainer { @@ -67,15 +92,41 @@ pub struct FilePath { pub path: String, } +impl From for FilePath { + fn from(it: String) -> Self { + FilePath { path: it } + } +} + +impl From<&str> for FilePath { + fn from(it: &str) -> Self { + FilePath { path: it.into() } + } +} + +impl FilePath { + fn get_extension(&self) -> &str { + self.path.split('.').last().unwrap_or("") + } + + fn is_object(&self) -> bool { + self.get_extension() == "o" + } +} + impl SourceContainer for FilePath { fn load_source(self, encoding: Option<&'static Encoding>) -> Result { - let mut file = File::open(&self.path).map_err(|err| err.to_string())?; - let source = create_source_code(&mut file, encoding)?; - - Ok(SourceCode { - source, - path: self.path, - }) + if self.is_object() { + Err(format!("{} is not a source file", &self.path)) + } else { + let mut file = File::open(&self.path).map_err(|err| err.to_string())?; + let source = create_source_code(&mut file, encoding)?; + + Ok(SourceCode { + source, + path: self.path, + }) + } } fn get_location(&self) -> &str { @@ -137,7 +188,8 @@ fn create_source_code( pub fn get_target_triple(triple: Option) -> TargetTriple { triple - .map(|it| TargetTriple::create(it.as_str())) + .as_ref() + .map(|it| TargetTriple::create(it)) .unwrap_or_else(TargetMachine::get_default_triple) } @@ -395,6 +447,180 @@ pub fn compile_module<'c, T: SourceContainer>( Ok(code_generator) } +fn create_file_paths(inputs: &[String]) -> Result, Diagnostic> { + let mut sources = Vec::new(); + for input in inputs { + let paths = glob(input).map_err(|e| { + Diagnostic::param_error(&format!("Failed to read glob pattern: {}, ({})", input, e)) + })?; + + for p in paths { + let path = + p.map_err(|err| Diagnostic::param_error(&format!("Illegal path: {:}", err)))?; + sources.push(FilePath { + path: path.to_string_lossy().to_string(), + }); + } + } + if sources.is_empty() { + return Err(Diagnostic::param_error(&format!( + "No such file(s): {}", + inputs.join(",") + ))); + } + Ok(sources) +} + +/// The driver function for the compilation +/// Sorts files that need compilation +/// Parses, validates and generates code for the given source files +/// Links all provided object files with the compilation result +/// Links any provided libraries +/// Returns the location of the output file +pub fn build_with_params(parameters: CompileParameters) -> Result<(), Diagnostic> { + let files = create_file_paths(¶meters.input)?; + let output = parameters + .output_name() + .ok_or_else(|| Diagnostic::param_error("Missing parameter: output-name"))?; + let out_format = parameters.output_format_or_default(); + let compile_options = CompileOptions { + output, + target: parameters.target, + format: out_format, + }; + let link_options = if !parameters.skip_linking { + Some(LinkOptions { + libraries: parameters.libraries, + library_pathes: parameters.library_pathes, + sysroot: parameters.sysroot, + }) + } else { + None + }; + + build(files, compile_options, link_options, parameters.encoding) +} + +/// The driver function for the compilation +/// Sorts files that need compilation +/// Parses, validates and generates code for the given source files +/// Links all provided object files with the compilation result +/// Links any provided libraries +/// Returns the location of the output file +pub fn build( + files: Vec, + compile_options: CompileOptions, + link_options: Option, + encoding: Option<&'static Encoding>, +) -> Result<(), Diagnostic> { + let mut objects = vec![]; + let mut sources = vec![]; + files.into_iter().for_each(|it| { + if it.is_object() { + objects.push(it); + } else { + sources.push(it); + } + }); + + if !sources.is_empty() { + compile( + &compile_options.output, + compile_options.format, + sources, + encoding, + compile_options.target.clone(), + )?; + objects.push(compile_options.output.as_str().into()); + } + + if let Some(link_options) = link_options { + link( + &compile_options.output, + compile_options.format, + objects, + link_options.library_pathes, + link_options.libraries, + compile_options.target, + link_options.sysroot, + )?; + } + + Ok(()) +} + +pub fn compile( + output: &str, + out_format: FormatOption, + sources: Vec, + encoding: Option<&'static Encoding>, + target: Option, +) -> Result<(), Diagnostic> { + let diagnostician = Diagnostician::default(); + match out_format { + FormatOption::Static | FormatOption::Relocatable => { + compile_to_static_obj(sources, encoding, output, target, diagnostician) + } + FormatOption::Shared => { + compile_to_shared_object(sources, encoding, output, target, diagnostician) + } + FormatOption::PIC => { + compile_to_shared_pic_object(sources, encoding, output, target, diagnostician) + } + FormatOption::Bitcode => compile_to_bitcode(sources, encoding, output, diagnostician), + FormatOption::IR => compile_to_ir(sources, encoding, output, diagnostician), + }?; + Ok(()) +} + +pub fn link( + output: &str, + out_format: FormatOption, + objects: Vec, + library_pathes: Vec, + libraries: Vec, + target: Option, + sysroot: Option, +) -> Result<(), Diagnostic> { + let linkable_formats = vec![ + FormatOption::Static, + FormatOption::Relocatable, + FormatOption::Shared, + FormatOption::PIC, + ]; + if linkable_formats.contains(&out_format) { + let triple = get_target_triple(target); + let mut linker = triple + .as_str() + .to_str() + .map_err(|e| Diagnostic::param_error(&e.to_string())) + .and_then(|triple| linker::Linker::new(triple).map_err(|e| e.into()))?; + linker.add_lib_path("."); + + for path in &objects { + linker.add_obj(&path.path); + } + + for path in &library_pathes { + linker.add_lib_path(path); + } + for library in &libraries { + linker.add_lib(library); + } + + if let Some(sysroot) = &sysroot { + linker.add_sysroot(sysroot); + } + + match out_format { + FormatOption::Static => linker.build_exectuable(Path::new(&output))?, + FormatOption::Relocatable => linker.build_relocatable(Path::new(&output))?, + _ => linker.build_shared_obj(Path::new(&output))?, + } + } + Ok(()) +} + #[cfg(test)] mod tests { mod multi_files; diff --git a/src/linker.rs b/src/linker.rs index 3a4b5691c5..7054255d4f 100644 --- a/src/linker.rs +++ b/src/linker.rs @@ -1,6 +1,7 @@ // This file is based on code from the Mun Programming Language // https://github.com/mun-lang/mun +use crate::diagnostics::Diagnostic; use std::path::{Path, PathBuf}; pub struct Linker { @@ -16,6 +17,7 @@ trait LinkerInterface { fn add_sysroot(&mut self, path: &str); fn build_shared_object(&mut self, path: &str); fn build_exectuable(&mut self, path: &str); + fn build_relocatable(&mut self, path: &str); fn finalize(&mut self) -> Result<(), LinkerError>; } @@ -34,10 +36,8 @@ impl Linker { } /// Add an object file or static library to linker input - pub fn add_obj<'a>(&'a mut self, file: &Path) -> &'a mut Self { - if let Some(file) = self.get_str_from_path(file) { - self.linker.add_obj(file); - } + pub fn add_obj<'a>(&'a mut self, path: &str) -> &'a mut Self { + self.linker.add_obj(path); self } @@ -47,7 +47,7 @@ impl Linker { self } - /// Add a library seaBoxh path to look in for libraries + /// Add a library path to look in for libraries pub fn add_lib<'a>(&'a mut self, path: &str) -> &'a mut Self { self.linker.add_lib(path); self @@ -77,6 +77,15 @@ impl Linker { Ok(()) } + /// Set the output file and run the linker to generate a relocatable object for further linking + pub fn build_relocatable(&mut self, path: &Path) -> Result<(), LinkerError> { + if let Some(file) = self.get_str_from_path(path) { + self.linker.build_relocatable(file); + self.linker.finalize()?; + } + Ok(()) + } + /// Check if the path is valid, log an error if it wasn't fn get_str_from_path<'a>(&mut self, path: &'a Path) -> Option<&'a str> { let filepath = path.to_str(); @@ -131,6 +140,12 @@ impl LinkerInterface for LdLinker { self.args.push(path.into()); } + fn build_relocatable(&mut self, path: &str) { + self.args.push("-relocatable".into()); + self.args.push("-o".into()); + self.args.push(path.into()); + } + fn finalize(&mut self) -> Result<(), LinkerError> { lld_rs::link(lld_rs::LldFlavor::Elf, &self.args) .ok() @@ -182,16 +197,18 @@ pub enum LinkerError { Path(PathBuf), } -impl From for String { +impl From for Diagnostic { fn from(error: LinkerError) -> Self { match error { - LinkerError::Link(e) => e, - LinkerError::Path(path) => { - format!("path contains invalid UTF-8 characters: {}", path.display()) - } - LinkerError::Target(tgt) => { - format!("linker not available for target platform: {}", tgt) - } + LinkerError::Link(e) => Diagnostic::link_error(&e), + LinkerError::Path(path) => Diagnostic::link_error(&format!( + "path contains invalid UTF-8 characters: {}", + path.display() + )), + LinkerError::Target(tgt) => Diagnostic::link_error(&format!( + "linker not available for target platform: {}", + tgt + )), } } } @@ -207,24 +224,3 @@ fn creation_test() { panic!("Linker target should have returned an error!"); } } - -#[test] -fn linker_error_test() { - let msg = "error message"; - let link_err = LinkerError::Link(msg.into()); - assert_eq!(String::from(link_err), msg.to_string()); - - let path = "/abc/def"; - let link_err = LinkerError::Path(path.into()); - assert_eq!( - String::from(link_err), - format!("path contains invalid UTF-8 characters: {}", path) - ); - - let target = "redox"; - let link_err = LinkerError::Target(target.into()); - assert_eq!( - String::from(link_err), - format!("linker not available for target platform: {}", target) - ); -} diff --git a/src/main.rs b/src/main.rs index bd1b14a719..6b4742fc6b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,16 +17,9 @@ //! [`ST`]: https://en.wikipedia.org/wiki/Structured_text //! [`IEC61131-3`]: https://en.wikipedia.org/wiki/IEC_61131-3 //! [`IR`]: https://llvm.org/docs/LangRef.html -use std::path::Path; -use glob::glob; -use rusty::diagnostics::Diagnostician; -use rusty::{ - cli::{CompileParameters, FormatOption, ParameterError}, - compile_to_bitcode, compile_to_ir, compile_to_shared_object, compile_to_shared_pic_object, - compile_to_static_obj, get_target_triple, FilePath, -}; -mod linker; +use rusty::build_with_params; +use rusty::cli::{CompileParameters, ParameterError}; fn main() { let args: Vec = std::env::args().collect(); @@ -35,108 +28,11 @@ fn main() { CompileParameters::parse(args); match compile_parameters { Ok(cp) => { - if let Err(msg) = main_compile(cp) { - println!("Error: {}", msg); + if let Err(msg) = build_with_params(cp) { + println!("Error: {:?}", msg); std::process::exit(1); } } Err(err) => err.exit(), // prints the nice message to std-out } } - -fn create_file_paths(inputs: &[String]) -> Result, String> { - let mut sources = Vec::new(); - for input in inputs { - let paths = - glob(input).map_err(|e| format!("Failed to read glob pattern: {}, ({})", input, e))?; - - for p in paths { - let path = p.map_err(|err| format!("Illegal path: {:}", err))?; - sources.push(FilePath { - path: path.to_string_lossy().to_string(), - }); - } - } - if sources.is_empty() { - return Err(format!("No such file(s): {}", inputs.join(","))); - } - Ok(sources) -} - -fn main_compile(parameters: CompileParameters) -> Result<(), String> { - let sources = create_file_paths(¶meters.input)?; - - let output_filename = parameters - .output_name() - .ok_or_else(|| "Missing parameter: output-name".to_string())?; - let encoding = parameters.encoding; - - let out_format = parameters.output_format_or_default(); - let diagnostician = Diagnostician::default(); - let compile_result = match out_format { - FormatOption::Static => compile_to_static_obj( - sources, - encoding, - output_filename.as_str(), - parameters.target.clone(), - diagnostician, - ), - FormatOption::Shared => compile_to_shared_object( - sources, - encoding, - output_filename.as_str(), - parameters.target.clone(), - diagnostician, - ), - FormatOption::PIC => compile_to_shared_pic_object( - sources, - encoding, - output_filename.as_str(), - parameters.target.clone(), - diagnostician, - ), - FormatOption::Bitcode => { - compile_to_bitcode(sources, encoding, output_filename.as_str(), diagnostician) - } - FormatOption::IR => compile_to_ir(sources, encoding, &output_filename, diagnostician), - }; - - //unwrap a potential error - compile_result.map_err(|compile_err| compile_err.get_message().to_string())?; - - let linkable_formats = vec![ - FormatOption::Static, - FormatOption::Shared, - FormatOption::PIC, - ]; - if linkable_formats.contains(&out_format) && !parameters.skip_linking { - let triple = get_target_triple(parameters.target); - let mut linker = triple - .as_str() - .to_str() - .map_err(|e| e.to_string()) - .and_then(|triple| linker::Linker::new(triple).map_err(|e| e.into()))?; - linker - .add_lib_path(".") - .add_obj(Path::new(&output_filename)); - - for path in ¶meters.library_pathes { - linker.add_lib_path(path); - } - for library in ¶meters.libraries { - linker.add_lib(library); - } - - if let Some(sysroot) = ¶meters.sysroot { - linker.add_sysroot(sysroot); - } - - if out_format == FormatOption::Static { - linker.build_exectuable(Path::new(&output_filename))?; - } else { - linker.build_shared_obj(Path::new(&output_filename))?; - } - } - - Ok(()) -} diff --git a/tests/integration/data/linking/file1.st b/tests/integration/data/linking/file1.st new file mode 100644 index 0000000000..a0195e506b --- /dev/null +++ b/tests/integration/data/linking/file1.st @@ -0,0 +1,8 @@ +@EXTERNAL +FUNCTION func2 : DINT +END_FUNCTION + +FUNCTION func1 : DINT + func2(); +END_FUNCTION + diff --git a/tests/integration/data/linking/file2.st b/tests/integration/data/linking/file2.st new file mode 100644 index 0000000000..78d90c8ae3 --- /dev/null +++ b/tests/integration/data/linking/file2.st @@ -0,0 +1,8 @@ +@EXTERNAL +FUNCTION func1 : DINT +END_FUNCTION + +FUNCTION func2 : DINT + func1(); +END_FUNCTION + diff --git a/tests/integration/linking.rs b/tests/integration/linking.rs new file mode 100644 index 0000000000..094f60eb47 --- /dev/null +++ b/tests/integration/linking.rs @@ -0,0 +1,246 @@ +use std::{env, fs}; + +use crate::get_test_file; +use rusty::{build, diagnostics::Diagnostic, CompileOptions, FilePath, FormatOption, LinkOptions}; + +#[test] +fn link_as_shared_object() { + let file1 = FilePath { + path: get_test_file("linking/file1.st"), + }; + let file2 = FilePath { + path: get_test_file("linking/file2.st"), + }; + + let mut out = env::temp_dir(); + out.push("shared1.so"); + let out1 = out.into_os_string().into_string().unwrap(); + let mut out = env::temp_dir(); + out.push("shared2.o"); + let out2 = out.into_os_string().into_string().unwrap(); + + //Compile file 2 into obj + build( + vec![file2], + CompileOptions { + output: out2.clone(), + format: FormatOption::Shared, + target: Some("x86_64-unkown-linux-gnu".to_string()), + }, + None, + None, + ) + .unwrap(); + + //Compile file1 as shared object with file2 as param + build( + vec![file1, out2.as_str().into()], + CompileOptions { + output: out1.clone(), + format: FormatOption::Shared, + target: Some("x86_64-unkown-linux-gnu".to_string()), + }, + Some(LinkOptions { + libraries: vec![], + library_pathes: vec![], + sysroot: None, + }), + None, + ) + .unwrap(); + + //Delete it + fs::remove_file(&out1).unwrap(); + fs::remove_file(&out2).unwrap(); +} + +#[test] +fn link_as_pic_object() { + let file1 = FilePath { + path: get_test_file("linking/file1.st"), + }; + let file2 = FilePath { + path: get_test_file("linking/file2.st"), + }; + + let mut out = env::temp_dir(); + out.push("pic1.so"); + let out1 = out.into_os_string().into_string().unwrap(); + let mut out = env::temp_dir(); + out.push("pic2.o"); + let out2 = out.into_os_string().into_string().unwrap(); + + //Compile file 2 into obj + + build( + vec![file2], + CompileOptions { + output: out2.clone(), + format: FormatOption::PIC, + target: Some("x86_64-unkown-linux-gnu".to_string()), + }, + None, + None, + ) + .unwrap(); + + //Compile file1 as shared object with file2 as param + build( + vec![file1, out2.as_str().into()], + CompileOptions { + output: out1.clone(), + format: FormatOption::PIC, + target: Some("x86_64-unkown-linux-gnu".to_string()), + }, + Some(LinkOptions { + libraries: vec![], + library_pathes: vec![], + sysroot: None, + }), + None, + ) + .unwrap(); + + //Delete it + fs::remove_file(&out1).unwrap(); + fs::remove_file(&out2).unwrap(); +} + +#[test] +fn link_as_static_object() { + let file1 = FilePath { + path: get_test_file("linking/file1.st"), + }; + let file2 = FilePath { + path: get_test_file("linking/file2.st"), + }; + + let mut out = env::temp_dir(); + out.push("static1.o"); + let out1 = out.into_os_string().into_string().unwrap(); + let mut out = env::temp_dir(); + out.push("static2.o"); + let out2 = out.into_os_string().into_string().unwrap(); + + //Compile file 2 into obj + + build( + vec![file2], + CompileOptions { + output: out2.clone(), + format: FormatOption::Static, + target: Some("x86_64-unkown-linux-gnu".to_string()), + }, + None, + None, + ) + .unwrap(); + + //Compile file1 as shared object with file2 as param + build( + vec![file1, out2.as_str().into()], + CompileOptions { + output: out1.clone(), + format: FormatOption::Static, + target: Some("x86_64-unkown-linux-gnu".to_string()), + }, + Some(LinkOptions { + libraries: vec![], + library_pathes: vec![], + sysroot: None, + }), + None, + ) + .unwrap(); + + //Delete it + fs::remove_file(&out1).unwrap(); + fs::remove_file(&out2).unwrap(); +} + +#[test] +fn link_as_relocatable_object() { + let file1 = FilePath { + path: get_test_file("linking/file1.st"), + }; + let file2 = FilePath { + path: get_test_file("linking/file2.st"), + }; + + let mut out = env::temp_dir(); + out.push("reloc1.o"); + let out1 = out.into_os_string().into_string().unwrap(); + let mut out = env::temp_dir(); + out.push("reloc2.o"); + let out2 = out.into_os_string().into_string().unwrap(); + + //Compile file 2 into obj + + build( + vec![file2], + CompileOptions { + output: out2.clone(), + format: FormatOption::Static, + target: Some("x86_64-unkown-linux-gnu".to_string()), + }, + None, + None, + ) + .unwrap(); + + //Compile file1 as shared object with file2 as param + build( + vec![file1, out2.as_str().into()], + CompileOptions { + output: out1.clone(), + format: FormatOption::Relocatable, + target: Some("x86_64-unkown-linux-gnu".to_string()), + }, + Some(LinkOptions { + libraries: vec![], + library_pathes: vec![], + sysroot: None, + }), + None, + ) + .unwrap(); + + //Delete it + fs::remove_file(&out1).unwrap(); + fs::remove_file(&out2).unwrap(); +} + +#[test] +fn link_missing_file() { + let file1 = FilePath { + path: get_test_file("linking/file1.st"), + }; + let mut out = env::temp_dir(); + out.push("missing.o"); + let out = out.into_os_string().into_string().unwrap(); + //Compile file1 as shared object with file2 as param + let res = build( + vec![file1], + CompileOptions { + output: out.clone(), + format: FormatOption::Static, + target: Some("x86_64-unkown-linux-gnu".to_string()), + }, + Some(LinkOptions { + libraries: vec![], + library_pathes: vec![], + sysroot: None, + }), + None, + ); + + match res { + Err(err) => { + assert_eq!(Diagnostic::link_error(&format!("lld: error: undefined symbol: func2\n>>> referenced by main\n>>> {}:(func1)\n>>> did you mean: func1\n>>> defined in: {}\n",out, out)), err); + } + _ => panic!("Expected link failure"), + } + + //Delete it + fs::remove_file(&out).unwrap(); +} diff --git a/tests/tests.rs b/tests/tests.rs index 80993d3942..3903dc0b33 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -48,6 +48,7 @@ mod correctness { mod integration { mod external_files; + mod linking; mod multi_files; }