@@ -18,7 +18,7 @@ use std::fs;
18
18
use std:: path:: PathBuf ;
19
19
use std:: process;
20
20
21
- use getopts:: { Matches , Options } ;
21
+ use getopts:: { Options } ;
22
22
23
23
use Build ;
24
24
use config:: Config ;
@@ -75,7 +75,22 @@ pub enum Subcommand {
75
75
76
76
impl Flags {
77
77
pub fn parse ( args : & [ String ] ) -> Flags {
78
+ let mut extra_help = String :: new ( ) ;
79
+ let mut subcommand_help = format ! ( "\
80
+ Usage: x.py <subcommand> [options] [<paths>...]
81
+
82
+ Subcommands:
83
+ build Compile either the compiler or libraries
84
+ test Build and run some test suites
85
+ bench Build and run some benchmarks
86
+ doc Build documentation
87
+ clean Clean out build directories
88
+ dist Build and/or install distribution artifacts
89
+
90
+ To learn more about a subcommand, run `./x.py <subcommand> -h`" ) ;
91
+
78
92
let mut opts = Options :: new ( ) ;
93
+ // Options common to all subcommands
79
94
opts. optflagmulti ( "v" , "verbose" , "use verbose output (-vv for very verbose)" ) ;
80
95
opts. optflag ( "i" , "incremental" , "use incremental compilation" ) ;
81
96
opts. optopt ( "" , "config" , "TOML configuration file for build" , "FILE" ) ;
@@ -89,26 +104,38 @@ impl Flags {
89
104
opts. optopt ( "j" , "jobs" , "number of jobs to run in parallel" , "JOBS" ) ;
90
105
opts. optflag ( "h" , "help" , "print this help message" ) ;
91
106
92
- let usage = |exit_code, opts : & Options | -> ! {
93
- let subcommand_help = format ! ( "\
94
- Usage: x.py <subcommand> [options] [<paths>...]
95
-
96
- Subcommands:
97
- build Compile either the compiler or libraries
98
- test Build and run some test suites
99
- bench Build and run some benchmarks
100
- doc Build documentation
101
- clean Clean out build directories
102
- dist Build and/or install distribution artifacts
107
+ // fn usage()
108
+ let usage = |exit_code : i32 , opts : & Options , subcommand_help : & str , extra_help : & str | -> ! {
109
+ println ! ( "{}" , opts. usage( subcommand_help) ) ;
110
+ if !extra_help. is_empty ( ) {
111
+ println ! ( "{}" , extra_help) ;
112
+ }
113
+ process:: exit ( exit_code) ;
114
+ } ;
103
115
104
- To learn more about a subcommand, run `./x.py <subcommand> -h`" ) ;
116
+ // Get subcommand
117
+ let matches = opts. parse ( & args[ ..] ) . unwrap_or_else ( |e| {
118
+ // Invalid argument/option format
119
+ println ! ( "\n {}\n " , e) ;
120
+ usage ( 1 , & opts, & subcommand_help, & extra_help) ;
121
+ } ) ;
122
+ let subcommand = match matches. free . get ( 0 ) {
123
+ Some ( s) => { s } ,
124
+ None => {
125
+ // No subcommand -- lets only show the general usage and subcommand help in this case.
126
+ println ! ( "{}\n " , subcommand_help) ;
127
+ process:: exit ( 0 ) ;
128
+ }
129
+ } ;
105
130
106
- println ! ( "{}" , opts. usage( & subcommand_help) ) ;
131
+ // Get any optional paths which occur after the subcommand
132
+ let cwd = t ! ( env:: current_dir( ) ) ;
133
+ let paths = matches. free [ 1 ..] . iter ( ) . map ( |p| cwd. join ( p) ) . collect :: < Vec < _ > > ( ) ;
107
134
108
- let subcommand = args . get ( 0 ) . map ( |s| & * * s ) ;
109
- match subcommand {
110
- Some ( "build" ) => {
111
- println ! ( "\
135
+ // Some subcommands have specific arguments help text
136
+ match subcommand. as_str ( ) {
137
+ "build" => {
138
+ subcommand_help . push_str ( "\n
112
139
Arguments:
113
140
This subcommand accepts a number of paths to directories to the crates
114
141
and/or artifacts to compile. For example:
@@ -125,12 +152,11 @@ Arguments:
125
152
126
153
For a quick build with a usable compile, you can pass:
127
154
128
- ./x.py build --stage 1 src/libtest
129
- " ) ;
130
- }
131
-
132
- Some ( "test" ) => {
133
- println ! ( "\
155
+ ./x.py build --stage 1 src/libtest" ) ;
156
+ }
157
+ "test" => {
158
+ opts. optmulti ( "" , "test-args" , "extra arguments" , "ARGS" ) ;
159
+ subcommand_help. push_str ( "\n
134
160
Arguments:
135
161
This subcommand accepts a number of paths to directories to tests that
136
162
should be compiled and run. For example:
@@ -143,12 +169,13 @@ Arguments:
143
169
compiled and tested.
144
170
145
171
./x.py test
146
- ./x.py test --stage 1
147
- " ) ;
148
- }
149
-
150
- Some ( "doc" ) => {
151
- println ! ( "\
172
+ ./x.py test --stage 1" ) ;
173
+ }
174
+ "bench" => {
175
+ opts. optmulti ( "" , "test-args" , "extra arguments" , "ARGS" ) ;
176
+ }
177
+ "doc" => {
178
+ subcommand_help. push_str ( "\n
152
179
Arguments:
153
180
This subcommand accepts a number of paths to directories of documentation
154
181
to build. For example:
@@ -160,143 +187,104 @@ Arguments:
160
187
If no arguments are passed then everything is documented:
161
188
162
189
./x.py doc
163
- ./x.py doc --stage 1
164
- " ) ;
165
- }
166
-
167
- _ => { }
190
+ ./x.py doc --stage 1" ) ;
168
191
}
169
-
170
-
171
- if let Some ( subcommand) = subcommand {
172
- if subcommand == "build" ||
173
- subcommand == "test" ||
174
- subcommand == "bench" ||
175
- subcommand == "doc" ||
176
- subcommand == "clean" ||
177
- subcommand == "dist" {
178
- if args. iter ( ) . any ( |a| a == "-v" ) {
179
- let flags = Flags :: parse ( & [ "build" . to_string ( ) ] ) ;
180
- let mut config = Config :: default ( ) ;
181
- config. build = flags. build . clone ( ) ;
182
- let mut build = Build :: new ( flags, config) ;
183
- metadata:: build ( & mut build) ;
184
- step:: build_rules ( & build) . print_help ( subcommand) ;
185
- } else {
186
- println ! ( "Run `./x.py {} -h -v` to see a list of available paths." ,
187
- subcommand) ;
188
- }
189
-
190
- println ! ( "" ) ;
191
- }
192
+ "dist" => {
193
+ opts. optflag ( "" , "install" , "run installer as well" ) ;
192
194
}
193
-
194
- process:: exit ( exit_code) ;
195
+ _ => { }
195
196
} ;
196
- if args . len ( ) == 0 {
197
- println ! ( "a subcommand must be passed" ) ;
198
- usage ( 1 , & opts ) ;
199
- }
200
- let parse = | opts : & Options | {
201
- let m = opts . parse ( & args [ 1 .. ] ) . unwrap_or_else ( |e| {
202
- println ! ( "failed to parse options: {}" , e ) ;
203
- usage ( 1 , opts ) ;
204
- } ) ;
205
- if m . opt_present ( "h" ) {
206
- usage ( 0 , opts ) ;
197
+
198
+ // All subcommands can have an optional "Available paths" section
199
+ if matches . opt_present ( "verbose" ) {
200
+ let flags = Flags :: parse ( & [ "build" . to_string ( ) ] ) ;
201
+ let mut config = Config :: default ( ) ;
202
+ config . build = flags . build . clone ( ) ;
203
+ let mut build = Build :: new ( flags , config ) ;
204
+ metadata :: build ( & mut build ) ;
205
+ let maybe_rules_help = step :: build_rules ( & build ) . get_help ( subcommand ) ;
206
+ if maybe_rules_help . is_some ( ) {
207
+ extra_help . push_str ( maybe_rules_help . unwrap ( ) . as_str ( ) ) ;
207
208
}
208
- return m
209
- } ;
209
+ } else {
210
+ extra_help. push_str ( format ! ( "Run `./x.py {} -h -v` to see a list of available paths." ,
211
+ subcommand) . as_str ( ) ) ;
212
+ }
210
213
211
- let cwd = t ! ( env:: current_dir( ) ) ;
212
- let remaining_as_path = |m : & Matches | {
213
- m. free . iter ( ) . map ( |p| cwd. join ( p) ) . collect :: < Vec < _ > > ( )
214
- } ;
215
- // TODO: Parse subcommand nicely up at top, so options can occur before the subcommand.
216
- // TODO: Get the subcommand-specific options below into the help output
214
+ // User passed in -h/--help?
215
+ if matches. opt_present ( "help" ) {
216
+ usage ( 0 , & opts, & subcommand_help, & extra_help) ;
217
+ }
217
218
218
- let m: Matches ;
219
- let cmd = match & args[ 0 ] [ ..] {
219
+ let cmd = match subcommand. as_str ( ) {
220
220
"build" => {
221
- m = parse ( & opts) ;
222
- Subcommand :: Build { paths : remaining_as_path ( & m) }
221
+ Subcommand :: Build { paths : paths }
223
222
}
224
223
"test" => {
225
- opts. optmulti ( "" , "test-args" , "extra arguments" , "ARGS" ) ;
226
- m = parse ( & opts) ;
227
224
Subcommand :: Test {
228
- paths : remaining_as_path ( & m ) ,
229
- test_args : m . opt_strs ( "test-args" ) ,
225
+ paths : paths ,
226
+ test_args : matches . opt_strs ( "test-args" ) ,
230
227
}
231
228
}
232
229
"bench" => {
233
- opts. optmulti ( "" , "test-args" , "extra arguments" , "ARGS" ) ;
234
- m = parse ( & opts) ;
235
230
Subcommand :: Bench {
236
- paths : remaining_as_path ( & m ) ,
237
- test_args : m . opt_strs ( "test-args" ) ,
231
+ paths : paths ,
232
+ test_args : matches . opt_strs ( "test-args" ) ,
238
233
}
239
234
}
240
235
"doc" => {
241
- m = parse ( & opts) ;
242
- Subcommand :: Doc { paths : remaining_as_path ( & m) }
236
+ Subcommand :: Doc { paths : paths }
243
237
}
244
238
"clean" => {
245
- m = parse ( & opts) ;
246
- if m. free . len ( ) > 0 {
247
- println ! ( "clean takes no arguments" ) ;
248
- usage ( 1 , & opts) ;
239
+ if matches. free . len ( ) > 0 {
240
+ println ! ( "\n clean takes no arguments\n " ) ;
241
+ usage ( 1 , & opts, & subcommand_help, & extra_help) ;
249
242
}
250
243
Subcommand :: Clean
251
244
}
252
245
"dist" => {
253
- opts. optflag ( "" , "install" , "run installer as well" ) ;
254
- m = parse ( & opts) ;
255
246
Subcommand :: Dist {
256
- paths : remaining_as_path ( & m ) ,
257
- install : m . opt_present ( "install" ) ,
247
+ paths : paths ,
248
+ install : matches . opt_present ( "install" ) ,
258
249
}
259
250
}
260
- "--help" => usage ( 0 , & opts) ,
261
251
_ => {
262
- usage ( 1 , & opts) ;
252
+ usage ( 1 , & opts, & subcommand_help , & extra_help ) ;
263
253
}
264
254
} ;
265
255
266
256
267
- let cfg_file = m . opt_str ( "config" ) . map ( PathBuf :: from) . or_else ( || {
257
+ let cfg_file = matches . opt_str ( "config" ) . map ( PathBuf :: from) . or_else ( || {
268
258
if fs:: metadata ( "config.toml" ) . is_ok ( ) {
269
259
Some ( PathBuf :: from ( "config.toml" ) )
270
260
} else {
271
261
None
272
262
}
273
263
} ) ;
274
264
275
- let mut stage = m. opt_str ( "stage" ) . map ( |j| j. parse ( ) . unwrap ( ) ) ;
276
-
277
- let incremental = m. opt_present ( "i" ) ;
265
+ let mut stage = matches. opt_str ( "stage" ) . map ( |j| j. parse ( ) . unwrap ( ) ) ;
278
266
279
- if incremental {
267
+ if matches . opt_present ( " incremental" ) {
280
268
if stage. is_none ( ) {
281
269
stage = Some ( 1 ) ;
282
270
}
283
271
}
284
272
285
273
Flags {
286
- verbose : m . opt_count ( "v " ) ,
274
+ verbose : matches . opt_count ( "verbose " ) ,
287
275
stage : stage,
288
- on_fail : m . opt_str ( "on-fail" ) ,
289
- keep_stage : m . opt_str ( "keep-stage" ) . map ( |j| j. parse ( ) . unwrap ( ) ) ,
290
- build : m . opt_str ( "build" ) . unwrap_or_else ( || {
276
+ on_fail : matches . opt_str ( "on-fail" ) ,
277
+ keep_stage : matches . opt_str ( "keep-stage" ) . map ( |j| j. parse ( ) . unwrap ( ) ) ,
278
+ build : matches . opt_str ( "build" ) . unwrap_or_else ( || {
291
279
env:: var ( "BUILD" ) . unwrap ( )
292
280
} ) ,
293
- host : split ( m . opt_strs ( "host" ) ) ,
294
- target : split ( m . opt_strs ( "target" ) ) ,
281
+ host : split ( matches . opt_strs ( "host" ) ) ,
282
+ target : split ( matches . opt_strs ( "target" ) ) ,
295
283
config : cfg_file,
296
- src : m . opt_str ( "src" ) . map ( PathBuf :: from) ,
297
- jobs : m . opt_str ( "jobs" ) . map ( |j| j. parse ( ) . unwrap ( ) ) ,
284
+ src : matches . opt_str ( "src" ) . map ( PathBuf :: from) ,
285
+ jobs : matches . opt_str ( "jobs" ) . map ( |j| j. parse ( ) . unwrap ( ) ) ,
298
286
cmd : cmd,
299
- incremental : incremental,
287
+ incremental : matches . opt_present ( " incremental" ) ,
300
288
}
301
289
}
302
290
}
0 commit comments