From 67dafe9c03e3f8d96c98adc06db6947cf099ee5e Mon Sep 17 00:00:00 2001 From: Alexander Ulmer Date: Sun, 29 Aug 2021 09:35:39 +0200 Subject: [PATCH] Support linking on Linux (#243) * implement linking on Linux * add option -c to omit linking * adjust default filename when linking * update user manual with information on linking * link API documentation from the book * add test for get_target_triple() * add unit tests for linker.rs * install LLVM for Style and Docs as well * fix documentation workflow * implement linker builder * Add library parameters Co-authored-by: Ghaith Hachem --- .github/workflows/doc.yml | 9 ++ .github/workflows/rust.yml | 7 ++ .gitignore | 2 + Cargo.lock | 33 ++++- Cargo.toml | 1 + book/src/build_and_install.md | 2 +- book/src/intro_1.md | 4 + book/src/using_rusty.md | 51 +++++++- examples/hello_world.st | 16 +++ src/cli.rs | 64 ++++++++-- src/compile_error.rs | 3 + src/lib.rs | 65 ++++++++-- src/linker.rs | 220 ++++++++++++++++++++++++++++++++++ src/main.rs | 51 +++++++- 14 files changed, 498 insertions(+), 30 deletions(-) create mode 100644 examples/hello_world.st create mode 100644 src/linker.rs diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 19518cb104..f20e5f7f93 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -5,6 +5,10 @@ on: pull_request: branches: [ master ] +env: + toolchain-version: 1.53.0 + llvm-version: "11.0.1" + jobs: cargo-docs: runs-on: ubuntu-latest @@ -19,6 +23,11 @@ jobs: uses: peaceiris/actions-mdbook@v1 with: mdbook-version: 'latest' + - name: Install LLVM + uses: ghaith/install-llvm-action@latest + with: + version: ${{ env.llvm-version }} + directory: "./llvm" - name: Build API documentation uses: actions-rs/cargo@v1 with: diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f000a0022f..a1927c37c6 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -33,6 +33,7 @@ jobs: uses: ghaith/install-llvm-action@latest with: version: ${{ env.llvm-version }} + directory: "./llvm" - name: Cargo check uses: actions-rs/cargo@v1 @@ -114,6 +115,12 @@ jobs: override: true components: clippy, rustfmt + - name: Install LLVM + uses: ghaith/install-llvm-action@latest + with: + version: ${{ env.llvm-version }} + directory: "./llvm" + - name: Run cargo fmt uses: actions-rs/cargo@v1 with: diff --git a/.gitignore b/.gitignore index fc7ef7029c..aa045ff96e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ *.ir *.o *.bc +*.a +*.elf diff --git a/Cargo.lock b/Cargo.lock index fda7a0d3f5..60b2679324 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -240,7 +240,7 @@ dependencies = [ "lazy_static", "libc", "regex", - "semver", + "semver 0.11.0", ] [[package]] @@ -282,6 +282,19 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "mun_lld" +version = "110.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34212cc9f5402af8c65a1d8962852730363a052b8ee1786cce194c2dc9af80f" +dependencies = [ + "cc", + "lazy_static", + "libc", + "regex", + "semver 0.9.0", +] + [[package]] name = "num-integer" version = "0.1.44" @@ -442,6 +455,7 @@ dependencies = [ "indexmap", "inkwell", "logos", + "mun_lld", "pretty_assertions", "structopt", "thiserror", @@ -453,15 +467,30 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser 0.7.0", +] + [[package]] name = "semver" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" dependencies = [ - "semver-parser", + "semver-parser 0.10.2", ] +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "semver-parser" version = "0.10.2" diff --git a/Cargo.toml b/Cargo.toml index 7866b15f28..c6b51bc766 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ glob = "0.3.0" encoding_rs = "0.8" encoding_rs_io = "0.1" codespan-reporting = "0.11.1" +mun_lld = "110.0.0" [lib] diff --git a/book/src/build_and_install.md b/book/src/build_and_install.md index 98926e68d0..2a88fe1cf3 100644 --- a/book/src/build_and_install.md +++ b/book/src/build_and_install.md @@ -3,7 +3,7 @@ ## Prerequisites To be able to build the source code, you will need to [install Rust](https://www.rust-lang.org/tools/install) and LLVM 11, along with the standard build tools (`build-essential`) and `libz-dev` on your machine. -For Linux the package manager version of LLVM (e.g. `llvm-11-dev` for apt) will work fine, for Windows, you need a +For Linux the package manager version of LLVM (e.g. `llvm-11-dev`, `liblld-11-dev` for apt) will work fine, for Windows, you need a [special build](https://github.com/ghaith/llvm-package-windows/releases/tag/v11.0.1). If you want to clone and work on the repository, you'll also need _git_. diff --git a/book/src/intro_1.md b/book/src/intro_1.md index cd72e85a51..e6364621bd 100644 --- a/book/src/intro_1.md +++ b/book/src/intro_1.md @@ -8,6 +8,8 @@ puts out static or shared objects as well as LLVM IR or bitcode by the flip of a command line flag. We are aiming towards an open-source industry-grade ST compiler supporting at least the features in 2nd edition IEC 61131 standard. +You might also want to refer to the [API documentation](https://ghaith.github.io/rusty/api/rusty/). + ## Supported Language Concepts ### POUs - ✔ Program @@ -40,6 +42,7 @@ supporting at least the features in 2nd edition IEC 61131 standard. - ✔ Call statements - ✔ Implicit call arguments - ✔ Explicit call arguments +- ✔ EXIT, CONTINUE statements ### Control Structures - ✔ IF Statement @@ -47,6 +50,7 @@ supporting at least the features in 2nd edition IEC 61131 standard. - ✔ FOR Loops - ✔ WHILE Loops - ✔ REPEAT Loops +- ✔ RETURN statement ### Expressions - ✔ Arithmetic Operators diff --git a/book/src/using_rusty.md b/book/src/using_rusty.md index 8180902b57..5b705fe441 100644 --- a/book/src/using_rusty.md +++ b/book/src/using_rusty.md @@ -22,12 +22,53 @@ More examples: - `rustyc --ir src/*.st` will compile all st files in the src-folder. - `rustyc --ir "**/*.st"` will compile all st-files in the current folder and its subfolders recursively. -## Compiling a static object +## Example: Building a hello world program +### Writing the code +We want to print something to the terminal, so we're going to declare external functions +for that and link with libc when we're done. This program can also be found at +`examples/hello_world.st` in the source tree of Rusty. -## Compiling a linkable object +* `_start` is our entry point to the program, because most linker scripts define it this way. -## Creating a shared library +* Since we don't have a `crt0` right now, we have to call the `exit()` function by ourselves after we're +done. Otherwise, the program will most likely crash (because it tries to return to a function that never +existed). -## Linking with an external application +```st +@EXTERNAL FUNCTION puts : DINT +VAR_INPUT + text : STRING; +END_VAR +END_FUNCTION -## Writing a main +@EXTERNAL FUNCTION exit : DINT +VAR_INPUT + status : DINT; +END_VAR +END_FUNCTION + +FUNCTION _start : DINT + puts('hello, world!'); + exit(0); +END_FUNCTION +``` + +### Compiling with rusty +Compiling with rusty is very easy. If you just want to build an object file, then do this: +```bash +rustyc -c hello_world.st -o hello_world.o +``` + +### Linking an executable +Instead, you can also compile this into an executable and run it: +```bash +rustyc hello_world.st -o hello_world -L/path/to/libs -lc +./hello_world +``` + +Please note that RuSTy will attempt to link the generated object file by default to generate +an executable if you didn't specify something else (option `-c`). +* The `-lc` flag tells the linker it should link against `libc`. Depending on the available libraries on your system, +the linker will prefer a dynamically linked library if available, and revert to a static one otherwise. +* You add library search pathes by providing additional `-L /path/...` options. By default, this will be +the current directory. \ No newline at end of file diff --git a/examples/hello_world.st b/examples/hello_world.st new file mode 100644 index 0000000000..f85e6d472d --- /dev/null +++ b/examples/hello_world.st @@ -0,0 +1,16 @@ +@EXTERNAL FUNCTION puts : DINT +VAR_INPUT + text : STRING; +END_VAR +END_FUNCTION + +@EXTERNAL FUNCTION exit : DINT +VAR_INPUT + status : DINT; +END_VAR +END_FUNCTION + +FUNCTION _start : DINT + puts('hello, world!'); + exit(0); +END_FUNCTION diff --git a/src/cli.rs b/src/cli.rs index a91a0c5a89..e04dcbef2b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -62,6 +62,9 @@ pub struct CompileParameters { )] pub output_bit_code: bool, + #[structopt(short = "c", help = "Do not link after compiling object code")] + pub skip_linking: bool, + #[structopt( long, name = "target-triple", @@ -85,6 +88,17 @@ pub struct CompileParameters { )] // having a vec allows bash to resolve *.st itself pub input: Vec, + + #[structopt( + name = "library-path", + long, + short = "L", + help = "Search path for libraries, used for linking" + )] + pub library_pathes: Vec, + + #[structopt(name = "library", long, short = "l", help = "Library name to link")] + pub libraries: Vec, } fn parse_encoding(encoding: &str) -> Result<&'static Encoding, String> { @@ -122,20 +136,21 @@ impl CompileParameters { /// return the output filename with the correct ending pub fn output_name(&self) -> Option { + let out_format = self.output_format_or_default(); if let Some(n) = &self.output { Some(n.to_string()) } else { - let ending = match self.output_format_or_default() { - FormatOption::Bitcode => "bc", - FormatOption::Static => "o", - FormatOption::Shared => "so", - FormatOption::PIC => "so", - FormatOption::IR => "ir", + let ending = match out_format { + FormatOption::Bitcode => ".bc", + FormatOption::Static if self.skip_linking => ".o", + FormatOption::Static => "", + FormatOption::Shared | FormatOption::PIC => ".so", + FormatOption::IR => ".ir", }; let output_name = self.input.first().unwrap(); let basename = Path::new(output_name).file_stem()?.to_str()?; - Some(format!("{}.{}", basename, ending)) + Some(format!("{}{}", basename, ending)) } } } @@ -234,13 +249,17 @@ mod cli_tests { assert_eq!(parameters.output_name().unwrap(), "charlie.so".to_string()); let parameters = - CompileParameters::parse(vec_of_strings!("examples/test/delta.st", "--static")) + CompileParameters::parse(vec_of_strings!("examples/test/delta.st", "--static", "-c")) .unwrap(); assert_eq!(parameters.output_name().unwrap(), "delta.o".to_string()); let parameters = CompileParameters::parse(vec_of_strings!("examples/test/echo", "--bc")).unwrap(); assert_eq!(parameters.output_name().unwrap(), "echo.bc".to_string()); + + let parameters = + CompileParameters::parse(vec_of_strings!("examples/test/echo.st")).unwrap(); + assert_eq!(parameters.output_name().unwrap(), "echo".to_string()); } #[test] @@ -338,6 +357,35 @@ mod cli_tests { assert_eq!(parameters.output_shared_obj, false); } + #[test] + fn library_path_added() { + let parameters = CompileParameters::parse(vec_of_strings!( + "input.st", + "--library-path", + "xxx", + "-L", + "test", + "-L.", + "-L/tmp" + )) + .unwrap(); + assert_eq!(parameters.library_pathes, vec!["xxx", "test", ".", "/tmp"]); + } + + #[test] + fn libraries_added() { + let parameters = CompileParameters::parse(vec_of_strings!( + "input.st", + "-l", + "test", + "-lc", + "--library", + "xx" + )) + .unwrap(); + assert_eq!(parameters.libraries, vec!["test", "c", "xx"]); + } + #[test] fn cli_supports_version() { match CompileParameters::parse(vec_of_strings!("input.st", "--version")) { diff --git a/src/compile_error.rs b/src/compile_error.rs index b2082b56b1..8aca1d5347 100644 --- a/src/compile_error.rs +++ b/src/compile_error.rs @@ -39,6 +39,9 @@ pub enum CompileError { #[error("Cannot write File {path:}: {reason:}")] IoWriteError { path: String, reason: String }, + + #[error("Cannot link: {reason:}")] + LinkerError { reason: String }, } impl From for CompileError { diff --git a/src/lib.rs b/src/lib.rs index afba998b43..c3c9da1d0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -239,6 +239,13 @@ fn create_source_code( Ok(buffer) } +pub fn get_target_triple(triple: Option) -> TargetTriple { + triple + .map(|it| TargetTriple::create(it.as_str())) + .or_else(|| Some(TargetMachine::get_default_triple())) + .unwrap() +} + /// /// Compiles the given source into an object file and saves it in output /// @@ -247,15 +254,11 @@ fn compile_to_obj( encoding: Option<&'static Encoding>, output: &str, reloc: RelocMode, - triple: Option, + triple: TargetTriple, ) -> Result<(), CompileError> { let initialization_config = &InitializationConfig::default(); Target::initialize_all(initialization_config); - let triple = triple - .map(|it| TargetTriple::create(it.as_str())) - .or_else(|| Some(TargetMachine::get_default_triple())) - .unwrap(); let target = Target::from_triple(&triple).unwrap(); let machine = target .create_target_machine( @@ -293,7 +296,13 @@ pub fn compile_to_static_obj( output: &str, target: Option, ) -> Result<(), CompileError> { - compile_to_obj(sources, encoding, output, RelocMode::Default, target) + compile_to_obj( + sources, + encoding, + output, + RelocMode::Default, + get_target_triple(target), + ) } /// Compiles a given source string to a shared position independent object and saves the output. @@ -310,7 +319,13 @@ pub fn compile_to_shared_pic_object( output: &str, target: Option, ) -> Result<(), CompileError> { - compile_to_obj(sources, encoding, output, RelocMode::PIC, target) + compile_to_obj( + sources, + encoding, + output, + RelocMode::PIC, + get_target_triple(target), + ) } /// Compiles a given source string to a dynamic non PIC object and saves the output. @@ -327,7 +342,13 @@ pub fn compile_to_shared_object( output: &str, target: Option, ) -> Result<(), CompileError> { - compile_to_obj(sources, encoding, output, RelocMode::DynamicNoPic, target) + compile_to_obj( + sources, + encoding, + output, + RelocMode::DynamicNoPic, + get_target_triple(target), + ) } /// @@ -465,7 +486,33 @@ fn parse(source: &str) -> ParsedAst { #[cfg(test)] mod tests { - use crate::create_source_code; + use inkwell::targets::TargetMachine; + + use crate::{create_source_code, get_target_triple}; + + #[test] + fn test_get_target_triple() { + let triple = get_target_triple(None); + assert_eq!( + triple.as_str().to_str().unwrap(), + TargetMachine::get_default_triple() + .as_str() + .to_str() + .unwrap() + ); + + // let triple = get_target_triple(Some("abcdef".into())); + // assert_eq!( + // triple.as_str().to_str().unwrap(), + // TargetMachine::get_default_triple() + // .as_str() + // .to_str() + // .unwrap() + // ); + + let triple = get_target_triple(Some("x86_64-pc-linux-gnu".into())); + assert_eq!(triple.as_str().to_str().unwrap(), "x86_64-pc-linux-gnu"); + } #[test] fn windows_encoded_file_content_read() { diff --git a/src/linker.rs b/src/linker.rs new file mode 100644 index 0000000000..00c8011ae2 --- /dev/null +++ b/src/linker.rs @@ -0,0 +1,220 @@ +// This file is based on code from the Mun Programming Language +// https://github.com/mun-lang/mun + +use std::path::{Path, PathBuf}; + +pub struct Linker { + errors: Vec, + linker: Box, +} + +trait LinkerInterface { + fn get_platform(&self) -> String; + fn add_obj(&mut self, path: &str); + fn add_lib(&mut self, path: &str); + fn add_lib_path(&mut self, path: &str); + fn build_shared_object(&mut self, path: &str); + fn build_exectuable(&mut self, path: &str); + fn finalize(&mut self) -> Result<(), LinkerError>; +} + +impl Linker { + pub fn new(target: &str) -> Result { + let target_os = target.split('-').collect::>()[2]; + let linker = match target_os { + "linux" => Ok(Box::new(LdLinker::new())), + //"win32" | "windows" => Ok(Box::new(MsvcLinker::new())), + _ => Err(LinkerError::Target(target_os.into())), + }?; + Ok(Linker { + errors: Vec::default(), + 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); + } + self + } + + /// Add a library seaBoxh path to look in for libraries + pub fn add_lib_path<'a>(&'a mut self, path: &str) -> &'a mut Self { + self.linker.add_lib_path(path); + self + } + + /// Add a library seaBoxh 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 + } + + /// Set the output file and run the linker to generate a shared object + pub fn build_shared_obj(&mut self, path: &Path) -> Result<(), LinkerError> { + if let Some(file) = self.get_str_from_path(path) { + self.linker.build_shared_object(file); + self.linker.finalize()?; + } + Ok(()) + } + + /// Set the output file and run the linker to generate an executable + pub fn build_exectuable(&mut self, path: &Path) -> Result<(), LinkerError> { + if let Some(file) = self.get_str_from_path(path) { + self.linker.build_exectuable(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(); + if filepath.is_none() { + self.errors.push(LinkerError::Path(path.into())); + } + filepath + } +} + +struct LdLinker { + args: Vec, +} + +impl LdLinker { + fn new() -> LdLinker { + LdLinker { + args: Vec::default(), + } + } +} + +impl LinkerInterface for LdLinker { + fn get_platform(&self) -> String { + "Linux".into() + } + + fn add_obj(&mut self, path: &str) { + self.args.push(path.into()); + } + + fn add_lib_path(&mut self, path: &str) { + self.args.push(format!("-L{}", path)); + } + + fn add_lib(&mut self, path: &str) { + self.args.push(format!("-l{}", path)); + } + + fn build_shared_object(&mut self, path: &str) { + self.args.push("--shared".into()); + self.args.push("-o".into()); + self.args.push(path.into()); + } + + fn build_exectuable(&mut self, path: &str) { + self.args.push("-o".into()); + self.args.push(path.into()); + } + + fn finalize(&mut self) -> Result<(), LinkerError> { + println!("{:?}", self.args); + mun_lld::link(mun_lld::LldFlavor::Elf, &self.args) + .ok() + .map_err(LinkerError::Link) + } +} + +/* TODO: Implement Windows linker + +struct MsvcLinker { + args: Vec, +} + +impl LinkerInterface for MsvcLinker { + fn get_platform(&self) -> String { + + } + + fn add_obj(&mut self, path: &str) { + + } + + fn add_lib_path(&mut self, path: &str) { + + } + + fn build_shared_object(&mut self, path: &Path) { + + } + + fn build_exectuable(&mut self, path: &Path) { + + } + + fn finalize(&mut self) -> Result<(), LinkerError>{ + + } +}*/ + +#[derive(Debug, PartialEq)] +pub enum LinkerError { + /// Error emitted by the linker + Link(String), + + /// Invalid target + Target(String), + + /// Error in path conversion + Path(PathBuf), +} + +impl From for String { + 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) + } + } + } +} + +#[test] +fn creation_test() { + let linker = Linker::new("x86_64-pc-linux-gnu").unwrap(); + assert_eq!(linker.linker.get_platform(), "Linux"); + + if let Err(tgt) = Linker::new("x86_64-pc-redox-abc") { + assert_eq!(tgt, LinkerError::Target("redox".into())); + } else { + 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 1d0e833f03..f5b136b77d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,11 +17,15 @@ //! [`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::{ cli::{CompileParameters, FormatOption, ParameterError}, - compile_to_bitcode, compile_to_ir, compile_to_shared_object, compile_to_static_obj, FilePath, + 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; fn main() { let args: Vec = std::env::args().collect(); @@ -66,22 +70,32 @@ fn main_compile(parameters: CompileParameters) -> Result<(), String> { let output_filename = parameters.output_name().unwrap(); let encoding = parameters.encoding; - match parameters.output_format_or_default() { + let out_format = parameters.output_format_or_default(); + match out_format { FormatOption::Static => { compile_to_static_obj( sources, encoding, output_filename.as_str(), - parameters.target, + parameters.target.clone(), ) .unwrap(); } - FormatOption::Shared | FormatOption::PIC => { + FormatOption::Shared => { compile_to_shared_object( sources, encoding, output_filename.as_str(), - parameters.target, + parameters.target.clone(), + ) + .unwrap(); + } + FormatOption::PIC => { + compile_to_shared_pic_object( + sources, + encoding, + output_filename.as_str(), + parameters.target.clone(), ) .unwrap(); } @@ -92,5 +106,32 @@ fn main_compile(parameters: CompileParameters) -> Result<(), String> { compile_to_ir(sources, encoding, &output_filename).unwrap(); } } + + 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 = linker::Linker::new(triple.as_str().to_str().unwrap())?; + 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 out_format == FormatOption::Static { + linker.build_exectuable(Path::new(&output_filename))?; + } else { + linker.build_shared_obj(Path::new(&output_filename))?; + } + } + Ok(()) }