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