summary refs log tree commit diff
path: root/src/librustpkg/parse_args.rs
blob: 8f21c6d3bfa68f65494197283068b5b8f4e3483d (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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use context::{Context, RustcFlags, Trans, Link, Nothing, Pretty, Analysis, Assemble,
                       LLVMAssemble, LLVMCompileBitcode};
use context::{Command, flags_forbidden_for_cmd};
use rustc::version;
use exit_codes::{BAD_FLAG_CODE};
use rustc::driver::{session};

use usage;

use extra::{getopts};
use std::{result};
use std::hashmap::HashSet;

///  Result of parsing command line arguments
pub struct ParseResult {
    // Command
    command: Command,
    // Args
    args: ~[~str],
    // Parsed command line flags
    context: Context,
    // Path to system root
    sysroot: Option<~str>
}

/// Parses command line arguments of rustpkg.
/// Returns a triplet (command, remaining_args, context)
pub fn parse_args(args: &[~str]) -> Result<ParseResult, int> {
    let opts = ~[ getopts::optflag("no-link"),
                                        getopts::optflag("no-trans"),
                 // n.b. Ignores different --pretty options for now
                                        getopts::optflag("pretty"),
                                        getopts::optflag("parse-only"),
                 getopts::optflag("S"), getopts::optflag("assembly"),
                 getopts::optmulti("c"), getopts::optmulti("cfg"),
                 getopts::optflag("v"), getopts::optflag("version"),
                 getopts::optflag("r"), getopts::optflag("rust-path-hack"),
                                        getopts::optopt("sysroot"),
                                        getopts::optflag("emit-llvm"),
                                        getopts::optopt("linker"),
                                        getopts::optopt("link-args"),
                                        getopts::optopt("opt-level"),
                 getopts::optflag("O"),
                                        getopts::optflag("save-temps"),
                                        getopts::optopt("target"),
                                        getopts::optopt("target-cpu"),
                 getopts::optmulti("Z")                                   ];
    let matches = &match getopts::getopts(args, opts) {
        result::Ok(m) => m,
        result::Err(f) => {
            error!("{}", f.to_err_msg());
            return Err(1);
        }
    };
    let no_link = matches.opt_present("no-link");
    let no_trans = matches.opt_present("no-trans");
    let supplied_sysroot = matches.opt_str("sysroot");
    let generate_asm = matches.opt_present("S") ||
        matches.opt_present("assembly");
    let parse_only = matches.opt_present("parse-only");
    let pretty = matches.opt_present("pretty");
    let emit_llvm = matches.opt_present("emit-llvm");

    if matches.opt_present("v") ||
       matches.opt_present("version") {
        version(args[0]);
        return Err(0);
    }

    let use_rust_path_hack = matches.opt_present("r") ||
                             matches.opt_present("rust-path-hack");

    let linker = matches.opt_str("linker");
    let link_args = matches.opt_str("link-args");
    let cfgs = matches.opt_strs("cfg") + matches.opt_strs("c");
    let mut user_supplied_opt_level = true;
    let opt_level = match matches.opt_str("opt-level") {
        Some(~"0") => session::No,
        Some(~"1") => session::Less,
        Some(~"2") => session::Default,
        Some(~"3") => session::Aggressive,
        _ if matches.opt_present("O") => session::Default,
        _ => {
            user_supplied_opt_level = false;
            session::No
        }
    };

    let save_temps = matches.opt_present("save-temps");
    let target     = matches.opt_str("target");
    let target_cpu = matches.opt_str("target-cpu");
    let experimental_features = {
        let strs = matches.opt_strs("Z");
        if matches.opt_present("Z") {
            Some(strs)
        }
        else {
            None
        }
    };

    let mut args = matches.free.clone();
    args.shift();

    if (args.len() < 1) {
        usage::general();
        return Err(1);
    }

    let rustc_flags = RustcFlags {
        linker: linker,
        link_args: link_args,
        optimization_level: opt_level,
        compile_upto: if no_trans {
            Trans
        } else if no_link {
            Link
        } else if pretty {
            Pretty
        } else if parse_only {
            Analysis
        } else if emit_llvm && generate_asm {
            LLVMAssemble
        } else if generate_asm {
            Assemble
        } else if emit_llvm {
            LLVMCompileBitcode
        } else {
            Nothing
        },
        save_temps: save_temps,
        target: target,
        target_cpu: target_cpu,
        additional_library_paths:
            HashSet::new(), // No way to set this from the rustpkg command line
        experimental_features: experimental_features
    };

    let cmd_opt = args.iter().filter_map( |s| from_str(s.clone())).next();
    let command = match(cmd_opt){
        None => {
            debug!("No legal command. Returning 0");
            usage::general();
            return Err(0);
        }
        Some(cmd) => {
            let bad_option = flags_forbidden_for_cmd(&rustc_flags,
                                                              cfgs,
                                                              cmd,
                                                              user_supplied_opt_level);
            if bad_option {
                usage::usage_for_command(cmd);
                debug!("Bad  option, returning BAD_FLAG_CODE");
                return Err(BAD_FLAG_CODE);
            } else {
                cmd
            }
        }
    };

    // Pop off all flags, plus the command
    let mut remaining_args: ~[~str] = args.iter().skip_while(|&s| {
        let maybe_command: Option<Command> = from_str(*s);
        maybe_command.is_none()
    }).map(|s| s.clone()).collect();
    remaining_args.shift();

    let context = Context{
        rustc_flags: rustc_flags,
        cfgs: cfgs,
        use_rust_path_hack: use_rust_path_hack,
    };
    Ok(ParseResult {
        command:  command,
        args: remaining_args,
        context: context,
        sysroot: supplied_sysroot
    })
}