summaryrefslogtreecommitdiff
path: root/src/main.rs
blob: 9ff86d96a702fe7448c778a474e344190dc8745d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
@TODO:
- static libraries
*/

extern crate clap;

use std::io;
use io::Write;
use clap::Parser;

#[cfg(target_endian = "big")]
compile_error! {"WHY do you have a big endian machine???? it's the 21st century, buddy. this program won't work fuck you"}

use tinyld::linker;

#[derive(Parser, Debug)]
struct Args {
	/// Input files: object files (.o) and shared libraries (.so) are supported.
	inputs: Vec<String>,
	/// If set, the program will not be linked against libc.
	///
	/// This makes the executable smaller.
	#[arg(long = "no-stdlib", default_value_t = false)]
	no_std_lib: bool,
	/// If set, the program will be linked against libstdc++.
	///
	/// This is needed when using any C++ library functions.
	#[arg(long = "stdc++", default_value_t = false)]
	std_cpp: bool,
	/// Output executable path.
	#[arg(short = 'o', long = "output", default_value = "a.out")]
	output: String,
	/// The name of the function which will be used as the entry point.
	#[arg(short = 'e', long = "entry", default_value = "entry")]
	entry: String,
	/// :3
	#[arg(long = "nya")]
	nya: bool,
	/// C compiler
	///
	/// Note: clang *really* wants to generate `R_386_PLT32` relocations
	///  even when you beg it not to.
	#[arg(long = "cc", default_value = "gcc")]
	cc: String,
	/// C compiler flags
	///
	/// The C compiler is invoked using `(cc) (cflags) (C file) -o (object file)`
	#[arg(long = "cflags", default_values = linker::Linker::DEFAULT_CFLAGS)]
	cflags: Vec<String>,
	/// C++ compiler
	#[arg(long = "cxx", default_value = "g++")]
	cxx: String,
	/// C++ compiler flags
	#[arg(long = "cxxflags", default_values = linker::Linker::DEFAULT_CXXFLAGS)]
	cxxflags: Vec<String>,
	/// verbose mode
	#[arg(short = 'v', long = "verbose")]
	verbose: bool,
}

fn main_() -> Result<(), String> {
	let args = Args::parse();

	if args.nya {
		println!("hai uwu ^_^");
		return Ok(());
	}

	let inputs = &args.inputs;

	let mut linker = linker::Linker::new();

	linker.set_cc(&args.cc);
	linker.set_cflags(&args.cflags);
	linker.set_cxx(&args.cxx);
	linker.set_cxxflags(&args.cxxflags);

	let warning_handler = |w| {
		// termcolor is a goddamn nightmare to use
		// I DONT FUCKING CARE IF WRITING TO STDOUT FAILS
		// DONT MAKE ME UNWRAP EVERYTHING
		eprintln!("\x1b[93mwarning:\x1b[0m {w}");
	};
	linker.set_warning_handler(warning_handler);

	if inputs.is_empty() {
		if cfg!(debug_assertions) {
			// ease of use when debugging
			linker.add_input("test.c")?;
		} else {
			return Err("no inputs provided.".into());
		}
	}

	if !args.no_std_lib {
		linker.add_input("libc.so.6")?;
	}
	if args.std_cpp {
		linker.add_input("libstdc++.so.6")?;
	}

	for input in inputs.iter() {
		if args.verbose {
			println!("processing source file {input}...");
		}
		linker.add_input(input)?;
	}

	if args.verbose {
		print!("linking {}... ", args.output);
	}
	
	io::stdout().flush().unwrap_or(());
	let info = linker.link_to_file(&args.output, &args.entry)?;
	
	if args.verbose {
		println!("\x1b[92msuccess!\x1b[0m");
		println!("data size:      {:7} bytes", info.data_size);
		println!("executable size:{:7} bytes", info.exec_size);
	}
	
	Ok(())
}

fn main() {
	if let Err(e) = main_() {
		eprintln!("\x1b[91merror:\x1b[0m {e}");
	}
}