midenc_debug/
cli.rs

1use std::{ffi::OsString, path::PathBuf, sync::Arc};
2
3use clap::{ColorChoice, Parser};
4use midenc_session::{
5    add_target_link_libraries,
6    diagnostics::{DefaultSourceManager, Emitter},
7    ColorChoice as MDColorChoice, InputFile, LinkLibrary, Options, ProjectType, Session, TargetEnv,
8};
9
10/// Run a compiled Miden program with the Miden VM
11#[derive(Default, Debug, Parser)]
12#[command(name = "debug")]
13pub struct Debugger {
14    /// The working directory for the compiler
15    ///
16    /// By default this will be the working directory the compiler is executed from
17    #[arg(long, value_name = "DIR", help_heading = "Output")]
18    pub working_dir: Option<PathBuf>,
19    /// The path to the root directory of the Miden toolchain libraries
20    ///
21    /// By default this is assumed to be ~/.miden/toolchains/<version>
22    #[arg(
23        long,
24        value_name = "DIR",
25        env = "MIDENC_SYSROOT",
26        help_heading = "Compiler"
27    )]
28    pub sysroot: Option<PathBuf>,
29    /// Whether, and how, to color terminal output
30    #[arg(
31        long,
32        value_enum,
33        default_value_t = ColorChoice::Auto,
34        default_missing_value = "auto",
35        num_args(0..=1),
36        help_heading = "Diagnostics"
37    )]
38    pub color: ColorChoice,
39    /// The target environment to compile for
40    #[arg(
41        long,
42        value_name = "TARGET",
43        default_value_t = TargetEnv::Base,
44        help_heading = "Compiler"
45    )]
46    pub target: TargetEnv,
47    /// Specify the function to call as the entrypoint for the program
48    /// in the format `<module_name>::<function>`
49    #[arg(long, help_heading = "Compiler", hide(true))]
50    pub entrypoint: Option<String>,
51    /// Specify one or more search paths for link libraries requested via `-l`
52    #[arg(
53        long = "search-path",
54        short = 'L',
55        value_name = "PATH",
56        help_heading = "Linker"
57    )]
58    pub search_path: Vec<PathBuf>,
59    /// Link compiled projects to the specified library NAME.
60    ///
61    /// The optional KIND can be provided to indicate what type of library it is.
62    ///
63    /// NAME must either be an absolute path (with extension when applicable), or
64    /// a library namespace (no extension). The former will be used as the path
65    /// to load the library, without looking for it in the library search paths,
66    /// while the latter will be located in the search path based on its KIND.
67    ///
68    /// See below for valid KINDs:
69    #[arg(
70        long = "link-library",
71        short = 'l',
72        value_name = "[KIND=]NAME",
73        value_delimiter = ',',
74        next_line_help(true),
75        help_heading = "Linker"
76    )]
77    pub link_libraries: Vec<LinkLibrary>,
78}
79
80impl Debugger {
81    /// Construct a [Compiler] programatically
82    pub fn new_session<I, A, S>(inputs: I, emitter: Option<Arc<dyn Emitter>>, argv: A) -> Session
83    where
84        I: IntoIterator<Item = InputFile>,
85        A: IntoIterator<Item = S>,
86        S: Into<OsString> + Clone,
87    {
88        let argv = [OsString::from("run")]
89            .into_iter()
90            .chain(argv.into_iter().map(|arg| arg.into()));
91        let mut matches = <Self as clap::CommandFactory>::command()
92            .try_get_matches_from(argv)
93            .unwrap_or_else(|err| err.exit());
94
95        let opts = <Self as clap::FromArgMatches>::from_arg_matches_mut(&mut matches)
96            .map_err(format_error::<Self>)
97            .unwrap_or_else(|err| err.exit());
98
99        let inputs = inputs.into_iter().collect();
100        opts.into_session(inputs, emitter)
101    }
102
103    /// Use this configuration to obtain a [Session] used for compilation
104    pub fn into_session(
105        self,
106        inputs: Vec<InputFile>,
107        emitter: Option<Arc<dyn Emitter>>,
108    ) -> Session {
109        let cwd = self
110            .working_dir
111            .unwrap_or_else(|| std::env::current_dir().expect("no working directory available"));
112
113        // Map clap color choices to internal color choice
114        let color = match self.color {
115            ColorChoice::Auto => MDColorChoice::Auto,
116            ColorChoice::Always => MDColorChoice::Always,
117            ColorChoice::Never => MDColorChoice::Never,
118        };
119
120        // Consolidate all compiler options
121        let mut options = Options::new(None, self.target, ProjectType::Program, cwd, self.sysroot)
122            .with_color(color);
123        options.search_paths = self.search_path;
124
125        let link_libraries = add_target_link_libraries(self.link_libraries, &self.target);
126        options.link_libraries = link_libraries;
127
128        options.entrypoint = self.entrypoint;
129
130        let target_dir = std::env::temp_dir();
131        let source_manager = Arc::new(DefaultSourceManager::default());
132        Session::new(inputs, None, None, target_dir, options, emitter, source_manager)
133    }
134}
135
136fn format_error<I: clap::CommandFactory>(err: clap::Error) -> clap::Error {
137    let mut cmd = I::command();
138    err.format(&mut cmd)
139}