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    #[arg(long, help_heading = "Compiler", hide(true))]
49    pub entrypoint: Option<String>,
50    /// Specify one or more search paths for link libraries requested via `-l`
51    #[arg(
52        long = "search-path",
53        short = 'L',
54        value_name = "PATH",
55        help_heading = "Linker"
56    )]
57    pub search_path: Vec<PathBuf>,
58    /// Link compiled projects to the specified library NAME.
59    ///
60    /// The optional KIND can be provided to indicate what type of library it is.
61    ///
62    /// NAME must either be an absolute path (with extension when applicable), or
63    /// a library namespace (no extension). The former will be used as the path
64    /// to load the library, without looking for it in the library search paths,
65    /// while the latter will be located in the search path based on its KIND.
66    ///
67    /// See below for valid KINDs:
68    #[arg(
69        long = "link-library",
70        short = 'l',
71        value_name = "[KIND=]NAME",
72        value_delimiter = ',',
73        next_line_help(true),
74        help_heading = "Linker"
75    )]
76    pub link_libraries: Vec<LinkLibrary>,
77}
78
79impl Debugger {
80    /// Construct a [Compiler] programatically
81    pub fn new_session<I, A, S>(inputs: I, emitter: Option<Arc<dyn Emitter>>, argv: A) -> Session
82    where
83        I: IntoIterator<Item = InputFile>,
84        A: IntoIterator<Item = S>,
85        S: Into<OsString> + Clone,
86    {
87        let argv = [OsString::from("run")]
88            .into_iter()
89            .chain(argv.into_iter().map(|arg| arg.into()));
90        let mut matches = <Self as clap::CommandFactory>::command()
91            .try_get_matches_from(argv)
92            .unwrap_or_else(|err| err.exit());
93
94        let opts = <Self as clap::FromArgMatches>::from_arg_matches_mut(&mut matches)
95            .map_err(format_error::<Self>)
96            .unwrap_or_else(|err| err.exit());
97
98        let inputs = inputs.into_iter().collect();
99        opts.into_session(inputs, emitter)
100    }
101
102    /// Use this configuration to obtain a [Session] used for compilation
103    pub fn into_session(
104        self,
105        inputs: Vec<InputFile>,
106        emitter: Option<Arc<dyn Emitter>>,
107    ) -> Session {
108        let cwd = self
109            .working_dir
110            .unwrap_or_else(|| std::env::current_dir().expect("no working directory available"));
111
112        // Map clap color choices to internal color choice
113        let color = match self.color {
114            ColorChoice::Auto => MDColorChoice::Auto,
115            ColorChoice::Always => MDColorChoice::Always,
116            ColorChoice::Never => MDColorChoice::Never,
117        };
118
119        // Consolidate all compiler options
120        let mut options = Options::new(None, self.target, ProjectType::Program, cwd, self.sysroot)
121            .with_color(color);
122        options.search_paths = self.search_path;
123
124        let link_libraries = add_target_link_libraries(self.link_libraries, &self.target);
125        options.link_libraries = link_libraries;
126
127        options.entrypoint = self.entrypoint;
128
129        let target_dir = std::env::temp_dir();
130        let source_manager = Arc::new(DefaultSourceManager::default());
131        Session::new(inputs, None, None, target_dir, options, emitter, source_manager)
132    }
133}
134
135fn format_error<I: clap::CommandFactory>(err: clap::Error) -> clap::Error {
136    let mut cmd = I::command();
137    err.format(&mut cmd)
138}