1
1
#![ feature( box_syntax) ]
2
2
#![ feature( rustc_private) ]
3
+ #![ feature( try_from) ]
3
4
4
5
extern crate env_logger;
5
6
extern crate getopts;
@@ -13,223 +14,136 @@ extern crate rustc_metadata;
13
14
extern crate semverver;
14
15
extern crate syntax;
15
16
16
- use rustc:: {
17
- hir:: def_id:: * ,
18
- middle:: cstore:: ExternCrate ,
19
- session:: {
20
- config:: { self , ErrorOutputType , Input } ,
21
- Session ,
22
- } ,
23
- } ;
24
- use rustc_codegen_utils:: codegen_backend:: CodegenBackend ;
25
- use rustc_driver:: { driver, Compilation , CompilerCalls , RustcDefaultCalls } ;
26
- use rustc_metadata:: cstore:: CStore ;
17
+ use rustc:: { hir:: def_id:: * , middle:: cstore:: ExternCrate } ;
18
+ use rustc_driver:: { driver:: CompileController , Compilation } ;
27
19
use semverver:: semcheck:: run_analysis;
20
+ use std:: convert:: TryInto ;
28
21
use std:: {
29
- path:: { Path , PathBuf } ,
30
- process:: Command ,
22
+ path:: Path ,
23
+ process:: { exit , Command } ,
31
24
} ;
32
- use syntax:: { ast, source_map:: Pos } ;
33
-
34
- /// After the typechecker has finished it's work, perform our checks.
35
- fn callback ( state : & driver:: CompileState , version : & str , verbose : bool , api_guidelines : bool ) {
36
- let tcx = state. tcx . unwrap ( ) ;
37
-
38
- // To select the old and new crates we look at the position of the declaration in the
39
- // source file. The first one will be the `old` and the other will be `new`. This is
40
- // unfortunately a bit hacky... See issue #64 for details.
41
-
42
- let mut crates: Vec < _ > = tcx
43
- . crates ( )
44
- . iter ( )
45
- . flat_map ( |crate_num| {
46
- let def_id = DefId {
47
- krate : * crate_num,
48
- index : CRATE_DEF_INDEX ,
49
- } ;
50
-
51
- match * tcx. extern_crate ( def_id) {
52
- Some ( ExternCrate {
53
- span, direct : true , ..
54
- } ) if span. data ( ) . lo . to_usize ( ) > 0 => Some ( ( span. data ( ) . lo . to_usize ( ) , def_id) ) ,
55
- _ => None ,
56
- }
57
- } )
58
- . collect ( ) ;
59
-
60
- crates. sort_by_key ( |& ( span_lo, _) | span_lo) ;
61
-
62
- if let [ ( _, old_def_id) , ( _, new_def_id) ] = * crates. as_slice ( ) {
63
- debug ! ( "running semver analysis" ) ;
64
- let changes = run_analysis ( tcx, old_def_id, new_def_id) ;
65
- changes. output ( tcx. sess , version, verbose, api_guidelines) ;
66
- } else {
67
- tcx. sess . err ( "could not find crate old and new crates" ) ;
68
- }
69
- }
25
+ use syntax:: source_map:: Pos ;
70
26
71
- /// A wrapper to control compilation.
72
- struct SemVerVerCompilerCalls {
73
- /// The wrapped compilation handle.
74
- default : Box < RustcDefaultCalls > ,
75
- /// The version of the old crate.
76
- version : String ,
77
- /// The output mode.
78
- verbose : bool ,
79
- /// Output only changes that are breaking according to the API guidelines.
80
- api_guidelines : bool ,
81
- }
82
-
83
- impl SemVerVerCompilerCalls {
84
- /// Construct a new compilation wrapper, given a version string.
85
- pub fn new ( version : String , verbose : bool , api_guidelines : bool ) -> Box < Self > {
86
- Box :: new ( Self {
87
- default : Box :: new ( RustcDefaultCalls ) ,
88
- version,
89
- verbose,
90
- api_guidelines,
91
- } )
92
- }
93
-
94
- pub fn get_default ( & self ) -> Box < RustcDefaultCalls > {
95
- self . default . clone ( )
96
- }
97
-
98
- pub fn get_version ( & self ) -> & String {
99
- & self . version
100
- }
101
-
102
- pub fn get_api_guidelines ( & self ) -> bool {
103
- self . api_guidelines
104
- }
105
- }
106
-
107
- impl < ' a > CompilerCalls < ' a > for SemVerVerCompilerCalls {
108
- fn early_callback (
109
- & mut self ,
110
- matches : & getopts:: Matches ,
111
- sopts : & config:: Options ,
112
- cfg : & ast:: CrateConfig ,
113
- descriptions : & rustc_errors:: registry:: Registry ,
114
- output : ErrorOutputType ,
115
- ) -> Compilation {
116
- debug ! ( "running rust-semverver early_callback" ) ;
117
- self . default
118
- . early_callback ( matches, sopts, cfg, descriptions, output)
119
- }
120
-
121
- fn no_input (
122
- & mut self ,
123
- matches : & getopts:: Matches ,
124
- sopts : & config:: Options ,
125
- cfg : & ast:: CrateConfig ,
126
- odir : & Option < PathBuf > ,
127
- ofile : & Option < PathBuf > ,
128
- descriptions : & rustc_errors:: registry:: Registry ,
129
- ) -> Option < ( Input , Option < PathBuf > ) > {
130
- debug ! ( "running rust-semverver no_input" ) ;
131
- self . default
132
- . no_input ( matches, sopts, cfg, odir, ofile, descriptions)
133
- }
134
-
135
- fn late_callback (
136
- & mut self ,
137
- trans_crate : & CodegenBackend ,
138
- matches : & getopts:: Matches ,
139
- sess : & Session ,
140
- cstore : & CStore ,
141
- input : & Input ,
142
- odir : & Option < PathBuf > ,
143
- ofile : & Option < PathBuf > ,
144
- ) -> Compilation {
145
- debug ! ( "running rust-semverver late_callback" ) ;
146
- self . default
147
- . late_callback ( trans_crate, matches, sess, cstore, input, odir, ofile)
148
- }
149
-
150
- fn build_controller (
151
- self : Box < Self > ,
152
- sess : & Session ,
153
- matches : & getopts:: Matches ,
154
- ) -> driver:: CompileController < ' a > {
155
- let default = self . get_default ( ) ;
156
- let version = self . get_version ( ) . clone ( ) ;
157
- let api_guidelines = self . get_api_guidelines ( ) ;
158
- let Self { verbose, .. } = * self ;
159
- let mut controller = CompilerCalls :: build_controller ( default, sess, matches) ;
160
- let old_callback = std:: mem:: replace ( & mut controller. after_analysis . callback , box |_| { } ) ;
161
-
162
- controller. after_analysis . callback = box move |state| {
163
- debug ! ( "running rust-semverver after_analysis callback" ) ;
164
- callback ( state, & version, verbose, api_guidelines) ;
165
- debug ! ( "running other after_analysis callback" ) ;
166
- old_callback ( state) ;
167
- } ;
168
- controller. after_analysis . stop = Compilation :: Stop ;
169
-
170
- controller
171
- }
27
+ /// Display semverver version.
28
+ fn show_version ( ) {
29
+ println ! ( env!( "CARGO_PKG_VERSION" ) ) ;
172
30
}
173
31
174
32
/// Main routine.
175
33
///
176
34
/// Find the sysroot before passing our args to the compiler driver, after registering our custom
177
35
/// compiler driver.
178
36
fn main ( ) {
179
- if env_logger:: try_init ( ) . is_err ( ) {
180
- eprintln ! ( "ERROR: could not initialize logger" ) ;
181
- }
37
+ rustc_driver:: init_rustc_env_logger ( ) ;
182
38
183
39
debug ! ( "running rust-semverver compiler driver" ) ;
40
+ exit (
41
+ rustc_driver:: run ( move || {
42
+ use std:: env;
43
+
44
+ if std:: env:: args ( ) . any ( |a| a == "--version" || a == "-V" ) {
45
+ show_version ( ) ;
46
+ exit ( 0 ) ;
47
+ }
48
+
49
+ let sys_root = option_env ! ( "SYSROOT" )
50
+ . map ( String :: from)
51
+ . or_else ( || std:: env:: var ( "SYSROOT" ) . ok ( ) )
52
+ . or_else ( || {
53
+ let home = option_env ! ( "RUSTUP_HOME" ) . or ( option_env ! ( "MULTIRUST_HOME" ) ) ;
54
+ let toolchain = option_env ! ( "RUSTUP_TOOLCHAIN" ) . or ( option_env ! ( "MULTIRUST_TOOLCHAIN" ) ) ;
55
+ home. and_then ( |home| toolchain. map ( |toolchain| format ! ( "{}/toolchains/{}" , home, toolchain) ) )
56
+ } )
57
+ . or_else ( || {
58
+ Command :: new ( "rustc" )
59
+ . arg ( "--print" )
60
+ . arg ( "sysroot" )
61
+ . output ( )
62
+ . ok ( )
63
+ . and_then ( |out| String :: from_utf8 ( out. stdout ) . ok ( ) )
64
+ . map ( |s| s. trim ( ) . to_owned ( ) )
65
+ } )
66
+ . expect ( "need to specify SYSROOT env var during clippy compilation, or use rustup or multirust" ) ;
67
+
68
+ // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
69
+ // We're invoking the compiler programmatically, so we ignore this/
70
+ let mut orig_args: Vec < String > = env:: args ( ) . collect ( ) ;
71
+ if orig_args. len ( ) <= 1 {
72
+ std:: process:: exit ( 1 ) ;
73
+ }
74
+
75
+ if Path :: new ( & orig_args[ 1 ] ) . file_stem ( ) == Some ( "rustc" . as_ref ( ) ) {
76
+ // we still want to be able to invoke it normally though
77
+ orig_args. remove ( 1 ) ;
78
+ }
79
+
80
+ // this conditional check for the --sysroot flag is there so users can call
81
+ // `clippy_driver` directly
82
+ // without having to pass --sysroot or anything
83
+ let args: Vec < String > = if orig_args. iter ( ) . any ( |s| s == "--sysroot" ) {
84
+ orig_args. clone ( )
85
+ } else {
86
+ orig_args
87
+ . clone ( )
88
+ . into_iter ( )
89
+ . chain ( Some ( "--sysroot" . to_owned ( ) ) )
90
+ . chain ( Some ( sys_root) )
91
+ . collect ( )
92
+ } ;
93
+
94
+ let verbose = std:: env:: var ( "RUST_SEMVER_VERBOSE" ) == Ok ( "true" . to_string ( ) ) ;
95
+ let api_guidelines = std:: env:: var ( "RUST_SEMVER_API_GUIDELINES" ) == Ok ( "true" . to_string ( ) ) ;
96
+ let version = if let Ok ( ver) = std:: env:: var ( "RUST_SEMVER_CRATE_VERSION" ) {
97
+ ver
98
+ } else {
99
+ "no_version" . to_owned ( )
100
+ } ;
184
101
185
- let home = option_env ! ( "RUSTUP_HOME" ) ;
186
- let toolchain = option_env ! ( "RUSTUP_TOOLCHAIN" ) ;
187
- let sys_root: PathBuf = if let ( Some ( home) , Some ( toolchain) ) = ( home, toolchain) {
188
- Path :: new ( home) . join ( "toolchains" ) . join ( toolchain)
189
- } else {
190
- option_env ! ( "SYSROOT" )
191
- . map ( |s| s. to_owned ( ) )
192
- . or_else ( || {
193
- Command :: new ( "rustc" )
194
- . args ( & [ "--print" , "sysroot" ] )
195
- . output ( )
196
- . ok ( )
197
- . and_then ( |out| String :: from_utf8 ( out. stdout ) . ok ( ) )
198
- . map ( |s| s. trim ( ) . to_owned ( ) )
199
- } )
200
- . expect ( "need to specify SYSROOT env var during compilation, or use rustup" )
201
- . into ( )
202
- } ;
203
-
204
- assert ! (
205
- sys_root. exists( ) ,
206
- "sysroot \" {}\" does not exist" ,
207
- sys_root. display( )
208
- ) ;
209
-
210
- let result = rustc_driver:: run ( move || {
211
- let args: Vec < String > = if std:: env:: args ( ) . any ( |s| s == "--sysroot" ) {
212
- std:: env:: args ( ) . collect ( )
213
- } else {
214
- std:: env:: args ( )
215
- . chain ( Some ( "--sysroot" . to_owned ( ) ) )
216
- . chain ( Some ( sys_root. to_str ( ) . unwrap ( ) . to_owned ( ) ) )
217
- . collect ( )
218
- } ;
219
-
220
- let version = if let Ok ( ver) = std:: env:: var ( "RUST_SEMVER_CRATE_VERSION" ) {
221
- ver
222
- } else {
223
- "no_version" . to_owned ( )
224
- } ;
225
-
226
- let verbose = std:: env:: var ( "RUST_SEMVER_VERBOSE" ) == Ok ( "true" . to_string ( ) ) ;
227
- let api_guidelines = std:: env:: var ( "RUST_SEMVER_API_GUIDELINES" ) == Ok ( "true" . to_string ( ) ) ;
228
-
229
- let cc = SemVerVerCompilerCalls :: new ( version, verbose, api_guidelines) ;
230
- rustc_driver:: run_compiler ( & args, cc, None , None )
231
- } ) ;
232
-
233
- #[ cfg_attr( feature = "cargo-clippy" , allow( clippy:: cast_possible_truncation) ) ]
234
- std:: process:: exit ( result as i32 ) ;
102
+ let mut controller = CompileController :: basic ( ) ;
103
+
104
+ controller. after_analysis . callback = Box :: new ( move |state| {
105
+ debug ! ( "running rust-semverver after_analysis..." ) ;
106
+ let tcx = state. tcx . unwrap ( ) ;
107
+
108
+ // To select the old and new crates we look at the position of the declaration in the
109
+ // source file. The first one will be the `old` and the other will be `new`. This is
110
+ // unfortunately a bit hacky... See issue #64 for details.
111
+
112
+ let mut crates: Vec < _ > = tcx
113
+ . crates ( )
114
+ . iter ( )
115
+ . flat_map ( |crate_num| {
116
+ let def_id = DefId {
117
+ krate : * crate_num,
118
+ index : CRATE_DEF_INDEX ,
119
+ } ;
120
+
121
+ match * tcx. extern_crate ( def_id) {
122
+ Some ( ExternCrate {
123
+ span, direct : true , ..
124
+ } ) if span. data ( ) . lo . to_usize ( ) > 0 => Some ( ( span. data ( ) . lo . to_usize ( ) , def_id) ) ,
125
+ _ => None ,
126
+ }
127
+ } )
128
+ . collect ( ) ;
129
+
130
+ crates. sort_by_key ( |& ( span_lo, _) | span_lo) ;
131
+
132
+ if let [ ( _, old_def_id) , ( _, new_def_id) ] = * crates. as_slice ( ) {
133
+ debug ! ( "running semver analysis" ) ;
134
+ let changes = run_analysis ( tcx, old_def_id, new_def_id) ;
135
+ changes. output ( tcx. sess , & version, verbose, api_guidelines) ;
136
+ } else {
137
+ tcx. sess . err ( "could not find crate old and new crates" ) ;
138
+ }
139
+
140
+ debug ! ( "running rust-semverver after_analysis finished!" ) ;
141
+ } ) ;
142
+ controller. after_analysis . stop = Compilation :: Stop ;
143
+
144
+ let args = args;
145
+ rustc_driver:: run_compiler ( & args, Box :: new ( controller) , None , None )
146
+ } ) . try_into ( )
147
+ . expect ( "exit code too large" ) ,
148
+ )
235
149
}
0 commit comments