22
33use crate :: build:: CApiConfig ;
44use crate :: install:: InstallPaths ;
5- use std:: path:: { Component , Path , PathBuf } ;
5+ use log:: warn;
6+ use pkg_config;
7+ use std:: {
8+ collections:: HashSet ,
9+ path:: { Component , Path , PathBuf } ,
10+ } ;
611
712fn canonicalize < P : AsRef < Path > > ( path : P ) -> String {
813 let mut stack = Vec :: with_capacity ( 16 ) ;
@@ -57,6 +62,12 @@ fn canonicalize<P: AsRef<Path>>(path: P) -> String {
5762 }
5863}
5964
65+ #[ derive( Debug , Clone ) ]
66+ struct PkgConfigDedupInformation {
67+ requires : Vec < pkg_config:: Library > ,
68+ requires_private : Vec < pkg_config:: Library > ,
69+ }
70+
6071#[ derive( Debug , Clone ) ]
6172pub struct PkgConfig {
6273 prefix : PathBuf ,
@@ -77,6 +88,8 @@ pub struct PkgConfig {
7788 cflags : Vec < String > ,
7889
7990 conflicts : Vec < String > ,
91+
92+ dedup : PkgConfigDedupInformation ,
8093}
8194
8295impl PkgConfig {
@@ -99,19 +112,61 @@ impl PkgConfig {
99112 Some ( reqs) => reqs. split ( ',' ) . map ( |s| s. trim ( ) . to_string ( ) ) . collect ( ) ,
100113 _ => Vec :: new ( ) ,
101114 } ;
115+
116+ let requires_libs = {
117+ let cfg = {
118+ let mut c = pkg_config:: Config :: new ( ) ;
119+ // This is not sinkholed by cargo-c
120+ c. env_metadata ( false ) ;
121+ c. cargo_metadata ( false ) ;
122+
123+ c
124+ } ;
125+
126+ // TODO: log which probe fails
127+ requires
128+ . iter ( )
129+ . flat_map ( |req| {
130+ let c = cfg. probe ( req) ;
131+ if c. is_err ( ) {
132+ warn ! ( "WARNING: library not found: {}" , c. as_ref( ) . err( ) . unwrap( ) )
133+ }
134+ c
135+ } )
136+ . collect :: < Vec < _ > > ( )
137+ } ;
138+
102139 let requires_private = match & capi_config. pkg_config . requires_private {
103140 Some ( reqs) => reqs. split ( ',' ) . map ( |s| s. trim ( ) . to_string ( ) ) . collect ( ) ,
104141 _ => Vec :: new ( ) ,
105142 } ;
106143
144+ let requires_private_libs = {
145+ let cfg = {
146+ let mut c = pkg_config:: Config :: new ( ) ;
147+ c. statik ( true ) ;
148+ // This is not sinkholed by cargo-c
149+ c. env_metadata ( false ) ;
150+ c. cargo_metadata ( false ) ;
151+
152+ c
153+ } ;
154+
155+ // TODO: log which probe fails
156+ requires_private
157+ . iter ( )
158+ . flat_map ( |req| cfg. probe ( req) )
159+ . collect :: < Vec < _ > > ( )
160+ } ;
161+
107162 let mut libdir = PathBuf :: new ( ) ;
108163 libdir. push ( "${libdir}" ) ;
109164 if let Some ( subdir) = & capi_config. library . install_subdir {
110165 libdir. push ( subdir) ;
111166 }
112167
113168 let libs = vec ! [
114- format!( "-L{}" , libdir. display( ) ) ,
169+ format!( "-L{}" , canonicalize ( libdir. display( ) . to_string ( ) ) ) ,
115170 format!( "-l{}" , capi_config. library. name) ,
116171 ] ;
117172
@@ -146,6 +201,11 @@ impl PkgConfig {
146201 cflags : vec ! [ cflags] ,
147202
148203 conflicts : Vec :: new ( ) ,
204+
205+ dedup : PkgConfigDedupInformation {
206+ requires : requires_libs,
207+ requires_private : requires_private_libs,
208+ } ,
149209 }
150210 }
151211
@@ -236,7 +296,81 @@ impl PkgConfig {
236296 self . render_help ( String :: with_capacity ( 1024 ) ) . unwrap ( )
237297 }
238298
299+ fn get_libs_cflags ( arg : & [ pkg_config:: Library ] ) -> ( HashSet < String > , HashSet < String > ) {
300+ let mut libs: HashSet < String > = HashSet :: new ( ) ;
301+ let mut cflags: HashSet < String > = HashSet :: new ( ) ;
302+
303+ for lib in arg. iter ( ) {
304+ for lib in lib. include_paths . iter ( ) {
305+ libs. insert ( format ! ( "-I{}" , lib. to_string_lossy( ) ) ) ;
306+ }
307+ for lib in lib. link_files . iter ( ) {
308+ libs. insert ( lib. to_string_lossy ( ) . to_string ( ) ) ;
309+ }
310+ for lib in lib. libs . iter ( ) {
311+ libs. insert ( format ! ( "-l{}" , lib) ) ;
312+ }
313+ for lib in lib. link_paths . iter ( ) {
314+ libs. insert ( format ! ( "-L{}" , lib. to_string_lossy( ) ) ) ;
315+ }
316+ for lib in lib. frameworks . iter ( ) {
317+ libs. insert ( format ! ( "-framework {}" , lib) ) ;
318+ }
319+ for lib in lib. framework_paths . iter ( ) {
320+ libs. insert ( format ! ( "-F{}" , lib. to_string_lossy( ) ) ) ;
321+ }
322+ for lib in lib. defines . iter ( ) {
323+ let v = match lib. 1 {
324+ Some ( v) => format ! ( "-D{}={}" , lib. 0 , v) ,
325+ None => format ! ( "D{}" , lib. 0 ) ,
326+ } ;
327+ libs. insert ( v) ;
328+ }
329+ for lib in lib. ld_args . iter ( ) {
330+ cflags. insert ( format ! ( "-Wl,{}" , lib. join( "," ) ) ) ;
331+ }
332+ }
333+
334+ ( libs, cflags)
335+ }
336+
337+ fn dedup_flags ( known_flags : & HashSet < String > , flags : & [ String ] ) -> String {
338+ flags
339+ . iter ( )
340+ . filter ( |& lib| {
341+ !known_flags. contains ( lib) || lib. starts_with ( "-Wl," ) || lib. starts_with ( "/LINK" )
342+ } )
343+ . map ( |lib| lib. as_str ( ) )
344+ . collect :: < Vec < _ > > ( )
345+ . join ( " " )
346+ }
347+
239348 fn render_help < W : core:: fmt:: Write > ( & self , mut w : W ) -> Result < W , core:: fmt:: Error > {
349+ // Dedup
350+ // What libs are already known here?
351+ let ( dedup_cflags, dedup_libs, dedup_libs_private) = {
352+ let ( known_libs, known_cflags) = PkgConfig :: get_libs_cflags ( & self . dedup . requires ) ;
353+
354+ let cflags = PkgConfig :: dedup_flags ( & known_cflags, & self . cflags ) ;
355+ let libs = PkgConfig :: dedup_flags ( & known_libs, & self . libs ) ;
356+
357+ // FIXME: There's no Cflags.private?
358+ let ( mut known_libs_private, _) =
359+ PkgConfig :: get_libs_cflags ( & self . dedup . requires_private ) ;
360+ // Need to be deduplicated against libs too!
361+ for i in & self . libs {
362+ known_libs_private. insert ( i. clone ( ) ) ;
363+ }
364+
365+ let libs_private = PkgConfig :: dedup_flags ( & known_libs_private, & self . libs_private ) ;
366+
367+ println ! (
368+ "{:?} => {:?} => {:?}" ,
369+ known_libs_private, self . libs_private, libs_private
370+ ) ;
371+ ( cflags, libs, libs_private)
372+ } ;
373+
240374 writeln ! ( w, "prefix={}" , canonicalize( & self . prefix) ) ?;
241375 writeln ! ( w, "exec_prefix={}" , canonicalize( & self . exec_prefix) ) ?;
242376 writeln ! ( w, "libdir={}" , canonicalize( & self . libdir) ) ?;
@@ -247,11 +381,11 @@ impl PkgConfig {
247381 writeln ! ( w, "Name: {}" , self . name) ?;
248382 writeln ! ( w, "Description: {}" , self . description. replace( '\n' , " " ) ) ?; // avoid endlines
249383 writeln ! ( w, "Version: {}" , self . version) ?;
250- writeln ! ( w, "Libs: {}" , self . libs . join ( " " ) ) ?;
251- writeln ! ( w, "Cflags: {}" , self . cflags . join ( " " ) ) ?;
384+ writeln ! ( w, "Libs: {}" , dedup_libs ) ?;
385+ writeln ! ( w, "Cflags: {}" , dedup_cflags ) ?;
252386
253387 if !self . libs_private . is_empty ( ) {
254- writeln ! ( w, "Libs.private: {}" , self . libs_private . join ( " " ) ) ?;
388+ writeln ! ( w, "Libs.private: {}" , dedup_libs_private ) ?;
255389 }
256390
257391 if !self . requires . is_empty ( ) {
0 commit comments