Annotation of src/usr.bin/pkg-config/pkg-config, Revision 1.6
1.1 ckuethe 1: #!/usr/bin/perl
1.6 ! ckuethe 2: # $OpenBSD: pkg-config,v 1.5 2006/11/27 23:57:51 ckuethe Exp $
1.1 ckuethe 3:
4: #$CSK: pkgconfig.pl,v 1.39 2006/11/27 16:26:20 ckuethe Exp $
5: # Copyright (c) 2006 Chris Kuethe <ckuethe@openbsd.org>
6: #
7: # Permission to use, copy, modify, and distribute this software for any
8: # purpose with or without fee is hereby granted, provided that the above
9: # copyright notice and this permission notice appear in all copies.
10: #
11: # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12: # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13: # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14: # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15: # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16: # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17: # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18:
19: use strict;
20: use warnings;
21: use Getopt::Long;
1.4 espie 22: use File::Basename;
1.1 ckuethe 23:
24: my @PKGPATH = qw(/usr/local/lib/pkgconfig /usr/X11R6/lib/pkgconfig );
25:
1.4 espie 26: if (defined($ENV{'PKG_CONFIG_PATH'}) && $ENV{'PKG_CONFIG_PATH'}) {
27: push(@PKGPATH, split /:/, $ENV{'PKG_CONFIG_PATH'});
1.1 ckuethe 28: }
29:
30: my $logfile = '';
1.4 espie 31: if (defined($ENV{'PKG_CONFIG_LOGFILE'}) && $ENV{'PKG_CONFIG_LOGFILE'}) {
1.1 ckuethe 32: $logfile = $ENV{'PKG_CONFIG_LOGFILE'};
33: }
34:
35: our $version = 0.19; # pretend to be this version of pkgconfig
36: my $parse_args = 1;
37: my ($deps, $privdeps, $var, $val, $p, $f);
38:
39: our %configs = ();
40: our %mode = ();
41: our $D = 0; # debug flag
42:
43: $/ = undef;
1.4 espie 44: if ($logfile) {
45: open my $L, ">>" . $logfile;
46: print $L '[' . join('] [', $0, @ARGV) . "]\n";
47: close $L;
1.1 ckuethe 48: }
49:
50: # combo arg-parsing and dependency resolution loop. Hopefully when the loop
51: # terminates, we have a full list of packages upon which we depend, and the
52: # right set of compiler and linker flags to use them.
53: #
54: # as each .pc file is loaded, it is stored in %configs, indexed by package
55: # name. this makes it possible to then pull out flags or do substitutions
56: # without having to go back and reload the files from disk
57:
58: Getopt::Long::Configure('no_ignore_case');
59: GetOptions( 'debug' => \$D,
60: 'help' => \&help, #does not return
61: 'usage' => \&help, #does not return
62: 'list-all' => \&do_list, #does not return
63: 'version' => sub { print "$version\n" ; exit(0);} ,
64: 'errors-to-stdout' => sub { $mode{'estdout'} = 1},
65: 'print-errors' => sub { $mode{'printerr'} = 1},
66: 'atleast-pkgconfig-version=s' => \$mode{'minvers'},
67:
68: 'cflags' => sub { $mode{'cflags'} = 3},
69: 'cflags-only-I' => sub { $mode{'cflags'} |= 1},
70: 'cflags-only-other' => sub { $mode{'cflags'} |= 2},
71: 'libs' => sub { $mode{'libs'} = 7},
72: 'libs-only-l' => sub { $mode{'libs'} |= 1},
73: 'libs-only-L' => sub { $mode{'libs'} |= 2},
74: 'libs-only-other' => sub { $mode{'libs'} |= 4},
75: 'exists' => sub { $mode{'exists'} = 1} ,
76: 'static' => sub { $mode{'static'} = 1},
77: 'uninstalled' => sub { $mode{'uninstalled'} = 1},
78: 'atleast-version=s' => \$mode{'atleast-version'},
1.6 ! ckuethe 79: 'modversion:s' => \$mode{'modversion'},
1.1 ckuethe 80: 'variable=s' => \$mode{'variable'}
81: );
82:
1.4 espie 83: print STDERR "\n[" . join('] [', $0, @ARGV) . "]\n" if $D;
84: self_version($mode{'minvers'}) if $mode{'minvers'}; #does not return
1.6 ! ckuethe 85: if (defined $mode{'modversion'}) {
! 86: if ($mode{'modversion'}) {
! 87: do_modversion($mode{'modversion'}) ; #does not return
! 88: } else {
! 89: print $version . "\n";
! 90: exit 0;
! 91: }
! 92: }
1.1 ckuethe 93:
94: $p = join(' ', @ARGV);
95: $p =~ s/\s+/ /g;
96: $p =~ s/^\s//g;
1.4 espie 97: @ARGV = split /\s+/, $p;
1.1 ckuethe 98:
1.4 espie 99: if (defined $mode{'exists'}) {
100: while (@ARGV) {
1.1 ckuethe 101: if ((@ARGV >= 2) && ($ARGV[1] =~ /[<=>]+/) &&
1.4 espie 102: ($ARGV[2] =~ /[0-9\.]+/)) {
103: exit 1 unless versionmatch(@ARGV);
104: shift @ARGV; shift @ARGV; shift @ARGV;
1.1 ckuethe 105: } else {
1.4 espie 106: exit 1 unless pathresolve($ARGV[0]);
107: shift @ARGV;
1.1 ckuethe 108: }
109: }
1.4 espie 110: exit 0;
1.1 ckuethe 111: }
112:
1.4 espie 113: do_variable($ARGV[0], $mode{'variable'}) if $mode{'variable'};
1.1 ckuethe 114:
115: while (@ARGV){
116: $p = $ARGV[0];
117: if ((@ARGV >= 2) && ($ARGV[1] =~ /[<=>]+/) &&
1.4 espie 118: ($ARGV[2] =~ /[0-9\.]+/)) {
119: shift @ARGV;
120: shift @ARGV;
1.1 ckuethe 121: }
1.4 espie 122: shift @ARGV;
1.1 ckuethe 123: $p =~ s/,//g;
1.4 espie 124: unless ($configs{$p}) { # don't reprocess things we've seen
125: print STDERR "processing $p\n" if $D;
126: if ($f = pathresolve($p)) { # locate the .pc file
127: exit 0 if defined $mode{'exists'};
1.1 ckuethe 128:
129: $configs{$p} = slurp($f); # load the config
130: $deps = '';
1.4 espie 131: if ($configs{$p} =~ /\bRequires: +(\w.+?)\n/) {
1.1 ckuethe 132: $deps = $1;
133: # XXX how should i handle versions?
134: $deps =~ s/[<>=]+\s*[0-9\.]+\s*//;
135: $deps =~ tr/,/ /;
136: }
137: print STDERR "package $p requires '$deps'\n"
1.4 espie 138: if $D && $deps;
139: push(@ARGV, split /\s+/, $deps) if $deps;
1.1 ckuethe 140:
141: $privdeps = '';
1.4 espie 142: if ($configs{$p} =~ /\bRequires\.private: +(\w.+?)\n/) {
1.1 ckuethe 143: $privdeps = $1;
144: # XXX how should i handle versions?
145: $privdeps =~ s/[<>=]+\s*[0-9\.]+\s*//;
146: }
147: print STDERR "package $p requires (private) '" .
1.4 espie 148: $privdeps . "'\n" if $D && $privdeps;
149: push(@ARGV, split /\s+/, $privdeps) if $privdeps;
1.1 ckuethe 150:
151: } else {
1.4 espie 152: warn "can't find $p\n";
153: exit 1;
1.1 ckuethe 154: }
155: }
156: }
157:
1.4 espie 158: do_cflags() if $mode{'cflags'};
159: do_libs() if $mode{'libs'};
1.1 ckuethe 160:
1.4 espie 161: exit 0;
1.1 ckuethe 162:
163: ###########################################################################
164:
165: # look for the .pc file in each of the PKGPATH elements. Return the path or
166: # undef if it's not there
1.4 espie 167: sub pathresolve
168: {
169: my ($p) = @_;
170:
171: foreach my $d (@PKGPATH) {
172: $f = "$d/$p.pc";
173: print STDERR "pathresolve($p) looking in $f\n" if $D;
174: last if -f $f;
1.1 ckuethe 175: $f = undef;
176: }
177: return $f;
178: }
179:
180:
181: # Given a filename, return its contents. Also do variable substitutions.
1.4 espie 182: sub slurp
183: {
184: my ($f) = @_;
185:
186: open my $F, '<', $f or return undef;
187: print STDERR "slurp($f) OK\n" if $D;
188: $f = <$F>;
189: close $F;
1.1 ckuethe 190: $f = varsub($f);
191: return $f;
192: }
193:
194: # Do variable substitutions, so if "target=x11" is present (for example),
195: # any lines referring to $target are filled in properly.
1.4 espie 196: sub varsub
197: {
198: my ($buf) = @_;
199:
1.1 ckuethe 200: my ($var, $val);
201:
1.4 espie 202: while ($buf =~ /\${(\w+)}/gsm) {
1.1 ckuethe 203: $var = $1;
1.4 espie 204: if ($buf =~ /${var}=(.+?)\n/s) {
1.1 ckuethe 205: $val = $1;
206: $buf =~ s/\${$var}/$val/g;
207: }
208: }
209: return $buf;
210: }
211:
212: #if the variable option is set, pull out the named variable
1.4 espie 213: sub do_variable
214: {
1.1 ckuethe 215: my ($p, $v, undef) = @_;
216: my ($f);
217:
1.4 espie 218: exit 1 unless $f = pathresolve($p);
219: exit 1 unless $f = slurp($f);
1.1 ckuethe 220:
1.4 espie 221: exit 1 unless $f =~ /\b${v}=(.+?)\n/;
1.1 ckuethe 222: print "$1\n";
1.4 espie 223: exit 0;
1.1 ckuethe 224: }
225:
226: #if the modversion option is set, pull out the compiler flags
1.4 espie 227: sub do_modversion
228: {
1.1 ckuethe 229: my ($p, undef) = @_;
230: my ($f);
1.6 ! ckuethe 231:
! 232: print "\$p ='$p'\n";
1.1 ckuethe 233:
1.4 espie 234: exit 1 unless $f = pathresolve($p);
235: exit 1 unless $f = slurp($f);
1.1 ckuethe 236:
1.4 espie 237: exit 1 unless $f =~ /\bVersion:\s+(.+?)\n/;
1.1 ckuethe 238: print "$1\n";
1.4 espie 239: exit 0;
1.1 ckuethe 240: }
241:
242: #if the cflags option is set, pull out the compiler flags
1.4 espie 243: sub do_cflags
244: {
1.1 ckuethe 245: my %words; # store them as a hash to get de-duplicating
246: my @out;
247:
1.4 espie 248: foreach my $p (keys %configs) {
249: if ($configs{$p} =~ /\bCflags:\s+(.+?)\n/) {
250: foreach my $q (split /\s+/, $1) {
251: $words{$q}=1;
252: }
1.1 ckuethe 253: }
254: }
1.4 espie 255: foreach my $k (sort keys %words) {
256: push(@out, $k) if $k =~ /^-I/ && ($mode{'cflags'} & 1);
257: push(@out, $k) if $k =~ /^-[^I]/ && ($mode{'cflags'} & 2);
1.1 ckuethe 258: }
1.4 espie 259: print join(' ', @out), "\n";
260: return undef;
1.1 ckuethe 261: }
262:
263: #if the lib option is set, pull out the linker flags
1.4 espie 264: sub do_libs
265: {
1.1 ckuethe 266: my %words; # store them as a hash to get de-duplicating
267: my @out;
268:
1.4 espie 269: foreach my $p (keys %configs) {
270: if ($configs{$p} =~ /\bLibs:\s+(.+?)\n/) {
271: foreach my $q (split /\s+/, $1) {
272: $words{$q}=1;
273: }
1.1 ckuethe 274: }
275: }
1.4 espie 276: foreach my $k (sort keys %words) {
277: push(@out, $k) if $k =~ /^-l/ && ($mode{'libs'} & 1);
278: push(@out, $k) if $k =~ /^-L/ && ($mode{'libs'} & 2);
279: push(@out, $k) if $k =~ /^-[^lL]/ && ($mode{'libs'} & 4);
1.1 ckuethe 280: }
1.4 espie 281: print join(' ', @out), "\n";
282: return undef;
1.1 ckuethe 283: }
284:
285: #list all packages
1.4 espie 286: sub do_list
287: {
1.1 ckuethe 288: my ($p, $x, $y, @files, $fname, $name);
1.4 espie 289: foreach my $p (@PKGPATH) {
290: push(@files, <$p/*.pc>);
291: }
1.1 ckuethe 292:
293: # Scan the lengths of the package names so I can make a format
294: # string to line the list up just like the real pkgconfig does.
295: $x = 0;
1.4 espie 296: foreach my $f (@files) {
297: $fname = basename($f, '.pc');
298: $y = length $fname;
1.1 ckuethe 299: $x = (($y > $x) ? $y : $x);
300: }
301: $x *= -1;
302:
1.4 espie 303: foreach my $f (@files) {
304: $p = slurp($f);
305: $fname = basename($f, '.pc');
306: if ($p =~ /Name: (\w[^\n]+)\n/gm) {
1.1 ckuethe 307: $name = $1;
1.4 espie 308: if ($p =~ /Description:\s+(\w[^\n]+)\n/gm) {
1.1 ckuethe 309: printf("%${x}s %s - %s\n", $fname, $name, $1);
310: }
311: }
312: }
1.4 espie 313: exit 0;
1.1 ckuethe 314: }
315:
1.4 espie 316: sub help
317: {
1.1 ckuethe 318: print <<EOF
319: Usage: $0 [options]
320: --debug - turn on debugging output
321: --help - this message
322: --usage - this message
323: --list-all - show all packages that $0 can find
324: --atleast-pkgconfig-version [version] - require a certain version of pkgconfig
325: --variable var package - return the definition of <var> in <package>
326: --cflags package [versionspec] [package [versionspec]]
327: --cflags-only-I - only output -Iincludepath flags
328: --cflags-only-other - only output flags that are not -I
329: --libs package [versionspec] [package [versionspec]]
330: --libs-only-l - only output -llib flags
331: --libs-only-L - only output -Llibpath flags
332: --libs-only-other - only output flags that are not -l or -L
333: --exists package [versionspec] [package [versionspec]]
334: --uninstalled - allow for uninstalled versions to be used
335: EOF
336: ;
1.4 espie 337: exit 1;
1.1 ckuethe 338: }
339:
340: # do we meet/beat the version the caller requested?
1.4 espie 341: sub self_version
342: {
343: my ($v) = @_;
344: my (@a, @b);
345:
346: @a = split /\./, $v;
347: @b = split /\./, $version;
1.1 ckuethe 348:
1.4 espie 349: if (($b[0] >= $a[0]) && ($b[1] >= $a[1])) {
350: exit 0;
1.1 ckuethe 351: } else {
1.4 espie 352: exit 1;
1.1 ckuethe 353: }
354: }
355:
356: # got a package meeting the requested specific version?
1.4 espie 357: sub versionmatch
358: {
1.1 ckuethe 359: my ($pname, $op, $ver, undef) = @_;
360: my (@want, @inst, $m, $f);
361:
1.4 espie 362: print STDERR "pname = '$pname'\n" if $D;
1.1 ckuethe 363: # can't possibly match if we can't find the file
1.4 espie 364: return 0 unless $f = pathresolve($pname);
1.1 ckuethe 365: # load the file
366: $configs{$pname} = slurp($f);
367: # can't possibly match if we can't find the version string
1.4 espie 368: return 0 unless $configs{$pname} =~ /Version: ([0-9\.]+)\n/gm;
1.1 ckuethe 369:
1.4 espie 370: print "comparing $ver (wanted) to $1 (installed)\n" if $D;
371: @inst = split /\./, $1;
372: @want = split /\./, $ver;
1.1 ckuethe 373:
1.4 espie 374: while (@inst && @want) { #so long as both lists have something
1.1 ckuethe 375: # bail if the requested version element beats existing
1.4 espie 376: return 1 if $inst[0] > $want[0];
377: return 0 if $inst[0] < $want[0];
378: shift @inst; shift @want;
1.1 ckuethe 379: }
380: # the version at least equals the requested. if the requested
381: # version has some micropatchlevel beyond the existing version,
382: # return failure
1.4 espie 383: return 0 if @want;
1.1 ckuethe 384: # and after all that, the version is good enough
385: return 1;
386: }