Annotation of src/usr.bin/pkg-config/pkg-config, Revision 1.8
1.1 ckuethe 1: #!/usr/bin/perl
1.8 ! ckuethe 2: # $OpenBSD: pkg-config,v 1.7 2006/11/28 01:54:14 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);
1.6 ckuethe 235:
236: print "\$p ='$p'\n";
1.1 ckuethe 237:
1.4 espie 238: exit 1 unless $f = pathresolve($p);
239: exit 1 unless $f = slurp($f);
1.1 ckuethe 240:
1.4 espie 241: exit 1 unless $f =~ /\bVersion:\s+(.+?)\n/;
1.1 ckuethe 242: print "$1\n";
1.4 espie 243: exit 0;
1.1 ckuethe 244: }
245:
246: #if the cflags option is set, pull out the compiler flags
1.4 espie 247: sub do_cflags
248: {
1.1 ckuethe 249: my %words; # store them as a hash to get de-duplicating
250: my @out;
251:
1.4 espie 252: foreach my $p (keys %configs) {
253: if ($configs{$p} =~ /\bCflags:\s+(.+?)\n/) {
254: foreach my $q (split /\s+/, $1) {
255: $words{$q}=1;
256: }
1.1 ckuethe 257: }
258: }
1.4 espie 259: foreach my $k (sort keys %words) {
260: push(@out, $k) if $k =~ /^-I/ && ($mode{'cflags'} & 1);
261: push(@out, $k) if $k =~ /^-[^I]/ && ($mode{'cflags'} & 2);
1.1 ckuethe 262: }
1.4 espie 263: print join(' ', @out), "\n";
264: return undef;
1.1 ckuethe 265: }
266:
267: #if the lib option is set, pull out the linker flags
1.4 espie 268: sub do_libs
269: {
1.1 ckuethe 270: my %words; # store them as a hash to get de-duplicating
271: my @out;
272:
1.4 espie 273: foreach my $p (keys %configs) {
274: if ($configs{$p} =~ /\bLibs:\s+(.+?)\n/) {
275: foreach my $q (split /\s+/, $1) {
276: $words{$q}=1;
277: }
1.1 ckuethe 278: }
279: }
1.4 espie 280: foreach my $k (sort keys %words) {
281: push(@out, $k) if $k =~ /^-l/ && ($mode{'libs'} & 1);
282: push(@out, $k) if $k =~ /^-L/ && ($mode{'libs'} & 2);
283: push(@out, $k) if $k =~ /^-[^lL]/ && ($mode{'libs'} & 4);
1.1 ckuethe 284: }
1.4 espie 285: print join(' ', @out), "\n";
286: return undef;
1.1 ckuethe 287: }
288:
289: #list all packages
1.4 espie 290: sub do_list
291: {
1.1 ckuethe 292: my ($p, $x, $y, @files, $fname, $name);
1.4 espie 293: foreach my $p (@PKGPATH) {
294: push(@files, <$p/*.pc>);
295: }
1.1 ckuethe 296:
297: # Scan the lengths of the package names so I can make a format
298: # string to line the list up just like the real pkgconfig does.
299: $x = 0;
1.4 espie 300: foreach my $f (@files) {
301: $fname = basename($f, '.pc');
302: $y = length $fname;
1.1 ckuethe 303: $x = (($y > $x) ? $y : $x);
304: }
305: $x *= -1;
306:
1.4 espie 307: foreach my $f (@files) {
308: $p = slurp($f);
309: $fname = basename($f, '.pc');
310: if ($p =~ /Name: (\w[^\n]+)\n/gm) {
1.1 ckuethe 311: $name = $1;
1.4 espie 312: if ($p =~ /Description:\s+(\w[^\n]+)\n/gm) {
1.1 ckuethe 313: printf("%${x}s %s - %s\n", $fname, $name, $1);
314: }
315: }
316: }
1.4 espie 317: exit 0;
1.1 ckuethe 318: }
319:
1.4 espie 320: sub help
321: {
1.1 ckuethe 322: print <<EOF
323: Usage: $0 [options]
324: --debug - turn on debugging output
325: --help - this message
326: --usage - this message
327: --list-all - show all packages that $0 can find
1.8 ! ckuethe 328: --version - print version of pkgconfig
! 329: --errors-to-stdout - direct error messages to stdout rather than stderr
! 330: --print-errors - print error messages in case of error
! 331: --silence-errors - don't print error messages in case of error
1.1 ckuethe 332: --atleast-pkgconfig-version [version] - require a certain version of pkgconfig
333: --cflags package [versionspec] [package [versionspec]]
334: --cflags-only-I - only output -Iincludepath flags
335: --cflags-only-other - only output flags that are not -I
336: --libs package [versionspec] [package [versionspec]]
337: --libs-only-l - only output -llib flags
338: --libs-only-L - only output -Llibpath flags
339: --libs-only-other - only output flags that are not -l or -L
340: --exists package [versionspec] [package [versionspec]]
341: --uninstalled - allow for uninstalled versions to be used
1.8 ! ckuethe 342: --static - adjust output for static linking
! 343: --atleast-version [version] - require a certain version of a package
! 344: --modversion [package] - query the version of a package
! 345: --variable var package - return the definition of <var> in <package>
1.1 ckuethe 346: EOF
347: ;
1.4 espie 348: exit 1;
1.1 ckuethe 349: }
350:
351: # do we meet/beat the version the caller requested?
1.4 espie 352: sub self_version
353: {
354: my ($v) = @_;
355: my (@a, @b);
356:
357: @a = split /\./, $v;
358: @b = split /\./, $version;
1.1 ckuethe 359:
1.4 espie 360: if (($b[0] >= $a[0]) && ($b[1] >= $a[1])) {
361: exit 0;
1.1 ckuethe 362: } else {
1.4 espie 363: exit 1;
1.1 ckuethe 364: }
365: }
366:
367: # got a package meeting the requested specific version?
1.4 espie 368: sub versionmatch
369: {
1.1 ckuethe 370: my ($pname, $op, $ver, undef) = @_;
371: my (@want, @inst, $m, $f);
372:
1.4 espie 373: print STDERR "pname = '$pname'\n" if $D;
1.1 ckuethe 374: # can't possibly match if we can't find the file
1.4 espie 375: return 0 unless $f = pathresolve($pname);
1.1 ckuethe 376: # load the file
377: $configs{$pname} = slurp($f);
378: # can't possibly match if we can't find the version string
1.4 espie 379: return 0 unless $configs{$pname} =~ /Version: ([0-9\.]+)\n/gm;
1.1 ckuethe 380:
1.4 espie 381: print "comparing $ver (wanted) to $1 (installed)\n" if $D;
382: @inst = split /\./, $1;
383: @want = split /\./, $ver;
1.1 ckuethe 384:
1.4 espie 385: while (@inst && @want) { #so long as both lists have something
1.1 ckuethe 386: # bail if the requested version element beats existing
1.4 espie 387: return 1 if $inst[0] > $want[0];
388: return 0 if $inst[0] < $want[0];
389: shift @inst; shift @want;
1.1 ckuethe 390: }
391: # the version at least equals the requested. if the requested
392: # version has some micropatchlevel beyond the existing version,
393: # return failure
1.4 espie 394: return 0 if @want;
1.1 ckuethe 395: # and after all that, the version is good enough
396: return 1;
397: }