pub fn parse_gnu<S: AsRef<OsStr>>(args: &[S]) -> Result<ParseOutcome>Expand description
Parses a GNU ld / gold / lld / mold command line.
args includes argv[0], which also selects the flavor when it is a name
such as ld64.qld (see select_flavor). Response files are read with
std::fs::read; use parse_gnu_with to supply them another way.
The options describe a link run the way the qld binary runs one:
LinkOptions::use_process_defaults is applied, so the link map goes to
standard output and LD_LIBRARY_PATH and the other variables GNU ld
reads are taken from the environment. parse_gnu_with parses the same
command line into a hermetic, silent link.
Warnings found while parsing, such as unknown -z keywords, are returned
in LinkOptions::warnings for the caller to report.
ยงErrors
Returns Error::Option for an unknown, unsupported or malformed option,
Error::Io for an unreadable response file, and
Error::Unimplemented for a flavor qld does not parse yet.
Examples found in repository?
34fn link(args: &[std::ffi::OsString], diagnostics: &dyn DiagnosticSink) -> qld::Result<()> {
35 let options = match qld::parse_gnu(args)? {
36 ParseOutcome::Link(options) => options,
37 ParseOutcome::Help => {
38 print!("{}", qld::args::usage());
39 return Ok(());
40 }
41 ParseOutcome::Version => {
42 println!("{}", qld::version_line());
43 return Ok(());
44 }
45 };
46 // Parsing does no reporting: warnings such as an unknown `-z` keyword
47 // come back in the options, for the caller to emit.
48 if !options.no_warnings {
49 for warning in &options.warnings {
50 diagnostics.emit(Diagnostic::warning(warning.clone()));
51 }
52 }
53 if options.fatal_warnings && !options.warnings.is_empty() {
54 return Err(qld::Error::Reported {
55 errors: options.warnings.len(),
56 });
57 }
58 qld::link(&options, diagnostics)
59}More examples
42fn main() -> qld::Result<()> {
43 let mut args = std::env::args().skip(1);
44 if let Some(limit) = args.next() {
45 let limit = Duration::from_millis(limit.parse().unwrap_or(1000));
46 let argv: Vec<String> = std::iter::once("ld".to_string()).chain(args).collect();
47 let qld::ParseOutcome::Link(options) = qld::parse_gnu(&argv)? else {
48 return Ok(());
49 };
50 let start = Instant::now();
51 let result = link_with_deadline(*options, limit);
52 match &result {
53 Ok(()) => eprintln!("linked in {:?}", start.elapsed()),
54 Err(error) if CancelToken::is_cancellation(error) => {
55 eprintln!("{error} after {:?}", start.elapsed());
56 }
57 Err(_) => {}
58 }
59 return result;
60 }
61
62 // Without arguments: link the in-memory demo program twice, once with
63 // plenty of time and once cancelled before it starts.
64 let mut options = LinkOptions::new();
65 options.kind = OutputKind::StaticExecutable;
66 options.push_input(
67 InputKind::bytes("main.o", objects::main_object()),
68 InputAttrs::default(),
69 );
70 options.push_input(
71 InputKind::bytes("answer.o", objects::answer_object(42)),
72 InputAttrs::default(),
73 );
74 let buffer = OutputBuffer::new();
75 options.output_buffer = Some(buffer.clone());
76
77 link_with_deadline(options.clone(), Duration::from_secs(10))?;
78 println!(
79 "with a 10 s deadline: linked {} bytes",
80 buffer.take().map_or(0, |b| b.len())
81 );
82
83 let token = CancelToken::new();
84 token.cancel();
85 options.cancel = Some(token);
86 match qld::link(&options, &Stderr::new(qld::PROGRAM_NAME)) {
87 Err(error) if CancelToken::is_cancellation(&error) => {
88 println!(
89 "cancelled before it started: {error}; output empty: {}",
90 !buffer.is_filled()
91 );
92 }
93 other => println!("unexpected: {other:?}"),
94 }
95 Ok(())
96}25fn main() -> qld::Result<()> {
26 // Object code from a compiler or JIT in the same process.
27 let main_o = objects::main_object();
28 let answer_o = objects::answer_object(42);
29
30 // 1. Buffers as inputs, the output into a buffer.
31 let mut options = LinkOptions::new();
32 options.kind = OutputKind::StaticExecutable;
33 options.push_input(
34 InputKind::bytes("main.o", main_o.clone()),
35 InputAttrs::default(),
36 );
37 options.push_input(
38 InputKind::bytes("answer.o", answer_o.clone()),
39 InputAttrs::default(),
40 );
41 let buffer = OutputBuffer::new();
42 options.output_buffer = Some(buffer.clone());
43 let diagnostics = Collect::new();
44 qld::link(&options, &diagnostics)?;
45 let first = buffer.take().expect("a successful link fills the buffer");
46 report("buffers", &first);
47
48 // 2. A command line whose files live in memory: `main.o`, and
49 // `libanswer.a` in a search directory that does not exist on disk.
50 let library = objects::archive(&[("answer.o", &answer_o, &["answer"])]);
51 let files = MemoryFiles::new()
52 .with("main.o", main_o)
53 .with("/virtual/lib/libanswer.a", library);
54 let argv = ["ld", "-static", "-L/virtual/lib", "main.o", "-lanswer"];
55 let qld::ParseOutcome::Link(options) = qld::parse_gnu(&argv)? else {
56 unreachable!("a link command line");
57 };
58 let mut options = *options;
59 options.input_provider = Some(Arc::new(files));
60 let buffer = OutputBuffer::new();
61 options.output_buffer = Some(buffer.clone());
62 qld::link(&options, &diagnostics)?;
63 let second = buffer.take().expect("a successful link fills the buffer");
64 report("memory files", &second);
65 assert_eq!(first, second, "same program either way");
66
67 for diagnostic in diagnostics.take_sorted() {
68 eprintln!("{}: {}", diagnostic.severity, diagnostic.message);
69 }
70 if let Some(path) = std::env::args_os().nth(1) {
71 std::fs::write(&path, &first).map_err(|error| qld::Error::io(&path, error))?;
72 #[cfg(unix)]
73 {
74 use std::os::unix::fs::PermissionsExt;
75 std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o755))
76 .map_err(|error| qld::Error::io(&path, error))?;
77 }
78 println!("wrote {} (it exits with status 42)", path.to_string_lossy());
79 }
80 Ok(())
81}