1010
1111//! A helper class for dealing with static archives
1212
13- use std:: env;
1413use std:: ffi:: { CString , CStr , OsString } ;
15- use std:: fs:: { self , File } ;
16- use std:: io:: prelude:: * ;
1714use std:: io;
1815use std:: mem;
1916use std:: path:: { Path , PathBuf } ;
20- use std:: process:: { Command , Output , Stdio } ;
2117use std:: ptr;
2218use std:: str;
2319
2420use libc;
2521use llvm:: archive_ro:: { ArchiveRO , Child } ;
2622use llvm:: { self , ArchiveKind } ;
2723use rustc:: session:: Session ;
28- use rustc_back:: tempdir:: TempDir ;
2924
3025pub struct ArchiveConfig < ' a > {
3126 pub sess : & ' a Session ,
@@ -41,7 +36,6 @@ pub struct ArchiveConfig<'a> {
4136#[ must_use = "must call build() to finish building the archive" ]
4237pub struct ArchiveBuilder < ' a > {
4338 config : ArchiveConfig < ' a > ,
44- work_dir : TempDir ,
4539 removals : Vec < String > ,
4640 additions : Vec < Addition > ,
4741 should_update_symbols : bool ,
@@ -55,17 +49,10 @@ enum Addition {
5549 } ,
5650 Archive {
5751 archive : ArchiveRO ,
58- archive_name : String ,
5952 skip : Box < FnMut ( & str ) -> bool > ,
6053 } ,
6154}
6255
63- enum Action < ' a > {
64- Remove ( & ' a [ String ] ) ,
65- AddObjects ( & ' a [ & ' a PathBuf ] , bool ) ,
66- UpdateSymbols ,
67- }
68-
6956pub fn find_library ( name : & str , search_paths : & [ PathBuf ] , sess : & Session )
7057 -> PathBuf {
7158 // On Windows, static libraries sometimes show up as libfoo.a and other
@@ -102,7 +89,6 @@ impl<'a> ArchiveBuilder<'a> {
10289 pub fn new ( config : ArchiveConfig < ' a > ) -> ArchiveBuilder < ' a > {
10390 ArchiveBuilder {
10491 config : config,
105- work_dir : TempDir :: new ( "rsar" ) . unwrap ( ) ,
10692 removals : Vec :: new ( ) ,
10793 additions : Vec :: new ( ) ,
10894 should_update_symbols : false ,
@@ -148,7 +134,7 @@ impl<'a> ArchiveBuilder<'a> {
148134 pub fn add_native_library ( & mut self , name : & str ) {
149135 let location = find_library ( name, & self . config . lib_search_paths ,
150136 self . config . sess ) ;
151- self . add_archive ( & location, name , |_| false ) . unwrap_or_else ( |e| {
137+ self . add_archive ( & location, |_| false ) . unwrap_or_else ( |e| {
152138 self . config . sess . fatal ( & format ! ( "failed to add native library {}: {}" ,
153139 location. to_string_lossy( ) , e) ) ;
154140 } ) ;
@@ -172,14 +158,14 @@ impl<'a> ArchiveBuilder<'a> {
172158 let metadata_filename =
173159 self . config . sess . cstore . metadata_filename ( ) . to_owned ( ) ;
174160
175- self . add_archive ( rlib, & name [ .. ] , move |fname : & str | {
161+ self . add_archive ( rlib, move |fname : & str | {
176162 let skip_obj = lto && fname. starts_with ( & obj_start)
177163 && fname. ends_with ( ".o" ) ;
178164 skip_obj || fname. ends_with ( bc_ext) || fname == metadata_filename
179165 } )
180166 }
181167
182- fn add_archive < F > ( & mut self , archive : & Path , name : & str , skip : F )
168+ fn add_archive < F > ( & mut self , archive : & Path , skip : F )
183169 -> io:: Result < ( ) >
184170 where F : FnMut ( & str ) -> bool + ' static
185171 {
@@ -190,7 +176,6 @@ impl<'a> ArchiveBuilder<'a> {
190176 } ;
191177 self . additions . push ( Addition :: Archive {
192178 archive : archive,
193- archive_name : name. to_string ( ) ,
194179 skip : Box :: new ( skip) ,
195180 } ) ;
196181 Ok ( ( ) )
@@ -214,234 +199,23 @@ impl<'a> ArchiveBuilder<'a> {
214199 /// Combine the provided files, rlibs, and native libraries into a single
215200 /// `Archive`.
216201 pub fn build ( & mut self ) {
217- let res = match self . llvm_archive_kind ( ) {
218- Some ( kind) => self . build_with_llvm ( kind) ,
219- None => self . build_with_ar_cmd ( ) ,
220- } ;
221- if let Err ( e) = res {
222- self . config . sess . fatal ( & format ! ( "failed to build archive: {}" , e) ) ;
223- }
224- }
225-
226- pub fn llvm_archive_kind ( & self ) -> Option < ArchiveKind > {
227- if unsafe { llvm:: LLVMVersionMinor ( ) < 7 } {
228- return None
229- }
230-
231- // Currently LLVM only supports writing archives in the 'gnu' format.
232- match & self . config . sess . target . target . options . archive_format [ ..] {
233- "gnu" => Some ( ArchiveKind :: K_GNU ) ,
234- "mips64" => Some ( ArchiveKind :: K_MIPS64 ) ,
235- "bsd" => Some ( ArchiveKind :: K_BSD ) ,
236- "coff" => Some ( ArchiveKind :: K_COFF ) ,
237- _ => None ,
238- }
239- }
240-
241- pub fn using_llvm ( & self ) -> bool {
242- self . llvm_archive_kind ( ) . is_some ( )
243- }
244-
245- fn build_with_ar_cmd ( & mut self ) -> io:: Result < ( ) > {
246- let removals = mem:: replace ( & mut self . removals , Vec :: new ( ) ) ;
247- let additions = mem:: replace ( & mut self . additions , Vec :: new ( ) ) ;
248- let should_update_symbols = mem:: replace ( & mut self . should_update_symbols ,
249- false ) ;
250-
251- // Don't use fs::copy because libs may be installed as read-only and we
252- // want to modify this archive, so we use `io::copy` to not preserve
253- // permission bits.
254- if let Some ( ref s) = self . config . src {
255- io:: copy ( & mut File :: open ( s) ?,
256- & mut File :: create ( & self . config . dst ) ?) ?;
257- }
258-
259- if removals. len ( ) > 0 {
260- self . run ( None , Action :: Remove ( & removals) ) ;
261- }
262-
263- let mut members = Vec :: new ( ) ;
264- for addition in additions {
265- match addition {
266- Addition :: File { path, name_in_archive } => {
267- let dst = self . work_dir . path ( ) . join ( & name_in_archive) ;
268- fs:: copy ( & path, & dst) ?;
269- members. push ( PathBuf :: from ( name_in_archive) ) ;
270- }
271- Addition :: Archive { archive, archive_name, mut skip } => {
272- self . add_archive_members ( & mut members, archive,
273- & archive_name, & mut * skip) ?;
274- }
275- }
276- }
277-
278- // Get an absolute path to the destination, so `ar` will work even
279- // though we run it from `self.work_dir`.
280- let mut objects = Vec :: new ( ) ;
281- let mut total_len = self . config . dst . to_string_lossy ( ) . len ( ) ;
282-
283- if members. is_empty ( ) {
284- if should_update_symbols {
285- self . run ( Some ( self . work_dir . path ( ) ) , Action :: UpdateSymbols ) ;
286- }
287- return Ok ( ( ) )
288- }
289-
290- // Don't allow the total size of `args` to grow beyond 32,000 bytes.
291- // Windows will raise an error if the argument string is longer than
292- // 32,768, and we leave a bit of extra space for the program name.
293- const ARG_LENGTH_LIMIT : usize = 32_000 ;
294-
295- for member_name in & members {
296- let len = member_name. to_string_lossy ( ) . len ( ) ;
297-
298- // `len + 1` to account for the space that's inserted before each
299- // argument. (Windows passes command-line arguments as a single
300- // string, not an array of strings.)
301- if total_len + len + 1 > ARG_LENGTH_LIMIT {
302- // Add the archive members seen so far, without updating the
303- // symbol table.
304- self . run ( Some ( self . work_dir . path ( ) ) ,
305- Action :: AddObjects ( & objects, false ) ) ;
306-
307- objects. clear ( ) ;
308- total_len = self . config . dst . to_string_lossy ( ) . len ( ) ;
309- }
310-
311- objects. push ( member_name) ;
312- total_len += len + 1 ;
313- }
314-
315- // Add the remaining archive members, and update the symbol table if
316- // necessary.
317- self . run ( Some ( self . work_dir . path ( ) ) ,
318- Action :: AddObjects ( & objects, should_update_symbols) ) ;
319- Ok ( ( ) )
320- }
321-
322- fn add_archive_members ( & mut self , members : & mut Vec < PathBuf > ,
323- archive : ArchiveRO , name : & str ,
324- skip : & mut FnMut ( & str ) -> bool ) -> io:: Result < ( ) > {
325- // Next, we must rename all of the inputs to "guaranteed unique names".
326- // We write each file into `self.work_dir` under its new unique name.
327- // The reason for this renaming is that archives are keyed off the name
328- // of the files, so if two files have the same name they will override
329- // one another in the archive (bad).
330- //
331- // We skip any files explicitly desired for skipping, and we also skip
332- // all SYMDEF files as these are just magical placeholders which get
333- // re-created when we make a new archive anyway.
334- for file in archive. iter ( ) {
335- let file = file. map_err ( string_to_io_error) ?;
336- if !is_relevant_child ( & file) {
337- continue
338- }
339- let filename = file. name ( ) . unwrap ( ) ;
340- if skip ( filename) {
341- continue
202+ let kind = match self . llvm_archive_kind ( ) {
203+ Ok ( kind) => kind,
204+ Err ( kind) => {
205+ self . config . sess . fatal ( & format ! ( "Don't know how to build archive of type: {}" ,
206+ kind) ) ;
342207 }
343- let filename = Path :: new ( filename) . file_name ( ) . unwrap ( )
344- . to_str ( ) . unwrap ( ) ;
345-
346- // Archives on unix systems typically do not have slashes in
347- // filenames as the `ar` utility generally only uses the last
348- // component of a path for the filename list in the archive. On
349- // Windows, however, archives assembled with `lib.exe` will preserve
350- // the full path to the file that was placed in the archive,
351- // including path separators.
352- //
353- // The code below is munging paths so it'll go wrong pretty quickly
354- // if there's some unexpected slashes in the filename, so here we
355- // just chop off everything but the filename component. Note that
356- // this can cause duplicate filenames, but that's also handled below
357- // as well.
358- let filename = Path :: new ( filename) . file_name ( ) . unwrap ( )
359- . to_str ( ) . unwrap ( ) ;
360-
361- // An archive can contain files of the same name multiple times, so
362- // we need to be sure to not have them overwrite one another when we
363- // extract them. Consequently we need to find a truly unique file
364- // name for us!
365- let mut new_filename = String :: new ( ) ;
366- for n in 0 .. {
367- let n = if n == 0 { String :: new ( ) } else { format ! ( "-{}" , n) } ;
368- new_filename = format ! ( "r{}-{}-{}" , n, name, filename) ;
369-
370- // LLDB (as mentioned in back::link) crashes on filenames of
371- // exactly
372- // 16 bytes in length. If we're including an object file with
373- // exactly 16-bytes of characters, give it some prefix so
374- // that it's not 16 bytes.
375- new_filename = if new_filename. len ( ) == 16 {
376- format ! ( "lldb-fix-{}" , new_filename)
377- } else {
378- new_filename
379- } ;
380-
381- let present = members. iter ( ) . filter_map ( |p| {
382- p. file_name ( ) . and_then ( |f| f. to_str ( ) )
383- } ) . any ( |s| s == new_filename) ;
384- if !present {
385- break
386- }
387- }
388- let dst = self . work_dir . path ( ) . join ( & new_filename) ;
389- File :: create ( & dst) ?. write_all ( file. data ( ) ) ?;
390- members. push ( PathBuf :: from ( new_filename) ) ;
391- }
392- Ok ( ( ) )
393- }
394-
395- fn run ( & self , cwd : Option < & Path > , action : Action ) -> Output {
396- let abs_dst = env:: current_dir ( ) . unwrap ( ) . join ( & self . config . dst ) ;
397- let ar = & self . config . ar_prog ;
398- let mut cmd = Command :: new ( ar) ;
399- cmd. env ( "PATH" , & self . config . command_path ) ;
400- cmd. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: piped ( ) ) ;
401- self . prepare_ar_action ( & mut cmd, & abs_dst, action) ;
402- info ! ( "{:?}" , cmd) ;
208+ } ;
403209
404- if let Some ( p) = cwd {
405- cmd. current_dir ( p) ;
406- info ! ( "inside {:?}" , p. display( ) ) ;
210+ if let Err ( e) = self . build_with_llvm ( kind) {
211+ self . config . sess . fatal ( & format ! ( "failed to build archive: {}" , e) ) ;
407212 }
408213
409- let sess = & self . config . sess ;
410- match cmd. spawn ( ) {
411- Ok ( prog) => {
412- let o = prog. wait_with_output ( ) . unwrap ( ) ;
413- if !o. status . success ( ) {
414- sess. struct_err ( & format ! ( "{:?} failed with: {}" , cmd, o. status) )
415- . note ( & format ! ( "stdout ---\n {}" ,
416- str :: from_utf8( & o. stdout) . unwrap( ) ) )
417- . note ( & format ! ( "stderr ---\n {}" ,
418- str :: from_utf8( & o. stderr) . unwrap( ) ) )
419- . emit ( ) ;
420- sess. abort_if_errors ( ) ;
421- }
422- o
423- } ,
424- Err ( e) => {
425- sess. fatal ( & format ! ( "could not exec `{}`: {}" ,
426- self . config. ar_prog, e) ) ;
427- }
428- }
429214 }
430215
431- fn prepare_ar_action ( & self , cmd : & mut Command , dst : & Path , action : Action ) {
432- match action {
433- Action :: Remove ( files) => {
434- cmd. arg ( "d" ) . arg ( dst) . args ( files) ;
435- }
436- Action :: AddObjects ( objs, update_symbols) => {
437- cmd. arg ( if update_symbols { "crs" } else { "crS" } )
438- . arg ( dst)
439- . args ( objs) ;
440- }
441- Action :: UpdateSymbols => {
442- cmd. arg ( "s" ) . arg ( dst) ;
443- }
444- }
216+ fn llvm_archive_kind ( & self ) -> Result < ArchiveKind , & str > {
217+ let kind = & self . config . sess . target . target . options . archive_format [ ..] ;
218+ kind. parse ( ) . map_err ( |_| kind)
445219 }
446220
447221 fn build_with_llvm ( & mut self , kind : ArchiveKind ) -> io:: Result < ( ) > {
@@ -480,7 +254,7 @@ impl<'a> ArchiveBuilder<'a> {
480254 strings. push ( path) ;
481255 strings. push ( name) ;
482256 }
483- Addition :: Archive { archive, archive_name : _ , mut skip } => {
257+ Addition :: Archive { archive, mut skip } => {
484258 for child in archive. iter ( ) {
485259 let child = child. map_err ( string_to_io_error) ?;
486260 if !is_relevant_child ( & child) {
0 commit comments