diff --git a/contrib/pkgconf/AUTHORS b/contrib/pkgconf/AUTHORS new file mode 100644 index 00000000000..9ba1de45cb0 --- /dev/null +++ b/contrib/pkgconf/AUTHORS @@ -0,0 +1,41 @@ +A. Wilcox +Alexander Tsoy +Alexpux +Alon Bar-Lev +Alyx +Ariadne Conill +Baptiste Daroussin +Baptiste Daroussin +Bryan Drewery +Dag-Erling Smørgrav +Dan Kegel +Dan Kegel +Dan Nicholson +David Michael +Emil Renner Berthing +Fabian Groffen +Graham Ollis +Gregor Richards +Ignacio Casal Quinteiro +Igor Gnatenko +Issam Maghni +JD Horelick +Jason Dusek +Javier Viguera +Jean-Sébastien Pédron +John Hein +Jussi Pakkanen +Leorize +Luca Barbato +Marcin Wojdyr +Maxin B. John +Michał Górny +Mike Frysinger +Seungha Yang +TingPing +Tobias Kortkamp +Tony Theodore +Volker Braun +Yu Kobayashi +orbea +✈ Graham ✈ diff --git a/contrib/pkgconf/CODE_OF_CONDUCT.md b/contrib/pkgconf/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..1dc77b3ef9a --- /dev/null +++ b/contrib/pkgconf/CODE_OF_CONDUCT.md @@ -0,0 +1,27 @@ +# pkgconf Code of Conduct + +We invite and encourage everybody to express their opinions on relevant +topics. All participants should at all times feel at ease to do so without +fearing any form of attack, reprisal or harassment. We ask everybody to be +respectful and considerate towards each other, especially when attempting +to provide constructive criticism. + +To foster tolerance, respect and hospitality in our community, we agree not +to engage in discriminatory, disparaging or offensive speech or actions, +including as to (but not limited to) gender, sexuality, race, nationality, +religion or profession. We are a community of many different nationalities +and backgrounds, and we cherish our strength in diversity. + + +## Reporting incidents + +Please report any incidents which may be perceived as violations to +`ariadne+conduct@dereferenced.org`. Incidents will be investigated and, +if warranted, acted upon. + + +## Credits + +This CoC is derived from the [FSFE Code of Conduct][fsfe-coc]. + + [fsfe-coc]: https://fsfe.org/about/codeofconduct.en.html diff --git a/contrib/pkgconf/COPYING b/contrib/pkgconf/COPYING new file mode 100644 index 00000000000..35c7c16aa7e --- /dev/null +++ b/contrib/pkgconf/COPYING @@ -0,0 +1,10 @@ +Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 + pkgconf authors (see AUTHORS file in source directory). + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +This software is provided 'as is' and without any warranty, express or +implied. In no event shall the authors be liable for any damages arising +from the use of this software. diff --git a/contrib/pkgconf/Kyuafile.in b/contrib/pkgconf/Kyuafile.in new file mode 100644 index 00000000000..1490fef49c8 --- /dev/null +++ b/contrib/pkgconf/Kyuafile.in @@ -0,0 +1,5 @@ +syntax(2) + +test_suite('pkgconf') + +include('tests/Kyuafile') diff --git a/contrib/pkgconf/Makefile.am b/contrib/pkgconf/Makefile.am new file mode 100644 index 00000000000..93486a17a0b --- /dev/null +++ b/contrib/pkgconf/Makefile.am @@ -0,0 +1,217 @@ +libdir = @libdir@ +datarootdir = @datarootdir@ +datadir = @datadir@ +includedir = @includedir@ +system_includedir = @SYSTEM_INCLUDEDIR@ +system_libdir = @SYSTEM_LIBDIR@ +pkg_default_dir = @PKG_DEFAULT_PATH@ +personality_dir = @PERSONALITY_PATH@ +pkgconfigdir = $(libdir)/pkgconfig +nodist_pkgconfig_DATA = libpkgconf.pc + +ACLOCAL_AMFLAGS = -I m4 +AM_CFLAGS = -DPERSONALITY_PATH=\"$(personality_dir)\" -DPKG_DEFAULT_PATH=\"$(pkg_default_dir)\" -DSYSTEM_INCLUDEDIR=\"$(system_includedir)\" -DSYSTEM_LIBDIR=\"$(system_libdir)\" + +bin_PROGRAMS = pkgconf bomtool +lib_LTLIBRARIES = libpkgconf.la + +EXTRA_DIST = pkg.m4 \ + meson.build \ + meson_options.txt \ + pkgconf.wxs.in \ + txt2rtf.py \ + libpkgconf/meson.build \ + libpkgconf/config.h.meson \ + libpkgconf/win-dirent.h \ + tests/lib-relocatable/lib/pkgconfig/foo.pc \ + tests/lib1/argv-parse-2.pc \ + tests/lib1/billion-laughs.pc \ + tests/lib1/dos-lineendings.pc \ + tests/lib1/paren-quoting.pc \ + tests/lib1/argv-parse-3.pc \ + tests/lib1/foo.pc \ + tests/lib1/foobar.pc \ + tests/lib1/unavailable-provider.pc \ + tests/lib1/prefix-foo1.pc \ + tests/lib1/argv-parse.pc \ + tests/lib1/framework-1.pc \ + tests/lib1/prefix-foo2.pc \ + tests/lib1/bar.pc \ + tests/lib1/framework-2.pc \ + tests/lib1/private-libs-duplication.pc \ + tests/lib1/baz.pc \ + tests/lib1/incomplete.pc \ + tests/lib1/quotes.pc \ + tests/lib1/case-sensitivity.pc \ + tests/lib1/intermediary-1.pc \ + tests/lib1/static-archive-libs.pc \ + tests/lib1/cflags-libs-only.pc \ + tests/lib1/intermediary-2.pc \ + tests/lib1/static-libs.pc \ + tests/lib1/circular-1.pc \ + tests/lib1/missing-require.pc \ + tests/lib1/sysroot-dir.pc \ + tests/lib1/circular-2.pc \ + tests/lib1/multiline.pc \ + tests/lib1/multiline-bogus.pc \ + tests/lib1/tilde-quoting.pc \ + tests/lib1/circular-3.pc \ + tests/lib1/no-trailing-newline.pc \ + tests/lib1/tilde.pc \ + tests/lib1/comments-in-fields.pc \ + tests/lib1/nocflag.pc \ + tests/lib1/typelibdir.pc \ + tests/lib2/foo.pc \ + tests/lib1/comments.pc \ + tests/lib1/nolib.pc \ + tests/lib3/bar.pc \ + tests/lib1/conflicts.pc \ + tests/lib1/omg-uninstalled.pc \ + tests/lib1/omg-sysroot-uninstalled.pc \ + tests/lib1/isystem.pc \ + tests/lib1/idirafter.pc \ + tests/lib1/idirafter-ordering.pc \ + tests/lib1/depgraph-break.pc \ + tests/lib1/cflags-whitespace.pc \ + tests/lib1/cflags-whitespace-trailing.pc \ + tests/lib1/provides.pc \ + tests/lib1/provides-request-simple.pc \ + tests/lib1/flag-order-1.pc \ + tests/lib1/flag-order-3.pc \ + tests/lib1/variable-whitespace.pc \ + tests/lib1/fragment-collision.pc \ + tests/lib1/fragment-collision-intermediary.pc \ + tests/lib1/fragment-collision-1.pc \ + tests/lib1/fragment-collision-2.pc \ + tests/lib1/fragment-comment.pc \ + tests/lib1/fragment-escaping-1.pc \ + tests/lib1/fragment-escaping-2.pc \ + tests/lib1/fragment-escaping-3.pc \ + tests/lib1/fragment-quoting.pc \ + tests/lib1/fragment-quoting-2.pc \ + tests/lib1/fragment-quoting-3.pc \ + tests/lib1/fragment-quoting-5.pc \ + tests/lib1/fragment-quoting-7.pc \ + tests/lib1/fragment-groups.pc \ + tests/lib1/fragment-groups-2.pc \ + tests/lib1/fragment-group-a.pc \ + tests/lib1/fragment-group-b.pc \ + tests/lib1/fragment-group-c.pc \ + tests/lib1/malformed-1.pc \ + tests/lib1/malformed-quoting.pc \ + tests/lib1/malformed-version.pc \ + tests/lib1/metapackage.pc \ + tests/lib1/metapackage-1.pc \ + tests/lib1/metapackage-2.pc \ + tests/lib1/metapackage-3.pc \ + tests/lib1/explicit-sysroot.pc \ + tests/lib1/escaped-backslash.pc \ + tests/lib1/cflags-internal.pc \ + tests/lib1/requires-internal.pc \ + tests/lib1/requires-internal-2.pc \ + tests/lib1/requires-internal-missing.pc \ + tests/lib1/requires-internal-collision.pc \ + tests/lib1/tuple-quoting.pc \ + tests/lib1/empty-tuple.pc \ + tests/lib1/orphaned-requires-private.pc \ + tests/lib1/pcfiledir.pc \ + tests/lib1/sysroot-dir-2.pc \ + tests/lib1/sysroot-dir-3.pc \ + tests/lib1/sysroot-dir-4.pc \ + tests/lib1/sysroot-dir-5.pc \ + tests/lib1/child-prefix/pkgconfig/child-prefix-1.pc \ + tests/lib1/cflags-libs-private-a.pc \ + tests/lib1/cflags-libs-private-b.pc \ + tests/lib1/cflags-libs-private-c.pc \ + tests/lib1/truncated.pc \ + tests/lib1/c-comment.pc \ + tests/meson.build \ + $(test_scripts) \ + doc/conf.py \ + doc/extract.py \ + doc/index.rst \ + doc/libpkgconf.rst \ + doc/libpkgconf-argvsplit.rst \ + doc/libpkgconf-audit.rst \ + doc/libpkgconf-cache.rst \ + doc/libpkgconf-client.rst \ + doc/libpkgconf-dependency.rst \ + doc/libpkgconf-fragment.rst \ + doc/libpkgconf-path.rst \ + doc/libpkgconf-pkg.rst \ + doc/libpkgconf-queue.rst \ + doc/libpkgconf-tuple.rst + +test_scripts= tests/basic.sh \ + tests/builtins.sh \ + tests/conflicts.sh \ + tests/framework.sh \ + tests/parser.sh \ + tests/provides.sh \ + tests/regress.sh \ + tests/requires.sh \ + tests/symlink.sh \ + tests/sysroot.sh \ + tests/version.sh + +test_sh = $(test_scripts) +check_SCRIPTS = ${test_sh:.sh=} + +SUFFIXES= .sh + +nobase_pkginclude_HEADERS = libpkgconf/bsdstubs.h libpkgconf/iter.h libpkgconf/libpkgconf.h libpkgconf/stdinc.h libpkgconf/libpkgconf-api.h +libpkgconf_la_SOURCES = \ + libpkgconf/audit.c \ + libpkgconf/buffer.c \ + libpkgconf/cache.c \ + libpkgconf/client.c \ + libpkgconf/pkg.c \ + libpkgconf/bsdstubs.c \ + libpkgconf/fragment.c \ + libpkgconf/argvsplit.c \ + libpkgconf/fileio.c \ + libpkgconf/tuple.c \ + libpkgconf/dependency.c \ + libpkgconf/queue.c \ + libpkgconf/path.c \ + libpkgconf/personality.c \ + libpkgconf/parser.c +libpkgconf_la_LDFLAGS = -no-undefined -version-info 7:0:0 -export-symbols-regex '^pkgconf_' + +dist_man_MANS = \ + man/bomtool.1 \ + man/pkgconf.1 \ + man/pkg.m4.7 \ + man/pc.5 \ + man/pkgconf-personality.5 + +pkgconf_LDADD = libpkgconf.la +pkgconf_SOURCES = \ + cli/main.c \ + cli/getopt_long.c \ + cli/renderer-msvc.c +pkgconf_CPPFLAGS = -I$(top_srcdir)/libpkgconf -I$(top_srcdir)/cli +noinst_HEADERS = \ + cli/getopt_long.h \ + cli/renderer-msvc.h + +bomtool_LDADD = libpkgconf.la +bomtool_SOURCES = \ + cli/bomtool/main.c \ + cli/getopt_long.c +bomtool_CPPFLAGS = -I$(top_srcdir)/libpkgconf -I$(top_srcdir)/cli -I$(top_srcdir)/cli/bomtool + +dist_doc_DATA = README.md AUTHORS + +m4datadir = $(datadir)/aclocal +m4data_DATA = pkg.m4 + +CLEANFILES = $(EXTRA_PROGRAMS) \ + $(check_SCRIPTS) + +check: pkgconf $(check_SCRIPTS) + kyua --config=none test --kyuafile='$(top_builddir)/Kyuafile' \ + --build-root='$(top_builddir)' + +.sh: + install -m 755 $< $@ diff --git a/contrib/pkgconf/Makefile.lite b/contrib/pkgconf/Makefile.lite new file mode 100644 index 00000000000..6d123433119 --- /dev/null +++ b/contrib/pkgconf/Makefile.lite @@ -0,0 +1,77 @@ +# Copyright (c) 2019 William Pitcock +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# This software is provided 'as is' and without any warranty, express or +# implied. In no event shall the authors be liable for any damages arising +# from the use of this software. + +# pkgconf-lite is a staticly-linked version of pkgconf that does not include +# all features, notably it does not include cross-compile support and MSVC +# support. It does not include the libpkgconf library. + +SRCS = \ + libpkgconf/argvsplit.c \ + libpkgconf/audit.c \ + libpkgconf/bsdstubs.c \ + libpkgconf/cache.c \ + libpkgconf/client.c \ + libpkgconf/dependency.c \ + libpkgconf/fileio.c \ + libpkgconf/fragment.c \ + libpkgconf/parser.c \ + libpkgconf/path.c \ + libpkgconf/personality.c \ + libpkgconf/pkg.c \ + libpkgconf/queue.c \ + libpkgconf/tuple.c \ + cli/getopt_long.c \ + cli/main.c +OBJS = ${SRCS:.c=.o} +CFLAGS = ${STATIC} -DPKGCONF_LITE -I. -Ilibpkgconf -Icli -DSYSTEM_LIBDIR=\"${SYSTEM_LIBDIR}\" -DSYSTEM_INCLUDEDIR=\"${SYSTEM_INCLUDEDIR}\" -DPKG_DEFAULT_PATH=\"${PKG_DEFAULT_PATH}\" +STATIC = +STRIP = strip + +all: pkgconf-lite + +libpkgconf/config.h: + @echo '#define PACKAGE_NAME "pkgconf-lite"' >> $@ + @echo '#define PACKAGE_BUGREPORT "https://git.dereferenced.org/pkgconf/pkgconf/issues"' >> $@ + @echo '#define PACKAGE_VERSION "2.5.1"' >> $@ + @echo '#define PACKAGE PACKAGE_NAME " " PACKAGE_VERSION' >> $@ + @echo '#define HAVE_STRLCPY' >> $@ + @echo '#define HAVE_STRLCAT' >> $@ + @echo '#define HAVE_STRNDUP' >> $@ + +pkgconf-lite: preflight libpkgconf/config.h ${OBJS} + ${CC} ${STATIC} -o $@ ${OBJS} + ${STRIP} $@ + +clean: + rm -f libpkgconf/config.h + rm -f ${OBJS} + rm -f pkgconf-lite + +preflight: preflight-system-libdir preflight-system-includedir preflight-pkg-default-path + +preflight-system-libdir: + @if test -z "${SYSTEM_LIBDIR}"; then \ + echo "SYSTEM_LIBDIR not set."; \ + exit 1; \ + fi + +preflight-system-includedir: + @if test -z "${SYSTEM_INCLUDEDIR}"; then \ + echo "SYSTEM_INCLUDEDIR not set."; \ + exit 1; \ + fi + +preflight-pkg-default-path: + @if test -z "${PKG_DEFAULT_PATH}"; then \ + echo "PKG_DEFAULT_PATH not set."; \ + exit 1; \ + fi + +.PHONY: preflight preflight-system-libdir preflight-system-includedir preflight-pkg-default-path clean diff --git a/contrib/pkgconf/NEWS b/contrib/pkgconf/NEWS new file mode 100644 index 00000000000..8b2947649e1 --- /dev/null +++ b/contrib/pkgconf/NEWS @@ -0,0 +1,943 @@ +Changes from previous version of pkgconf +======================================== + +Changes from 2.5.0 to 2.5.1: +---------------------------- + +* Fix processing of empty dependency lists. + +Changes from 2.4.3 to 2.5.0: +---------------------------- + +* Added a manual page for bomtool. + +* Add support for preloaded packages. + These are modules which are preloaded into the package database + and preferred over searching the module search path when present. + +* Refactor Windows registry PKG_CONFIG_PATH support so that it + augments the main directory search list instead of being treated + as a special case. + +* Processing of `--with-path` arguments by the pkgconf CLI is + now deferred until libpkgconf is fully initialized, effectively + aligning behavior with PKG_CONFIG_PATH processing. + +* Fix several minor memory safety bugs which were identified by + the GCC 15 static analyzer. + +* Added support for pledge(2) and unveil(2) on systems where + this functionality is available. + +* Significant improvements to pkgconf's manual pages. + Patches by Ingo Schwarze and Jonathan Gray (OpenBSD). + +* Remove questionable default-static assumption on Windows that + was inherited from the original pkg-config. Most distributions + of pkgconf on Windows were already patching this out. + Patch by Kai Pastor. + +* Add -D_POSIX_C_SOURCE=200809L to the build definitions, which + is needed for readlinkat on glibc. + Patch by Filipe Laíns. + +Changes from 2.4.2 to 2.4.3: +---------------------------- + +* Fix additional logic errors relating to the new fragment trees + functionality. + +Changes from 2.4.1 to 2.4.2: +---------------------------- + +* Fix several logic errors in the pkg-config file parser that were + surfaced by recent refactoring work. + +* Fix BSD make compatibility so that it generates the test data + before running kyua on BSD make implementations. + +Changes from 2.4.0 to 2.4.1: +---------------------------- + +* Ensure the full DAG is solved for all query types. + +Changes from 2.3.0 to 2.4.0: +---------------------------- + +* Allow multiple package names in solution-based queries such as + `--print-requires`, `--print-requires-private` and `--print-provides`. + +* Use `_DEFAULT_SOURCE` where appropriate on Meson. + +* Add an abstract buffer type and use it when loading files from disk + instead of a 64KB buffer. This ensures large pkg-config files are + not truncated. + +* Disable graph recursion in `--variable` queries as it was generating + duplicate output. + +* Add infrastructure for tracking fragment group relations and convert + storage of fragments to use a tree-like structure instead of string + concatenation. + +* Add support for tracking linker groups, e.g. + + -Wl,--start-group -la -lb -lc -Wl,--end-group + + as fragment groups. + +* Properly contextualize the sysroot directory when processing package + information, ensuring packages where ${pc_sysrootdir} does not match + the default are properly processed. + +Changes from 2.2.0 to 2.3.0: +---------------------------- + +* Fix compile with Meson on Solaris by defining __EXTENSIONS__. + +* Add support for the PKG_CONFIG_RELOCATE_PATHS environmental variable. + When set, the program will act as if --define-prefix is always enabled. + +* Color solution nodes that were part of the original query, and use + that coloring to skip over dependencies when generating DocumentNames + in bomtool. + +* Enhance --env option to support variables with both --variable=varname + and --print-variables. + +* Add --exists-cflags option which creates synthetic preprocessor + definition flags for every queried dependency when found. + +* Document that Requires.private is always used for header paths. + Patch by Petr Písař. + +* Fix minor documentation typos. + Patch by Pierce. + +* Ensure string comparisons using functions are done with + unsigned bytes to avoid undefined behavior. + Patch by Taylor R Campbell. + +* Fix parsing edge-case bugs with dependency versions. + Patch by Kai Pastor. + +* Change PKG_PROG_PKG_CONFIG autoconf macro to add a customizable + failure handler if pkg-config is not found. + Patch by Ismael Luceno. + +Changes from 2.1.1 to 2.2.0: +---------------------------- + +* libpkgconf SOVERSION is now 5. + +* Significant solver rework to flatten both requires and requires.private + dependencies in a single pass. Improves performance slightly and ensures + proper dependency order. + Patches by Kai Pastor. + +* Improve `--digraph` output to reflect more of the solver's state in the + rendered dependency graph. + Patches by Kai Pastor. + +* Do not reference the graph root by name when presenting error messages about + directly requested dependency nodes. + Patch by Kai Pastor. + +Changes from 2.1.0 to 2.1.1: +---------------------------- + +* Documentation fixes from Sam James and Stefan Weil. + +* Fix --modversion with constraints. + Patch by Kai Pastor. + +* Reintroduce an optimization to the dependency graph walker which avoids + revisiting already visited nodes. + Patch by Yi Chou with some modifications. + +* Add a regression test to check that the dependency flattener is working + as expected. + Patch by Kai Pastor. + +Changes from 2.0.3 to 2.1.0: +---------------------------- + +* Do not flatten the solver solution into the original world used as + input to the solver. + Patches by Kai Pastor. + +* Fix warnings with GCC 14 -Walloc-size. + Patch by Sam James. + +* Add --solution to the pkgconf CLI to dump the solver state. + +* Improve the --digraph output to clarify cancelled edges in a given + solution. + +* Demote requires dependencies to requires.private when a parent + dependency is pulled in via requires.private. + +* Trim trailing whitespace when processing package arguments. + Patch by Colin Gillespie. + +* Avoid strncmp() in --modversion version comparison. + Patch by Colin Gillespie. + +* Update autoconf compile flag checking macro. + Patch by Peter Kokot. + +* Add system default path configuration to Meson. + Patch by L. E. Segovia. + +* Fix order of PKG_CONFIG_LIBDIR and PKG_CONFIG_PATH element processing. + +Changes from 2.0.2 to 2.0.3: +---------------------------- + +* Fix some edge-cases with the new `--modversion` implementation + and add additional regression tests. + Patch by Colin Gillespie. + +* Fix some format specifiers to use PRIu64 in debug tracing. + +Changes from 2.0.1 to 2.0.2: +---------------------------- + +* Fix long-standing bug where package identifiers for "uninstalled" + packages incorrectly included the "-uninstalled" suffix. + + This was exposed by the recent change to `--modversion` in 2.0.1. + +Changes from 2.0.0 to 2.0.1: +---------------------------- + +* The behavior of --modversion was largely reverted back to the traditional + pkg-config behavior, but still operates on a solved dependency graph. + + The order of --modversion output is based on the dependency resolution + queue which is passed to the solver, which itself generally maps to the + order of the constrants provided on the command line. + +* A new flag, --verbose, has been added. When used with `--modversion`, it + is possible to disambiguate which version belongs to which module: + + % pkgconf --modversion --verbose foo bar + foo: 1.2.3 + bar: 1.3 + +Changes from 1.9.5 to 2.0.0: +---------------------------- + +* When flattening the dependency graph, retain the latest seen edges + rather than the earliest. + +* Fix a long-standing bug where the dependency resolution queue was + evaluated in reverse. This bug masked the aforementioned dependency + flattening bug in many cases. + +* Fix handling of --with-path, which was appending paths to the search + list rather than prepending them as intended. + +* Error when --modversion is requested with more than one package, as + the output is ambiguous. + +Changes from 1.9.4 to 1.9.5: +---------------------------- + +* Fix incorrect assumptions involving the use of ctype(3) functions. + Patch by Taylor R Campbell. + +* Fix detection of provided functions on autoconf. + Patches by Harmen Stoppels. + +* Fix deletion of tests/meson.build by the autoconf build system. + Patch by h30032433. + +* Fix quoting rules in argvsplit.c. + Patch by huyubiao. + +* Update libpkgconf documentation and documentation building scripts. + Patches by Andrew Shadura. + +* Enforce maximum package count correctly for --modversion. + +Changes from 1.9.3 to 1.9.4: +---------------------------- + +* Fix a buffer overflow vulnerability involving very large variable expansions. + CVE-2023-24056 + +* Fix a bunch of minor regressions with the solver. + +* Create separate solutions for `--cflags` and `--libs` when `--static` is not + used. + +* Remove final trailing whitespace in pkgconf_fragment_render_buf(). + +* Revert broken pkg.m4 change involving querying module versions in + PKG_CHECK_MODULES. + +* Fix handling of tildes in version strings. + +* Various C99 formatting string fixes involving SIZE_FMT_SPECIFIER. + +Changes from 1.9.2 to 1.9.3: +---------------------------- + +* Fix a bunch of minor code issues pointed out using Clang static analyzer. + +* New API: pkgconf_solution_free(), which frees a compiled solution graph. + +* Fix behavior when overriding global variables with `--define-variable`. + +Changes from 1.9.1 to 1.9.2: +---------------------------- + +* Do not try to break dependency cycles across dependency lists. This causes + the solved graph to sometimes miss required dependency nodes because the + solver detected an incorrect dependency cycle. + +* New API: pkgconf_queue_solve(), which replaces pkgconf_queue_apply(). + pkgconf_queue_apply is now deprecated and should not be used in new code. + +Changes from 1.9.0 to 1.9.1: +---------------------------- + +* Skip graph flattening and traversal for query types which only make sense + for a single pkg-config module. + + The old solver walked these graphs with --maximum-traverse-depth=1 in + these cases, but this is no longer helpful because the graph is flattened + by the new solver. + +Changes from 1.8.0 to 1.9.0: +---------------------------- + +* pkgconf 1.9.0 is the first testing release in the pkgconf 2.0 development + series. While it is believed to be suitable for production, there may be + bugs due to the overall redesign of the solver and other initiatives. + Additionally, a future release of pkgconf plans will have additional ABI + breaks for the libpkgconf library before the pkgconf 2.0 release is cut. + +* There is now a new solver that is designed to provide higher performance + with complicated graphs, which works by flattening the dependency graph + into a smaller set of dependencies. This graph can then be evaluated + instead of the original dependency graph without having to visit every + edge in the graph. + + NOTE: This solver, while providing significant performance improvements, + does so, at the cost of changed behavior for some edge cases (such as + circular dependencies). + +* Bug fixes: + - Resolved several memory leaks with edge cases when using libpkgconf + directly. + - pkgconf CLI now consistently frees libpkgconf resources under all + circumstances. + - SYSROOT rules are no longer applied to `-uninstalled` packages by + default. Use `PKG_CONFIG_PKGCONF1_SYSROOT_RULES` for legacy behavior. + +* A new `--license` selector has been added to the pkgconf CLI. This uses + SPDX expressions which can be set as the `License` field in `.pc` files. + See the `pc(5)` manpage for more information. + +* The canonical location for pkgconf maintenance going forward is + . This is presently + mirrored to GitHub for user convenience, but that mirroring will + be terminated at some point (due to GitHub Copilot). + +Changes from 1.7.4 to 1.8.0: +---------------------------- + +* This is the last planned maintenance branch. I see pkgconf as basically + a finished tool at this point, and very few people were ultimately interested + in libpkgconf. So, from here on out, it will just be bug fixes only and + very minor enhancements. + +* Bug fixes: + - Improved path handling on Windows to conform to what the MSYS2 + and Cygwin teams were already modifying pkgconf to do. + Patches by Christoph Reiter. + - Fix a minor memory leak relating to cross-personalities. + Patch by Stone Tickle. + - Fix static builds for Windows on Meson. + Patch by Alexander Neumann. + - Fix some edge cases with --redefine-prefix. + Patch by midipix. + - Do not prepend sysroot_dir if the .pc file does not exist in the + sysroot. + Patch by Sandro Mani. + - Do not perform path filtering on default system include and library + path lists. This fixes consistency with other mechanisms that modify + these path lists. + +* Enhancements: + - Document the --validate option in the manpage. + Patch by orbea. + +Changes from 1.7.3 to 1.7.4: +---------------------------- + +* Bug fixes: + - Fix null-dereference crash when pulling a malformed 'uninstalled' + .pc file into a dependency tree. Patch by Tobias Stöckmann. + - Fix truncation of comment characters when quoted. + - Fix handling of .pc module names in --list-all on Windows. + Patch by Ryan Scott. + - Handle platforms where realpath(3) requires a pre-allocated buffer. + Patch by Fabian Groffen. + - Fix version whitespace warning. + Patch by Christoph Reiter. + +* Enhancements: + - Rewrite DOS paths on native Windows builds that don't use + Cygwin/MSYS. + - Add WantDefaultPure cross-compiler personality option. + - Prefer --static --pure linking on Windows. + - Add PKG_CONFIG_DONT_DEFINE_PREFIX environment variable. + Patch by Jeff Moguillansky. + - Many improvements when building pkgconf with Meson. + Patches by Christoph Reiter. + +Changes from 1.7.2 to 1.7.3: +---------------------------- + +* Bug fixes: + - Fix a possible out of boundary write when evaluating dependencies. + Patch by Tobias Stöckmann. + - Fix escaping logic on Windows. Patch by Vincent Torri. + - Fix out of boundary reads and writes with a malformed fragment. + Patches by Tobias Stöckmann. + - Fix a possible out of boundary write when evaluating tuples. + Patch by Tobias Stöckmann. + +Changes from 1.7.1 to 1.7.2: +---------------------------- + +* Bug fixes: + - Fix a windows-specific crash relating to path fixups. + +Changes from 1.7.0 to 1.7.1: +---------------------------- + +* Bug fixes: + - Fix a possible out of boundary access in the parser for the + cross-compile database. Patch by Tobias Stöckmann. + - Missing files for building with Meson are now included in the + tarball. Patch by Neal Gompa. + - Fix calculation of package atoms on Windows with paths that + use both directory separator characters. + +Changes from 1.6.3 to 1.7.0: +---------------------------- + +* Bug fixes: + - Fix a possible buffer overflow involving newline escaping. + Patch by Tobias Stöckmann. + - Fix an out of boundary access in the parser. + Patch by Tobias Stöckmann. + - Fix leakage of strcmp() result value in pkgconf_compare_version() + responses. + - Return the default personality if loading a cross-compile + personality file failed. + - Do not complain about newlines when validating package versions. + - Properly detect strndup() on Windows when building with Meson. + +* Enhancements: + - A new --shared option and WantDefaultStatic cross-compile + configuration option have been added. This allows for toolchains + to specify that static linking should be used by default. + - Support for the PKG_CONFIG_MSVC_SYNTAX environment variable has + been added. Patch by Dan Kegel. + - Support for the PKGCONF_PKG_PKGF_DONT_MERGE_SPECIAL_FRAGMENTS + client flag which disables emulation of freedesktop.org pkg-config + fragment merging semantics has been added. + Patch by Karen Arutyunov. + +Changes from 1.6.2 to 1.6.3: +---------------------------- + +* Bug fixes: + - Properly tokenize versions. Versions cannot logically contain + whitespace, as dependency-lists would not properly tokenize if + they could. A diagnostic is generated for malformed version + strings containing whitespace when --validate is used. + +* Enhancements: + - CMake support has been dropped. Use Meson to build on Windows. + +Changes from 1.6.1 to 1.6.2: +---------------------------- + +* Bug fixes: + - Fixed a memory leak when deduplicating paths. + - Fixed strndup-related build regression on Windows. + +* Enhancements: + - Added pkgconf-lite variant. pkgconf-lite is a stripped down + variant of pkgconf that only includes pkg-config features. + - Added --modversion description to pkgconf(1) man page. + +Changes from 1.6.0 to 1.6.1: +---------------------------- + +* Bug fixes: + - Fixed an issue where a personality may not be properly selected + due to argv[0] containing a full path. + - Fixed a regression where having an empty PKG_CONFIG_LIBDIR + environment variable would not eliminate the default search + paths. + - Use POSIX realpath(3) instead of readlink() for deduplicating the + search path. Use _fullpath() on Windows for the same purpose. + - The dequoting logic for tuples has been improved to ensure that + quotes *inside* a value remain quoted when necessary. + +Changes from 1.5.4 to 1.6.0: +---------------------------- + +* Bug fixes: + - Fixed issue where packages which referenced missing packages in + Requires.private may have crashed due to memory corruption issues + in some circumstances. + - Fixed warnings reported by GCC 8 diagnostics. + +* Enhancements: + - Add LIBPKGCONF_VERSION and LIBPKGCONF_VERSION_STR macros for + determining libpkgconf version. + - Add pkgconf_fragment_copy_list() to copy a fragment list to + another fragment list. + +Changes from 1.5.3 to 1.5.4: +---------------------------- + +* Bug fixes: + - fix build on Windows with Meson + - fix edge cases for path canonicalization (especially on Windows) + +Changes from 1.5.2 to 1.5.3: +---------------------------- + +* Security fixes: + - Fix edge cases involving dequoting zero-length tuples that can lead to a + buffer overflow under the right circumstances. Thanks to A. Wilcox for + reporting and supplying a patch. (MR 3) + +Changes from 1.5.1 to 1.5.2: +---------------------------- + +* Bug fixes: + - Ensure environment variables override values learned from personality files + or built-in defaults. + +* Documentation enhancements: + - Add pkgconf-personality(5) manpage documenting the personality file format. + +Changes from 1.5.0 to 1.5.1: +---------------------------- + +* Bug fixes: + - fixed a crash with some invalid multi-line .pc files + +Changes from 1.4.2 to 1.5.0: +--------------------------- + +* Administrative: + - The git repository has moved to , + due to the acquisition of GitHub by Microsoft. + +* Overall enhancements: + - pkgconf now supports the proposed Requires.internal pkg-config extension, + by merging it with the Requires.private list (there is no functional difference + between the two in our resolver implementation) + - Support for cross-compilation personalities have been added. To make use of this + functionality, create a file in the new personality.d directory that sits inside + the pkgconfig directory. The personality file format is described in + pc-personality(5). (github #166) + - Support for Haiku has been added, including interpretation of BELIBRARIES and + other toolchain specifics. (github #180) + - Testsuite support can be disabled when building with Meson. (github #175) + +* Bug fixes: + - tuples are now appropriately dequoted when added by the parser (github #186). + +* Various Windows enhancements: + - CMake supports building with GCC on Windows. (github #179) + - Prefix rewriting has been improved. (github #177) + - PKGCONF_API support has been implemented when building with Meson, + allowing Meson to be used to build pkgconf on Windows. (github #174) + +* Documentation fixes: + - The manpages have been linted and fixed. (github #181, #182, #183) + - The description of pkgconf --exists has been corrected. (github #173) + +Changes from 1.4.1 to 1.4.2: +---------------------------- + +* Bug fixes: + - ensure pkgconf_dependency_t nodes have a solution marked when satisfied + by an indirect provider (github #172) + +Changes from 1.4.0 to 1.4.1: +---------------------------- + +* Bug fixes: + - revert some quoting changes because they don't work well with certain + GCC edge cases (github #168) + +* Enhancements: + - add limited support for --cflags with --msvc-syntax + +Changes from 1.3.7 to 1.4.0: +---------------------------- + +* Notable libpkgconf API changes: + - pkgconf_pkg_t.requires has been renamed to pkgconf_pkg_t.required for + C++20 compatibility. + +* Enhancements: + - pkgconf and libpkgconf has been ported to Windows as native binaries. + - improved compatibility with freedesktop.org pkg-config's ${pc_sysrootdir} + usage pattern. + - do not mention PKG_CONFIG_SKIP_CONFLICTS environmental variable when + simplified errors are requested, as with PKG_CONFIG_PATH. + - the dependency solver now stores solutions to dependency graph elements + it visits, allowing for the dependency graph to be incrementally solved. + this improves dependency solving time by an order of magnitude in most + cases. + - new --env option allows for exporting cflags/libs fragments as export + variables + - new support for building pkgconf with CMake and Meson + - improved compiler warning flag detection on autoconf and CMake + - removed PKGCONF_BUFSIZE allocations from the stack where possible + - allow for customizing the way fragment lists are rendered using a callback API + - new support for --msvc-syntax output using the new fragment rendering callbacks + - fragments are now quoted according to POSIX literal rules + - new variables on the pkg-config builtin: + - ${pc_system_includedirs}: the system includedir search path known by pkgconf + - ${pc_system_libdirs}: the system libdir search path known by pkgconf + - new manpages: + - pc(5) describing pkgconf's interpretation of pkg-config .pc files + - pkg.m4(7) describing the autotools macros bundled with pkgconf + +* Bug fixes: + - fix pkgconf_pkg_t.id generation on native Windows where either \ or / are usable + as path separator. + - add missing --modversion to --help output + - do not evaluate module paths for modules that are not actually on disk + - ensure we work on a zeroed buffer prior to calling realpath(2) with it + - fix path deduplication edge case when cache-inodes feature is unavailable + - fix path rewriting regression with PKG_CONFIG_SYSROOT_DIR when + PKG_CONFIG_SYSROOT_DIR is set to / + - fix crash in edge case where a .pc file has misquoting in a fragment list. + - fix logic edge case when comparing relocated paths + +Changes from 1.3.6 to 1.3.7: +---------------------------- + +* Enhancements: + - improved diagnostics for malformed packages. + +* Bug fixes: + - reject packages which contain incomplete metadata in post-parse phase. + +Changes from 1.3.5 to 1.3.6: +---------------------------- + +* Enhancements: + - add many cflags to the protected set: -Wa, -Wl, -Wp, -ansi, -std=, -stdlib=, + -pedantic, -pthread, -trigraphs, -nostdinc, -nostdlibinc, -nobuiltininc. + +* Bug fixes: + - handle -include cflag fragments properly. + +Changes from 1.3.4 to 1.3.5: +---------------------------- + +* Bug fixes: + - fix --variable output for compatibility some broken configure scripts when they + request the same variable from multiple packages + +Changes from 1.3.3 to 1.3.4: +---------------------------- + +* Bug fixes: + - fix a quoting issue exposed by the Go testsuite + +Changes from 1.3.2 to 1.3.3: +---------------------------- + +* Bug fixes: + - back out disabling the dependency resolver for single-package queries, it caused + too many regressions. + +* Enhancements: + - allow explicitly disabling the dependency resolver via new environment variable, + PKG_CONFIG_MINIMUM_TRAVERSE_DEPTH=1. while pkgconf could already do this using + --minimum-traverse-depth=1, other pkg-config implementations do not have this + option, so adding an environment variable allows to make better use of this + feature (other implementations won't error due to unknown option this way) + +Changes from 1.3.1 to 1.3.2: +---------------------------- + +* Bug fixes: + - rewrite handling of --modversion, --print-variables and --variable to not require + the dependency resolver + - ensure we disable the dependency resolver in all cases where it is a single-package + query (1.3.1 did not go far enough) + +Changes from 1.3.0 to 1.3.1: +---------------------------- + +* Features: + - implement --short-errors + +* Bug fixes: + - only consider a single package at a time with --print-requires, --print-requires-private, + --print-provides, --modversion, --print-variable and --print-variables + +* Enhancements: + - synchronized latest freedesktop.org changes to pkg.m4 + - improve error reporting with legacy --atleast-version and similar flags. + +Changes from 1.2.0 to 1.3.0: +---------------------------- + +* Features: + - pkgconf --debug now provides a facility for tracing most relevant libpkgconf operations + - libpkgconf: add warn and trace handlers for warnings + - replace realpath() with faster, lighter weight path normalization function (github #112) + - pkgconf CLI now emulates pkg-config quoting rules precisely, while allowing direct access + to the actual fragments via libpkgconf + +* Bug fixes: + - pkg: properly separate static and virtual packages so they are not inappropriately + optimized out of the dependency graph (github #108) + - argvsplit: do not consider ' and " to have similar rules to escape sequences (github #111) + - pkg: strip trailing whitespace when parsing .pc files + +* Enhancements: + - argvsplit basically rewritten from scratch + - many code fixes spotted by coverity + - add PKG_CONFIG_DONT_RELOCATE_PATHS and --dont-relocate-paths environment variables to + disable path relocation feature if needed + - remove extra whitespace that was present for compatibility with older pkg-config releases + (github #113) + +Changes from 1.1.0 to 1.2.0: +---------------------------- + +* Features: + - new --path option lists the .pc files which provided the requested dependencies + - new path relocation API: pkgconf_path_relocate(), which wraps functions such as + realpath() and cygwin_conv_path(). + - new --with-path option adds a path to the search list + - new --define-prefix and --dont-define-prefix features enable automatic prefix + detection for relocatable SDKs. this is mostly useful on windows. + +* Bug fixes: + - fragments: fix even more edge cases involving token concatenation + - path lists: don't attempt to collect path inodes if the filter is disabled + - path lists: explicitly avoid uninitialised data for the path inode cache + - client: properly handle --keep-system-cflags and --keep-system-libs + +* Enhancements: + - windows: build libpkgconf as a DLL + - fragments: only munge fragments if sysroot_dir is actually set + - overall API: resolver flags have been moved to being a client-object setting + instead of used for every function invocation + +Changes from 1.0.1 to 1.1.0: +---------------------------- + +* Features: + - new Provides system allows alternate .pc files to provide a dependency + - stable library API (with documentation): http://pkgconf.readthedocs.io/ + +* Enhancements: + - make it possible to programmatically declare dependencies instead of just using the parser + - testsuite migrated to run under kyua + - provide a libpkgconf.pc file for consumers to use + - pkgconf client: new --pure flag to enable dependency graph optimization in --static mode + - significant .pc parser speedups using bsearch(3). + - handle -idirafter in the same way as -isystem CFLAGS + - learn toolchain "system" paths from GCC environment variables, if present + - filter duplicate PKG_CONFIG_PATH (and other) entries by inode + +* Bug fixes: + - fragments: fix another edge case involving empty tokens being concatenated onto previous tokens + (github #99) + - libpkgconf: remove dependencies on config.h in public headers + +Changes from 1.0.0 to 1.0.1: +---------------------------- + +* Enhancements: + - new stub implementation of --print-provides (github #95) + +* Bug fixes: + - fragments: fix an edge case involving path-only fragments and PKG_CONFIG_SYSROOT_DIR (github #94) + +Changes from 0.9.12 to 1.0.0: +----------------------------- + +* Features: + - new library: libpkgconf + +* Enhancements: + - testsuite: use an explicit prefix on all tests + - build: switch to automake + +* Bug fixes: + - cast all usage of ctype(3) functions + - do not expand variables passed via --define-variable for compatibility with pkg-config 0.29 + - let the CFLAGS being user settable + +Changes from 0.9.11 to 0.9.12: +------------------------------ + +* Features: + - add --list-package-names + +* Enhancements: + - ensure -I and -L are never pushed back + +* Bug fixes: + - fix implicit conversion warnings with variables over 31bits + +Changes from 0.9.10 to 0.9.11: +------------------------------ + +* Features: + - add --validate + +* Enhancements: + - add large file support checks in autoconf + +* Bug fixes: + - fix private lib deduplication + - handle --static correctly in some more esoteric scenarios + +Changes from 0.9.9 to 0.9.10: +----------------------------- + +* Features: + +* Enhancements: + +* Bug fixes: + - Fix parser when dealing with commented lines + +Changes from 0.9.8 to 0.9.9: +---------------------------- + +* Features: + - add a sub out --print-provides + +* Enhancements: + +* Bug fixes: + - Fix parser when dealing with comments in fields + +Changes from 0.9.7 to 0.9.8: +---------------------------- + +* Features: + +* Enhancements: + - Convert manpages to mdoc(7) + +* Bug fixes: + - Fix parsing multiline fields + +Changes from 0.9.6 to 0.9.7: +---------------------------- + +* Features: + +* Enhancements: + - Convert manpages to mdoc(7) + +* Bug fixes: + - Fix parsing multiline fields + +Changes from 0.9.5 to 0.9.6: +---------------------------- + +* Features: + - add a sub --debug + +* Enhancements: + - Do not hardcode non-posix install(1) + +* Bug fixes: + - fix --with-system-includedir and --with-system-libdir behaviour + +Changes from 0.9.4 to 0.9.5: +---------------------------- + +* Features: + +* Enhancements: + - Make all variables but CFLAGS and LIBS case sensitive + +* Bug fixes: + +Changes from 0.9.3 to 0.9.4: +---------------------------- + +* Features: + - Add a pkgconf(1) manpage + +* Enhancements: + - Improve support for MacOS -framework + +* Bug fixes: + +Changes from 0.9.2 to 0.9.3: +---------------------------- + +* Features: + - Add support for CFLAGS.private + +* Enhancements: + - Support out of source build + - Improved private libs deduplication + +* Bug fixes: + +Changes from 0.9.1 to 0.9.2: +---------------------------- + +* Features: + +* Enhancements: + +* Bug fixes: + - Fix PKG_CONFIG_PATH being ignored when a .pc is directly supplied from + command line + +Changes from 0.9.0 to 0.9.1: +---------------------------- + +* Features: + +* Enhancements: + - --simulate: print depgraph operations + - --simulate: print bytecode program as a human-readable AST + +* Bug fixes: + - reset parser state on new package atom + +Changes from 0.8.12 to 0.9.0: +---------------------------- + +* Features: + +* Enhancements: + - Rework the internal cache API + - Rework the internal code to use the new pkg_list_t framework + - Rework PKG_CONFIG_PATH handling code + +* Bug fixes: + - fix multi-recursion with -framework diff --git a/contrib/pkgconf/README.md b/contrib/pkgconf/README.md new file mode 100644 index 00000000000..257d686a62b --- /dev/null +++ b/contrib/pkgconf/README.md @@ -0,0 +1,130 @@ +# pkgconf [![test](https://github.com/pkgconf/pkgconf/actions/workflows/test.yml/badge.svg)](https://github.com/pkgconf/pkgconf/actions/workflows/test.yml) + +`pkgconf` is a program which helps to configure compiler and linker flags for +development libraries. It is a superset of the functionality provided by +pkg-config from freedesktop.org, but does not provide bug-compatibility with +the original pkg-config. + +`libpkgconf` is a library which provides access to most of `pkgconf`'s functionality, +to allow other tooling such as compilers and IDEs to discover and use libraries +configured by pkgconf. + +## release tarballs + +Release tarballs are available on [distfiles.ariadne.space][distfiles]. + + [distfiles]: https://distfiles.ariadne.space/pkgconf/ + +## build system setup + +If you would like to use the git sources directly, or a snapshot of the +sources from GitHub, you will need to regenerate the autotools build +system artifacts yourself, or use Meson instead. For example, on Alpine: + + $ apk add autoconf automake libtool build-base + $ sh ./autogen.sh + +## pkgconf-lite + +If you only need the original pkg-config functionality, there is also pkgconf-lite, +which builds the `pkgconf` frontend and relevant portions of `libpkgconf` functionality +into a single binary: + + $ make -f Makefile.lite + +## why `pkgconf` over original `pkg-config`? + +pkgconf builds a flattened directed dependency graph, which allows for more insight +into relationships between dependencies, allowing for some link-time dependency +optimization, which allows for the user to more conservatively link their binaries, +which may be helpful in some environments, such as when prelink(1) is being used. + +The solver is also optimized to handle large dependency graphs with hundreds of +thousands of edges, which can be seen in any project using the Abseil frameworks +for example. + +In addition, pkgconf has full support for virtual packages, while the original +pkg-config does not, as well as fully supporting `Conflicts` at dependency +resolution time, which is more efficient than checking for `Conflicts` while +walking the dependency graph. + +## linker flags optimization + +pkgconf, when used effectively, can make optimizations to avoid overlinking binaries. + +This functionality depends on the pkg-config module properly declaring its dependency +tree instead of using `Libs` and `Cflags` fields to directly link against other modules +which have pkg-config metadata files installed. + +The practice of using `Libs` and `Cflags` to describe unrelated dependencies is +not recommended in [Dan Nicholson's pkg-config tutorial][fd-tut] for this reason. + + [fd-tut]: http://people.freedesktop.org/~dbn/pkg-config-guide.html + +## bug compatibility with original pkg-config + +In general, we do not provide bug-level compatibility with pkg-config. + +What that means is, if you feel that there is a legitimate regression versus pkg-config, +do let us know, but also make sure that the .pc files are valid and follow the rules of +the [pkg-config tutorial][fd-tut], as most likely fixing them to follow the specified +rules will solve the problem. + +## debug output + +Please use only the stable interfaces to query pkg-config. Do not screen-scrape the +output from `--debug`: this is sent to `stderr` for a reason, it is not intended to be +scraped. The `--debug` output is **not** a stable interface, and should **never** be +depended on as a source of information. If you need a stable interface to query pkg-config +which is not covered, please get in touch. + +## compiling `pkgconf` and `libpkgconf` on UNIX + +pkgconf is basically compiled the same way any other autotools-based project is +compiled: + + $ ./configure + $ make + $ sudo make install + +If you are installing pkgconf into a custom prefix, such as `/opt/pkgconf`, you will +likely want to define the default system includedir and libdir for your toolchain. +To do this, use the `--with-system-includedir` and `--with-system-libdir` configure +flags like so: + + $ ./configure \ + --prefix=/opt/pkgconf \ + --with-system-libdir=/lib:/usr/lib \ + --with-system-includedir=/usr/include + $ make + $ sudo make install + +## compiling `pkgconf` and `libpkgconf` with Meson (usually for Windows) + +pkgconf is compiled using [Meson](https://mesonbuild.com) on Windows. In theory, you could also use +Meson to build on UNIX, but this is not recommended at this time as pkgconf is typically built +much earlier than Meson. + + $ meson setup build -Dtests=disabled + $ meson compile -C build + $ meson install -C build + +There are a few defines such as `SYSTEM_LIBDIR`, `PKGCONFIGDIR` and `SYSTEM_INCLUDEDIR`. +However, on Windows, the default `PKGCONFIGDIR` value is usually overridden at runtime based +on path relocation. + +## pkg-config symlink + +If you want pkgconf to be used when you invoke `pkg-config`, you should install a +symlink for this. We do not do this for you, as we believe it is better for vendors +to make this determination themselves. + + $ ln -sf pkgconf /usr/bin/pkg-config + +## contacts + +You can report bugs at . + +There is a mailing list at . + +You can contact us via IRC at `#pkgconf` at `irc.oftc.net`. diff --git a/contrib/pkgconf/autogen.sh b/contrib/pkgconf/autogen.sh new file mode 100755 index 00000000000..92bd5b67897 --- /dev/null +++ b/contrib/pkgconf/autogen.sh @@ -0,0 +1,87 @@ +#! /bin/sh + +TOP_DIR=$(dirname $0) +LAST_DIR=$PWD + +if test ! -f $TOP_DIR/configure.ac ; then + echo "You must execute this script from the top level directory." + exit 1 +fi + +AUTOCONF=${AUTOCONF:-autoconf} +ACLOCAL=${ACLOCAL:-aclocal} +AUTOHEADER=${AUTOHEADER:-autoheader} +AUTOMAKE=${AUTOMAKE:-automake} +LIBTOOLIZE=${LIBTOOLIZE:-libtoolize} + +dump_help_screen () +{ + echo "Usage: $0 [options]" + echo + echo "options:" + echo " -n skip CVS changelog creation" + echo " -h,--help show this help screen" + echo + exit 0 +} + +parse_options () +{ + while test "$1" != "" ; do + case $1 in + -h|--help) + dump_help_screen + ;; + -n) + SKIP_CVS_CHANGELOG=yes + ;; + *) + echo Invalid argument - $1 + dump_help_screen + ;; + esac + shift + done +} + +run_or_die () +{ + COMMAND=$1 + + # check for empty commands + if test -z "$COMMAND" ; then + echo "*warning* no command specified" + return 1 + fi + + shift; + + OPTIONS="$@" + + # print a message + echo -n "*info* running $COMMAND" + if test -n "$OPTIONS" ; then + echo " ($OPTIONS)" + else + echo + fi + + # run or die + $COMMAND $OPTIONS ; RESULT=$? + if test $RESULT -ne 0 ; then + echo "*error* $COMMAND failed. (exit code = $RESULT)" + exit 1 + fi + + return 0 +} + +parse_options "$@" + +cd $TOP_DIR + +run_or_die $ACLOCAL +run_or_die $AUTOHEADER +run_or_die $AUTOCONF +run_or_die $LIBTOOLIZE --install +run_or_die $AUTOMAKE --add-missing diff --git a/contrib/pkgconf/cli/bomtool/main.c b/contrib/pkgconf/cli/bomtool/main.c new file mode 100644 index 00000000000..1e8391aa5ae --- /dev/null +++ b/contrib/pkgconf/cli/bomtool/main.c @@ -0,0 +1,368 @@ +/* + * bomtool/main.c + * main() routine, printer functions + * + * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + * pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include "libpkgconf/config.h" +#include +#include +#include "getopt_long.h" + +#define PKG_VERSION (((uint64_t) 1) << 1) +#define PKG_ABOUT (((uint64_t) 1) << 2) +#define PKG_HELP (((uint64_t) 1) << 3) + +static const char *spdx_version = "SPDX-2.2"; +static const char *bom_license = "CC0-1.0"; +static const char *document_ref = "SPDXRef-DOCUMENT"; + +static pkgconf_client_t pkg_client; +static uint64_t want_flags; +static size_t maximum_package_count = 0; +static int maximum_traverse_depth = 2000; +FILE *error_msgout = NULL; + +static bool +error_handler(const char *msg, const pkgconf_client_t *client, void *data) +{ + (void) client; + (void) data; + fprintf(error_msgout, "%s", msg); + return true; +} + +static const char * +sbom_spdx_identity(pkgconf_pkg_t *pkg) +{ + static char buf[PKGCONF_ITEM_SIZE]; + + snprintf(buf, sizeof buf, "%sC64%s", pkg->id, pkg->version); + + return buf; +} + +static const char * +sbom_name(pkgconf_pkg_t *world) +{ + static char buf[PKGCONF_BUFSIZE]; + pkgconf_node_t *node; + + pkgconf_strlcpy(buf, "SBOM-SPDX", sizeof buf); + + PKGCONF_FOREACH_LIST_ENTRY(world->required.head, node) + { + pkgconf_dependency_t *dep = node->data; + pkgconf_pkg_t *match = dep->match; + + if ((dep->flags & PKGCONF_PKG_DEPF_QUERY) != PKGCONF_PKG_DEPF_QUERY) + continue; + + if (!dep->match) + continue; + + pkgconf_strlcat(buf, "-", sizeof buf); + pkgconf_strlcat(buf, sbom_spdx_identity(match), sizeof buf); + } + + return buf; +} + +static void +write_sbom_header(pkgconf_client_t *client, pkgconf_pkg_t *world) +{ + (void) client; + (void) world; + + printf("SPDXVersion: %s\n", spdx_version); + printf("DataLicense: %s\n", bom_license); + printf("SPDXID: %s\n", document_ref); + printf("DocumentName: %s\n", sbom_name(world)); + printf("DocumentNamespace: https://spdx.org/spdxdocs/bomtool-%s\n", PACKAGE_VERSION); + printf("Creator: Tool: bomtool %s\n", PACKAGE_VERSION); + + printf("\n\n"); +} + +static const char * +sbom_identity(pkgconf_pkg_t *pkg) +{ + static char buf[PKGCONF_ITEM_SIZE]; + + snprintf(buf, sizeof buf, "%s@%s", pkg->id, pkg->version); + + return buf; +} + +static void +write_sbom_package(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *unused) +{ + (void) client; + (void) unused; + + if (pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL) + return; + + printf("##### Package: %s\n\n", sbom_identity(pkg)); + + printf("PackageName: %s\n", sbom_identity(pkg)); + printf("SPDXID: SPDXRef-Package-%s\n", sbom_spdx_identity(pkg)); + printf("PackageVersion: %s\n", pkg->version); + printf("PackageDownloadLocation: NOASSERTION\n"); + printf("PackageVerificationCode: NOASSERTION\n"); + + /* XXX: What about projects? */ + if (pkg->maintainer != NULL) + printf("PackageSupplier: Person: %s\n", pkg->maintainer); + + if (pkg->url != NULL) + printf("PackageHomePage: %s\n", pkg->url); + + printf("PackageLicenseDeclared: %s\n", pkg->license != NULL ? pkg->license : "NOASSERTION"); + + if (pkg->copyright != NULL) + printf("PackageCopyrightText: %s\n", pkg->copyright); + + if (pkg->description != NULL) + printf("PackageSummary: %s\n", pkg->description); + + printf("\n\n"); +} + +static void +write_sbom_relationships(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *unused) +{ + (void) client; + (void) unused; + + char baseref[PKGCONF_ITEM_SIZE]; + pkgconf_node_t *node; + + if (pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL) + return; + + snprintf(baseref, sizeof baseref, "SPDXRef-Package-%sC64%s", pkg->id, pkg->version); + + PKGCONF_FOREACH_LIST_ENTRY(pkg->required.head, node) + { + pkgconf_dependency_t *dep = node->data; + pkgconf_pkg_t *match = dep->match; + + if (!dep->match) + continue; + + printf("Relationship: %s DEPENDS_ON SPDXRef-Package-%s\n", baseref, sbom_spdx_identity(match)); + printf("Relationship: SPDXRef-Package-%s DEPENDENCY_OF %s\n", sbom_spdx_identity(match), baseref); + } + + PKGCONF_FOREACH_LIST_ENTRY(pkg->requires_private.head, node) + { + pkgconf_dependency_t *dep = node->data; + pkgconf_pkg_t *match = dep->match; + + if (!dep->match) + continue; + + printf("Relationship: %s DEPENDS_ON SPDXRef-Package-%s\n", baseref, sbom_spdx_identity(match)); + printf("Relationship: SPDXRef-Package-%s DEV_DEPENDENCY_OF %s\n", sbom_spdx_identity(match), baseref); + } + + if (pkg->required.head != NULL || pkg->requires_private.head != NULL) + printf("\n\n"); +} + +static bool +generate_sbom_from_world(pkgconf_client_t *client, pkgconf_pkg_t *world) +{ + int eflag; + pkgconf_node_t *node; + + write_sbom_header(client, world); + + eflag = pkgconf_pkg_traverse(client, world, write_sbom_package, NULL, maximum_traverse_depth, 0); + if (eflag != PKGCONF_PKG_ERRF_OK) + return false; + + eflag = pkgconf_pkg_traverse(client, world, write_sbom_relationships, NULL, maximum_traverse_depth, 0); + if (eflag != PKGCONF_PKG_ERRF_OK) + return false; + + PKGCONF_FOREACH_LIST_ENTRY(world->required.head, node) + { + pkgconf_dependency_t *dep = node->data; + pkgconf_pkg_t *match = dep->match; + + if (!dep->match) + continue; + + printf("Relationship: %s DESCRIBES SPDXRef-Package-%s\n", document_ref, sbom_spdx_identity(match)); + } + + return true; +} + +static int +version(void) +{ + printf("bomtool %s\n", PACKAGE_VERSION); + return EXIT_SUCCESS; +} + +static int +about(void) +{ + printf("bomtool (%s %s)\n", PACKAGE_NAME, PACKAGE_VERSION); + printf("Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021\n"); + printf(" pkgconf authors (see AUTHORS in documentation directory).\n\n"); + printf("Permission to use, copy, modify, and/or distribute this software for any\n"); + printf("purpose with or without fee is hereby granted, provided that the above\n"); + printf("copyright notice and this permission notice appear in all copies.\n\n"); + printf("This software is provided 'as is' and without any warranty, express or\n"); + printf("implied. In no event shall the authors be liable for any damages arising\n"); + printf("from the use of this software.\n\n"); + printf("Report bugs at <%s>.\n", PACKAGE_BUGREPORT); + return EXIT_SUCCESS; +} + +static int +usage(void) +{ + printf("usage: bomtool [--flags] [modules]\n"); + + printf("\nbasic options:\n\n"); + + printf(" --help this message\n"); + printf(" --about print bomtool version and license to stdout\n"); + printf(" --version print bomtool version to stdout\n"); + + return EXIT_SUCCESS; +} + +int +main(int argc, char *argv[]) +{ + int ret = EXIT_SUCCESS; + pkgconf_list_t pkgq = PKGCONF_LIST_INITIALIZER; + unsigned int want_client_flags = PKGCONF_PKG_PKGF_SEARCH_PRIVATE; + pkgconf_cross_personality_t *personality = pkgconf_cross_personality_default(); + pkgconf_pkg_t world = { + .id = "virtual:world", + .realname = "virtual world package", + .flags = PKGCONF_PKG_PROPF_STATIC | PKGCONF_PKG_PROPF_VIRTUAL, + }; + + error_msgout = stderr; + + struct pkg_option options[] = { + { "version", no_argument, &want_flags, PKG_VERSION, }, + { "about", no_argument, &want_flags, PKG_ABOUT, }, + { "help", no_argument, &want_flags, PKG_HELP, }, + { NULL, 0, NULL, 0 } + }; + + while ((ret = pkg_getopt_long_only(argc, argv, "", options, NULL)) != -1) + { + switch (ret) + { + case '?': + case ':': + return EXIT_FAILURE; + default: + break; + } + } + + pkgconf_client_init(&pkg_client, error_handler, NULL, personality); + + /* we have determined what features we want most likely. in some cases, we override later. */ + pkgconf_client_set_flags(&pkg_client, want_client_flags); + + /* at this point, want_client_flags should be set, so build the dir list */ + pkgconf_client_dir_list_build(&pkg_client, personality); + + if ((want_flags & PKG_ABOUT) == PKG_ABOUT) + return about(); + + if ((want_flags & PKG_VERSION) == PKG_VERSION) + return version(); + + if ((want_flags & PKG_HELP) == PKG_HELP) + return usage(); + + while (1) + { + const char *package = argv[pkg_optind]; + + if (package == NULL) + break; + + /* check if there is a limit to the number of packages allowed to be included, if so and we have hit + * the limit, stop adding packages to the queue. + */ + if (maximum_package_count > 0 && pkgq.length > maximum_package_count) + break; + + while (isspace((unsigned char)package[0])) + package++; + + /* skip empty packages */ + if (package[0] == '\0') { + pkg_optind++; + continue; + } + + if (argv[pkg_optind + 1] == NULL || !PKGCONF_IS_OPERATOR_CHAR(*(argv[pkg_optind + 1]))) + { + pkgconf_queue_push(&pkgq, package); + pkg_optind++; + } + else + { + char packagebuf[PKGCONF_BUFSIZE]; + + snprintf(packagebuf, sizeof packagebuf, "%s %s %s", package, argv[pkg_optind + 1], argv[pkg_optind + 2]); + pkg_optind += 3; + + pkgconf_queue_push(&pkgq, packagebuf); + } + } + + if (pkgq.head == NULL) + { + fprintf(stderr, "Please specify at least one package name on the command line.\n"); + ret = EXIT_FAILURE; + goto out; + } + + ret = EXIT_SUCCESS; + + if (!pkgconf_queue_solve(&pkg_client, &pkgq, &world, maximum_traverse_depth)) + { + ret = EXIT_FAILURE; + goto out; + } + + if (!generate_sbom_from_world(&pkg_client, &world)) + { + ret = EXIT_FAILURE; + goto out; + } + +out: + pkgconf_solution_free(&pkg_client, &world); + pkgconf_queue_free(&pkgq); + pkgconf_cross_personality_deinit(personality); + pkgconf_client_deinit(&pkg_client); + + return ret; +} diff --git a/contrib/pkgconf/cli/getopt_long.c b/contrib/pkgconf/cli/getopt_long.c new file mode 100644 index 00000000000..c47215fae4c --- /dev/null +++ b/contrib/pkgconf/cli/getopt_long.c @@ -0,0 +1,643 @@ +/* $OpenBSD: getopt_long.c,v 1.21 2006/09/22 17:22:05 millert Exp $ */ +/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ + +/* + * Copyright (c) 2002 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "getopt_long.h" + +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif + +#define PKGCONF_HACK_LOGICAL_OR_ALL_VALUES + +int pkg_opterr = 1; /* if error message should be printed */ +int pkg_optind = 1; /* index into parent argv vector */ +int pkg_optopt = '?'; /* character checked for validity */ +int pkg_optreset; /* reset getopt */ +char *pkg_optarg; /* argument associated with option */ + +#define PRINT_ERROR ((pkg_opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +/* add some padding to EMSG to avoid overrun */ +#define EMSG "\0\0\0\0" + +#ifdef GNU_COMPATIBLE +#define NO_PREFIX (-1) +#define D_PREFIX 0 +#define DD_PREFIX 1 +#define W_PREFIX 2 +#endif + +static int getopt_internal(int, char * const *, const char *, + const struct pkg_option *, int *, int); +static int parse_long_options(char * const *, const char *, + const struct pkg_option *, int *, int, int); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static char *place = EMSG; /* option letter processing */ + +/* XXX: set pkg_optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char illoptchar[] = "illegal option -- %c"; /* From P1003.2 */ +#ifdef GNU_COMPATIBLE +static int dash_prefix = NO_PREFIX; +static const char gnuoptchar[] = "invalid option -- %c"; + +static const char recargstring[] = "option `%s%s' requires an argument"; +static const char ambig[] = "option `%s%.*s' is ambiguous"; +static const char noarg[] = "option `%s%.*s' doesn't allow an argument"; +static const char illoptstring[] = "unrecognized option `%s%s'"; +#else +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptstring[] = "unknown option -- %s"; +#endif + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ +static int +parse_long_options(char * const *nargv, const char *options, + const struct pkg_option *long_options, int *idx, int short_too, int flags) +{ + char *current_argv, *has_equal; +#ifdef GNU_COMPATIBLE + char *current_dash; +#endif + size_t current_argv_len; + int i, match, exact_match, second_partial_match; + + current_argv = place; +#ifdef GNU_COMPATIBLE + switch (dash_prefix) { + case D_PREFIX: + current_dash = "-"; + break; + case DD_PREFIX: + current_dash = "--"; + break; + case W_PREFIX: + current_dash = "-W "; + break; + default: + current_dash = ""; + break; + } +#endif + match = -1; + exact_match = 0; + second_partial_match = 0; + + pkg_optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + exact_match = 1; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* first partial match */ + match = i; + else if ((flags & FLAG_LONGONLY) || + long_options[i].has_arg != + long_options[match].has_arg || + long_options[i].flag != long_options[match].flag || + long_options[i].val != long_options[match].val) + second_partial_match = 1; + } + if (!exact_match && second_partial_match) { + /* ambiguous abbreviation */ + if (PRINT_ERROR) { + fprintf(stderr, "pkgconf: "); + fprintf(stderr, ambig, +#ifdef GNU_COMPATIBLE + current_dash, +#endif + (int)current_argv_len, + current_argv); + fprintf(stderr, "\n"); + } + pkg_optopt = 0; + return (BADCH); + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) { + fprintf(stderr, "pkgconf: "); + fprintf(stderr, noarg, +#ifdef GNU_COMPATIBLE + current_dash, +#endif + (int)current_argv_len, + current_argv); + fprintf(stderr, "\n"); + } + /* + * XXX: GNU sets pkg_optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + pkg_optopt = (int)long_options[match].val; + else + pkg_optopt = 0; +#ifdef GNU_COMPATIBLE + return (BADCH); +#else + return (BADARG); +#endif + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + pkg_optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + pkg_optarg = nargv[pkg_optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (pkg_optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) { + fprintf(stderr, "pkgconf: "); + fprintf(stderr, recargstring, +#ifdef GNU_COMPATIBLE + current_dash, +#endif + current_argv); + fprintf(stderr, "\n"); + } + /* + * XXX: GNU sets pkg_optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + pkg_optopt = (int)long_options[match].val; + else + pkg_optopt = 0; + --pkg_optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --pkg_optind; + return (-1); + } + if (PRINT_ERROR) { + fprintf(stderr, "pkgconf: "); + fprintf(stderr, illoptstring, +#ifdef GNU_COMPATIBLE + current_dash, +#endif + current_argv); + fprintf(stderr, "\n"); + } + pkg_optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { +#ifdef PKGCONF_HACK_LOGICAL_OR_ALL_VALUES + *long_options[match].flag |= long_options[match].val; +#else + *long_options[match].flag = long_options[match].val; +#endif + return (0); + } else + return ((int)long_options[match].val); +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int +getopt_internal(int nargc, char * const *nargv, const char *options, + const struct pkg_option *long_options, int *idx, int flags) +{ + char *oli; /* option letter list index */ + int optchar, short_too; + int posixly_correct; /* no static, can be changed on the fly */ + + if (options == NULL) + return (-1); + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + */ + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); +#ifdef GNU_COMPATIBLE + if (*options == '-') + flags |= FLAG_ALLARGS; + else if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; +#else + if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + else if (*options == '-') + flags |= FLAG_ALLARGS; +#endif + + if (*options == '+' || *options == '-') + options++; + + /* + * XXX Some GNU programs (like cvs) set pkg_optind to 0 instead of + * XXX using pkg_optreset. Work around this braindamage. + */ + if (pkg_optind == 0) + pkg_optind = pkg_optreset = 1; + + pkg_optarg = NULL; + if (pkg_optreset) + nonopt_start = nonopt_end = -1; +start: + if (pkg_optreset || !*place) { /* update scanning pointer */ + pkg_optreset = 0; + if (pkg_optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + pkg_optind, nargv); + pkg_optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set pkg_optind + * to the first of them. + */ + pkg_optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[pkg_optind]) != '-' || +#ifdef GNU_COMPATIBLE + place[1] == '\0') { +#else + (place[1] == '\0' && strchr(options, '-') == NULL)) { +#endif + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + pkg_optarg = nargv[pkg_optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = pkg_optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + pkg_optind, nargv); + nonopt_start = pkg_optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + pkg_optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = pkg_optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + pkg_optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + pkg_optind, nargv); + pkg_optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[pkg_optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; +#ifdef GNU_COMPATIBLE + dash_prefix = D_PREFIX; +#endif + if (*place == '-') { + place++; /* --foo long option */ +#ifdef GNU_COMPATIBLE + dash_prefix = DD_PREFIX; +#endif + } else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too, flags); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + (optchar == (int)'-' && *place != '\0') || + (oli = strchr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++pkg_optind; +#ifdef GNU_COMPATIBLE + if (PRINT_ERROR) { + fprintf(stderr, "pkgconf: "); + fprintf(stderr, posixly_correct ? illoptchar : gnuoptchar, + optchar); + fprintf(stderr, "\n"); + } +#else + if (PRINT_ERROR) { + fprintf(stderr, "pkgconf: "); + fprintf(stderr, illoptchar, optchar); + fprintf(stderr, "\n"); + } +#endif + pkg_optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++pkg_optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) { + fprintf(stderr, "pkgconf: "); + fprintf(stderr, recargchar, optchar); + fprintf(stderr, "\n"); + } + pkg_optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[pkg_optind]; +#ifdef GNU_COMPATIBLE + dash_prefix = W_PREFIX; +#endif + optchar = parse_long_options(nargv, options, long_options, + idx, 0, flags); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++pkg_optind; + } else { /* takes (optional) argument */ + pkg_optarg = NULL; + if (*place) /* no white space */ + pkg_optarg = place; + else if (oli[1] != ':') { /* arg not optional */ + if (++pkg_optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) { + fprintf(stderr, "pkgconf: "); + fprintf(stderr, recargchar, optchar); + fprintf(stderr, "\n"); + } + pkg_optopt = optchar; + return (BADARG); + } else + pkg_optarg = nargv[pkg_optind]; + } + place = EMSG; + ++pkg_optind; + } + /* dump back option letter */ + return (optchar); +} + +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ +int +pkg_getopt(int nargc, char * const *nargv, const char *options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +pkg_getopt_long(int nargc, char * const *nargv, const char *options, + const struct pkg_option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} + +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int +pkg_getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct pkg_option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); +} diff --git a/contrib/pkgconf/cli/getopt_long.h b/contrib/pkgconf/cli/getopt_long.h new file mode 100644 index 00000000000..b1242915ab5 --- /dev/null +++ b/contrib/pkgconf/cli/getopt_long.h @@ -0,0 +1,70 @@ +/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GETOPT_LONG_H_ +#define _GETOPT_LONG_H_ + +#include + +/* + * GNU-like getopt_long()/getopt_long_only() with 4.4BSD optreset extension. + * getopt() is declared here too for GNU programs. + */ +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +struct pkg_option { + /* name of long option */ + const char *name; + /* + * one of no_argument, required_argument, and optional_argument: + * whether option takes an argument + */ + int has_arg; + /* if not NULL, set *flag to val when option found */ + uint64_t *flag; + /* if flag not NULL, value to set *flag to; else return value */ + uint64_t val; +}; + +int pkg_getopt_long(int, char * const *, const char *, + const struct pkg_option *, int *); +int pkg_getopt_long_only(int, char * const *, const char *, + const struct pkg_option *, int *); +int pkg_getopt(int, char * const [], const char *); + +extern char *pkg_optarg; /* getopt(3) external variables */ +extern int pkg_optind, pkg_opterr, pkg_optopt; +extern int pkg_optreset; /* getopt(3) external variable */ + +#endif diff --git a/contrib/pkgconf/cli/main.c b/contrib/pkgconf/cli/main.c new file mode 100644 index 00000000000..53eee4c8744 --- /dev/null +++ b/contrib/pkgconf/cli/main.c @@ -0,0 +1,1823 @@ +/* + * main.c + * main() routine, printer functions + * + * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + * pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include "libpkgconf/config.h" +#include +#include +#include "getopt_long.h" +#ifndef PKGCONF_LITE +#include "renderer-msvc.h" +#endif +#ifdef _WIN32 +#include /* for _setmode() */ +#include +#endif + +#define PKG_CFLAGS_ONLY_I (((uint64_t) 1) << 2) +#define PKG_CFLAGS_ONLY_OTHER (((uint64_t) 1) << 3) +#define PKG_CFLAGS (PKG_CFLAGS_ONLY_I|PKG_CFLAGS_ONLY_OTHER) +#define PKG_LIBS_ONLY_LDPATH (((uint64_t) 1) << 5) +#define PKG_LIBS_ONLY_LIBNAME (((uint64_t) 1) << 6) +#define PKG_LIBS_ONLY_OTHER (((uint64_t) 1) << 7) +#define PKG_LIBS (PKG_LIBS_ONLY_LDPATH|PKG_LIBS_ONLY_LIBNAME|PKG_LIBS_ONLY_OTHER) +#define PKG_MODVERSION (((uint64_t) 1) << 8) +#define PKG_REQUIRES (((uint64_t) 1) << 9) +#define PKG_REQUIRES_PRIVATE (((uint64_t) 1) << 10) +#define PKG_VARIABLES (((uint64_t) 1) << 11) +#define PKG_DIGRAPH (((uint64_t) 1) << 12) +#define PKG_KEEP_SYSTEM_CFLAGS (((uint64_t) 1) << 13) +#define PKG_KEEP_SYSTEM_LIBS (((uint64_t) 1) << 14) +#define PKG_VERSION (((uint64_t) 1) << 15) +#define PKG_ABOUT (((uint64_t) 1) << 16) +#define PKG_ENV_ONLY (((uint64_t) 1) << 17) +#define PKG_ERRORS_ON_STDOUT (((uint64_t) 1) << 18) +#define PKG_SILENCE_ERRORS (((uint64_t) 1) << 19) +#define PKG_IGNORE_CONFLICTS (((uint64_t) 1) << 20) +#define PKG_STATIC (((uint64_t) 1) << 21) +#define PKG_NO_UNINSTALLED (((uint64_t) 1) << 22) +#define PKG_UNINSTALLED (((uint64_t) 1) << 23) +#define PKG_LIST (((uint64_t) 1) << 24) +#define PKG_HELP (((uint64_t) 1) << 25) +#define PKG_PRINT_ERRORS (((uint64_t) 1) << 26) +#define PKG_SIMULATE (((uint64_t) 1) << 27) +#define PKG_NO_CACHE (((uint64_t) 1) << 28) +#define PKG_PROVIDES (((uint64_t) 1) << 29) +#define PKG_VALIDATE (((uint64_t) 1) << 30) +#define PKG_LIST_PACKAGE_NAMES (((uint64_t) 1) << 31) +#define PKG_NO_PROVIDES (((uint64_t) 1) << 32) +#define PKG_PURE (((uint64_t) 1) << 33) +#define PKG_PATH (((uint64_t) 1) << 34) +#define PKG_DEFINE_PREFIX (((uint64_t) 1) << 35) +#define PKG_DONT_DEFINE_PREFIX (((uint64_t) 1) << 36) +#define PKG_DONT_RELOCATE_PATHS (((uint64_t) 1) << 37) +#define PKG_DEBUG (((uint64_t) 1) << 38) +#define PKG_SHORT_ERRORS (((uint64_t) 1) << 39) +#define PKG_EXISTS (((uint64_t) 1) << 40) +#define PKG_MSVC_SYNTAX (((uint64_t) 1) << 41) +#define PKG_INTERNAL_CFLAGS (((uint64_t) 1) << 42) +#define PKG_DUMP_PERSONALITY (((uint64_t) 1) << 43) +#define PKG_SHARED (((uint64_t) 1) << 44) +#define PKG_DUMP_LICENSE (((uint64_t) 1) << 45) +#define PKG_SOLUTION (((uint64_t) 1) << 46) +#define PKG_EXISTS_CFLAGS (((uint64_t) 1) << 47) +#define PKG_FRAGMENT_TREE (((uint64_t) 1) << 48) + +static pkgconf_client_t pkg_client; +static const pkgconf_fragment_render_ops_t *want_render_ops = NULL; + +static uint64_t want_flags; +static int verbosity = 0; +static int maximum_traverse_depth = 2000; +static size_t maximum_package_count = 0; + +static char *want_variable = NULL; +static char *want_fragment_filter = NULL; + +FILE *error_msgout = NULL; +FILE *logfile_out = NULL; + +static bool +error_handler(const char *msg, const pkgconf_client_t *client, void *data) +{ + (void) client; + (void) data; + fprintf(error_msgout, "%s", msg); + return true; +} + +static bool +print_list_entry(const pkgconf_pkg_t *entry, void *data) +{ + (void) data; + + if (entry->flags & PKGCONF_PKG_PROPF_UNINSTALLED) + return false; + + printf("%-30s %s - %s\n", entry->id, entry->realname, entry->description); + + return false; +} + +static bool +print_package_entry(const pkgconf_pkg_t *entry, void *data) +{ + (void) data; + + if (entry->flags & PKGCONF_PKG_PROPF_UNINSTALLED) + return false; + + printf("%s\n", entry->id); + + return false; +} + +static bool +filter_cflags(const pkgconf_client_t *client, const pkgconf_fragment_t *frag, void *data) +{ + int got_flags = 0; + (void) client; + (void) data; + + if (!(want_flags & PKG_KEEP_SYSTEM_CFLAGS) && pkgconf_fragment_has_system_dir(client, frag)) + return false; + + if (want_fragment_filter != NULL && (strchr(want_fragment_filter, frag->type) == NULL || !frag->type)) + return false; + + if (frag->type == 'I') + got_flags = PKG_CFLAGS_ONLY_I; + else + got_flags = PKG_CFLAGS_ONLY_OTHER; + + return (want_flags & got_flags) != 0; +} + +static bool +filter_libs(const pkgconf_client_t *client, const pkgconf_fragment_t *frag, void *data) +{ + int got_flags = 0; + (void) client; + (void) data; + + if (!(want_flags & PKG_KEEP_SYSTEM_LIBS) && pkgconf_fragment_has_system_dir(client, frag)) + return false; + + if (want_fragment_filter != NULL && (strchr(want_fragment_filter, frag->type) == NULL || !frag->type)) + return false; + + switch (frag->type) + { + case 'L': got_flags = PKG_LIBS_ONLY_LDPATH; break; + case 'l': got_flags = PKG_LIBS_ONLY_LIBNAME; break; + default: got_flags = PKG_LIBS_ONLY_OTHER; break; + } + + return (want_flags & got_flags) != 0; +} + +static void +print_variables(pkgconf_pkg_t *pkg) +{ + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(pkg->vars.head, node) + { + pkgconf_tuple_t *tuple = node->data; + + printf("%s\n", tuple->key); + } +} + +static void +print_requires(pkgconf_pkg_t *pkg) +{ + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(pkg->required.head, node) + { + pkgconf_dependency_t *dep = node->data; + + printf("%s", dep->package); + + if (dep->version != NULL) + printf(" %s %s", pkgconf_pkg_get_comparator(dep), dep->version); + + printf("\n"); + } +} + +static void +print_requires_private(pkgconf_pkg_t *pkg) +{ + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(pkg->requires_private.head, node) + { + pkgconf_dependency_t *dep = node->data; + + printf("%s", dep->package); + + if (dep->version != NULL) + printf(" %s %s", pkgconf_pkg_get_comparator(dep), dep->version); + + printf("\n"); + } +} + +static void +print_provides(pkgconf_pkg_t *pkg) +{ + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(pkg->provides.head, node) + { + pkgconf_dependency_t *dep = node->data; + + printf("%s", dep->package); + + if (dep->version != NULL) + printf(" %s %s", pkgconf_pkg_get_comparator(dep), dep->version); + + printf("\n"); + } +} + +static bool +apply_provides(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int maxdepth) +{ + pkgconf_node_t *iter; + (void) client; + (void) unused; + (void) maxdepth; + + PKGCONF_FOREACH_LIST_ENTRY(world->required.head, iter) + { + pkgconf_dependency_t *dep = iter->data; + pkgconf_pkg_t *pkg = dep->match; + + print_provides(pkg); + } + + return true; +} + +#ifndef PKGCONF_LITE +static void +print_digraph_node(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data) +{ + pkgconf_node_t *node; + (void) client; + pkgconf_pkg_t **last_seen = data; + + if(pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL) + return; + + if (pkg->flags & PKGCONF_PKG_PROPF_VISITED_PRIVATE) + printf("\"%s\" [fontname=Sans fontsize=8 fontcolor=gray color=gray]\n", pkg->id); + else + printf("\"%s\" [fontname=Sans fontsize=8]\n", pkg->id); + + if (last_seen != NULL) + { + if (*last_seen != NULL) + printf("\"%s\" -> \"%s\" [fontname=Sans fontsize=8 color=red]\n", (*last_seen)->id, pkg->id); + + *last_seen = pkg; + } + + PKGCONF_FOREACH_LIST_ENTRY(pkg->required.head, node) + { + pkgconf_dependency_t *dep = node->data; + const char *dep_id = (dep->match != NULL) ? dep->match->id : dep->package; + + if ((dep->flags & PKGCONF_PKG_DEPF_PRIVATE) == 0) + printf("\"%s\" -> \"%s\" [fontname=Sans fontsize=8]\n", pkg->id, dep_id); + else + printf("\"%s\" -> \"%s\" [fontname=Sans fontsize=8 color=gray]\n", pkg->id, dep_id); + } + + PKGCONF_FOREACH_LIST_ENTRY(pkg->requires_private.head, node) + { + pkgconf_dependency_t *dep = node->data; + const char *dep_id = (dep->match != NULL) ? dep->match->id : dep->package; + + printf("\"%s\" -> \"%s\" [fontname=Sans fontsize=8 color=gray]\n", pkg->id, dep_id); + } +} + +static bool +apply_digraph(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int maxdepth) +{ + int eflag; + pkgconf_list_t *list = data; + pkgconf_pkg_t *last_seen = NULL; + pkgconf_node_t *iter; + + printf("digraph deptree {\n"); + printf("edge [color=blue len=7.5 fontname=Sans fontsize=8]\n"); + printf("node [fontname=Sans fontsize=8]\n"); + printf("\"user:request\" [fontname=Sans fontsize=8]\n"); + + PKGCONF_FOREACH_LIST_ENTRY(list->head, iter) + { + pkgconf_queue_t *pkgq = iter->data; + pkgconf_pkg_t *pkg = pkgconf_pkg_find(client, pkgq->package); + printf("\"user:request\" -> \"%s\" [fontname=Sans fontsize=8]\n", pkg == NULL ? pkgq->package : pkg->id); + if (pkg != NULL) + pkgconf_pkg_unref(client, pkg); + } + + eflag = pkgconf_pkg_traverse(client, world, print_digraph_node, &last_seen, maxdepth, 0); + + if (eflag != PKGCONF_PKG_ERRF_OK) + return false; + + printf("}\n"); + return true; +} + +static void +print_solution_node(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *unused) +{ + (void) client; + (void) unused; + + printf("%s (%"PRIu64")%s\n", pkg->id, pkg->identifier, (pkg->flags & PKGCONF_PKG_PROPF_VISITED_PRIVATE) == PKGCONF_PKG_PROPF_VISITED_PRIVATE ? " [private]" : ""); +} + +static bool +apply_print_solution(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int maxdepth) +{ + int eflag; + + eflag = pkgconf_pkg_traverse(client, world, print_solution_node, unused, maxdepth, 0); + + return eflag == PKGCONF_PKG_ERRF_OK; +} +#endif + +static bool +apply_modversion(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int maxdepth) +{ + pkgconf_node_t *queue_iter; + pkgconf_list_t *pkgq = data; + (void) client; + (void) maxdepth; + + PKGCONF_FOREACH_LIST_ENTRY(pkgq->head, queue_iter) + { + pkgconf_node_t *world_iter; + pkgconf_queue_t *queue_node = queue_iter->data; + + PKGCONF_FOREACH_LIST_ENTRY(world->required.head, world_iter) + { + pkgconf_dependency_t *dep = world_iter->data; + pkgconf_pkg_t *pkg = dep->match; + + const size_t name_len = strlen(pkg->why); + if (name_len > strlen(queue_node->package) || + strncmp(pkg->why, queue_node->package, name_len) || + (queue_node->package[name_len] != 0 && + !isspace((unsigned char)queue_node->package[name_len]) && + !PKGCONF_IS_OPERATOR_CHAR(queue_node->package[name_len]))) + continue; + + if (pkg->version != NULL) { + if (verbosity) + printf("%s: ", pkg->id); + + printf("%s\n", pkg->version); + } + + break; + } + } + + return true; +} + +static bool +apply_variables(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int maxdepth) +{ + pkgconf_node_t *iter; + (void) client; + (void) unused; + (void) maxdepth; + + PKGCONF_FOREACH_LIST_ENTRY(world->required.head, iter) + { + pkgconf_dependency_t *dep = iter->data; + pkgconf_pkg_t *pkg = dep->match; + + print_variables(pkg); + } + + return true; +} + +static bool +apply_path(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int maxdepth) +{ + pkgconf_node_t *iter; + (void) client; + (void) unused; + (void) maxdepth; + + PKGCONF_FOREACH_LIST_ENTRY(world->required.head, iter) + { + pkgconf_dependency_t *dep = iter->data; + pkgconf_pkg_t *pkg = dep->match; + + /* a module entry with no filename is either virtual, static (builtin) or synthesized. */ + if (pkg->filename != NULL) + printf("%s\n", pkg->filename); + } + + return true; +} + +static bool +apply_variable(pkgconf_client_t *client, pkgconf_pkg_t *world, void *variable, int maxdepth) +{ + pkgconf_node_t *iter; + (void) maxdepth; + + PKGCONF_FOREACH_LIST_ENTRY(world->required.head, iter) + { + pkgconf_dependency_t *dep = iter->data; + pkgconf_pkg_t *pkg = dep->match; + const char *var; + + var = pkgconf_tuple_find(client, &pkg->vars, variable); + + if (var != NULL) + printf("%s%s", iter->prev != NULL ? " " : "", var); + } + + printf("\n"); + + return true; +} + +static bool +apply_env_var(const char *prefix, pkgconf_client_t *client, pkgconf_pkg_t *world, int maxdepth, + unsigned int (*collect_fn)(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *list, int maxdepth), + bool (*filter_fn)(const pkgconf_client_t *client, const pkgconf_fragment_t *frag, void *data), + void (*postprocess_fn)(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *fragment_list)) +{ + pkgconf_list_t unfiltered_list = PKGCONF_LIST_INITIALIZER; + pkgconf_list_t filtered_list = PKGCONF_LIST_INITIALIZER; + unsigned int eflag; + char *render_buf; + + eflag = collect_fn(client, world, &unfiltered_list, maxdepth); + if (eflag != PKGCONF_PKG_ERRF_OK) + return false; + + pkgconf_fragment_filter(client, &filtered_list, &unfiltered_list, filter_fn, NULL); + + if (postprocess_fn != NULL) + postprocess_fn(client, world, &filtered_list); + + if (filtered_list.head == NULL) + goto out; + + render_buf = pkgconf_fragment_render(&filtered_list, true, want_render_ops); + printf("%s='%s'\n", prefix, render_buf); + free(render_buf); + +out: + pkgconf_fragment_free(&unfiltered_list); + pkgconf_fragment_free(&filtered_list); + + return true; +} + +static void +maybe_add_module_definitions(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *fragment_list) +{ + pkgconf_node_t *world_iter; + + if ((want_flags & PKG_EXISTS_CFLAGS) != PKG_EXISTS_CFLAGS) + return; + + PKGCONF_FOREACH_LIST_ENTRY(world->required.head, world_iter) + { + pkgconf_dependency_t *dep = world_iter->data; + char havebuf[PKGCONF_ITEM_SIZE]; + char *p; + + if ((dep->flags & PKGCONF_PKG_DEPF_QUERY) != PKGCONF_PKG_DEPF_QUERY) + continue; + + if (dep->match == NULL) + continue; + + snprintf(havebuf, sizeof havebuf, "HAVE_%s", dep->match->id); + + for (p = havebuf; *p; p++) + { + switch (*p) + { + case ' ': + case '-': + *p = '_'; + break; + + default: + *p = toupper((unsigned char) *p); + } + } + + pkgconf_fragment_insert(client, fragment_list, 'D', havebuf, false); + } +} + +static void +apply_env_variables(pkgconf_client_t *client, pkgconf_pkg_t *world, const char *env_prefix) +{ + (void) client; + pkgconf_node_t *world_iter; + + PKGCONF_FOREACH_LIST_ENTRY(world->required.head, world_iter) + { + pkgconf_dependency_t *dep = world_iter->data; + pkgconf_pkg_t *pkg = dep->match; + pkgconf_node_t *tuple_iter; + + if ((dep->flags & PKGCONF_PKG_DEPF_QUERY) != PKGCONF_PKG_DEPF_QUERY) + continue; + + if (dep->match == NULL) + continue; + + PKGCONF_FOREACH_LIST_ENTRY(pkg->vars.head, tuple_iter) + { + pkgconf_tuple_t *tuple = tuple_iter->data; + char havebuf[PKGCONF_ITEM_SIZE]; + char *p; + + if (want_variable != NULL && strcmp(want_variable, tuple->key)) + continue; + + snprintf(havebuf, sizeof havebuf, "%s_%s", env_prefix, tuple->key); + + for (p = havebuf; *p; p++) + { + switch (*p) + { + case ' ': + case '-': + *p = '_'; + break; + + default: + *p = toupper((unsigned char) *p); + } + } + + printf("%s='%s'\n", havebuf, tuple->value); + } + } +} + +static bool +apply_env(pkgconf_client_t *client, pkgconf_pkg_t *world, void *env_prefix_p, int maxdepth) +{ + const char *want_env_prefix = env_prefix_p, *it; + char workbuf[PKGCONF_ITEM_SIZE]; + + for (it = want_env_prefix; *it != '\0'; it++) + if (!isalpha((unsigned char)*it) && + !isdigit((unsigned char)*it)) + return false; + + snprintf(workbuf, sizeof workbuf, "%s_CFLAGS", want_env_prefix); + if (!apply_env_var(workbuf, client, world, maxdepth, pkgconf_pkg_cflags, filter_cflags, maybe_add_module_definitions)) + return false; + + snprintf(workbuf, sizeof workbuf, "%s_LIBS", want_env_prefix); + if (!apply_env_var(workbuf, client, world, maxdepth, pkgconf_pkg_libs, filter_libs, NULL)) + return false; + + if ((want_flags & PKG_VARIABLES) == PKG_VARIABLES || want_variable != NULL) + apply_env_variables(client, world, want_env_prefix); + + return true; +} + +static bool +apply_cflags(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int maxdepth) +{ + pkgconf_list_t unfiltered_list = PKGCONF_LIST_INITIALIZER; + pkgconf_list_t filtered_list = PKGCONF_LIST_INITIALIZER; + int eflag; + char *render_buf; + (void) unused; + + eflag = pkgconf_pkg_cflags(client, world, &unfiltered_list, maxdepth); + if (eflag != PKGCONF_PKG_ERRF_OK) + return false; + + pkgconf_fragment_filter(client, &filtered_list, &unfiltered_list, filter_cflags, NULL); + maybe_add_module_definitions(client, world, &filtered_list); + + if (filtered_list.head == NULL) + goto out; + + render_buf = pkgconf_fragment_render(&filtered_list, true, want_render_ops); + printf("%s", render_buf); + free(render_buf); + +out: + pkgconf_fragment_free(&unfiltered_list); + pkgconf_fragment_free(&filtered_list); + + return true; +} + +static bool +apply_libs(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int maxdepth) +{ + pkgconf_list_t unfiltered_list = PKGCONF_LIST_INITIALIZER; + pkgconf_list_t filtered_list = PKGCONF_LIST_INITIALIZER; + int eflag; + char *render_buf; + (void) unused; + + eflag = pkgconf_pkg_libs(client, world, &unfiltered_list, maxdepth); + if (eflag != PKGCONF_PKG_ERRF_OK) + return false; + + pkgconf_fragment_filter(client, &filtered_list, &unfiltered_list, filter_libs, NULL); + + if (filtered_list.head == NULL) + goto out; + + render_buf = pkgconf_fragment_render(&filtered_list, true, want_render_ops); + printf("%s", render_buf); + free(render_buf); + +out: + pkgconf_fragment_free(&unfiltered_list); + pkgconf_fragment_free(&filtered_list); + + return true; +} + +static bool +apply_requires(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int maxdepth) +{ + pkgconf_node_t *iter; + (void) client; + (void) unused; + (void) maxdepth; + + PKGCONF_FOREACH_LIST_ENTRY(world->required.head, iter) + { + pkgconf_dependency_t *dep = iter->data; + pkgconf_pkg_t *pkg = dep->match; + + print_requires(pkg); + } + + return true; +} + +static bool +apply_requires_private(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int maxdepth) +{ + pkgconf_node_t *iter; + (void) client; + (void) unused; + (void) maxdepth; + + PKGCONF_FOREACH_LIST_ENTRY(world->required.head, iter) + { + pkgconf_dependency_t *dep = iter->data; + pkgconf_pkg_t *pkg = dep->match; + + print_requires_private(pkg); + } + return true; +} + +static void +check_uninstalled(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data) +{ + int *retval = data; + (void) client; + + if (pkg->flags & PKGCONF_PKG_PROPF_UNINSTALLED) + *retval = EXIT_SUCCESS; +} + +static bool +apply_uninstalled(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int maxdepth) +{ + int eflag; + + eflag = pkgconf_pkg_traverse(client, world, check_uninstalled, data, maxdepth, 0); + + if (eflag != PKGCONF_PKG_ERRF_OK) + return false; + + return true; +} + +#ifndef PKGCONF_LITE +static void +print_graph_node(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data) +{ + pkgconf_node_t *n; + + (void) client; + (void) data; + + printf("node '%s' {\n", pkg->id); + + if (pkg->version != NULL) + printf(" version = '%s';\n", pkg->version); + + PKGCONF_FOREACH_LIST_ENTRY(pkg->required.head, n) + { + pkgconf_dependency_t *dep = n->data; + printf(" dependency '%s'", dep->package); + if (dep->compare != PKGCONF_CMP_ANY) + { + printf(" {\n"); + printf(" comparator = '%s';\n", pkgconf_pkg_get_comparator(dep)); + printf(" version = '%s';\n", dep->version); + printf(" };\n"); + } + else + printf(";\n"); + } + + printf("};\n"); +} + +static bool +apply_simulate(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int maxdepth) +{ + int eflag; + + eflag = pkgconf_pkg_traverse(client, world, print_graph_node, data, maxdepth, 0); + + if (eflag != PKGCONF_PKG_ERRF_OK) + return false; + + return true; +} +#endif + +static void +print_fragment_tree_branch(pkgconf_list_t *fragment_list, int indent) +{ + pkgconf_node_t *iter; + + PKGCONF_FOREACH_LIST_ENTRY(fragment_list->head, iter) + { + pkgconf_fragment_t *frag = iter->data; + + if (frag->type) + printf("%*s'-%c%s' [type %c]\n", indent, "", frag->type, frag->data, frag->type); + else + printf("%*s'%s' [untyped]\n", indent, "", frag->data); + + print_fragment_tree_branch(&frag->children, indent + 2); + } + + if (fragment_list->head != NULL) + printf("\n"); +} + +static bool +apply_fragment_tree(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int maxdepth) +{ + pkgconf_list_t unfiltered_list = PKGCONF_LIST_INITIALIZER; + int eflag; + + (void) data; + + eflag = pkgconf_pkg_cflags(client, world, &unfiltered_list, maxdepth); + if (eflag != PKGCONF_PKG_ERRF_OK) + return false; + + eflag = pkgconf_pkg_libs(client, world, &unfiltered_list, maxdepth); + if (eflag != PKGCONF_PKG_ERRF_OK) + return false; + + print_fragment_tree_branch(&unfiltered_list, 0); + pkgconf_fragment_free(&unfiltered_list); + + return true; +} + +static void +print_license(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data) +{ + (void) client; + (void) data; + + if (pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL) + return; + + /* NOASSERTION is the default when the license is unknown, per SPDX spec § 3.15 */ + printf("%s: %s\n", pkg->id, pkg->license != NULL ? pkg->license : "NOASSERTION"); +} + +static bool +apply_license(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int maxdepth) +{ + int eflag; + + eflag = pkgconf_pkg_traverse(client, world, print_license, data, maxdepth, 0); + + if (eflag != PKGCONF_PKG_ERRF_OK) + return false; + + return true; +} + +static void +version(void) +{ + printf("%s\n", PACKAGE_VERSION); +} + +static void +about(void) +{ + printf("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); + printf("Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021\n"); + printf(" pkgconf authors (see AUTHORS in documentation directory).\n\n"); + printf("Permission to use, copy, modify, and/or distribute this software for any\n"); + printf("purpose with or without fee is hereby granted, provided that the above\n"); + printf("copyright notice and this permission notice appear in all copies.\n\n"); + printf("This software is provided 'as is' and without any warranty, express or\n"); + printf("implied. In no event shall the authors be liable for any damages arising\n"); + printf("from the use of this software.\n\n"); + printf("Report bugs at <%s>.\n", PACKAGE_BUGREPORT); +} + +static void +usage(void) +{ + printf("usage: %s [OPTIONS] [LIBRARIES]\n", PACKAGE_NAME); + + printf("\nbasic options:\n\n"); + + printf(" --help this message\n"); + printf(" --about print pkgconf version and license to stdout\n"); + printf(" --version print supported pkg-config version to stdout\n"); + printf(" --verbose print additional information\n"); + printf(" --atleast-pkgconfig-version check whether or not pkgconf is compatible\n"); + printf(" with a specified pkg-config version\n"); + printf(" --errors-to-stdout print all errors on stdout instead of stderr\n"); + printf(" --print-errors ensure all errors are printed\n"); + printf(" --short-errors be less verbose about some errors\n"); + printf(" --silence-errors explicitly be silent about errors\n"); + printf(" --list-all list all known packages\n"); + printf(" --list-package-names list all known package names\n"); +#ifndef PKGCONF_LITE + printf(" --simulate simulate walking the calculated dependency graph\n"); +#endif + printf(" --no-cache do not cache already seen packages when\n"); + printf(" walking the dependency graph\n"); + printf(" --log-file=filename write an audit log to a specified file\n"); + printf(" --with-path=path adds a directory to the search path\n"); + printf(" --define-prefix override the prefix variable with one that is guessed based on\n"); + printf(" the location of the .pc file\n"); + printf(" --dont-define-prefix do not override the prefix variable under any circumstances\n"); + printf(" --prefix-variable=varname sets the name of the variable that pkgconf considers\n"); + printf(" to be the package prefix\n"); + printf(" --relocate=path relocates a path and exits (mostly for testsuite)\n"); + printf(" --dont-relocate-paths disables path relocation support\n"); + +#ifndef PKGCONF_LITE + printf("\ncross-compilation personality support:\n\n"); + printf(" --personality=triplet|filename sets the personality to 'triplet' or a file named 'filename'\n"); + printf(" --dump-personality dumps details concerning selected personality\n"); +#endif + + printf("\nchecking specific pkg-config database entries:\n\n"); + + printf(" --atleast-version require a specific version of a module\n"); + printf(" --exact-version require an exact version of a module\n"); + printf(" --max-version require a maximum version of a module\n"); + printf(" --exists check whether or not a module exists\n"); + printf(" --uninstalled check whether or not an uninstalled module will be used\n"); + printf(" --no-uninstalled never use uninstalled modules when satisfying dependencies\n"); + printf(" --no-provides do not use 'provides' rules to resolve dependencies\n"); + printf(" --maximum-traverse-depth maximum allowed depth for dependency graph\n"); + printf(" --static be more aggressive when computing dependency graph\n"); + printf(" (for static linking)\n"); + printf(" --shared use a simplified dependency graph (usually default)\n"); + printf(" --pure optimize a static dependency graph as if it were a normal\n"); + printf(" dependency graph\n"); + printf(" --env-only look only for package entries in PKG_CONFIG_PATH\n"); + printf(" --ignore-conflicts ignore 'conflicts' rules in modules\n"); + printf(" --validate validate specific .pc files for correctness\n"); + + printf("\nquerying specific pkg-config database fields:\n\n"); + + printf(" --define-variable=varname=value define variable 'varname' as 'value'\n"); + printf(" --variable=varname print specified variable entry to stdout\n"); + printf(" --cflags print required CFLAGS to stdout\n"); + printf(" --cflags-only-I print required include-dir CFLAGS to stdout\n"); + printf(" --cflags-only-other print required non-include-dir CFLAGS to stdout\n"); + printf(" --libs print required linker flags to stdout\n"); + printf(" --libs-only-L print required LDPATH linker flags to stdout\n"); + printf(" --libs-only-l print required LIBNAME linker flags to stdout\n"); + printf(" --libs-only-other print required other linker flags to stdout\n"); + printf(" --print-requires print required dependency frameworks to stdout\n"); + printf(" --print-requires-private print required dependency frameworks for static\n"); + printf(" linking to stdout\n"); + printf(" --print-provides print provided dependencies to stdout\n"); + printf(" --print-variables print all known variables in module to stdout\n"); +#ifndef PKGCONF_LITE + printf(" --digraph print entire dependency graph in graphviz 'dot' format\n"); + printf(" --solution print dependency graph solution in a simple format\n"); +#endif + printf(" --keep-system-cflags keep -I%s entries in cflags output\n", SYSTEM_INCLUDEDIR); + printf(" --keep-system-libs keep -L%s entries in libs output\n", SYSTEM_LIBDIR); + printf(" --path show the exact filenames for any matching .pc files\n"); + printf(" --modversion print the specified module's version to stdout\n"); + printf(" --internal-cflags do not filter 'internal' cflags from output\n"); + printf(" --license print the specified module's license to stdout if known\n"); + printf(" --exists-cflags add -DHAVE_FOO fragments to cflags for each found module\n"); + + printf("\nfiltering output:\n\n"); +#ifndef PKGCONF_LITE + printf(" --msvc-syntax print translatable fragments in MSVC syntax\n"); +#endif + printf(" --fragment-filter=types filter output fragments to the specified types\n"); + printf(" --env=prefix print output as shell-compatible environmental variables\n"); + printf(" --fragment-tree visualize printed CFLAGS/LIBS fragments as a tree\n"); + + printf("\nreport bugs to <%s>.\n", PACKAGE_BUGREPORT); +} + +static void +relocate_path(const char *path) +{ + char buf[PKGCONF_BUFSIZE]; + + pkgconf_strlcpy(buf, path, sizeof buf); + pkgconf_path_relocate(buf, sizeof buf); + + printf("%s\n", buf); +} + +#ifndef PKGCONF_LITE +static void +dump_personality(const pkgconf_cross_personality_t *p) +{ + pkgconf_node_t *n; + + printf("Triplet: %s\n", p->name); + + if (p->sysroot_dir) + printf("SysrootDir: %s\n", p->sysroot_dir); + + printf("DefaultSearchPaths: "); + PKGCONF_FOREACH_LIST_ENTRY(p->dir_list.head, n) + { + pkgconf_path_t *pn = n->data; + printf("%s ", pn->path); + } + + printf("\n"); + printf("SystemIncludePaths: "); + PKGCONF_FOREACH_LIST_ENTRY(p->filter_includedirs.head, n) + { + pkgconf_path_t *pn = n->data; + printf("%s ", pn->path); + } + + printf("\n"); + printf("SystemLibraryPaths: "); + PKGCONF_FOREACH_LIST_ENTRY(p->filter_libdirs.head, n) + { + pkgconf_path_t *pn = n->data; + printf("%s ", pn->path); + } + + printf("\n"); +} + +static pkgconf_cross_personality_t * +deduce_personality(char *argv[]) +{ + const char *argv0 = argv[0]; + char *i, *prefix; + pkgconf_cross_personality_t *out; + + i = strrchr(argv0, '/'); + if (i != NULL) + argv0 = i + 1; + +#if defined(_WIN32) || defined(_WIN64) + i = strrchr(argv0, '\\'); + if (i != NULL) + argv0 = i + 1; +#endif + + i = strstr(argv0, "-pkg"); + if (i == NULL) + return pkgconf_cross_personality_default(); + + prefix = pkgconf_strndup(argv0, i - argv0); + out = pkgconf_cross_personality_find(prefix); + free(prefix); + if (out == NULL) + return pkgconf_cross_personality_default(); + + return out; +} +#endif + +static void +unveil_handler(const pkgconf_client_t *client, const char *path, const char *permissions) +{ + (void) client; + + if (pkgconf_unveil(path, permissions) == -1) + { + fprintf(stderr, "pkgconf: unveil failed: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } +} + +static bool +unveil_search_paths(const pkgconf_client_t *client, const pkgconf_cross_personality_t *personality) +{ + pkgconf_node_t *n; + + if (pkgconf_unveil("/dev/null", "rwc") == -1) + return false; + + PKGCONF_FOREACH_LIST_ENTRY(client->dir_list.head, n) + { + pkgconf_path_t *pn = n->data; + + if (pkgconf_unveil(pn->path, "r") == -1) + return false; + } + + PKGCONF_FOREACH_LIST_ENTRY(personality->dir_list.head, n) + { + pkgconf_path_t *pn = n->data; + + if (pkgconf_unveil(pn->path, "r") == -1) + return false; + } + + pkgconf_client_set_unveil_handler(&pkg_client, unveil_handler); + + return true; +} + +int +main(int argc, char *argv[]) +{ + int ret; + pkgconf_list_t pkgq = PKGCONF_LIST_INITIALIZER; + pkgconf_list_t dir_list = PKGCONF_LIST_INITIALIZER; + char *builddir; + char *sysroot_dir; + char *env_traverse_depth; + char *required_pkgconfig_version = NULL; + char *required_exact_module_version = NULL; + char *required_max_module_version = NULL; + char *required_module_version = NULL; + char *logfile_arg = NULL; + char *want_env_prefix = NULL; + unsigned int want_client_flags = PKGCONF_PKG_PKGF_NONE; + pkgconf_cross_personality_t *personality = NULL; + bool opened_error_msgout = false; + pkgconf_pkg_t world = { + .id = "virtual:world", + .realname = "virtual world package", + .flags = PKGCONF_PKG_PROPF_STATIC | PKGCONF_PKG_PROPF_VIRTUAL, + }; + + if (pkgconf_pledge("stdio rpath wpath cpath unveil", NULL) == -1) + { + fprintf(stderr, "pkgconf: pledge failed: %s\n", strerror(errno)); + return EXIT_FAILURE; + } + + want_flags = 0; + +#ifdef _WIN32 + /* When running regression tests in cygwin, and building native + * executable, tests fail unless native executable outputs unix + * line endings. Come to think of it, this will probably help + * real people who use cygwin build environments but native pkgconf, too. + */ + _setmode(fileno(stdout), O_BINARY); + _setmode(fileno(stderr), O_BINARY); +#endif + + struct pkg_option options[] = { + { "version", no_argument, &want_flags, PKG_VERSION|PKG_PRINT_ERRORS, }, + { "about", no_argument, &want_flags, PKG_ABOUT|PKG_PRINT_ERRORS, }, + { "atleast-version", required_argument, NULL, 2, }, + { "atleast-pkgconfig-version", required_argument, NULL, 3, }, + { "libs", no_argument, &want_flags, PKG_LIBS|PKG_PRINT_ERRORS, }, + { "cflags", no_argument, &want_flags, PKG_CFLAGS|PKG_PRINT_ERRORS, }, + { "modversion", no_argument, &want_flags, PKG_MODVERSION|PKG_PRINT_ERRORS, }, + { "variable", required_argument, NULL, 7, }, + { "exists", no_argument, &want_flags, PKG_EXISTS, }, + { "print-errors", no_argument, &want_flags, PKG_PRINT_ERRORS, }, + { "short-errors", no_argument, &want_flags, PKG_SHORT_ERRORS, }, + { "maximum-traverse-depth", required_argument, NULL, 11, }, + { "static", no_argument, &want_flags, PKG_STATIC, }, + { "shared", no_argument, &want_flags, PKG_SHARED, }, + { "pure", no_argument, &want_flags, PKG_PURE, }, + { "print-requires", no_argument, &want_flags, PKG_REQUIRES, }, + { "print-variables", no_argument, &want_flags, PKG_VARIABLES|PKG_PRINT_ERRORS, }, +#ifndef PKGCONF_LITE + { "digraph", no_argument, &want_flags, PKG_DIGRAPH, }, + { "solution", no_argument, &want_flags, PKG_SOLUTION, }, +#endif + { "help", no_argument, &want_flags, PKG_HELP, }, + { "env-only", no_argument, &want_flags, PKG_ENV_ONLY, }, + { "print-requires-private", no_argument, &want_flags, PKG_REQUIRES_PRIVATE, }, + { "cflags-only-I", no_argument, &want_flags, PKG_CFLAGS_ONLY_I|PKG_PRINT_ERRORS, }, + { "cflags-only-other", no_argument, &want_flags, PKG_CFLAGS_ONLY_OTHER|PKG_PRINT_ERRORS, }, + { "libs-only-L", no_argument, &want_flags, PKG_LIBS_ONLY_LDPATH|PKG_PRINT_ERRORS, }, + { "libs-only-l", no_argument, &want_flags, PKG_LIBS_ONLY_LIBNAME|PKG_PRINT_ERRORS, }, + { "libs-only-other", no_argument, &want_flags, PKG_LIBS_ONLY_OTHER|PKG_PRINT_ERRORS, }, + { "uninstalled", no_argument, &want_flags, PKG_UNINSTALLED, }, + { "no-uninstalled", no_argument, &want_flags, PKG_NO_UNINSTALLED, }, + { "keep-system-cflags", no_argument, &want_flags, PKG_KEEP_SYSTEM_CFLAGS, }, + { "keep-system-libs", no_argument, &want_flags, PKG_KEEP_SYSTEM_LIBS, }, + { "define-variable", required_argument, NULL, 27, }, + { "exact-version", required_argument, NULL, 28, }, + { "max-version", required_argument, NULL, 29, }, + { "ignore-conflicts", no_argument, &want_flags, PKG_IGNORE_CONFLICTS, }, + { "errors-to-stdout", no_argument, &want_flags, PKG_ERRORS_ON_STDOUT, }, + { "silence-errors", no_argument, &want_flags, PKG_SILENCE_ERRORS, }, + { "list-all", no_argument, &want_flags, PKG_LIST|PKG_PRINT_ERRORS, }, + { "list-package-names", no_argument, &want_flags, PKG_LIST_PACKAGE_NAMES|PKG_PRINT_ERRORS, }, +#ifndef PKGCONF_LITE + { "simulate", no_argument, &want_flags, PKG_SIMULATE, }, +#endif + { "no-cache", no_argument, &want_flags, PKG_NO_CACHE, }, + { "print-provides", no_argument, &want_flags, PKG_PROVIDES, }, + { "no-provides", no_argument, &want_flags, PKG_NO_PROVIDES, }, + { "debug", no_argument, &want_flags, PKG_DEBUG|PKG_PRINT_ERRORS, }, + { "validate", no_argument, &want_flags, PKG_VALIDATE|PKG_PRINT_ERRORS|PKG_ERRORS_ON_STDOUT }, + { "log-file", required_argument, NULL, 40 }, + { "path", no_argument, &want_flags, PKG_PATH }, + { "with-path", required_argument, NULL, 42 }, + { "prefix-variable", required_argument, NULL, 43 }, + { "define-prefix", no_argument, &want_flags, PKG_DEFINE_PREFIX }, + { "relocate", required_argument, NULL, 45 }, + { "dont-define-prefix", no_argument, &want_flags, PKG_DONT_DEFINE_PREFIX }, + { "dont-relocate-paths", no_argument, &want_flags, PKG_DONT_RELOCATE_PATHS }, + { "env", required_argument, NULL, 48 }, +#ifndef PKGCONF_LITE + { "msvc-syntax", no_argument, &want_flags, PKG_MSVC_SYNTAX }, +#endif + { "fragment-filter", required_argument, NULL, 50 }, + { "internal-cflags", no_argument, &want_flags, PKG_INTERNAL_CFLAGS }, +#ifndef PKGCONF_LITE + { "dump-personality", no_argument, &want_flags, PKG_DUMP_PERSONALITY }, + { "personality", required_argument, NULL, 53 }, +#endif + { "license", no_argument, &want_flags, PKG_DUMP_LICENSE }, + { "verbose", no_argument, NULL, 55 }, + { "exists-cflags", no_argument, &want_flags, PKG_EXISTS_CFLAGS }, + { "fragment-tree", no_argument, &want_flags, PKG_FRAGMENT_TREE }, + { NULL, 0, NULL, 0 } + }; + +#ifndef PKGCONF_LITE + if (getenv("PKG_CONFIG_EARLY_TRACE")) + { + error_msgout = stderr; + pkgconf_client_set_trace_handler(&pkg_client, error_handler, NULL); + } +#endif + + while ((ret = pkg_getopt_long_only(argc, argv, "", options, NULL)) != -1) + { + switch (ret) + { + case 2: + required_module_version = pkg_optarg; + break; + case 3: + required_pkgconfig_version = pkg_optarg; + break; + case 7: + want_variable = pkg_optarg; + break; + case 11: + maximum_traverse_depth = atoi(pkg_optarg); + break; + case 27: + pkgconf_tuple_define_global(&pkg_client, pkg_optarg); + break; + case 28: + required_exact_module_version = pkg_optarg; + break; + case 29: + required_max_module_version = pkg_optarg; + break; + case 40: + logfile_arg = pkg_optarg; + break; + case 42: + pkgconf_path_prepend(pkg_optarg, &dir_list, true); + break; + case 43: + pkgconf_client_set_prefix_varname(&pkg_client, pkg_optarg); + break; + case 45: + relocate_path(pkg_optarg); + return EXIT_SUCCESS; + case 48: + want_env_prefix = pkg_optarg; + break; + case 50: + want_fragment_filter = pkg_optarg; + break; +#ifndef PKGCONF_LITE + case 53: + personality = pkgconf_cross_personality_find(pkg_optarg); + break; +#endif + case 55: + verbosity++; + break; + case '?': + case ':': + ret = EXIT_FAILURE; + goto out; + default: + break; + } + } + + if (personality == NULL) { +#ifndef PKGCONF_LITE + personality = deduce_personality(argv); +#else + personality = pkgconf_cross_personality_default(); +#endif + } + +#ifndef PKGCONF_LITE + if ((want_flags & PKG_DUMP_PERSONALITY) == PKG_DUMP_PERSONALITY) + { + dump_personality(personality); + return EXIT_SUCCESS; + } +#endif + + /* now, bring up the client. settings are preserved since the client is prealloced */ + pkgconf_client_init(&pkg_client, error_handler, NULL, personality); + + /* unveil the entire search path now that we have loaded the personality data. */ + if (!unveil_search_paths(&pkg_client, personality)) + { + fprintf(stderr, "pkgconf: unveil failed: %s\n", strerror(errno)); + return EXIT_FAILURE; + } + +#ifndef PKGCONF_LITE + if ((want_flags & PKG_MSVC_SYNTAX) == PKG_MSVC_SYNTAX || getenv("PKG_CONFIG_MSVC_SYNTAX") != NULL) + want_render_ops = msvc_renderer_get(); +#endif + + if ((env_traverse_depth = getenv("PKG_CONFIG_MAXIMUM_TRAVERSE_DEPTH")) != NULL) + maximum_traverse_depth = atoi(env_traverse_depth); + + if ((want_flags & PKG_PRINT_ERRORS) != PKG_PRINT_ERRORS) + want_flags |= (PKG_SILENCE_ERRORS); + + if ((want_flags & PKG_SILENCE_ERRORS) == PKG_SILENCE_ERRORS && !getenv("PKG_CONFIG_DEBUG_SPEW")) + want_flags |= (PKG_SILENCE_ERRORS); + else + want_flags &= ~(PKG_SILENCE_ERRORS); + + if (getenv("PKG_CONFIG_DONT_RELOCATE_PATHS")) + want_flags |= (PKG_DONT_RELOCATE_PATHS); + + if ((want_flags & PKG_VALIDATE) == PKG_VALIDATE || (want_flags & PKG_DEBUG) == PKG_DEBUG) + pkgconf_client_set_warn_handler(&pkg_client, error_handler, NULL); + +#ifndef PKGCONF_LITE + if ((want_flags & PKG_DEBUG) == PKG_DEBUG) + pkgconf_client_set_trace_handler(&pkg_client, error_handler, NULL); +#endif + + pkgconf_path_prepend_list(&pkg_client.dir_list, &dir_list); + pkgconf_path_free(&dir_list); + + if ((want_flags & PKG_ABOUT) == PKG_ABOUT) + { + about(); + + ret = EXIT_SUCCESS; + goto out; + } + + if ((want_flags & PKG_VERSION) == PKG_VERSION) + { + version(); + + ret = EXIT_SUCCESS; + goto out; + } + + if ((want_flags & PKG_HELP) == PKG_HELP) + { + usage(); + + ret = EXIT_SUCCESS; + goto out; + } + + if (getenv("PKG_CONFIG_FDO_SYSROOT_RULES")) + want_client_flags |= PKGCONF_PKG_PKGF_FDO_SYSROOT_RULES; + + if (getenv("PKG_CONFIG_PKGCONF1_SYSROOT_RULES")) + want_client_flags |= PKGCONF_PKG_PKGF_PKGCONF1_SYSROOT_RULES; + + if ((want_flags & PKG_SHORT_ERRORS) == PKG_SHORT_ERRORS) + want_client_flags |= PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS; + + if ((want_flags & PKG_DONT_RELOCATE_PATHS) == PKG_DONT_RELOCATE_PATHS) + want_client_flags |= PKGCONF_PKG_PKGF_DONT_RELOCATE_PATHS; + + error_msgout = stderr; + if ((want_flags & PKG_ERRORS_ON_STDOUT) == PKG_ERRORS_ON_STDOUT) + error_msgout = stdout; + if ((want_flags & PKG_SILENCE_ERRORS) == PKG_SILENCE_ERRORS) { + error_msgout = fopen(PATH_DEV_NULL, "w"); + opened_error_msgout = true; + } + + if ((want_flags & PKG_IGNORE_CONFLICTS) == PKG_IGNORE_CONFLICTS || getenv("PKG_CONFIG_IGNORE_CONFLICTS") != NULL) + want_client_flags |= PKGCONF_PKG_PKGF_SKIP_CONFLICTS; + + if ((want_flags & PKG_STATIC) == PKG_STATIC || personality->want_default_static) + want_client_flags |= (PKGCONF_PKG_PKGF_SEARCH_PRIVATE | PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS); + + if ((want_flags & PKG_SHARED) == PKG_SHARED) + want_client_flags &= ~(PKGCONF_PKG_PKGF_SEARCH_PRIVATE | PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS); + + /* if --static and --pure are both specified, then disable merge-back. + * this allows for a --static which searches private modules, but has the same fragment behaviour as if + * --static were disabled. see for rationale. + */ + if ((want_flags & PKG_PURE) == PKG_PURE || getenv("PKG_CONFIG_PURE_DEPGRAPH") != NULL || personality->want_default_pure) + want_client_flags &= ~PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS; + + if ((want_flags & PKG_ENV_ONLY) == PKG_ENV_ONLY) + want_client_flags |= PKGCONF_PKG_PKGF_ENV_ONLY; + + if ((want_flags & PKG_NO_CACHE) == PKG_NO_CACHE) + want_client_flags |= PKGCONF_PKG_PKGF_NO_CACHE; + +/* On Windows we want to always redefine the prefix by default + * but allow that behavior to be manually disabled */ +#if !defined(_WIN32) && !defined(_WIN64) + if ((want_flags & PKG_DEFINE_PREFIX) == PKG_DEFINE_PREFIX || getenv("PKG_CONFIG_RELOCATE_PATHS") != NULL) +#endif + want_client_flags |= PKGCONF_PKG_PKGF_REDEFINE_PREFIX; + + if ((want_flags & PKG_NO_UNINSTALLED) == PKG_NO_UNINSTALLED || getenv("PKG_CONFIG_DISABLE_UNINSTALLED") != NULL) + want_client_flags |= PKGCONF_PKG_PKGF_NO_UNINSTALLED; + + if ((want_flags & PKG_NO_PROVIDES) == PKG_NO_PROVIDES) + want_client_flags |= PKGCONF_PKG_PKGF_SKIP_PROVIDES; + + if ((want_flags & PKG_DONT_DEFINE_PREFIX) == PKG_DONT_DEFINE_PREFIX || getenv("PKG_CONFIG_DONT_DEFINE_PREFIX") != NULL) + want_client_flags &= ~PKGCONF_PKG_PKGF_REDEFINE_PREFIX; + + if ((want_flags & PKG_INTERNAL_CFLAGS) == PKG_INTERNAL_CFLAGS) + want_client_flags |= PKGCONF_PKG_PKGF_DONT_FILTER_INTERNAL_CFLAGS; + + /* if these selectors are used, it means that we are querying metadata. + * so signal to libpkgconf that we only want to walk the flattened dependency set. + */ + if ((want_flags & PKG_MODVERSION) == PKG_MODVERSION || + (want_flags & PKG_REQUIRES) == PKG_REQUIRES || + (want_flags & PKG_REQUIRES_PRIVATE) == PKG_REQUIRES_PRIVATE || + (want_flags & PKG_PROVIDES) == PKG_PROVIDES || + (want_flags & PKG_VARIABLES) == PKG_VARIABLES || + (want_flags & PKG_PATH) == PKG_PATH || + want_variable != NULL) + maximum_traverse_depth = 1; + + /* if we are asking for a variable, path or list of variables, this only makes sense + * for a single package. + */ + if ((want_flags & PKG_VARIABLES) == PKG_VARIABLES || + (want_flags & PKG_PATH) == PKG_PATH || + want_variable != NULL) + maximum_package_count = 1; + + if (getenv("PKG_CONFIG_ALLOW_SYSTEM_CFLAGS") != NULL) + want_flags |= PKG_KEEP_SYSTEM_CFLAGS; + + if (getenv("PKG_CONFIG_ALLOW_SYSTEM_LIBS") != NULL) + want_flags |= PKG_KEEP_SYSTEM_LIBS; + + if ((builddir = getenv("PKG_CONFIG_TOP_BUILD_DIR")) != NULL) + pkgconf_client_set_buildroot_dir(&pkg_client, builddir); + + if ((want_flags & PKG_REQUIRES_PRIVATE) == PKG_REQUIRES_PRIVATE || + (want_flags & PKG_CFLAGS)) + { + want_client_flags |= PKGCONF_PKG_PKGF_SEARCH_PRIVATE; + } + + if ((sysroot_dir = getenv("PKG_CONFIG_SYSROOT_DIR")) != NULL) + { + const char *destdir; + + pkgconf_client_set_sysroot_dir(&pkg_client, sysroot_dir); + + if ((destdir = getenv("DESTDIR")) != NULL) + { + if (!strcmp(destdir, sysroot_dir)) + want_client_flags |= PKGCONF_PKG_PKGF_FDO_SYSROOT_RULES; + } + } + + /* we have determined what features we want most likely. in some cases, we override later. */ + pkgconf_client_set_flags(&pkg_client, want_client_flags); + + /* at this point, want_client_flags should be set, so build the dir list */ + pkgconf_client_dir_list_build(&pkg_client, personality); + + /* preload any files in PKG_CONFIG_PRELOADED_FILES */ + pkgconf_client_preload_from_environ(&pkg_client, "PKG_CONFIG_PRELOADED_FILES"); + + if (required_pkgconfig_version != NULL) + { + if (pkgconf_compare_version(PACKAGE_VERSION, required_pkgconfig_version) >= 0) + ret = EXIT_SUCCESS; + else + ret = EXIT_FAILURE; + + goto out; + } + + if ((want_flags & PKG_LIST) == PKG_LIST) + { + pkgconf_scan_all(&pkg_client, NULL, print_list_entry); + ret = EXIT_SUCCESS; + goto out; + } + + if ((want_flags & PKG_LIST_PACKAGE_NAMES) == PKG_LIST_PACKAGE_NAMES) + { + pkgconf_scan_all(&pkg_client, NULL, print_package_entry); + ret = EXIT_SUCCESS; + goto out; + } + + if (logfile_arg == NULL) + logfile_arg = getenv("PKG_CONFIG_LOG"); + + if (logfile_arg != NULL) + { + if (pkgconf_unveil(logfile_arg, "rwc") == -1) + { + fprintf(stderr, "pkgconf: unveil failed: %s\n", strerror(errno)); + return EXIT_FAILURE; + } + + logfile_out = fopen(logfile_arg, "w"); + pkgconf_audit_set_log(&pkg_client, logfile_out); + } + + if (required_module_version != NULL) + { + pkgconf_pkg_t *pkg = NULL; + pkgconf_node_t *node; + pkgconf_list_t deplist = PKGCONF_LIST_INITIALIZER; + + while (argv[pkg_optind]) + { + pkgconf_dependency_parse_str(&pkg_client, &deplist, argv[pkg_optind], 0); + pkg_optind++; + } + + PKGCONF_FOREACH_LIST_ENTRY(deplist.head, node) + { + pkgconf_dependency_t *pkgiter = node->data; + + pkg = pkgconf_pkg_find(&pkg_client, pkgiter->package); + if (pkg == NULL) + { + if (want_flags & PKG_PRINT_ERRORS) + pkgconf_error(&pkg_client, "Package '%s' was not found\n", pkgiter->package); + + ret = EXIT_FAILURE; + goto cleanup; + } + + if (pkgconf_compare_version(pkg->version, required_module_version) >= 0) + { + ret = EXIT_SUCCESS; + goto cleanup; + } + } + + ret = EXIT_FAILURE; +cleanup: + if (pkg != NULL) + pkgconf_pkg_unref(&pkg_client, pkg); + pkgconf_dependency_free(&deplist); + goto out; + } + else if (required_exact_module_version != NULL) + { + pkgconf_pkg_t *pkg = NULL; + pkgconf_node_t *node; + pkgconf_list_t deplist = PKGCONF_LIST_INITIALIZER; + + while (argv[pkg_optind]) + { + pkgconf_dependency_parse_str(&pkg_client, &deplist, argv[pkg_optind], 0); + pkg_optind++; + } + + PKGCONF_FOREACH_LIST_ENTRY(deplist.head, node) + { + pkgconf_dependency_t *pkgiter = node->data; + + pkg = pkgconf_pkg_find(&pkg_client, pkgiter->package); + if (pkg == NULL) + { + if (want_flags & PKG_PRINT_ERRORS) + pkgconf_error(&pkg_client, "Package '%s' was not found\n", pkgiter->package); + + ret = EXIT_FAILURE; + goto cleanup2; + } + + if (pkgconf_compare_version(pkg->version, required_exact_module_version) == 0) + { + ret = EXIT_SUCCESS; + goto cleanup2; + } + } + + ret = EXIT_FAILURE; +cleanup2: + if (pkg != NULL) + pkgconf_pkg_unref(&pkg_client, pkg); + pkgconf_dependency_free(&deplist); + goto out; + } + else if (required_max_module_version != NULL) + { + pkgconf_pkg_t *pkg = NULL; + pkgconf_node_t *node; + pkgconf_list_t deplist = PKGCONF_LIST_INITIALIZER; + + while (argv[pkg_optind]) + { + pkgconf_dependency_parse_str(&pkg_client, &deplist, argv[pkg_optind], 0); + pkg_optind++; + } + + PKGCONF_FOREACH_LIST_ENTRY(deplist.head, node) + { + pkgconf_dependency_t *pkgiter = node->data; + + pkg = pkgconf_pkg_find(&pkg_client, pkgiter->package); + if (pkg == NULL) + { + if (want_flags & PKG_PRINT_ERRORS) + pkgconf_error(&pkg_client, "Package '%s' was not found\n", pkgiter->package); + + ret = EXIT_FAILURE; + goto cleanup3; + } + + if (pkgconf_compare_version(pkg->version, required_max_module_version) <= 0) + { + ret = EXIT_SUCCESS; + goto cleanup3; + } + } + + ret = EXIT_FAILURE; +cleanup3: + if (pkg != NULL) + pkgconf_pkg_unref(&pkg_client, pkg); + pkgconf_dependency_free(&deplist); + goto out; + } + + while (1) + { + char *package = argv[pkg_optind]; + char *end; + + if (package == NULL) + break; + + /* check if there is a limit to the number of packages allowed to be included, if so and we have hit + * the limit, stop adding packages to the queue. + */ + if (maximum_package_count > 0 && pkgq.length >= maximum_package_count) + break; + + while (isspace((unsigned char)package[0])) + package++; + + /* skip empty packages */ + if (package[0] == '\0') { + pkg_optind++; + continue; + } + + end = package + strlen(package) - 1; + while(end > package && isspace((unsigned char)end[0])) end--; + end[1] = '\0'; + + if (argv[pkg_optind + 1] == NULL || !PKGCONF_IS_OPERATOR_CHAR(*(argv[pkg_optind + 1]))) + { + pkgconf_queue_push(&pkgq, package); + pkg_optind++; + } + else if (argv[pkg_optind + 2] == NULL) + { + char packagebuf[PKGCONF_BUFSIZE]; + + snprintf(packagebuf, sizeof packagebuf, "%s %s", package, argv[pkg_optind + 1]); + pkg_optind += 2; + + pkgconf_queue_push(&pkgq, packagebuf); + } + else + { + char packagebuf[PKGCONF_BUFSIZE]; + + snprintf(packagebuf, sizeof packagebuf, "%s %s %s", package, argv[pkg_optind + 1], argv[pkg_optind + 2]); + pkg_optind += 3; + + pkgconf_queue_push(&pkgq, packagebuf); + } + } + + if (pkgq.head == NULL) + { + fprintf(stderr, "Please specify at least one package name on the command line.\n"); + ret = EXIT_FAILURE; + goto out; + } + + ret = EXIT_SUCCESS; + + if (!pkgconf_queue_solve(&pkg_client, &pkgq, &world, maximum_traverse_depth)) + { + ret = EXIT_FAILURE; + goto out; + } + + /* we shouldn't need to unveil any more filesystem accesses from this point, so lock it down */ + if (pkgconf_unveil(NULL, NULL) == -1) + { + fprintf(stderr, "pkgconf: unveil lockdown failed: %s\n", strerror(errno)); + return EXIT_FAILURE; + } + +#ifndef PKGCONF_LITE + if ((want_flags & PKG_SIMULATE) == PKG_SIMULATE) + { + want_flags &= ~(PKG_CFLAGS|PKG_LIBS); + + pkgconf_client_set_flags(&pkg_client, want_client_flags | PKGCONF_PKG_PKGF_SKIP_ERRORS); + apply_simulate(&pkg_client, &world, NULL, -1); + } +#endif + + if ((want_flags & PKG_VALIDATE) == PKG_VALIDATE) + goto out; + + if ((want_flags & PKG_DUMP_LICENSE) == PKG_DUMP_LICENSE) + { + apply_license(&pkg_client, &world, &ret, 2); + goto out; + } + + if ((want_flags & PKG_UNINSTALLED) == PKG_UNINSTALLED) + { + ret = EXIT_FAILURE; + apply_uninstalled(&pkg_client, &world, &ret, 2); + goto out; + } + + if (want_env_prefix != NULL) + { + apply_env(&pkg_client, &world, want_env_prefix, 2); + goto out; + } + + if ((want_flags & PKG_PROVIDES) == PKG_PROVIDES) + { + want_flags &= ~(PKG_CFLAGS|PKG_LIBS); + apply_provides(&pkg_client, &world, NULL, 2); + } + +#ifndef PKGCONF_LITE + if ((want_flags & PKG_DIGRAPH) == PKG_DIGRAPH) + { + want_flags &= ~(PKG_CFLAGS|PKG_LIBS); + apply_digraph(&pkg_client, &world, &pkgq, 2); + } + + if ((want_flags & PKG_SOLUTION) == PKG_SOLUTION) + { + want_flags &= ~(PKG_CFLAGS|PKG_LIBS); + apply_print_solution(&pkg_client, &world, NULL, 2); + } +#endif + + if ((want_flags & PKG_MODVERSION) == PKG_MODVERSION) + { + want_flags &= ~(PKG_CFLAGS|PKG_LIBS); + apply_modversion(&pkg_client, &world, &pkgq, 2); + } + + if ((want_flags & PKG_PATH) == PKG_PATH) + { + want_flags &= ~(PKG_CFLAGS|PKG_LIBS); + + pkgconf_client_set_flags(&pkg_client, want_client_flags | PKGCONF_PKG_PKGF_SKIP_ROOT_VIRTUAL); + apply_path(&pkg_client, &world, NULL, 2); + } + + if ((want_flags & PKG_VARIABLES) == PKG_VARIABLES) + { + want_flags &= ~(PKG_CFLAGS|PKG_LIBS); + apply_variables(&pkg_client, &world, NULL, 2); + } + + if (want_variable) + { + want_flags &= ~(PKG_CFLAGS|PKG_LIBS); + + pkgconf_client_set_flags(&pkg_client, want_client_flags | PKGCONF_PKG_PKGF_SKIP_ROOT_VIRTUAL); + apply_variable(&pkg_client, &world, want_variable, 2); + } + + if ((want_flags & PKG_REQUIRES) == PKG_REQUIRES) + { + want_flags &= ~(PKG_CFLAGS|PKG_LIBS); + apply_requires(&pkg_client, &world, NULL, 2); + } + + if ((want_flags & PKG_REQUIRES_PRIVATE) == PKG_REQUIRES_PRIVATE) + { + want_flags &= ~(PKG_CFLAGS|PKG_LIBS); + + apply_requires_private(&pkg_client, &world, NULL, 2); + } + + if ((want_flags & PKG_FRAGMENT_TREE)) + { + want_flags &= ~(PKG_CFLAGS|PKG_LIBS); + + apply_fragment_tree(&pkg_client, &world, NULL, 2); + } + + if ((want_flags & PKG_CFLAGS)) + { + apply_cflags(&pkg_client, &world, NULL, 2); + } + + if ((want_flags & PKG_LIBS)) + { + if (want_flags & PKG_CFLAGS) + printf(" "); + + if (!(want_flags & PKG_STATIC)) + pkgconf_client_set_flags(&pkg_client, pkg_client.flags & ~PKGCONF_PKG_PKGF_SEARCH_PRIVATE); + + apply_libs(&pkg_client, &world, NULL, 2); + } + + if (want_flags & (PKG_CFLAGS|PKG_LIBS)) + printf("\n"); + +out: + pkgconf_solution_free(&pkg_client, &world); + pkgconf_queue_free(&pkgq); + pkgconf_cross_personality_deinit(personality); + pkgconf_client_deinit(&pkg_client); + + if (logfile_out != NULL) + fclose(logfile_out); + if (opened_error_msgout) + fclose(error_msgout); + + return ret; +} diff --git a/contrib/pkgconf/cli/renderer-msvc.c b/contrib/pkgconf/cli/renderer-msvc.c new file mode 100644 index 00000000000..8e31699bbca --- /dev/null +++ b/contrib/pkgconf/cli/renderer-msvc.c @@ -0,0 +1,172 @@ +/* + * renderer-msvc.c + * MSVC library syntax renderer + * + * Copyright (c) 2017 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include +#include + +#include +#include "renderer-msvc.h" + +static inline bool +fragment_should_quote(const pkgconf_fragment_t *frag) +{ + const char *src; + + if (frag->data == NULL) + return false; + + for (src = frag->data; *src; src++) + { + if (((*src < ' ') || + (*src >= (' ' + (frag->children.head != NULL ? 1 : 0)) && *src < '$') || + (*src > '$' && *src < '(') || + (*src > ')' && *src < '+') || + (*src > ':' && *src < '=') || + (*src > '=' && *src < '@') || + (*src > 'Z' && *src < '^') || + (*src == '`') || + (*src > 'z' && *src < '~') || + (*src > '~'))) + return true; + } + + return false; +} + +static inline size_t +fragment_len(const pkgconf_fragment_t *frag) +{ + size_t len = 1; + + if (frag->type) + len += 2; + + if (frag->data != NULL) + { + len += strlen(frag->data); + + if (fragment_should_quote(frag)) + len += 2; + } + + return len; +} + +static inline bool +allowed_fragment(const pkgconf_fragment_t *frag) +{ + return !(!frag->type || frag->data == NULL || strchr("DILl", frag->type) == NULL); +} + +static size_t +msvc_renderer_render_len(const pkgconf_list_t *list, bool escape) +{ + (void) escape; + + size_t out = 1; /* trailing nul */ + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(list->head, node) + { + const pkgconf_fragment_t *frag = node->data; + + if (!allowed_fragment(frag)) + continue; + + switch (frag->type) + { + case 'L': + out += 9; /* "/libpath:" */ + break; + case 'l': + out += 4; /* ".lib" */ + break; + default: + break; + } + + out += fragment_len(frag); + } + + return out; +} + +static void +msvc_renderer_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape) +{ + pkgconf_node_t *node; + char *bptr = buf; + + memset(buf, 0, buflen); + + PKGCONF_FOREACH_LIST_ENTRY(list->head, node) + { + const pkgconf_fragment_t *frag = node->data; + size_t buf_remaining = buflen - (bptr - buf); + size_t cnt; + + if (!allowed_fragment(frag)) + continue; + + if (fragment_len(frag) > buf_remaining) + break; + + switch(frag->type) { + case 'D': + case 'I': + *bptr++ = '/'; + *bptr++ = frag->type; + break; + case 'L': + cnt = pkgconf_strlcpy(bptr, "/libpath:", buf_remaining); + bptr += cnt; + buf_remaining -= cnt; + break; + } + + escape = fragment_should_quote(frag); + + if (escape) + *bptr++ = '"'; + + cnt = pkgconf_strlcpy(bptr, frag->data, buf_remaining); + bptr += cnt; + buf_remaining -= cnt; + + if (frag->type == 'l') + { + cnt = pkgconf_strlcpy(bptr, ".lib", buf_remaining); + bptr += cnt; + } + + if (escape) + *bptr++ = '"'; + + *bptr++ = ' '; + } + + *bptr = '\0'; +} + +static const pkgconf_fragment_render_ops_t msvc_renderer_ops = { + .render_len = msvc_renderer_render_len, + .render_buf = msvc_renderer_render_buf +}; + +const pkgconf_fragment_render_ops_t * +msvc_renderer_get(void) +{ + return &msvc_renderer_ops; +} diff --git a/contrib/pkgconf/cli/renderer-msvc.h b/contrib/pkgconf/cli/renderer-msvc.h new file mode 100644 index 00000000000..2a095fdc36b --- /dev/null +++ b/contrib/pkgconf/cli/renderer-msvc.h @@ -0,0 +1,23 @@ +/* + * renderer-msvc.h + * MSVC library syntax renderer header + * + * Copyright (c) 2017 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#ifndef RENDERER_MSVC_H +#define RENDERER_MSVC_H + +#include + +const pkgconf_fragment_render_ops_t *msvc_renderer_get(void); + +#endif diff --git a/contrib/pkgconf/configure.ac b/contrib/pkgconf/configure.ac new file mode 100644 index 00000000000..3fee9bd444e --- /dev/null +++ b/contrib/pkgconf/configure.ac @@ -0,0 +1,66 @@ +dnl configure.ac +dnl m4 preprocessor script for autotools +dnl +dnl Copyright (c) 2011, 2012, 2013, 2014 pkgconf authors (see AUTHORS). +dnl +dnl Permission to use, copy, modify, and/or distribute this software for any +dnl purpose with or without fee is hereby granted, provided that the above +dnl copyright notice and this permission notice appear in all copies. +dnl +dnl This software is provided 'as is' and without any warranty, express or +dnl implied. In no event shall the authors be liable for any damages arising +dnl from the use of this software. + +AC_PREREQ([2.71]) +AC_INIT([pkgconf],[2.5.1],[https://github.com/pkgconf/pkgconf/issues/new]) +AC_CONFIG_SRCDIR([cli/main.c]) +AC_CONFIG_MACRO_DIR([m4]) +AX_CHECK_COMPILE_FLAG([-Wall], [CFLAGS="$CFLAGS -Wall"]) +AX_CHECK_COMPILE_FLAG([-Wextra], [CFLAGS="$CFLAGS -Wextra"]) +AX_CHECK_COMPILE_FLAG([-Wformat=2], [CFLAGS="$CFLAGS -Wformat=2"]) +AX_CHECK_COMPILE_FLAG([-std=gnu99], [CFLAGS="$CFLAGS -std=gnu99"], [ + AX_CHECK_COMPILE_FLAG([-std=c99], [CFLAGS="$CFLAGS -std=c99"]) +]) +AC_CONFIG_HEADERS([libpkgconf/config.h]) +AC_CHECK_DECLS([strlcpy, strlcat, strndup], [], [], [[#include ]]) +AC_CHECK_DECLS([pledge, unveil], [], [], [[#include ]]) +AC_CHECK_DECLS([reallocarray]) +AC_CHECK_HEADERS([sys/stat.h]) +AM_INIT_AUTOMAKE([foreign dist-xz subdir-objects]) +AM_SILENT_RULES([yes]) +LT_INIT + +AC_SYS_LARGEFILE + +AC_ARG_WITH([personality-dir],[AS_HELP_STRING([--with-personality-dir],[specify + the place where cross-compile personality files will be found])], + PERSONALITY_PATH="$withval", + PERSONALITY_PATH="${datadir}/pkgconfig/personality.d:${sysconfdir}/pkgconfig/personality.d") + +AC_SUBST([PERSONALITY_PATH]) + +AC_ARG_WITH([pkg-config-dir],[AS_HELP_STRING([--with-pkg-config-dir],[specify + the place where pc files will be found])],PKG_DEFAULT_PATH="$withval", + PKG_DEFAULT_PATH="${libdir}/pkgconfig:${datadir}/pkgconfig") + +AC_SUBST([PKG_DEFAULT_PATH]) + +AC_ARG_WITH([system-libdir],[AS_HELP_STRING([--with-system-libdir],[specify the + system library directory (default LIBDIR)])], + SYSTEM_LIBDIR="$withval", SYSTEM_LIBDIR="${libdir}") + +AC_SUBST([SYSTEM_LIBDIR]) + +AC_ARG_WITH([system-includedir],[AS_HELP_STRING([--with-system-includedir],[specify the + system include directory (default INCLUDEDIR)])], + SYSTEM_INCLUDEDIR="$withval", SYSTEM_INCLUDEDIR="${includedir}") + +AC_SUBST([SYSTEM_INCLUDEDIR]) + +AC_PROG_CPP +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_LN_S + +AC_CONFIG_FILES([Makefile Kyuafile libpkgconf.pc tests/Kyuafile tests/test_env.sh]) +AC_OUTPUT diff --git a/contrib/pkgconf/doc/conf.py b/contrib/pkgconf/doc/conf.py new file mode 100644 index 00000000000..ca608c7f106 --- /dev/null +++ b/contrib/pkgconf/doc/conf.py @@ -0,0 +1,339 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# pkgconf documentation build configuration file, created by +# sphinx-quickstart on Sat Dec 10 16:54:40 2016. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +# +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'pkgconf' +copyright = '2016, pkgconf authors' +author = 'pkgconf authors' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '1.1.0' +# The full version, including alpha/beta/rc tags. +release = '1.1.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = 'en' + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# +# today = '' +# +# Else, today_fmt is used as the format for a strftime call. +# +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +# +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +# keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. +# " v documentation" by default. +# +# html_title = 'pkgconf v1.1.0' + +# A shorter title for the navigation bar. Default is the same as html_title. +# +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# +# html_logo = None + +# The name of an image file (relative to this directory) to use as a favicon of +# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +# +# html_extra_path = [] + +# If not None, a 'Last updated on:' timestamp is inserted at every page +# bottom, using the given strftime format. +# The empty string is equivalent to '%b %d, %Y'. +# +# html_last_updated_fmt = None + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# +# html_additional_pages = {} + +# If false, no module index is generated. +# +# html_domain_indices = True + +# If false, no index is generated. +# +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# +# html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh' +# +# html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# 'ja' uses this config value. +# 'zh' user can custom change `jieba` dictionary path. +# +# html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +# +# html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'pkgconfdoc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'pkgconf.tex', 'pkgconf Documentation', + 'pkgconf authors', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# +# latex_use_parts = False + +# If true, show page references after internal links. +# +# latex_show_pagerefs = False + +# If true, show URL addresses after external links. +# +# latex_show_urls = False + +# Documents to append as an appendix to all manuals. +# +# latex_appendices = [] + +# It false, will not define \strong, \code, itleref, \crossref ... but only +# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added +# packages. +# +# latex_keep_old_macro_names = True + +# If false, no module index is generated. +# +# latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'pkgconf', 'pkgconf Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +# +# man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'pkgconf', 'pkgconf Documentation', + author, 'pkgconf', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +# +# texinfo_appendices = [] + +# If false, no module index is generated. +# +# texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# +# texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +# +# texinfo_no_detailmenu = False diff --git a/contrib/pkgconf/doc/extract.py b/contrib/pkgconf/doc/extract.py new file mode 100644 index 00000000000..10ea6203786 --- /dev/null +++ b/contrib/pkgconf/doc/extract.py @@ -0,0 +1,149 @@ +# derived from https://github.com/jeanralphaviles/comment_parser/blob/master/comment_parser/parsers/c_parser.py +# MIT license - https://github.com/jeanralphaviles/comment_parser/blob/master/LICENSE + + +class Comment: + def __init__(self, comment, line, multiline): + self.comment = comment + self.line = line + self.multiline = multiline + + def __repr__(self): + return "Comment(comment=%r, line=%r, multiline=%r)" % (self.comment, self.line, self.multiline) + + @property + def clean_text(self): + if not self.multiline: + return self.comment.strip() + + lines = self.comment.splitlines() + cleanlines = [] + for line in lines: + if line[0:3] == ' * ': + cleanlines.append(line[3:]) + elif len(line) == 2: + cleanlines.append('') + return '\n'.join(cleanlines) + + @property + def doc_text(self): + text = self.clean_text + if '!doc' in text[0:4]: + return text[5:] + return None + + +class FileError(Exception): + pass + + +class UnterminatedCommentError(Exception): + pass + + +def extract_comments(filename): + """Extracts a list of comments from the given C family source file. + Comments are represented with the Comment class found in the common module. + C family comments come in two forms, single and multi-line comments. + - Single-line comments begin with '//' and continue to the end of line. + - Multi-line comments begin with '/*' and end with '*/' and can span + multiple lines of code. If a multi-line comment does not terminate + before EOF is reached, then an exception is raised. + Note that this doesn't take language-specific preprocessor directives into + consideration. + Args: + filename: String name of the file to extract comments from. + Returns: + Python list of Comment objects in the order that they appear in the file. + Raises: + FileError: File was unable to be open or read. + UnterminatedCommentError: Encountered an unterminated multi-line + comment. + """ + try: + with open(filename, 'r') as source_file: + state = 0 + current_comment = '' + comments = [] + line_counter = 1 + comment_start = 1 + while True: + char = source_file.read(1) + if not char: + if state == 3 or state == 4: + raise UnterminatedCommentError() + if state == 2: + # Was in single line comment. Create comment. + comment = Comment(current_comment, line_counter, False) + comments.append(comment) + return comments + if state == 0: + # Waiting for comment start character or beginning of + # string. + if char == '/': + state = 1 + elif char == '"': + state = 5 + elif state == 1: + # Found comment start character, classify next character and + # determine if single or multiline comment. + if char == '/': + state = 2 + elif char == '*': + comment_start = line_counter + state = 3 + else: + state = 0 + elif state == 2: + # In single line comment, read characters until EOL. + if char == '\n': + comment = Comment(current_comment, line_counter, False) + comments.append(comment) + current_comment = '' + state = 0 + else: + current_comment += char + elif state == 3: + # In multi-line comment, add characters until '*' + # encountered. + if char == '*': + state = 4 + else: + current_comment += char + elif state == 4: + # In multi-line comment with asterisk found. Determine if + # comment is ending. + if char == '/': + comment = Comment( + current_comment, comment_start, True) + comments.append(comment) + current_comment = '' + state = 0 + else: + current_comment += '*' + # Care for multiple '*' in a row + if char != '*': + current_comment += char + state = 3 + elif state == 5: + # In string literal, expect literal end or escape char. + if char == '"': + state = 0 + elif char == '\\': + state = 6 + elif state == 6: + # In string literal, escaping current char. + state = 5 + if char == '\n': + line_counter += 1 + except OSError as exception: + raise FileError(str(exception)) + + +if __name__ == '__main__': + import sys + from pprint import pprint + + comments = [comment for comment in extract_comments(sys.argv[1]) if comment.doc_text] + for comment in comments: + print(comment.doc_text) diff --git a/contrib/pkgconf/doc/index.rst b/contrib/pkgconf/doc/index.rst new file mode 100644 index 00000000000..b0c7c4bd24b --- /dev/null +++ b/contrib/pkgconf/doc/index.rst @@ -0,0 +1,14 @@ +pkgconf +======= + +.. toctree:: + :maxdepth: 2 + + libpkgconf + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`search` + diff --git a/contrib/pkgconf/doc/libpkgconf-argvsplit.rst b/contrib/pkgconf/doc/libpkgconf-argvsplit.rst new file mode 100644 index 00000000000..94ec6d93f85 --- /dev/null +++ b/contrib/pkgconf/doc/libpkgconf-argvsplit.rst @@ -0,0 +1,23 @@ + +libpkgconf `argvsplit` module +============================= + +This is a lowlevel module which provides parsing of strings into argument vectors, +similar to what a shell would do. + +.. c:function:: void pkgconf_argv_free(char **argv) + + Frees an argument vector. + + :param char** argv: The argument vector to free. + :return: nothing + +.. c:function:: int pkgconf_argv_split(const char *src, int *argc, char ***argv) + + Splits a string into an argument vector. + + :param char* src: The string to split. + :param int* argc: A pointer to an integer to store the argument count. + :param char*** argv: A pointer to a pointer for an argument vector. + :return: 0 on success, -1 on error. + :rtype: int diff --git a/contrib/pkgconf/doc/libpkgconf-audit.rst b/contrib/pkgconf/doc/libpkgconf-audit.rst new file mode 100644 index 00000000000..0baaba30818 --- /dev/null +++ b/contrib/pkgconf/doc/libpkgconf-audit.rst @@ -0,0 +1,35 @@ + +libpkgconf `audit` module +========================= + +The libpkgconf `audit` module contains the functions related to attaching an audit log file +to a ``pkgconf_client_t`` object. + +The audit log format is the same as the output generated by the ``PKG_CONFIG_LOG`` environment +variable. + +.. c:function:: void pkgconf_audit_set_log(pkgconf_client_t *client, FILE *auditf) + + Sets the audit log file pointer on `client` to `auditf`. + The callee is responsible for closing any previous log files. + + :param pkgconf_client_t* client: The client object to modify. + :param FILE* auditf: The file pointer for the already open log file. + :return: nothing + +.. c:function:: void pkgconf_audit_log(pkgconf_client_t *client, const char *format, ...) + + Logs a message to the opened audit log (if any). + + :param pkgconf_client_t* client: The client object the log message is for. + :param char* format: The format string to use for the log messages. + :return: nothing + +.. c:function:: void pkgconf_audit_log_dependency(pkgconf_client_t *client, const pkgconf_pkg_t *dep, const pkgconf_dependency_t *depnode) + + Convenience function which logs a dependency node to the opened audit log (if any). + + :param pkgconf_client_t* client: The client object the log message is for. + :param pkgconf_pkg_t* dep: The dependency package object being logged. + :param pkgconf_dependency_t* depnode: The dependency object itself being logged. + :return: nothing diff --git a/contrib/pkgconf/doc/libpkgconf-cache.rst b/contrib/pkgconf/doc/libpkgconf-cache.rst new file mode 100644 index 00000000000..adfa79d7958 --- /dev/null +++ b/contrib/pkgconf/doc/libpkgconf-cache.rst @@ -0,0 +1,45 @@ + +libpkgconf `cache` module +========================= + +The libpkgconf `cache` module manages a package/module object cache, allowing it to +avoid loading duplicate copies of a package/module. + +A cache is tied to a specific pkgconf client object, so package objects should not +be shared across threads. + +.. c:function:: pkgconf_pkg_t *pkgconf_cache_lookup(const pkgconf_client_t *client, const char *id) + + Looks up a package in the cache given an `id` atom, + such as ``gtk+-3.0`` and returns the already loaded version + if present. + + :param pkgconf_client_t* client: The client object to access. + :param char* id: The package atom to look up in the client object's cache. + :return: A package object if present, else ``NULL``. + :rtype: pkgconf_pkg_t * + +.. c:function:: void pkgconf_cache_add(pkgconf_client_t *client, pkgconf_pkg_t *pkg) + + Adds an entry for the package to the package cache. + The cache entry must be removed if the package is freed. + + :param pkgconf_client_t* client: The client object to modify. + :param pkgconf_pkg_t* pkg: The package object to add to the client object's cache. + :return: nothing + +.. c:function:: void pkgconf_cache_remove(pkgconf_client_t *client, pkgconf_pkg_t *pkg) + + Deletes a package from the client object's package cache. + + :param pkgconf_client_t* client: The client object to modify. + :param pkgconf_pkg_t* pkg: The package object to remove from the client object's cache. + :return: nothing + +.. c:function:: void pkgconf_cache_free(pkgconf_client_t *client) + + Releases all resources related to a client object's package cache. + This function should only be called to clear a client object's package cache, + as it may release any package in the cache. + + :param pkgconf_client_t* client: The client object to modify. diff --git a/contrib/pkgconf/doc/libpkgconf-client.rst b/contrib/pkgconf/doc/libpkgconf-client.rst new file mode 100644 index 00000000000..6816fdf4b34 --- /dev/null +++ b/contrib/pkgconf/doc/libpkgconf-client.rst @@ -0,0 +1,212 @@ + +libpkgconf `client` module +========================== + +The libpkgconf `client` module implements the `pkgconf_client_t` "client" object. +Client objects store all necessary state for libpkgconf allowing for multiple instances to run +in parallel. + +Client objects are not thread safe, in other words, a client object should not be shared across +thread boundaries. + +.. c:function:: void pkgconf_client_dir_list_build(pkgconf_client_t *client) + + Bootstraps the package search paths. If the ``PKGCONF_PKG_PKGF_ENV_ONLY`` `flag` is set on the client, + then only the ``PKG_CONFIG_PATH`` environment variable will be used, otherwise both the + ``PKG_CONFIG_PATH`` and ``PKG_CONFIG_LIBDIR`` environment variables will be used. + + :param pkgconf_client_t* client: The pkgconf client object to bootstrap. + :return: nothing + +.. c:function:: void pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality) + + Initialise a pkgconf client object. + + :param pkgconf_client_t* client: The client to initialise. + :param pkgconf_error_handler_func_t error_handler: An optional error handler to use for logging errors. + :param void* error_handler_data: user data passed to optional error handler + :param pkgconf_cross_personality_t* personality: the cross-compile personality to use for defaults + :return: nothing + +.. c:function:: pkgconf_client_t* pkgconf_client_new(pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality) + + Allocate and initialise a pkgconf client object. + + :param pkgconf_error_handler_func_t error_handler: An optional error handler to use for logging errors. + :param void* error_handler_data: user data passed to optional error handler + :param pkgconf_cross_personality_t* personality: cross-compile personality to use + :return: A pkgconf client object. + :rtype: pkgconf_client_t* + +.. c:function:: void pkgconf_client_deinit(pkgconf_client_t *client) + + Release resources belonging to a pkgconf client object. + + :param pkgconf_client_t* client: The client to deinitialise. + :return: nothing + +.. c:function:: void pkgconf_client_free(pkgconf_client_t *client) + + Release resources belonging to a pkgconf client object and then free the client object itself. + + :param pkgconf_client_t* client: The client to deinitialise and free. + :return: nothing + +.. c:function:: const char *pkgconf_client_get_sysroot_dir(const pkgconf_client_t *client) + + Retrieves the client's sysroot directory (if any). + + :param pkgconf_client_t* client: The client object being accessed. + :return: A string containing the sysroot directory or NULL. + :rtype: const char * + +.. c:function:: void pkgconf_client_set_sysroot_dir(pkgconf_client_t *client, const char *sysroot_dir) + + Sets or clears the sysroot directory on a client object. Any previous sysroot directory setting is + automatically released if one was previously set. + + Additionally, the global tuple ``$(pc_sysrootdir)`` is set as appropriate based on the new setting. + + :param pkgconf_client_t* client: The client object being modified. + :param char* sysroot_dir: The sysroot directory to set or NULL to unset. + :return: nothing + +.. c:function:: const char *pkgconf_client_get_buildroot_dir(const pkgconf_client_t *client) + + Retrieves the client's buildroot directory (if any). + + :param pkgconf_client_t* client: The client object being accessed. + :return: A string containing the buildroot directory or NULL. + :rtype: const char * + +.. c:function:: void pkgconf_client_set_buildroot_dir(pkgconf_client_t *client, const char *buildroot_dir) + + Sets or clears the buildroot directory on a client object. Any previous buildroot directory setting is + automatically released if one was previously set. + + Additionally, the global tuple ``$(pc_top_builddir)`` is set as appropriate based on the new setting. + + :param pkgconf_client_t* client: The client object being modified. + :param char* buildroot_dir: The buildroot directory to set or NULL to unset. + :return: nothing + +.. c:function:: bool pkgconf_error(const pkgconf_client_t *client, const char *format, ...) + + Report an error to a client-registered error handler. + + :param pkgconf_client_t* client: The pkgconf client object to report the error to. + :param char* format: A printf-style format string to use for formatting the error message. + :return: true if the error handler processed the message, else false. + :rtype: bool + +.. c:function:: bool pkgconf_warn(const pkgconf_client_t *client, const char *format, ...) + + Report an error to a client-registered warn handler. + + :param pkgconf_client_t* client: The pkgconf client object to report the error to. + :param char* format: A printf-style format string to use for formatting the warning message. + :return: true if the warn handler processed the message, else false. + :rtype: bool + +.. c:function:: bool pkgconf_trace(const pkgconf_client_t *client, const char *filename, size_t len, const char *funcname, const char *format, ...) + + Report a message to a client-registered trace handler. + + :param pkgconf_client_t* client: The pkgconf client object to report the trace message to. + :param char* filename: The file the function is in. + :param size_t lineno: The line number currently being executed. + :param char* funcname: The function name to use. + :param char* format: A printf-style format string to use for formatting the trace message. + :return: true if the trace handler processed the message, else false. + :rtype: bool + +.. c:function:: bool pkgconf_default_error_handler(const char *msg, const pkgconf_client_t *client, const void *data) + + The default pkgconf error handler. + + :param char* msg: The error message to handle. + :param pkgconf_client_t* client: The client object the error originated from. + :param void* data: An opaque pointer to extra data associated with the client for error handling. + :return: true (the function does nothing to process the message) + :rtype: bool + +.. c:function:: unsigned int pkgconf_client_get_flags(const pkgconf_client_t *client) + + Retrieves resolver-specific flags associated with a client object. + + :param pkgconf_client_t* client: The client object to retrieve the resolver-specific flags from. + :return: a bitfield of resolver-specific flags + :rtype: uint + +.. c:function:: void pkgconf_client_set_flags(pkgconf_client_t *client, unsigned int flags) + + Sets resolver-specific flags associated with a client object. + + :param pkgconf_client_t* client: The client object to set the resolver-specific flags on. + :return: nothing + +.. c:function:: const char *pkgconf_client_get_prefix_varname(const pkgconf_client_t *client) + + Retrieves the name of the variable that should contain a module's prefix. + In some cases, it is necessary to override this variable to allow proper path relocation. + + :param pkgconf_client_t* client: The client object to retrieve the prefix variable name from. + :return: the prefix variable name as a string + :rtype: const char * + +.. c:function:: void pkgconf_client_set_prefix_varname(pkgconf_client_t *client, const char *prefix_varname) + + Sets the name of the variable that should contain a module's prefix. + If the variable name is ``NULL``, then the default variable name (``prefix``) is used. + + :param pkgconf_client_t* client: The client object to set the prefix variable name on. + :param char* prefix_varname: The prefix variable name to set. + :return: nothing + +.. c:function:: pkgconf_client_get_warn_handler(const pkgconf_client_t *client) + + Returns the warning handler if one is set, else ``NULL``. + + :param pkgconf_client_t* client: The client object to get the warn handler from. + :return: a function pointer to the warn handler or ``NULL`` + +.. c:function:: pkgconf_client_set_warn_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t warn_handler, void *warn_handler_data) + + Sets a warn handler on a client object or uninstalls one if set to ``NULL``. + + :param pkgconf_client_t* client: The client object to set the warn handler on. + :param pkgconf_error_handler_func_t warn_handler: The warn handler to set. + :param void* warn_handler_data: Optional data to associate with the warn handler. + :return: nothing + +.. c:function:: pkgconf_client_get_error_handler(const pkgconf_client_t *client) + + Returns the error handler if one is set, else ``NULL``. + + :param pkgconf_client_t* client: The client object to get the error handler from. + :return: a function pointer to the error handler or ``NULL`` + +.. c:function:: pkgconf_client_set_error_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data) + + Sets a warn handler on a client object or uninstalls one if set to ``NULL``. + + :param pkgconf_client_t* client: The client object to set the error handler on. + :param pkgconf_error_handler_func_t error_handler: The error handler to set. + :param void* error_handler_data: Optional data to associate with the error handler. + :return: nothing + +.. c:function:: pkgconf_client_get_trace_handler(const pkgconf_client_t *client) + + Returns the error handler if one is set, else ``NULL``. + + :param pkgconf_client_t* client: The client object to get the error handler from. + :return: a function pointer to the error handler or ``NULL`` + +.. c:function:: pkgconf_client_set_trace_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t trace_handler, void *trace_handler_data) + + Sets a warn handler on a client object or uninstalls one if set to ``NULL``. + + :param pkgconf_client_t* client: The client object to set the error handler on. + :param pkgconf_error_handler_func_t trace_handler: The error handler to set. + :param void* trace_handler_data: Optional data to associate with the error handler. + :return: nothing diff --git a/contrib/pkgconf/doc/libpkgconf-dependency.rst b/contrib/pkgconf/doc/libpkgconf-dependency.rst new file mode 100644 index 00000000000..ce70d6c11c3 --- /dev/null +++ b/contrib/pkgconf/doc/libpkgconf-dependency.rst @@ -0,0 +1,90 @@ + +libpkgconf `dependency` module +============================== + +The `dependency` module provides support for building `dependency lists` (the basic component of the overall `dependency graph`) and +`dependency nodes` which store dependency information. + +.. c:function:: pkgconf_dependency_t *pkgconf_dependency_add(pkgconf_list_t *list, const char *package, const char *version, pkgconf_pkg_comparator_t compare) + + Adds a parsed dependency to a dependency list as a dependency node. + + :param pkgconf_client_t* client: The client object that owns the package this dependency list belongs to. + :param pkgconf_list_t* list: The dependency list to add a dependency node to. + :param char* package: The package `atom` to set on the dependency node. + :param char* version: The package `version` to set on the dependency node. + :param pkgconf_pkg_comparator_t compare: The comparison operator to set on the dependency node. + :param uint flags: Any flags to attach to the dependency node. + :return: A dependency node. + :rtype: pkgconf_dependency_t * + +.. c:function:: void pkgconf_dependency_append(pkgconf_list_t *list, pkgconf_dependency_t *tail) + + Adds a dependency node to a pre-existing dependency list. + + :param pkgconf_list_t* list: The dependency list to add a dependency node to. + :param pkgconf_dependency_t* tail: The dependency node to add to the tail of the dependency list. + :return: nothing + +.. c:function:: void pkgconf_dependency_free_one(pkgconf_dependency_t *dep) + + Frees a dependency node. + + :param pkgconf_dependency_t* dep: The dependency node to free. + :return: nothing + +.. c:function:: pkgconf_dependency_t *pkgconf_dependency_ref(pkgconf_client_t *owner, pkgconf_dependency_t *dep) + + Increases a dependency node's refcount. + + :param pkgconf_client_t* owner: The client object which owns the memory of this dependency node. + :param pkgconf_dependency_t* dep: The dependency to increase the refcount of. + :return: the dependency node on success, else NULL + +.. c:function:: void pkgconf_dependency_unref(pkgconf_client_t *owner, pkgconf_dependency_t *dep) + + Decreases a dependency node's refcount and frees it if necessary. + + :param pkgconf_client_t* owner: The client object which owns the memory of this dependency node. + :param pkgconf_dependency_t* dep: The dependency to decrease the refcount of. + :return: nothing + +.. c:function:: void pkgconf_dependency_free(pkgconf_list_t *list) + + Release a dependency list and its child dependency nodes. + + :param pkgconf_list_t* list: The dependency list to release. + :return: nothing + +.. c:function:: void pkgconf_dependency_parse_str(pkgconf_list_t *deplist_head, const char *depends) + + Parse a dependency declaration into a dependency list. + Commas are counted as whitespace to allow for constructs such as ``@SUBSTVAR@, zlib`` being processed + into ``, zlib``. + + :param pkgconf_client_t* client: The client object that owns the package this dependency list belongs to. + :param pkgconf_list_t* deplist_head: The dependency list to populate with dependency nodes. + :param char* depends: The dependency data to parse. + :param uint flags: Any flags to attach to the dependency nodes. + :return: nothing + +.. c:function:: void pkgconf_dependency_parse(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, pkgconf_list_t *deplist, const char *depends) + + Preprocess dependency data and then process that dependency declaration into a dependency list. + Commas are counted as whitespace to allow for constructs such as ``@SUBSTVAR@, zlib`` being processed + into ``, zlib``. + + :param pkgconf_client_t* client: The client object that owns the package this dependency list belongs to. + :param pkgconf_pkg_t* pkg: The package object that owns this dependency list. + :param pkgconf_list_t* deplist: The dependency list to populate with dependency nodes. + :param char* depends: The dependency data to parse. + :param uint flags: Any flags to attach to the dependency nodes. + :return: nothing + +.. c:function:: pkgconf_dependency_t *pkgconf_dependency_copy(pkgconf_client_t *client, const pkgconf_dependency_t *dep) + + Copies a dependency node to a new one. + + :param pkgconf_client_t* client: The client object that will own this dependency. + :param pkgconf_dependency_t* dep: The dependency node to copy. + :return: a pointer to a new dependency node, else NULL diff --git a/contrib/pkgconf/doc/libpkgconf-fragment.rst b/contrib/pkgconf/doc/libpkgconf-fragment.rst new file mode 100644 index 00000000000..00759b7de96 --- /dev/null +++ b/contrib/pkgconf/doc/libpkgconf-fragment.rst @@ -0,0 +1,116 @@ + +libpkgconf `fragment` module +============================ + +The `fragment` module provides low-level management and rendering of fragment lists. A +`fragment list` contains various `fragments` of text (such as ``-I /usr/include``) in a matter +which is composable, mergeable and reorderable. + +.. c:function:: void pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *string, unsigned int flags) + + Adds a `fragment` of text to a `fragment list`, possibly modifying the fragment if a sysroot is set. + + :param pkgconf_client_t* client: The pkgconf client being accessed. + :param pkgconf_list_t* list: The fragment list. + :param char* string: The string of text to add as a fragment to the fragment list. + :param uint flags: Parsing-related flags for the package. + :return: nothing + +.. c:function:: bool pkgconf_fragment_has_system_dir(const pkgconf_client_t *client, const pkgconf_fragment_t *frag) + + Checks if a `fragment` contains a `system path`. System paths are detected at compile time and optionally overridden by + the ``PKG_CONFIG_SYSTEM_INCLUDE_PATH`` and ``PKG_CONFIG_SYSTEM_LIBRARY_PATH`` environment variables. + + :param pkgconf_client_t* client: The pkgconf client object the fragment belongs to. + :param pkgconf_fragment_t* frag: The fragment being checked. + :return: true if the fragment contains a system path, else false + :rtype: bool + +.. c:function:: void pkgconf_fragment_copy(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_fragment_t *base, bool is_private) + + Copies a `fragment` to another `fragment list`, possibly removing a previous copy of the `fragment` + in a process known as `mergeback`. + + :param pkgconf_client_t* client: The pkgconf client being accessed. + :param pkgconf_list_t* list: The list the fragment is being added to. + :param pkgconf_fragment_t* base: The fragment being copied. + :param bool is_private: Whether the fragment list is a `private` fragment list (static linking). + :return: nothing + +.. c:function:: void pkgconf_fragment_copy_list(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_list_t *base) + + Copies a `fragment list` to another `fragment list`, possibly removing a previous copy of the fragments + in a process known as `mergeback`. + + :param pkgconf_client_t* client: The pkgconf client being accessed. + :param pkgconf_list_t* list: The list the fragments are being added to. + :param pkgconf_list_t* base: The list the fragments are being copied from. + :return: nothing + +.. c:function:: void pkgconf_fragment_filter(const pkgconf_client_t *client, pkgconf_list_t *dest, pkgconf_list_t *src, pkgconf_fragment_filter_func_t filter_func) + + Copies a `fragment list` to another `fragment list` which match a user-specified filtering function. + + :param pkgconf_client_t* client: The pkgconf client being accessed. + :param pkgconf_list_t* dest: The destination list. + :param pkgconf_list_t* src: The source list. + :param pkgconf_fragment_filter_func_t filter_func: The filter function to use. + :param void* data: Optional data to pass to the filter function. + :return: nothing + +.. c:function:: size_t pkgconf_fragment_render_len(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops) + + Calculates the required memory to store a `fragment list` when rendered as a string. + + :param pkgconf_list_t* list: The `fragment list` being rendered. + :param bool escape: Whether or not to escape special shell characters (deprecated). + :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``. + :return: the amount of bytes required to represent the `fragment list` when rendered + :rtype: size_t + +.. c:function:: void pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape, const pkgconf_fragment_render_ops_t *ops) + + Renders a `fragment list` into a buffer. + + :param pkgconf_list_t* list: The `fragment list` being rendered. + :param char* buf: The buffer to render the fragment list into. + :param size_t buflen: The length of the buffer. + :param bool escape: Whether or not to escape special shell characters (deprecated). + :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``. + :return: nothing + +.. c:function:: char *pkgconf_fragment_render(const pkgconf_list_t *list) + + Allocate memory and render a `fragment list` into it. + + :param pkgconf_list_t* list: The `fragment list` being rendered. + :param bool escape: Whether or not to escape special shell characters (deprecated). + :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``. + :return: An allocated string containing the rendered `fragment list`. + :rtype: char * + +.. c:function:: void pkgconf_fragment_delete(pkgconf_list_t *list, pkgconf_fragment_t *node) + + Delete a `fragment node` from a `fragment list`. + + :param pkgconf_list_t* list: The `fragment list` to delete from. + :param pkgconf_fragment_t* node: The `fragment node` to delete. + :return: nothing + +.. c:function:: void pkgconf_fragment_free(pkgconf_list_t *list) + + Delete an entire `fragment list`. + + :param pkgconf_list_t* list: The `fragment list` to delete. + :return: nothing + +.. c:function:: bool pkgconf_fragment_parse(const pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_list_t *vars, const char *value) + + Parse a string into a `fragment list`. + + :param pkgconf_client_t* client: The pkgconf client being accessed. + :param pkgconf_list_t* list: The `fragment list` to add the fragment entries to. + :param pkgconf_list_t* vars: A list of variables to use for variable substitution. + :param uint flags: Any parsing flags to be aware of. + :param char* value: The string to parse into fragments. + :return: true on success, false on parse error diff --git a/contrib/pkgconf/doc/libpkgconf-path.rst b/contrib/pkgconf/doc/libpkgconf-path.rst new file mode 100644 index 00000000000..04789440a2d --- /dev/null +++ b/contrib/pkgconf/doc/libpkgconf-path.rst @@ -0,0 +1,71 @@ + +libpkgconf `path` module +======================== + +The `path` module provides functions for manipulating lists of paths in a cross-platform manner. Notably, +it is used by the `pkgconf client` to parse the ``PKG_CONFIG_PATH``, ``PKG_CONFIG_LIBDIR`` and related environment +variables. + +.. c:function:: void pkgconf_path_add(const char *text, pkgconf_list_t *dirlist) + + Adds a path node to a path list. If the path is already in the list, do nothing. + + :param char* text: The path text to add as a path node. + :param pkgconf_list_t* dirlist: The path list to add the path node to. + :param bool filter: Whether to perform duplicate filtering. + :return: nothing + +.. c:function:: size_t pkgconf_path_split(const char *text, pkgconf_list_t *dirlist) + + Splits a given text input and inserts paths into a path list. + + :param char* text: The path text to split and add as path nodes. + :param pkgconf_list_t* dirlist: The path list to have the path nodes added to. + :param bool filter: Whether to perform duplicate filtering. + :return: number of path nodes added to the path list + :rtype: size_t + +.. c:function:: size_t pkgconf_path_build_from_environ(const char *envvarname, const char *fallback, pkgconf_list_t *dirlist) + + Adds the paths specified in an environment variable to a path list. If the environment variable is not set, + an optional default set of paths is added. + + :param char* envvarname: The environment variable to look up. + :param char* fallback: The fallback paths to use if the environment variable is not set. + :param pkgconf_list_t* dirlist: The path list to add the path nodes to. + :param bool filter: Whether to perform duplicate filtering. + :return: number of path nodes added to the path list + :rtype: size_t + +.. c:function:: bool pkgconf_path_match_list(const char *path, const pkgconf_list_t *dirlist) + + Checks whether a path has a matching prefix in a path list. + + :param char* path: The path to check against a path list. + :param pkgconf_list_t* dirlist: The path list to check the path against. + :return: true if the path list has a matching prefix, otherwise false + :rtype: bool + +.. c:function:: void pkgconf_path_copy_list(pkgconf_list_t *dst, const pkgconf_list_t *src) + + Copies a path list to another path list. + + :param pkgconf_list_t* dst: The path list to copy to. + :param pkgconf_list_t* src: The path list to copy from. + :return: nothing + +.. c:function:: void pkgconf_path_free(pkgconf_list_t *dirlist) + + Releases any path nodes attached to the given path list. + + :param pkgconf_list_t* dirlist: The path list to clean up. + :return: nothing + +.. c:function:: bool pkgconf_path_relocate(char *buf, size_t buflen) + + Relocates a path, possibly calling normpath() on it. + + :param char* buf: The path to relocate. + :param size_t buflen: The buffer length the path is contained in. + :return: true on success, false on error + :rtype: bool diff --git a/contrib/pkgconf/doc/libpkgconf-personality.rst b/contrib/pkgconf/doc/libpkgconf-personality.rst new file mode 100644 index 00000000000..134671ad121 --- /dev/null +++ b/contrib/pkgconf/doc/libpkgconf-personality.rst @@ -0,0 +1,27 @@ + +libpkgconf `personality` module +========================= + +.. c:function:: const pkgconf_cross_personality_t *pkgconf_cross_personality_default(void) + + Returns the default cross-compile personality. + + Not thread safe. + + :rtype: pkgconf_cross_personality_t* + :return: the default cross-compile personality + +.. c:function:: void pkgconf_cross_personality_deinit(pkgconf_cross_personality_t *) + + Decrements the count of default cross personality instances. + + Not thread safe. + + :rtype: void + +.. c:function:: pkgconf_cross_personality_t *pkgconf_cross_personality_find(const char *triplet) + + Attempts to find a cross-compile personality given a triplet. + + :rtype: pkgconf_cross_personality_t* + :return: the default cross-compile personality diff --git a/contrib/pkgconf/doc/libpkgconf-pkg.rst b/contrib/pkgconf/doc/libpkgconf-pkg.rst new file mode 100644 index 00000000000..044bd1f2f01 --- /dev/null +++ b/contrib/pkgconf/doc/libpkgconf-pkg.rst @@ -0,0 +1,154 @@ + +libpkgconf `pkg` module +======================= + +The `pkg` module provides dependency resolution services and the overall `.pc` file parsing +routines. + +.. c:function:: pkgconf_pkg_t *pkgconf_pkg_new_from_file(const pkgconf_client_t *client, const char *filename, FILE *f, unsigned int flags) + + Parse a .pc file into a pkgconf_pkg_t object structure. + + :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + :param char* filename: The filename of the package file (including full path). + :param FILE* f: The file object to read from. + :param uint flags: The flags to use when parsing. + :returns: A ``pkgconf_pkg_t`` object which contains the package data. + :rtype: pkgconf_pkg_t * + +.. c:function:: void pkgconf_pkg_free(pkgconf_client_t *client, pkgconf_pkg_t *pkg) + + Releases all releases for a given ``pkgconf_pkg_t`` object. + + :param pkgconf_client_t* client: The client which owns the ``pkgconf_pkg_t`` object, `pkg`. + :param pkgconf_pkg_t* pkg: The package to free. + :return: nothing + +.. c:function:: pkgconf_pkg_t *pkgconf_pkg_ref(const pkgconf_client_t *client, pkgconf_pkg_t *pkg) + + Adds an additional reference to the package object. + + :param pkgconf_client_t* client: The pkgconf client object which owns the package being referenced. + :param pkgconf_pkg_t* pkg: The package object being referenced. + :return: The package itself with an incremented reference count. + :rtype: pkgconf_pkg_t * + +.. c:function:: void pkgconf_pkg_unref(pkgconf_client_t *client, pkgconf_pkg_t *pkg) + + Releases a reference on the package object. If the reference count is 0, then also free the package. + + :param pkgconf_client_t* client: The pkgconf client object which owns the package being dereferenced. + :param pkgconf_pkg_t* pkg: The package object being dereferenced. + :return: nothing + +.. c:function:: pkgconf_pkg_t *pkgconf_scan_all(pkgconf_client_t *client, void *data, pkgconf_pkg_iteration_func_t func) + + Iterates over all packages found in the `package directory list`, running ``func`` on them. If ``func`` returns true, + then stop iteration and return the last iterated package. + + :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + :param void* data: An opaque pointer to data to provide the iteration function with. + :param pkgconf_pkg_iteration_func_t func: A function which is called for each package to determine if the package matches, + always return ``false`` to iterate over all packages. + :return: A package object reference if one is found by the scan function, else ``NULL``. + :rtype: pkgconf_pkg_t * + +.. c:function:: pkgconf_pkg_t *pkgconf_pkg_find(pkgconf_client_t *client, const char *name) + + Search for a package. + + :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + :param char* name: The name of the package `atom` to use for searching. + :return: A package object reference if the package was found, else ``NULL``. + :rtype: pkgconf_pkg_t * + +.. c:function:: int pkgconf_compare_version(const char *a, const char *b) + + Compare versions using RPM version comparison rules as described in the LSB. + + :param char* a: The first version to compare in the pair. + :param char* b: The second version to compare in the pair. + :return: -1 if the first version is less than, 0 if both versions are equal, 1 if the second version is less than. + :rtype: int + +.. c:function:: pkgconf_pkg_t *pkgconf_builtin_pkg_get(const char *name) + + Looks up a built-in package. The package should not be freed or dereferenced. + + :param char* name: An atom corresponding to a built-in package to search for. + :return: the built-in package if present, else ``NULL``. + :rtype: pkgconf_pkg_t * + +.. c:function:: const char *pkgconf_pkg_get_comparator(const pkgconf_dependency_t *pkgdep) + + Returns the comparator used in a depgraph dependency node as a string. + + :param pkgconf_dependency_t* pkgdep: The depgraph dependency node to return the comparator for. + :return: A string matching the comparator or ``"???"``. + :rtype: char * + +.. c:function:: pkgconf_pkg_comparator_t pkgconf_pkg_comparator_lookup_by_name(const char *name) + + Look up the appropriate comparator bytecode in the comparator set (defined + in ``pkg.c``, see ``pkgconf_pkg_comparator_names`` and ``pkgconf_pkg_comparator_impls``). + + :param char* name: The comparator to look up by `name`. + :return: The comparator bytecode if found, else ``PKGCONF_CMP_ANY``. + :rtype: pkgconf_pkg_comparator_t + +.. c:function:: pkgconf_pkg_t *pkgconf_pkg_verify_dependency(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags) + + Verify a pkgconf_dependency_t node in the depgraph. If the dependency is solvable, + return the appropriate ``pkgconf_pkg_t`` object, else ``NULL``. + + :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + :param pkgconf_dependency_t* pkgdep: The dependency graph node to solve. + :param uint* eflags: An optional pointer that, if set, will be populated with an error code from the resolver. + :return: On success, the appropriate ``pkgconf_pkg_t`` object to solve the dependency, else ``NULL``. + :rtype: pkgconf_pkg_t * + +.. c:function:: unsigned int pkgconf_pkg_verify_graph(pkgconf_client_t *client, pkgconf_pkg_t *root, int depth) + + Verify the graph dependency nodes are satisfiable by walking the tree using + ``pkgconf_pkg_traverse()``. + + :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + :param pkgconf_pkg_t* root: The root entry in the package dependency graph which should contain the top-level dependencies to resolve. + :param int depth: The maximum allowed depth for dependency resolution. + :return: On success, ``PKGCONF_PKG_ERRF_OK`` (0), else an error code. + :rtype: unsigned int + +.. c:function:: unsigned int pkgconf_pkg_traverse(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_pkg_traverse_func_t func, void *data, int maxdepth, unsigned int skip_flags) + + Walk and resolve the dependency graph up to `maxdepth` levels. + + :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + :param pkgconf_pkg_t* root: The root of the dependency graph. + :param pkgconf_pkg_traverse_func_t func: A traversal function to call for each resolved node in the dependency graph. + :param void* data: An opaque pointer to data to be passed to the traversal function. + :param int maxdepth: The maximum depth to walk the dependency graph for. -1 means infinite recursion. + :param uint skip_flags: Skip over dependency nodes containing the specified flags. A setting of 0 skips no dependency nodes. + :return: ``PKGCONF_PKG_ERRF_OK`` on success, else an error code. + :rtype: unsigned int + +.. c:function:: int pkgconf_pkg_cflags(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth) + + Walks a dependency graph and extracts relevant ``CFLAGS`` fragments. + + :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + :param pkgconf_pkg_t* root: The root of the dependency graph. + :param pkgconf_list_t* list: The fragment list to add the extracted ``CFLAGS`` fragments to. + :param int maxdepth: The maximum allowed depth for dependency resolution. -1 means infinite recursion. + :return: ``PKGCONF_PKG_ERRF_OK`` if successful, otherwise an error code. + :rtype: unsigned int + +.. c:function:: int pkgconf_pkg_libs(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth) + + Walks a dependency graph and extracts relevant ``LIBS`` fragments. + + :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + :param pkgconf_pkg_t* root: The root of the dependency graph. + :param pkgconf_list_t* list: The fragment list to add the extracted ``LIBS`` fragments to. + :param int maxdepth: The maximum allowed depth for dependency resolution. -1 means infinite recursion. + :return: ``PKGCONF_PKG_ERRF_OK`` if successful, otherwise an error code. + :rtype: unsigned int diff --git a/contrib/pkgconf/doc/libpkgconf-queue.rst b/contrib/pkgconf/doc/libpkgconf-queue.rst new file mode 100644 index 00000000000..43fca6960d2 --- /dev/null +++ b/contrib/pkgconf/doc/libpkgconf-queue.rst @@ -0,0 +1,78 @@ + +libpkgconf `queue` module +========================= + +The `queue` module provides an interface that allows easily building a dependency graph from an +arbitrary set of dependencies. It also provides support for doing "preflight" checks on the entire +dependency graph prior to working with it. + +Using the `queue` module functions is the recommended way of working with dependency graphs. + +.. c:function:: void pkgconf_queue_push(pkgconf_list_t *list, const char *package) + + Pushes a requested dependency onto the dependency resolver's queue. + + :param pkgconf_list_t* list: the dependency resolution queue to add the package request to. + :param char* package: the dependency atom requested + :return: nothing + +.. c:function:: bool pkgconf_queue_compile(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *list) + + Compile a dependency resolution queue into a dependency resolution problem if possible, otherwise report an error. + + :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + :param pkgconf_pkg_t* world: The designated root of the dependency graph. + :param pkgconf_list_t* list: The list of dependency requests to consider. + :return: true if the built dependency resolution problem is consistent, else false + :rtype: bool + +.. c:function:: void pkgconf_queue_free(pkgconf_list_t *list) + + Release any memory related to a dependency resolution queue. + + :param pkgconf_list_t* list: The dependency resolution queue to release. + :return: nothing + +.. c:function:: void pkgconf_solution_free(pkgconf_client_t *client, pkgconf_pkg_t *world, int maxdepth) + + Removes references to package nodes contained in a solution. + + :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + :param pkgconf_pkg_t* world: The root for the generated dependency graph. Should have PKGCONF_PKG_PROPF_VIRTUAL flag. + :returns: nothing + +.. c:function:: bool pkgconf_queue_solve(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_pkg_t *world, int maxdepth) + + Solves and flattens the dependency graph for the supplied dependency list. + + :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + :param pkgconf_list_t* list: The list of dependency requests to consider. + :param pkgconf_pkg_t* world: The root for the generated dependency graph, provided by the caller. Should have PKGCONF_PKG_PROPF_VIRTUAL flag. + :param int maxdepth: The maximum allowed depth for the dependency resolver. A depth of -1 means unlimited. + :returns: true if the dependency resolver found a solution, otherwise false. + :rtype: bool + +.. c:function:: void pkgconf_queue_apply(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_queue_apply_func_t func, int maxdepth, void *data) + + Attempt to compile a dependency resolution queue into a dependency resolution problem, then attempt to solve the problem and + feed the solution to a callback function if a complete dependency graph is found. + + This function should not be used in new code. Use pkgconf_queue_solve instead. + + :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + :param pkgconf_list_t* list: The list of dependency requests to consider. + :param pkgconf_queue_apply_func_t func: The callback function to call if a solution is found by the dependency resolver. + :param int maxdepth: The maximum allowed depth for the dependency resolver. A depth of -1 means unlimited. + :param void* data: An opaque pointer which is passed to the callback function. + :returns: true if the dependency resolver found a solution, otherwise false. + :rtype: bool + +.. c:function:: void pkgconf_queue_validate(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_queue_apply_func_t func, int maxdepth, void *data) + + Attempt to compile a dependency resolution queue into a dependency resolution problem, then attempt to solve the problem. + + :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + :param pkgconf_list_t* list: The list of dependency requests to consider. + :param int maxdepth: The maximum allowed depth for the dependency resolver. A depth of -1 means unlimited. + :returns: true if the dependency resolver found a solution, otherwise false. + :rtype: bool diff --git a/contrib/pkgconf/doc/libpkgconf-tuple.rst b/contrib/pkgconf/doc/libpkgconf-tuple.rst new file mode 100644 index 00000000000..419168b0e46 --- /dev/null +++ b/contrib/pkgconf/doc/libpkgconf-tuple.rst @@ -0,0 +1,92 @@ + +libpkgconf `tuple` module +========================= + +The `tuple` module provides key-value mappings backed by a linked list. The key-value +mapping is mainly used for variable substitution when parsing .pc files. + +There are two sets of mappings: a ``pkgconf_pkg_t`` specific mapping, and a `global` mapping. +The `tuple` module provides convenience wrappers for managing the `global` mapping, which is +attached to a given client object. + +.. c:function:: void pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value) + + Defines a global variable, replacing the previous declaration if one was set. + + :param pkgconf_client_t* client: The pkgconf client object to modify. + :param char* key: The key for the mapping (variable name). + :param char* value: The value for the mapped entry. + :return: nothing + +.. c:function:: void pkgconf_tuple_find_global(const pkgconf_client_t *client, const char *key) + + Looks up a global variable. + + :param pkgconf_client_t* client: The pkgconf client object to access. + :param char* key: The key or variable name to look up. + :return: the contents of the variable or ``NULL`` + :rtype: char * + +.. c:function:: void pkgconf_tuple_free_global(pkgconf_client_t *client) + + Delete all global variables associated with a pkgconf client object. + + :param pkgconf_client_t* client: The pkgconf client object to modify. + :return: nothing + +.. c:function:: void pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv) + + Parse and define a global variable. + + :param pkgconf_client_t* client: The pkgconf client object to modify. + :param char* kv: The variable in the form of ``key=value``. + :return: nothing + +.. c:function:: pkgconf_tuple_t *pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key, const char *value, bool parse) + + Optionally parse and then define a variable. + + :param pkgconf_client_t* client: The pkgconf client object to access. + :param pkgconf_list_t* list: The variable list to add the new variable to. + :param char* key: The name of the variable being added. + :param char* value: The value of the variable being added. + :param bool parse: Whether or not to parse the value for variable substitution. + :return: a variable object + :rtype: pkgconf_tuple_t * + +.. c:function:: char *pkgconf_tuple_find(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key) + + Look up a variable in a variable list. + + :param pkgconf_client_t* client: The pkgconf client object to access. + :param pkgconf_list_t* list: The variable list to search. + :param char* key: The variable name to search for. + :return: the value of the variable or ``NULL`` + :rtype: char * + +.. c:function:: char *pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *vars, const char *value, unsigned int flags) + + Parse an expression for variable substitution. + + :param pkgconf_client_t* client: The pkgconf client object to access. + :param pkgconf_list_t* list: The variable list to search for variables (along side the global variable list). + :param char* value: The ``key=value`` string to parse. + :param uint flags: Any flags to consider while parsing. + :return: the variable data with any variables substituted + :rtype: char * + +.. c:function:: void pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list) + + Deletes a variable object, removing it from any variable lists and releasing any memory associated + with it. + + :param pkgconf_tuple_t* tuple: The variable object to release. + :param pkgconf_list_t* list: The variable list the variable object is attached to. + :return: nothing + +.. c:function:: void pkgconf_tuple_free(pkgconf_list_t *list) + + Deletes a variable list and any variables attached to it. + + :param pkgconf_list_t* list: The variable list to delete. + :return: nothing diff --git a/contrib/pkgconf/doc/libpkgconf.rst b/contrib/pkgconf/doc/libpkgconf.rst new file mode 100644 index 00000000000..37167f0f25f --- /dev/null +++ b/contrib/pkgconf/doc/libpkgconf.rst @@ -0,0 +1,17 @@ +libpkgconf - an API for managing `pkg-config` modules +===================================================== + +.. toctree:: + :maxdepth: 2 + + libpkgconf-argvsplit + libpkgconf-audit + libpkgconf-cache + libpkgconf-client + libpkgconf-dependency + libpkgconf-fragment + libpkgconf-path + libpkgconf-personality + libpkgconf-pkg + libpkgconf-queue + libpkgconf-tuple diff --git a/contrib/pkgconf/libpkgconf.pc.in b/contrib/pkgconf/libpkgconf.pc.in new file mode 100644 index 00000000000..119fe57f827 --- /dev/null +++ b/contrib/pkgconf/libpkgconf.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=${prefix} +includedir=@includedir@ +libdir=@libdir@ + +Name: libpkgconf +Description: a library for accessing and manipulating development framework configuration +URL: https://gitea.treehouse.systems/ariadne/pkgconf +License: ISC +Version: @PACKAGE_VERSION@ +CFlags: -I${includedir}/pkgconf +Libs: -L${libdir} -lpkgconf diff --git a/contrib/pkgconf/libpkgconf/argvsplit.c b/contrib/pkgconf/libpkgconf/argvsplit.c new file mode 100644 index 00000000000..1ff221ac63f --- /dev/null +++ b/contrib/pkgconf/libpkgconf/argvsplit.c @@ -0,0 +1,161 @@ +/* + * argvsplit.c + * argv_split() routine + * + * Copyright (c) 2012, 2017 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include +#include + +/* + * !doc + * + * libpkgconf `argvsplit` module + * ============================= + * + * This is a lowlevel module which provides parsing of strings into argument vectors, + * similar to what a shell would do. + */ + +/* + * !doc + * + * .. c:function:: void pkgconf_argv_free(char **argv) + * + * Frees an argument vector. + * + * :param char** argv: The argument vector to free. + * :return: nothing + */ +void +pkgconf_argv_free(char **argv) +{ + free(argv[0]); + free(argv); +} + +/* + * !doc + * + * .. c:function:: int pkgconf_argv_split(const char *src, int *argc, char ***argv) + * + * Splits a string into an argument vector. + * + * :param char* src: The string to split. + * :param int* argc: A pointer to an integer to store the argument count. + * :param char*** argv: A pointer to a pointer for an argument vector. + * :return: 0 on success, -1 on error. + * :rtype: int + */ +int +pkgconf_argv_split(const char *src, int *argc, char ***argv) +{ + char *buf = calloc(1, strlen(src) + 1); + if (buf == NULL) + return -1; + + const char *src_iter; + char *dst_iter; + int argc_count = 0; + int argv_size = 5; + char quote = 0; + bool escaped = false; + + src_iter = src; + dst_iter = buf; + + *argv = calloc(argv_size, sizeof (void *)); + if (*argv == NULL) + { + free(buf); + return -1; + } + + (*argv)[argc_count] = dst_iter; + + while (*src_iter) + { + if (escaped) + { + /* POSIX: only \CHAR is special inside a double quote if CHAR is {$, `, ", \, newline}. */ + if (quote == '"') + { + if (!(*src_iter == '$' || *src_iter == '`' || *src_iter == '"' || *src_iter == '\\')) + *dst_iter++ = '\\'; + + *dst_iter++ = *src_iter; + } + else + { + *dst_iter++ = *src_iter; + } + + escaped = false; + } + else if (quote) + { + if (*src_iter == quote) + quote = 0; + else if (*src_iter == '\\' && quote != '\'') + escaped = true; + else + *dst_iter++ = *src_iter; + } + else if (isspace((unsigned char)*src_iter)) + { + if ((*argv)[argc_count] != NULL) + { + argc_count++, dst_iter++; + + if (argc_count == argv_size) + { + argv_size += 5; + *argv = realloc(*argv, sizeof(void *) * argv_size); + } + + (*argv)[argc_count] = dst_iter; + } + } + else switch(*src_iter) + { + case '\\': + escaped = true; + break; + + case '\"': + case '\'': + quote = *src_iter; + break; + + default: + *dst_iter++ = *src_iter; + break; + } + + src_iter++; + } + + if (escaped || quote) + { + free(*argv); + free(buf); + return -1; + } + + if (strlen((*argv)[argc_count])) + { + argc_count++; + } + + *argc = argc_count; + return 0; +} diff --git a/contrib/pkgconf/libpkgconf/audit.c b/contrib/pkgconf/libpkgconf/audit.c new file mode 100644 index 00000000000..a06eb24fc72 --- /dev/null +++ b/contrib/pkgconf/libpkgconf/audit.c @@ -0,0 +1,98 @@ +/* + * audit.c + * package audit log functions + * + * Copyright (c) 2016 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include + +/* + * !doc + * + * libpkgconf `audit` module + * ========================= + * + * The libpkgconf `audit` module contains the functions related to attaching an audit log file + * to a ``pkgconf_client_t`` object. + * + * The audit log format is the same as the output generated by the ``PKG_CONFIG_LOG`` environment + * variable. + */ + +/* + * !doc + * + * .. c:function:: void pkgconf_audit_set_log(pkgconf_client_t *client, FILE *auditf) + * + * Sets the audit log file pointer on `client` to `auditf`. + * The callee is responsible for closing any previous log files. + * + * :param pkgconf_client_t* client: The client object to modify. + * :param FILE* auditf: The file pointer for the already open log file. + * :return: nothing + */ +void +pkgconf_audit_set_log(pkgconf_client_t *client, FILE *auditf) +{ + client->auditf = auditf; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_audit_log(pkgconf_client_t *client, const char *format, ...) + * + * Logs a message to the opened audit log (if any). + * + * :param pkgconf_client_t* client: The client object the log message is for. + * :param char* format: The format string to use for the log messages. + * :return: nothing + */ +void +pkgconf_audit_log(pkgconf_client_t *client, const char *format, ...) +{ + va_list va; + + if (client->auditf == NULL) + return; + + va_start(va, format); + vfprintf(client->auditf, format, va); + va_end(va); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_audit_log_dependency(pkgconf_client_t *client, const pkgconf_pkg_t *dep, const pkgconf_dependency_t *depnode) + * + * Convenience function which logs a dependency node to the opened audit log (if any). + * + * :param pkgconf_client_t* client: The client object the log message is for. + * :param pkgconf_pkg_t* dep: The dependency package object being logged. + * :param pkgconf_dependency_t* depnode: The dependency object itself being logged. + * :return: nothing + */ +void +pkgconf_audit_log_dependency(pkgconf_client_t *client, const pkgconf_pkg_t *dep, const pkgconf_dependency_t *depnode) +{ + if (client->auditf == NULL) + return; + + fprintf(client->auditf, "%s ", dep->id); + if (depnode->version != NULL && depnode->compare != PKGCONF_CMP_ANY) + { + fprintf(client->auditf, "%s %s ", pkgconf_pkg_get_comparator(depnode), depnode->version); + } + + fprintf(client->auditf, "[%s]\n", dep->version); +} diff --git a/contrib/pkgconf/libpkgconf/bsdstubs.c b/contrib/pkgconf/libpkgconf/bsdstubs.c new file mode 100644 index 00000000000..a5291f7a5a9 --- /dev/null +++ b/contrib/pkgconf/libpkgconf/bsdstubs.c @@ -0,0 +1,197 @@ +/* $OpenBSD: strlcpy.c,v 1.10 2005/08/08 08:05:37 espie Exp $ */ +/* $OpenBSD: strlcat.c,v 1.12 2005/03/30 20:13:52 otto Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif + +#include +#include + +#if !HAVE_DECL_STRLCPY +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +static inline size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} +#endif + +#if !HAVE_DECL_STRLCAT +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +static inline size_t +strlcat(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} +#endif + +/* + * Copyright (c) 2012 William Pitcock . + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#if !HAVE_DECL_STRNDUP +/* + * Creates a memory buffer and copies at most 'len' characters to it. + * If 'len' is less than the length of the source string, truncation occured. + */ +static inline char * +strndup(const char *src, size_t len) +{ + char *out = malloc(len + 1); + pkgconf_strlcpy(out, src, len + 1); + return out; +} +#endif + +#if !HAVE_DECL_PLEDGE +static inline int +pledge(const char *promises, const char *execpromises) +{ + (void) promises; + (void) execpromises; + + return 0; +} +#endif + +#if !HAVE_DECL_UNVEIL +static inline int +unveil(const char *path, const char *permissions) +{ + (void) path; + (void) permissions; + + return 0; +} +#endif + +size_t +pkgconf_strlcpy(char *dst, const char *src, size_t siz) +{ + return strlcpy(dst, src, siz); +} + +size_t +pkgconf_strlcat(char *dst, const char *src, size_t siz) +{ + return strlcat(dst, src, siz); +} + +char * +pkgconf_strndup(const char *src, size_t len) +{ + return strndup(src, len); +} + +#if !HAVE_DECL_REALLOCARRAY +void * +reallocarray(void *ptr, size_t m, size_t n) +{ + if (n && m > -1 / n) + { + errno = ENOMEM; + return 0; + } + + return realloc(ptr, m * n); +} +#endif + +void * +pkgconf_reallocarray(void *ptr, size_t m, size_t n) +{ + return reallocarray(ptr, m, n); +} + +int +pkgconf_pledge(const char *promises, const char *execpromises) +{ + return pledge(promises, execpromises); +} + +int +pkgconf_unveil(const char *path, const char *permissions) +{ + return unveil(path, permissions); +} diff --git a/contrib/pkgconf/libpkgconf/bsdstubs.h b/contrib/pkgconf/libpkgconf/bsdstubs.h new file mode 100644 index 00000000000..21b9432a4a5 --- /dev/null +++ b/contrib/pkgconf/libpkgconf/bsdstubs.h @@ -0,0 +1,36 @@ +/* + * bsdstubs.h + * Header for stub BSD function prototypes if unavailable on a specific platform. + * + * Copyright (c) 2012 William Pitcock . + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#ifndef LIBPKGCONF_BSDSTUBS_H +#define LIBPKGCONF_BSDSTUBS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +PKGCONF_API extern size_t pkgconf_strlcpy(char *dst, const char *src, size_t siz); +PKGCONF_API extern size_t pkgconf_strlcat(char *dst, const char *src, size_t siz); +PKGCONF_API extern char *pkgconf_strndup(const char *src, size_t len); +PKGCONF_API extern void *pkgconf_reallocarray(void *ptr, size_t m, size_t n); +PKGCONF_API extern int pkgconf_pledge(const char *promises, const char *execpromises); +PKGCONF_API extern int pkgconf_unveil(const char *path, const char *permissions); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/contrib/pkgconf/libpkgconf/buffer.c b/contrib/pkgconf/libpkgconf/buffer.c new file mode 100644 index 00000000000..6857022db41 --- /dev/null +++ b/contrib/pkgconf/libpkgconf/buffer.c @@ -0,0 +1,87 @@ +/* + * buffer.c + * dynamically-managed buffers + * + * Copyright (c) 2024 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include +#include + +/* + * !doc + * + * libpkgconf `buffer` module + * ========================== + * + * The libpkgconf `buffer` module contains the functions related to managing + * dynamically-allocated buffers. + */ + +static inline size_t +target_allocation_size(size_t target_size) +{ + return 4096 + (4096 * (target_size / 4096)); +} + +void +pkgconf_buffer_append(pkgconf_buffer_t *buffer, const char *text) +{ + size_t needed = strlen(text) + 1; + size_t newsize = pkgconf_buffer_len(buffer) + needed; + + char *newbase = realloc(buffer->base, target_allocation_size(newsize)); + + /* XXX: silently failing here is antisocial */ + if (newbase == NULL) + return; + + char *newend = newbase + pkgconf_buffer_len(buffer); + pkgconf_strlcpy(newend, text, needed); + + buffer->base = newbase; + buffer->end = newend + needed; +} + +void +pkgconf_buffer_push_byte(pkgconf_buffer_t *buffer, char byte) +{ + size_t newsize = pkgconf_buffer_len(buffer) + 1; + char *newbase = realloc(buffer->base, target_allocation_size(newsize)); + + /* XXX: silently failing here remains antisocial */ + if (newbase == NULL) + return; + + char *newend = newbase + newsize; + *(newend - 1) = byte; + *newend = '\0'; + + buffer->base = newbase; + buffer->end = newend; +} + +void +pkgconf_buffer_trim_byte(pkgconf_buffer_t *buffer) +{ + size_t newsize = pkgconf_buffer_len(buffer) - 1; + char *newbase = realloc(buffer->base, target_allocation_size(newsize)); + + buffer->base = newbase; + buffer->end = newbase + newsize; + *(buffer->end) = '\0'; +} + +void +pkgconf_buffer_finalize(pkgconf_buffer_t *buffer) +{ + free(buffer->base); +} diff --git a/contrib/pkgconf/libpkgconf/cache.c b/contrib/pkgconf/libpkgconf/cache.c new file mode 100644 index 00000000000..883c8df7127 --- /dev/null +++ b/contrib/pkgconf/libpkgconf/cache.c @@ -0,0 +1,231 @@ +/* + * cache.c + * package object cache + * + * Copyright (c) 2013 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include +#include + +#include + +/* + * !doc + * + * libpkgconf `cache` module + * ========================= + * + * The libpkgconf `cache` module manages a package/module object cache, allowing it to + * avoid loading duplicate copies of a package/module. + * + * A cache is tied to a specific pkgconf client object, so package objects should not + * be shared across threads. + */ + +static int +cache_member_cmp(const void *a, const void *b) +{ + const char *key = a; + const pkgconf_pkg_t *pkg = *(void **) b; + + return strcmp(key, pkg->id); +} + +static int +cache_member_sort_cmp(const void *a, const void *b) +{ + const pkgconf_pkg_t *pkgA = *(void **) a; + const pkgconf_pkg_t *pkgB = *(void **) b; + + if (pkgA == NULL) + return 1; + + if (pkgB == NULL) + return -1; + + return strcmp(pkgA->id, pkgB->id); +} + +static void +cache_dump(const pkgconf_client_t *client) +{ + size_t i; + + PKGCONF_TRACE(client, "dumping package cache contents"); + + for (i = 0; i < client->cache_count; i++) + { + const pkgconf_pkg_t *pkg = client->cache_table[i]; + + PKGCONF_TRACE(client, SIZE_FMT_SPECIFIER": %p(%s)", + i, pkg, pkg == NULL ? "NULL" : pkg->id); + } +} + +/* + * !doc + * + * .. c:function:: pkgconf_pkg_t *pkgconf_cache_lookup(const pkgconf_client_t *client, const char *id) + * + * Looks up a package in the cache given an `id` atom, + * such as ``gtk+-3.0`` and returns the already loaded version + * if present. + * + * :param pkgconf_client_t* client: The client object to access. + * :param char* id: The package atom to look up in the client object's cache. + * :return: A package object if present, else ``NULL``. + * :rtype: pkgconf_pkg_t * + */ +pkgconf_pkg_t * +pkgconf_cache_lookup(pkgconf_client_t *client, const char *id) +{ + if (client->cache_table == NULL) + return NULL; + + pkgconf_pkg_t **pkg; + + pkg = bsearch(id, client->cache_table, + client->cache_count, sizeof (void *), + cache_member_cmp); + + if (pkg != NULL) + { + PKGCONF_TRACE(client, "found: %s @%p", id, *pkg); + return pkgconf_pkg_ref(client, *pkg); + } + + PKGCONF_TRACE(client, "miss: %s", id); + return NULL; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_cache_add(pkgconf_client_t *client, pkgconf_pkg_t *pkg) + * + * Adds an entry for the package to the package cache. + * The cache entry must be removed if the package is freed. + * + * :param pkgconf_client_t* client: The client object to modify. + * :param pkgconf_pkg_t* pkg: The package object to add to the client object's cache. + * :return: nothing + */ +void +pkgconf_cache_add(pkgconf_client_t *client, pkgconf_pkg_t *pkg) +{ + if (pkg == NULL) + return; + + pkgconf_pkg_ref(client, pkg); + + PKGCONF_TRACE(client, "added @%p to cache", pkg); + + /* mark package as cached */ + pkg->flags |= PKGCONF_PKG_PROPF_CACHED; + + ++client->cache_count; + client->cache_table = pkgconf_reallocarray(client->cache_table, + client->cache_count, sizeof (void *)); + client->cache_table[client->cache_count - 1] = pkg; + + qsort(client->cache_table, client->cache_count, + sizeof(void *), cache_member_sort_cmp); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_cache_remove(pkgconf_client_t *client, pkgconf_pkg_t *pkg) + * + * Deletes a package from the client object's package cache. + * + * :param pkgconf_client_t* client: The client object to modify. + * :param pkgconf_pkg_t* pkg: The package object to remove from the client object's cache. + * :return: nothing + */ +void +pkgconf_cache_remove(pkgconf_client_t *client, pkgconf_pkg_t *pkg) +{ + if (client->cache_table == NULL) + return; + + if (pkg == NULL) + return; + + if (!(pkg->flags & PKGCONF_PKG_PROPF_CACHED)) + return; + + PKGCONF_TRACE(client, "removed @%p from cache", pkg); + + pkgconf_pkg_t **slot; + + slot = bsearch(pkg->id, client->cache_table, + client->cache_count, sizeof (void *), + cache_member_cmp); + + if (slot == NULL) + return; + + (*slot)->flags &= ~PKGCONF_PKG_PROPF_CACHED; + pkgconf_pkg_unref(client, *slot); + *slot = NULL; + + qsort(client->cache_table, client->cache_count, + sizeof(void *), cache_member_sort_cmp); + + if (client->cache_table[client->cache_count - 1] != NULL) + { + PKGCONF_TRACE(client, "end of cache table refers to %p, not NULL", + client->cache_table[client->cache_count - 1]); + cache_dump(client); + abort(); + } + + client->cache_count--; + if (client->cache_count > 0) + { + client->cache_table = pkgconf_reallocarray(client->cache_table, + client->cache_count, sizeof(void *)); + } + else + { + free(client->cache_table); + client->cache_table = NULL; + } +} + +/* + * !doc + * + * .. c:function:: void pkgconf_cache_free(pkgconf_client_t *client) + * + * Releases all resources related to a client object's package cache. + * This function should only be called to clear a client object's package cache, + * as it may release any package in the cache. + * + * :param pkgconf_client_t* client: The client object to modify. + */ +void +pkgconf_cache_free(pkgconf_client_t *client) +{ + if (client->cache_table == NULL) + return; + + while (client->cache_count > 0) + pkgconf_cache_remove(client, client->cache_table[0]); + + free(client->cache_table); + client->cache_table = NULL; + client->cache_count = 0; + + PKGCONF_TRACE(client, "cleared package cache"); +} diff --git a/contrib/pkgconf/libpkgconf/client.c b/contrib/pkgconf/libpkgconf/client.c new file mode 100644 index 00000000000..4fe36ecd9af --- /dev/null +++ b/contrib/pkgconf/libpkgconf/client.c @@ -0,0 +1,817 @@ +/* + * client.c + * libpkgconf consumer lifecycle management + * + * Copyright (c) 2016 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include +#include +#include + +/* + * !doc + * + * libpkgconf `client` module + * ========================== + * + * The libpkgconf `client` module implements the `pkgconf_client_t` "client" object. + * Client objects store all necessary state for libpkgconf allowing for multiple instances to run + * in parallel. + * + * Client objects are not thread safe, in other words, a client object should not be shared across + * thread boundaries. + */ + +static void +trace_path_list(const pkgconf_client_t *client, const char *desc, pkgconf_list_t *list) +{ + const pkgconf_node_t *n; + + PKGCONF_TRACE(client, "%s:", desc); + PKGCONF_FOREACH_LIST_ENTRY(list->head, n) + { + const pkgconf_path_t *p = n->data; + + PKGCONF_TRACE(client, " - '%s'", p->path); + } +} + +/* + * !doc + * + * .. c:function:: void pkgconf_client_dir_list_build(pkgconf_client_t *client) + * + * Bootstraps the package search paths. If the ``PKGCONF_PKG_PKGF_ENV_ONLY`` `flag` is set on the client, + * then only the ``PKG_CONFIG_PATH`` environment variable will be used, otherwise both the + * ``PKG_CONFIG_PATH`` and ``PKG_CONFIG_LIBDIR`` environment variables will be used. + * + * :param pkgconf_client_t* client: The pkgconf client object to bootstrap. + * :return: nothing + */ +void +pkgconf_client_dir_list_build(pkgconf_client_t *client, const pkgconf_cross_personality_t *personality) +{ + pkgconf_path_build_from_environ("PKG_CONFIG_PATH", NULL, &client->dir_list, true); + + if (!(client->flags & PKGCONF_PKG_PKGF_ENV_ONLY)) + { + pkgconf_list_t dir_list = PKGCONF_LIST_INITIALIZER; + const pkgconf_list_t *prepend_list = &personality->dir_list; + +#ifdef _WIN32 + (void) pkgconf_path_build_from_registry(HKEY_CURRENT_USER, &client->dir_list, true); + (void) pkgconf_path_build_from_registry(HKEY_LOCAL_MACHINE, &client->dir_list, true); +#endif + + if (getenv("PKG_CONFIG_LIBDIR") != NULL) + { + /* PKG_CONFIG_LIBDIR= should empty the search path entirely. */ + (void) pkgconf_path_build_from_environ("PKG_CONFIG_LIBDIR", NULL, &dir_list, true); + prepend_list = &dir_list; + } + + pkgconf_path_copy_list(&client->dir_list, prepend_list); + pkgconf_path_free(&dir_list); + } +} + +/* + * !doc + * + * .. c:function:: void pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality) + * + * Initialise a pkgconf client object. + * + * :param pkgconf_client_t* client: The client to initialise. + * :param pkgconf_error_handler_func_t error_handler: An optional error handler to use for logging errors. + * :param void* error_handler_data: user data passed to optional error handler + * :param pkgconf_cross_personality_t* personality: the cross-compile personality to use for defaults + * :return: nothing + */ +void +pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality) +{ + client->error_handler_data = error_handler_data; + client->error_handler = error_handler; + client->auditf = NULL; + client->cache_table = NULL; + client->cache_count = 0; + +#ifndef PKGCONF_LITE + if (client->trace_handler == NULL) + pkgconf_client_set_trace_handler(client, NULL, NULL); +#endif + + if (client->unveil_handler == NULL) + pkgconf_client_set_unveil_handler(client, NULL); + + pkgconf_client_set_error_handler(client, error_handler, error_handler_data); + pkgconf_client_set_warn_handler(client, NULL, NULL); + + pkgconf_client_set_sysroot_dir(client, personality->sysroot_dir); + pkgconf_client_set_buildroot_dir(client, NULL); + pkgconf_client_set_prefix_varname(client, NULL); + + if(getenv("PKG_CONFIG_SYSTEM_LIBRARY_PATH") == NULL) + pkgconf_path_copy_list(&client->filter_libdirs, &personality->filter_libdirs); + else + pkgconf_path_build_from_environ("PKG_CONFIG_SYSTEM_LIBRARY_PATH", NULL, &client->filter_libdirs, false); + + if(getenv("PKG_CONFIG_SYSTEM_INCLUDE_PATH") == NULL) + pkgconf_path_copy_list(&client->filter_includedirs, &personality->filter_includedirs); + else + pkgconf_path_build_from_environ("PKG_CONFIG_SYSTEM_INCLUDE_PATH", NULL, &client->filter_includedirs, false); + + /* GCC uses these environment variables to define system include paths, so we should check them. */ +#ifdef __HAIKU__ + pkgconf_path_build_from_environ("BELIBRARIES", NULL, &client->filter_libdirs, false); +#else + pkgconf_path_build_from_environ("LIBRARY_PATH", NULL, &client->filter_libdirs, false); +#endif + pkgconf_path_build_from_environ("CPATH", NULL, &client->filter_includedirs, false); + pkgconf_path_build_from_environ("C_INCLUDE_PATH", NULL, &client->filter_includedirs, false); + pkgconf_path_build_from_environ("CPLUS_INCLUDE_PATH", NULL, &client->filter_includedirs, false); + pkgconf_path_build_from_environ("OBJC_INCLUDE_PATH", NULL, &client->filter_includedirs, false); + +#ifdef _WIN32 + /* also use the path lists that MSVC uses on windows */ + pkgconf_path_build_from_environ("INCLUDE", NULL, &client->filter_includedirs, false); +#endif + + PKGCONF_TRACE(client, "initialized client @%p", client); + + trace_path_list(client, "filtered library paths", &client->filter_libdirs); + trace_path_list(client, "filtered include paths", &client->filter_includedirs); +} + +/* + * !doc + * + * .. c:function:: pkgconf_client_t* pkgconf_client_new(pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality) + * + * Allocate and initialise a pkgconf client object. + * + * :param pkgconf_error_handler_func_t error_handler: An optional error handler to use for logging errors. + * :param void* error_handler_data: user data passed to optional error handler + * :param pkgconf_cross_personality_t* personality: cross-compile personality to use + * :return: A pkgconf client object. + * :rtype: pkgconf_client_t* + */ +pkgconf_client_t * +pkgconf_client_new(pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality) +{ + pkgconf_client_t *out = calloc(1, sizeof(pkgconf_client_t)); + if (out == NULL) + return NULL; + + pkgconf_client_init(out, error_handler, error_handler_data, personality); + return out; +} + +static void +unref_preload_list(pkgconf_client_t *client) +{ + pkgconf_node_t *n, *tn; + + PKGCONF_FOREACH_LIST_ENTRY_SAFE(client->preloaded_pkgs.head, n, tn) + { + pkgconf_pkg_t *pkg = n->data; + pkgconf_pkg_unref(client, pkg); + } +} + +/* + * !doc + * + * .. c:function:: void pkgconf_client_deinit(pkgconf_client_t *client) + * + * Release resources belonging to a pkgconf client object. + * + * :param pkgconf_client_t* client: The client to deinitialise. + * :return: nothing + */ +void +pkgconf_client_deinit(pkgconf_client_t *client) +{ + PKGCONF_TRACE(client, "deinit @%p", client); + + unref_preload_list(client); + + if (client->prefix_varname != NULL) + free(client->prefix_varname); + + if (client->sysroot_dir != NULL) + free(client->sysroot_dir); + + if (client->buildroot_dir != NULL) + free(client->buildroot_dir); + + pkgconf_path_free(&client->filter_libdirs); + pkgconf_path_free(&client->filter_includedirs); + + pkgconf_tuple_free_global(client); + pkgconf_path_free(&client->dir_list); + pkgconf_cache_free(client); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_client_free(pkgconf_client_t *client) + * + * Release resources belonging to a pkgconf client object and then free the client object itself. + * + * :param pkgconf_client_t* client: The client to deinitialise and free. + * :return: nothing + */ +void +pkgconf_client_free(pkgconf_client_t *client) +{ + pkgconf_client_deinit(client); + free(client); +} + +/* + * !doc + * + * .. c:function:: const char *pkgconf_client_get_sysroot_dir(const pkgconf_client_t *client) + * + * Retrieves the client's sysroot directory (if any). + * + * :param pkgconf_client_t* client: The client object being accessed. + * :return: A string containing the sysroot directory or NULL. + * :rtype: const char * + */ +const char * +pkgconf_client_get_sysroot_dir(const pkgconf_client_t *client) +{ + return client->sysroot_dir; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_client_set_sysroot_dir(pkgconf_client_t *client, const char *sysroot_dir) + * + * Sets or clears the sysroot directory on a client object. Any previous sysroot directory setting is + * automatically released if one was previously set. + * + * Additionally, the global tuple ``$(pc_sysrootdir)`` is set as appropriate based on the new setting. + * + * :param pkgconf_client_t* client: The client object being modified. + * :param char* sysroot_dir: The sysroot directory to set or NULL to unset. + * :return: nothing + */ +void +pkgconf_client_set_sysroot_dir(pkgconf_client_t *client, const char *sysroot_dir) +{ + if (client->sysroot_dir != NULL) + free(client->sysroot_dir); + + client->sysroot_dir = sysroot_dir != NULL ? strdup(sysroot_dir) : NULL; + + PKGCONF_TRACE(client, "set sysroot_dir to: %s", client->sysroot_dir != NULL ? client->sysroot_dir : ""); + + pkgconf_tuple_add_global(client, "pc_sysrootdir", client->sysroot_dir != NULL ? client->sysroot_dir : "/"); +} + +/* + * !doc + * + * .. c:function:: const char *pkgconf_client_get_buildroot_dir(const pkgconf_client_t *client) + * + * Retrieves the client's buildroot directory (if any). + * + * :param pkgconf_client_t* client: The client object being accessed. + * :return: A string containing the buildroot directory or NULL. + * :rtype: const char * + */ +const char * +pkgconf_client_get_buildroot_dir(const pkgconf_client_t *client) +{ + return client->buildroot_dir; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_client_set_buildroot_dir(pkgconf_client_t *client, const char *buildroot_dir) + * + * Sets or clears the buildroot directory on a client object. Any previous buildroot directory setting is + * automatically released if one was previously set. + * + * Additionally, the global tuple ``$(pc_top_builddir)`` is set as appropriate based on the new setting. + * + * :param pkgconf_client_t* client: The client object being modified. + * :param char* buildroot_dir: The buildroot directory to set or NULL to unset. + * :return: nothing + */ +void +pkgconf_client_set_buildroot_dir(pkgconf_client_t *client, const char *buildroot_dir) +{ + if (client->buildroot_dir != NULL) + free(client->buildroot_dir); + + client->buildroot_dir = buildroot_dir != NULL ? strdup(buildroot_dir) : NULL; + + PKGCONF_TRACE(client, "set buildroot_dir to: %s", client->buildroot_dir != NULL ? client->buildroot_dir : ""); + + pkgconf_tuple_add_global(client, "pc_top_builddir", client->buildroot_dir != NULL ? client->buildroot_dir : "$(top_builddir)"); +} + +/* + * !doc + * + * .. c:function:: bool pkgconf_error(const pkgconf_client_t *client, const char *format, ...) + * + * Report an error to a client-registered error handler. + * + * :param pkgconf_client_t* client: The pkgconf client object to report the error to. + * :param char* format: A printf-style format string to use for formatting the error message. + * :return: true if the error handler processed the message, else false. + * :rtype: bool + */ +bool +pkgconf_error(const pkgconf_client_t *client, const char *format, ...) +{ + char *errbuf; + ssize_t msgsize = 0; + bool ret; + va_list va; + + va_start(va, format); + msgsize = vsnprintf(NULL, 0, format, va); + va_end(va); + + if (msgsize < 0) + return false; + + msgsize++; + + errbuf = calloc(1, msgsize); + if (errbuf == NULL) + return false; + + va_start(va, format); + vsnprintf(errbuf, msgsize, format, va); + va_end(va); + + ret = client->error_handler(errbuf, client, client->error_handler_data); + free(errbuf); + + return ret; +} + +/* + * !doc + * + * .. c:function:: bool pkgconf_warn(const pkgconf_client_t *client, const char *format, ...) + * + * Report an error to a client-registered warn handler. + * + * :param pkgconf_client_t* client: The pkgconf client object to report the error to. + * :param char* format: A printf-style format string to use for formatting the warning message. + * :return: true if the warn handler processed the message, else false. + * :rtype: bool + */ +bool +pkgconf_warn(const pkgconf_client_t *client, const char *format, ...) +{ + char *errbuf; + ssize_t msgsize = 0; + bool ret; + va_list va; + + va_start(va, format); + msgsize = vsnprintf(NULL, 0, format, va); + va_end(va); + + if (msgsize < 0) + return false; + + msgsize++; + + errbuf = calloc(1, msgsize); + if (errbuf == NULL) + return false; + + va_start(va, format); + vsnprintf(errbuf, msgsize, format, va); + va_end(va); + + ret = client->warn_handler(errbuf, client, client->warn_handler_data); + free(errbuf); + + return ret; +} + +/* + * !doc + * + * .. c:function:: bool pkgconf_trace(const pkgconf_client_t *client, const char *filename, size_t len, const char *funcname, const char *format, ...) + * + * Report a message to a client-registered trace handler. + * + * :param pkgconf_client_t* client: The pkgconf client object to report the trace message to. + * :param char* filename: The file the function is in. + * :param size_t lineno: The line number currently being executed. + * :param char* funcname: The function name to use. + * :param char* format: A printf-style format string to use for formatting the trace message. + * :return: true if the trace handler processed the message, else false. + * :rtype: bool + */ +bool +pkgconf_trace(const pkgconf_client_t *client, const char *filename, size_t lineno, const char *funcname, const char *format, ...) +{ + char prefix[PKGCONF_ITEM_SIZE]; + char *errbuf = NULL; + ssize_t errlen; + char *finalbuf = NULL; + ssize_t finallen; + bool ret; + va_list va; + + if (client == NULL || client->trace_handler == NULL) + return false; + + snprintf(prefix, sizeof prefix, "%s:" SIZE_FMT_SPECIFIER " [%s]:", filename, lineno, funcname); + + va_start(va, format); + errlen = vsnprintf(NULL, 0, format, va); + va_end(va); + + if (errlen < 0) + return false; + + errlen++; + errbuf = calloc(1, errlen); + if (errbuf == NULL) + return false; + + va_start(va, format); + vsnprintf(errbuf, errlen, format, va); + va_end(va); + + finallen = snprintf(NULL, 0, "%s %s\n", prefix, errbuf); + if (finallen < 0) + return false; + + finallen++; + finalbuf = calloc(1, finallen); + if (finalbuf == NULL) + return false; + + snprintf(finalbuf, finallen, "%s %s\n", prefix, errbuf); + ret = client->trace_handler(finalbuf, client, client->trace_handler_data); + free(errbuf); + free(finalbuf); + + return ret; +} + +/* + * !doc + * + * .. c:function:: bool pkgconf_default_error_handler(const char *msg, const pkgconf_client_t *client, const void *data) + * + * The default pkgconf error handler. + * + * :param char* msg: The error message to handle. + * :param pkgconf_client_t* client: The client object the error originated from. + * :param void* data: An opaque pointer to extra data associated with the client for error handling. + * :return: true (the function does nothing to process the message) + * :rtype: bool + */ +bool +pkgconf_default_error_handler(const char *msg, const pkgconf_client_t *client, void *data) +{ + (void) msg; + (void) client; + (void) data; + + return true; +} + +static void +default_unveil_handler(const pkgconf_client_t *client, const char *path, const char *permissions) +{ + (void) client; + (void) path; + (void) permissions; +} + +/* + * !doc + * + * .. c:function:: unsigned int pkgconf_client_get_flags(const pkgconf_client_t *client) + * + * Retrieves resolver-specific flags associated with a client object. + * + * :param pkgconf_client_t* client: The client object to retrieve the resolver-specific flags from. + * :return: a bitfield of resolver-specific flags + * :rtype: uint + */ +unsigned int +pkgconf_client_get_flags(const pkgconf_client_t *client) +{ + return client->flags; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_client_set_flags(pkgconf_client_t *client, unsigned int flags) + * + * Sets resolver-specific flags associated with a client object. + * + * :param pkgconf_client_t* client: The client object to set the resolver-specific flags on. + * :return: nothing + */ +void +pkgconf_client_set_flags(pkgconf_client_t *client, unsigned int flags) +{ + client->flags = flags; +} + +/* + * !doc + * + * .. c:function:: const char *pkgconf_client_get_prefix_varname(const pkgconf_client_t *client) + * + * Retrieves the name of the variable that should contain a module's prefix. + * In some cases, it is necessary to override this variable to allow proper path relocation. + * + * :param pkgconf_client_t* client: The client object to retrieve the prefix variable name from. + * :return: the prefix variable name as a string + * :rtype: const char * + */ +const char * +pkgconf_client_get_prefix_varname(const pkgconf_client_t *client) +{ + return client->prefix_varname; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_client_set_prefix_varname(pkgconf_client_t *client, const char *prefix_varname) + * + * Sets the name of the variable that should contain a module's prefix. + * If the variable name is ``NULL``, then the default variable name (``prefix``) is used. + * + * :param pkgconf_client_t* client: The client object to set the prefix variable name on. + * :param char* prefix_varname: The prefix variable name to set. + * :return: nothing + */ +void +pkgconf_client_set_prefix_varname(pkgconf_client_t *client, const char *prefix_varname) +{ + if (prefix_varname == NULL) + prefix_varname = "prefix"; + + if (client->prefix_varname != NULL) + free(client->prefix_varname); + + client->prefix_varname = strdup(prefix_varname); + + PKGCONF_TRACE(client, "set prefix_varname to: %s", client->prefix_varname); +} + +/* + * !doc + * + * .. c:function:: pkgconf_client_get_warn_handler(const pkgconf_client_t *client) + * + * Returns the warning handler if one is set, else ``NULL``. + * + * :param pkgconf_client_t* client: The client object to get the warn handler from. + * :return: a function pointer to the warn handler or ``NULL`` + */ +pkgconf_error_handler_func_t +pkgconf_client_get_warn_handler(const pkgconf_client_t *client) +{ + return client->warn_handler; +} + +/* + * !doc + * + * .. c:function:: pkgconf_client_set_warn_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t warn_handler, void *warn_handler_data) + * + * Sets a warn handler on a client object or uninstalls one if set to ``NULL``. + * + * :param pkgconf_client_t* client: The client object to set the warn handler on. + * :param pkgconf_error_handler_func_t warn_handler: The warn handler to set. + * :param void* warn_handler_data: Optional data to associate with the warn handler. + * :return: nothing + */ +void +pkgconf_client_set_warn_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t warn_handler, void *warn_handler_data) +{ + client->warn_handler = warn_handler; + client->warn_handler_data = warn_handler_data; + + if (client->warn_handler == NULL) + { + PKGCONF_TRACE(client, "installing default warn handler"); + client->warn_handler = pkgconf_default_error_handler; + } +} + +/* + * !doc + * + * .. c:function:: pkgconf_client_get_error_handler(const pkgconf_client_t *client) + * + * Returns the error handler if one is set, else ``NULL``. + * + * :param pkgconf_client_t* client: The client object to get the error handler from. + * :return: a function pointer to the error handler or ``NULL`` + */ +pkgconf_error_handler_func_t +pkgconf_client_get_error_handler(const pkgconf_client_t *client) +{ + return client->error_handler; +} + +/* + * !doc + * + * .. c:function:: pkgconf_client_set_error_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data) + * + * Sets a warn handler on a client object or uninstalls one if set to ``NULL``. + * + * :param pkgconf_client_t* client: The client object to set the error handler on. + * :param pkgconf_error_handler_func_t error_handler: The error handler to set. + * :param void* error_handler_data: Optional data to associate with the error handler. + * :return: nothing + */ +void +pkgconf_client_set_error_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data) +{ + client->error_handler = error_handler; + client->error_handler_data = error_handler_data; + + if (client->error_handler == NULL) + { + PKGCONF_TRACE(client, "installing default error handler"); + client->error_handler = pkgconf_default_error_handler; + } +} + +/* + * !doc + * + * .. c:function:: pkgconf_client_get_unveil_handler(const pkgconf_client_t *client) + * + * Returns the unveil handler if one is set, else ``NULL``. + * + * :param pkgconf_client_t* client: The client object to get the unveil handler from. + * :return: a function pointer to the error handler or ``NULL`` + */ +pkgconf_unveil_handler_func_t +pkgconf_client_get_unveil_handler(const pkgconf_client_t *client) +{ + return client->unveil_handler; +} + +/* + * !doc + * + * .. c:function:: pkgconf_client_set_unveil_handler(pkgconf_client_t *client, pkgconf_unveil_handler_func_t unveil_handler) + * + * Sets an unveil handler on a client object or uninstalls one if set to ``NULL``. + * + * :param pkgconf_client_t* client: The client object to set the error handler on. + * :param pkgconf_unveil_handler_func_t unveil_handler: The unveil handler to set. + * :return: nothing + */ +void +pkgconf_client_set_unveil_handler(pkgconf_client_t *client, pkgconf_unveil_handler_func_t unveil_handler) +{ + client->unveil_handler = unveil_handler; + + if (client->unveil_handler == NULL) + { + PKGCONF_TRACE(client, "installing default unveil handler"); + client->unveil_handler = default_unveil_handler; + } +} + +#ifndef PKGCONF_LITE +/* + * !doc + * + * .. c:function:: pkgconf_client_get_trace_handler(const pkgconf_client_t *client) + * + * Returns the error handler if one is set, else ``NULL``. + * + * :param pkgconf_client_t* client: The client object to get the error handler from. + * :return: a function pointer to the error handler or ``NULL`` + */ +pkgconf_error_handler_func_t +pkgconf_client_get_trace_handler(const pkgconf_client_t *client) +{ + return client->trace_handler; +} + +/* + * !doc + * + * .. c:function:: pkgconf_client_set_trace_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t trace_handler, void *trace_handler_data) + * + * Sets a warn handler on a client object or uninstalls one if set to ``NULL``. + * + * :param pkgconf_client_t* client: The client object to set the error handler on. + * :param pkgconf_error_handler_func_t trace_handler: The error handler to set. + * :param void* trace_handler_data: Optional data to associate with the error handler. + * :return: nothing + */ +void +pkgconf_client_set_trace_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t trace_handler, void *trace_handler_data) +{ + client->trace_handler = trace_handler; + client->trace_handler_data = trace_handler_data; + + if (client->trace_handler == NULL) + { + client->trace_handler = pkgconf_default_error_handler; + PKGCONF_TRACE(client, "installing default trace handler"); + } +} +#endif + +/* + * !doc + * + * .. c:function:: bool pkgconf_client_preload_path(pkgconf_client_t *client, const char *path) + * + * Loads a pkg-config file into the preloaded packages set. + * + * :param pkgconf_client_t* client: The client object for preloading. + * :param char* path: The path to the pkg-config file to preload. + * :return: true on success, false on error + * :rtype: bool + */ +bool +pkgconf_client_preload_path(pkgconf_client_t *client, const char *path) +{ + pkgconf_pkg_t *pkg = pkgconf_pkg_new_from_path(client, path, PKGCONF_PKG_PROPF_PRELOADED); + if (pkg == NULL) + return false; + + pkgconf_pkg_ref(client, pkg); + pkgconf_node_insert_tail(&pkg->preload_node, pkg, &client->preloaded_pkgs); + + return true; +} + +/* + * !doc + * + * .. c:function:: bool pkgconf_client_preload_from_environ(pkgconf_client_t *client, const char *env) + * + * Loads zero or more pkg-config files specified in the given environmental + * variable. + * + * :param pkgconf_client_t* client: The client object for preloading. + * :param char* environ: The environment variable to use for preloading. + * :return: true on success, false on error + * :rtype: bool + */ +bool +pkgconf_client_preload_from_environ(pkgconf_client_t *client, const char *env) +{ + const char *data; + pkgconf_list_t pathlist = PKGCONF_LIST_INITIALIZER; + pkgconf_node_t *n; + bool ret; + + data = getenv(env); + if (data == NULL) + return true; + + pkgconf_path_split(data, &pathlist, true); + + PKGCONF_FOREACH_LIST_ENTRY(pathlist.head, n) + { + pkgconf_path_t *pn = n->data; + + ret = pkgconf_client_preload_path(client, pn->path); + if (!ret) + break; + } + + pkgconf_path_free(&pathlist); + + return ret; +} diff --git a/contrib/pkgconf/libpkgconf/config.h.meson b/contrib/pkgconf/libpkgconf/config.h.meson new file mode 100644 index 00000000000..2ea7db2e20d --- /dev/null +++ b/contrib/pkgconf/libpkgconf/config.h.meson @@ -0,0 +1,79 @@ +/* libpkgconf/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the `strlcat' function. */ +#mesondefine HAVE_STRLCAT + +/* Define to 1 if you have the `strlcpy' function. */ +#mesondefine HAVE_STRLCPY + +/* Define to 1 if you have the `strndup' function. */ +#mesondefine HAVE_STRNDUP + +/* Define to 1 if you have the `reallocarray' function. */ +#mesondefine HAVE_REALLOCARRAY + +/* Define to 1 if you have the `strlcat' function. */ +#mesondefine HAVE_DECL_STRLCAT + +/* Define to 1 if you have the `strlcpy' function. */ +#mesondefine HAVE_DECL_STRLCPY + +/* Define to 1 if you have the `strndup' function. */ +#mesondefine HAVE_DECL_STRNDUP + +/* Define to 1 if you have the `reallocarray' function. */ +#mesondefine HAVE_DECL_REALLOCARRAY + +/* Define to 1 if you have the `pledge' function. */ +#mesondefine HAVE_DECL_PLEDGE + +/* Define to 1 if you have the `unveil' function. */ +#mesondefine HAVE_DECL_UNVEIL + +/* Name of package */ +#mesondefine PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#mesondefine PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#mesondefine PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#mesondefine PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#mesondefine PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#mesondefine PACKAGE_URL + +/* Define to the version of this package. */ +#mesondefine PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#mesondefine STDC_HEADERS + +/* Version number of package */ +#mesondefine VERSION + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#mesondefine _FILE_OFFSET_BITS + +/* Define for large files, on AIX-style hosts. */ +#mesondefine _LARGE_FILES + +#mesondefine PKG_DEFAULT_PATH +#mesondefine SYSTEM_INCLUDEDIR +#mesondefine SYSTEM_LIBDIR +#mesondefine PERSONALITY_PATH + +/* Enable Solaris extensions. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif diff --git a/contrib/pkgconf/libpkgconf/dependency.c b/contrib/pkgconf/libpkgconf/dependency.c new file mode 100644 index 00000000000..9b64809a411 --- /dev/null +++ b/contrib/pkgconf/libpkgconf/dependency.c @@ -0,0 +1,506 @@ +/* + * dependency.c + * dependency parsing and management + * + * Copyright (c) 2011, 2012, 2013 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include +#include + +/* + * !doc + * + * libpkgconf `dependency` module + * ============================== + * + * The `dependency` module provides support for building `dependency lists` (the basic component of the overall `dependency graph`) and + * `dependency nodes` which store dependency information. + */ + +typedef enum { + OUTSIDE_MODULE = 0, + INSIDE_MODULE_NAME = 1, + BEFORE_OPERATOR = 2, + INSIDE_OPERATOR = 3, + AFTER_OPERATOR = 4, + INSIDE_VERSION = 5 +} parse_state_t; + +#define DEBUG_PARSE 0 + +static const char * +dependency_to_str(const pkgconf_dependency_t *dep, char *buf, size_t buflen) +{ + pkgconf_strlcpy(buf, dep->package, buflen); + if (dep->version != NULL) + { + pkgconf_strlcat(buf, " ", buflen); + pkgconf_strlcat(buf, pkgconf_pkg_get_comparator(dep), buflen); + pkgconf_strlcat(buf, " ", buflen); + pkgconf_strlcat(buf, dep->version, buflen); + } + + return buf; +} + +/* find a colliding dependency that is coloured differently */ +static inline pkgconf_dependency_t * +find_colliding_dependency(const pkgconf_dependency_t *dep, const pkgconf_list_t *list) +{ + const pkgconf_node_t *n; + + PKGCONF_FOREACH_LIST_ENTRY(list->head, n) + { + pkgconf_dependency_t *dep2 = n->data; + + if (strcmp(dep->package, dep2->package)) + continue; + + if (dep->flags != dep2->flags) + return dep2; + } + + return NULL; +} + +static inline pkgconf_dependency_t * +add_or_replace_dependency_node(pkgconf_client_t *client, pkgconf_dependency_t *dep, pkgconf_list_t *list) +{ + char depbuf[PKGCONF_ITEM_SIZE]; + pkgconf_dependency_t *dep2 = find_colliding_dependency(dep, list); + + /* there is already a node in the graph which describes this dependency */ + if (dep2 != NULL) + { + char depbuf2[PKGCONF_ITEM_SIZE]; + + PKGCONF_TRACE(client, "dependency collision: [%s/%x] -- [%s/%x]", + dependency_to_str(dep, depbuf, sizeof depbuf), dep->flags, + dependency_to_str(dep2, depbuf2, sizeof depbuf2), dep2->flags); + + /* prefer the uncoloured node, either dep or dep2 */ + if (dep->flags && dep2->flags == 0) + { + PKGCONF_TRACE(client, "dropping dependency [%s]@%p because of collision", depbuf, dep); + + pkgconf_dependency_unref(dep->owner, dep); + return NULL; + } + else if (dep2->flags && dep->flags == 0) + { + PKGCONF_TRACE(client, "dropping dependency [%s]@%p because of collision", depbuf2, dep2); + + pkgconf_node_delete(&dep2->iter, list); + pkgconf_dependency_unref(dep2->owner, dep2); + } + else + /* If both dependencies have equal strength, we keep both, because of situations like: + * Requires: foo > 1, foo < 3 + * + * If the situation is that both dependencies are literally equal, it is still harmless because + * fragment deduplication will handle the excessive fragments. + */ + PKGCONF_TRACE(client, "keeping both dependencies (harmless)"); + } + + PKGCONF_TRACE(client, "added dependency [%s] to list @%p; flags=%x", dependency_to_str(dep, depbuf, sizeof depbuf), list, dep->flags); + pkgconf_node_insert_tail(&dep->iter, pkgconf_dependency_ref(dep->owner, dep), list); + + /* This dependency is intentionally unowned. + * + * Internally we have no use for the returned type, and usually just + * discard it. However, there is a publig pkgconf_dependency_add + * function, which references this return value before returning it, + * giving ownership at that point. + */ + return dep; +} + +static inline pkgconf_dependency_t * +pkgconf_dependency_addraw(pkgconf_client_t *client, pkgconf_list_t *list, const char *package, size_t package_sz, const char *version, size_t version_sz, pkgconf_pkg_comparator_t compare, unsigned int flags) +{ + pkgconf_dependency_t *dep; + + dep = calloc(1, sizeof(pkgconf_dependency_t)); + if (dep == NULL) + return NULL; + + dep->package = pkgconf_strndup(package, package_sz); + + if (version_sz != 0) + dep->version = pkgconf_strndup(version, version_sz); + + dep->compare = compare; + dep->flags = flags; + dep->owner = client; + dep->refcount = 0; + + return add_or_replace_dependency_node(client, dep, list); +} + +/* + * !doc + * + * .. c:function:: pkgconf_dependency_t *pkgconf_dependency_add(pkgconf_list_t *list, const char *package, const char *version, pkgconf_pkg_comparator_t compare) + * + * Adds a parsed dependency to a dependency list as a dependency node. + * + * :param pkgconf_client_t* client: The client object that owns the package this dependency list belongs to. + * :param pkgconf_list_t* list: The dependency list to add a dependency node to. + * :param char* package: The package `atom` to set on the dependency node. + * :param char* version: The package `version` to set on the dependency node. + * :param pkgconf_pkg_comparator_t compare: The comparison operator to set on the dependency node. + * :param uint flags: Any flags to attach to the dependency node. + * :return: A dependency node. + * :rtype: pkgconf_dependency_t * + */ +pkgconf_dependency_t * +pkgconf_dependency_add(pkgconf_client_t *client, pkgconf_list_t *list, const char *package, const char *version, pkgconf_pkg_comparator_t compare, unsigned int flags) +{ + pkgconf_dependency_t *dep; + dep = pkgconf_dependency_addraw(client, list, package, strlen(package), version, + version != NULL ? strlen(version) : 0, compare, flags); + return pkgconf_dependency_ref(dep->owner, dep); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_dependency_append(pkgconf_list_t *list, pkgconf_dependency_t *tail) + * + * Adds a dependency node to a pre-existing dependency list. + * + * :param pkgconf_list_t* list: The dependency list to add a dependency node to. + * :param pkgconf_dependency_t* tail: The dependency node to add to the tail of the dependency list. + * :return: nothing + */ +void +pkgconf_dependency_append(pkgconf_list_t *list, pkgconf_dependency_t *tail) +{ + pkgconf_node_insert_tail(&tail->iter, tail, list); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_dependency_free_one(pkgconf_dependency_t *dep) + * + * Frees a dependency node. + * + * :param pkgconf_dependency_t* dep: The dependency node to free. + * :return: nothing + */ +void +pkgconf_dependency_free_one(pkgconf_dependency_t *dep) +{ + if (dep->match != NULL) + pkgconf_pkg_unref(dep->match->owner, dep->match); + + if (dep->package != NULL) + free(dep->package); + + if (dep->version != NULL) + free(dep->version); + + free(dep); +} + +/* + * !doc + * + * .. c:function:: pkgconf_dependency_t *pkgconf_dependency_ref(pkgconf_client_t *owner, pkgconf_dependency_t *dep) + * + * Increases a dependency node's refcount. + * + * :param pkgconf_client_t* owner: The client object which owns the memory of this dependency node. + * :param pkgconf_dependency_t* dep: The dependency to increase the refcount of. + * :return: the dependency node on success, else NULL + */ +pkgconf_dependency_t * +pkgconf_dependency_ref(pkgconf_client_t *client, pkgconf_dependency_t *dep) +{ + if (client != dep->owner) + return NULL; + + dep->refcount++; + PKGCONF_TRACE(client, "%s refcount@%p: %d", dep->package, dep, dep->refcount); + return dep; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_dependency_unref(pkgconf_client_t *owner, pkgconf_dependency_t *dep) + * + * Decreases a dependency node's refcount and frees it if necessary. + * + * :param pkgconf_client_t* owner: The client object which owns the memory of this dependency node. + * :param pkgconf_dependency_t* dep: The dependency to decrease the refcount of. + * :return: nothing + */ +void +pkgconf_dependency_unref(pkgconf_client_t *client, pkgconf_dependency_t *dep) +{ + if (client != dep->owner) + return; + + --dep->refcount; + PKGCONF_TRACE(client, "%s refcount@%p: %d", dep->package, dep, dep->refcount); + + if (dep->refcount <= 0) + pkgconf_dependency_free_one(dep); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_dependency_free(pkgconf_list_t *list) + * + * Release a dependency list and its child dependency nodes. + * + * :param pkgconf_list_t* list: The dependency list to release. + * :return: nothing + */ +void +pkgconf_dependency_free(pkgconf_list_t *list) +{ + pkgconf_node_t *node, *next; + + PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node) + { + pkgconf_dependency_t *dep = node->data; + + pkgconf_node_delete(&dep->iter, list); + pkgconf_dependency_unref(dep->owner, dep); + } + + pkgconf_list_zero(list); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_dependency_parse_str(pkgconf_list_t *deplist_head, const char *depends) + * + * Parse a dependency declaration into a dependency list. + * Commas are counted as whitespace to allow for constructs such as ``@SUBSTVAR@, zlib`` being processed + * into ``, zlib``. + * + * :param pkgconf_client_t* client: The client object that owns the package this dependency list belongs to. + * :param pkgconf_list_t* deplist_head: The dependency list to populate with dependency nodes. + * :param char* depends: The dependency data to parse. + * :param uint flags: Any flags to attach to the dependency nodes. + * :return: nothing + */ +void +pkgconf_dependency_parse_str(pkgconf_client_t *client, pkgconf_list_t *deplist_head, const char *depends, unsigned int flags) +{ + parse_state_t state = OUTSIDE_MODULE; + pkgconf_pkg_comparator_t compare = PKGCONF_CMP_ANY; + char cmpname[PKGCONF_ITEM_SIZE]; + size_t package_sz = 0, version_sz = 0, buf_sz = 0; + char *buf; + char *start = NULL; + char *ptr = NULL; + char *vstart = NULL; + char *package = NULL, *version = NULL; + char *cnameptr = cmpname; + char *cnameend = cmpname + PKGCONF_ITEM_SIZE - 1; + + if (!*depends) + return; + + memset(cmpname, '\0', sizeof cmpname); + + buf_sz = strlen(depends) * 2; + buf = calloc(1, buf_sz); + if (buf == NULL) + return; + + pkgconf_strlcpy(buf, depends, buf_sz); + pkgconf_strlcat(buf, " ", buf_sz); + + start = ptr = buf; + + while (*ptr) + { + switch (state) + { + case OUTSIDE_MODULE: + if (!PKGCONF_IS_MODULE_SEPARATOR(*ptr)) + state = INSIDE_MODULE_NAME; + + break; + + case INSIDE_MODULE_NAME: + if (isspace((unsigned char)*ptr)) + { + const char *sptr = ptr; + + while (*sptr && isspace((unsigned char)*sptr)) + sptr++; + + if (*sptr == '\0') + state = OUTSIDE_MODULE; + else if (PKGCONF_IS_MODULE_SEPARATOR(*sptr)) + state = OUTSIDE_MODULE; + else if (PKGCONF_IS_OPERATOR_CHAR(*sptr)) + state = BEFORE_OPERATOR; + else + state = OUTSIDE_MODULE; + } + else if (PKGCONF_IS_MODULE_SEPARATOR(*ptr)) + state = OUTSIDE_MODULE; + else if (*(ptr + 1) == '\0') + { + ptr++; + state = OUTSIDE_MODULE; + } + + if (state != INSIDE_MODULE_NAME && start != ptr) + { + char *iter = start; + + while (PKGCONF_IS_MODULE_SEPARATOR(*iter)) + iter++; + + package = iter; + package_sz = ptr - iter; + start = ptr; + } + + if (state == OUTSIDE_MODULE) + { + pkgconf_dependency_addraw(client, deplist_head, package, package_sz, NULL, 0, compare, flags); + + compare = PKGCONF_CMP_ANY; + package_sz = 0; + } + + break; + + case BEFORE_OPERATOR: + if (PKGCONF_IS_OPERATOR_CHAR(*ptr)) + { + state = INSIDE_OPERATOR; + if (cnameptr < cnameend) + *cnameptr++ = *ptr; + } + + break; + + case INSIDE_OPERATOR: + if (PKGCONF_IS_OPERATOR_CHAR(*ptr)) + { + if (cnameptr < cnameend) + *cnameptr++ = *ptr; + break; + } + + state = AFTER_OPERATOR; + compare = pkgconf_pkg_comparator_lookup_by_name(cmpname); + // fallthrough + + case AFTER_OPERATOR: + if (!isspace((unsigned char)*ptr)) + { + vstart = ptr; + state = INSIDE_VERSION; + } + break; + + case INSIDE_VERSION: + if (PKGCONF_IS_MODULE_SEPARATOR(*ptr) || *(ptr + 1) == '\0') + { + version = vstart; + version_sz = ptr - vstart; + state = OUTSIDE_MODULE; + + pkgconf_dependency_addraw(client, deplist_head, package, package_sz, version, version_sz, compare, flags); + + compare = PKGCONF_CMP_ANY; + cnameptr = cmpname; + memset(cmpname, 0, sizeof cmpname); + package_sz = 0; + } + + if (state == OUTSIDE_MODULE) + start = ptr; + break; + } + + ptr++; + } + + free(buf); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_dependency_parse(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, pkgconf_list_t *deplist, const char *depends) + * + * Preprocess dependency data and then process that dependency declaration into a dependency list. + * Commas are counted as whitespace to allow for constructs such as ``@SUBSTVAR@, zlib`` being processed + * into ``, zlib``. + * + * :param pkgconf_client_t* client: The client object that owns the package this dependency list belongs to. + * :param pkgconf_pkg_t* pkg: The package object that owns this dependency list. + * :param pkgconf_list_t* deplist: The dependency list to populate with dependency nodes. + * :param char* depends: The dependency data to parse. + * :param uint flags: Any flags to attach to the dependency nodes. + * :return: nothing + */ +void +pkgconf_dependency_parse(pkgconf_client_t *client, pkgconf_pkg_t *pkg, pkgconf_list_t *deplist, const char *depends, unsigned int flags) +{ + char *kvdepends = pkgconf_tuple_parse(client, &pkg->vars, depends, pkg->flags); + + pkgconf_dependency_parse_str(client, deplist, kvdepends, flags); + free(kvdepends); +} + +/* + * !doc + * + * .. c:function:: pkgconf_dependency_t *pkgconf_dependency_copy(pkgconf_client_t *client, const pkgconf_dependency_t *dep) + * + * Copies a dependency node to a new one. + * + * :param pkgconf_client_t* client: The client object that will own this dependency. + * :param pkgconf_dependency_t* dep: The dependency node to copy. + * :return: a pointer to a new dependency node, else NULL + */ +pkgconf_dependency_t * +pkgconf_dependency_copy(pkgconf_client_t *client, const pkgconf_dependency_t *dep) +{ + pkgconf_dependency_t *new_dep; + + new_dep = calloc(1, sizeof(pkgconf_dependency_t)); + if (new_dep == NULL) + return NULL; + + new_dep->package = strdup(dep->package); + + if (dep->version != NULL) + new_dep->version = strdup(dep->version); + + new_dep->compare = dep->compare; + new_dep->flags = dep->flags; + new_dep->owner = client; + new_dep->refcount = 0; + + if (dep->match != NULL) + new_dep->match = pkgconf_pkg_ref(client, dep->match); + + return pkgconf_dependency_ref(client, new_dep); +} diff --git a/contrib/pkgconf/libpkgconf/fileio.c b/contrib/pkgconf/libpkgconf/fileio.c new file mode 100644 index 00000000000..d4f001b9c3e --- /dev/null +++ b/contrib/pkgconf/libpkgconf/fileio.c @@ -0,0 +1,113 @@ +/* + * fileio.c + * File reading utilities + * + * Copyright (c) 2012, 2025 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include +#include + +bool +pkgconf_fgetline(pkgconf_buffer_t *buffer, FILE *stream) +{ + bool quoted = false; + int c = '\0', c2; + + while ((c = getc(stream)) != EOF) + { + if (c == '\\' && !quoted) + { + quoted = true; + continue; + } + else if (c == '#') + { + if (!quoted) { + /* Skip the rest of the line */ + do { + c = getc(stream); + } while (c != '\n' && c != EOF); + pkgconf_buffer_push_byte(buffer, c); + break; + } + else + pkgconf_buffer_push_byte(buffer, c); + + quoted = false; + continue; + } + else if (c == '\n') + { + if (quoted) + { + /* Trim spaces */ + do { + c2 = getc(stream); + } while (c2 == '\t' || c2 == ' '); + + ungetc(c2, stream); + + quoted = false; + continue; + } + else + { + pkgconf_buffer_push_byte(buffer, c); + } + + break; + } + else if (c == '\r') + { + pkgconf_buffer_push_byte(buffer, '\n'); + + if ((c2 = getc(stream)) == '\n') + { + if (quoted) + { + quoted = false; + continue; + } + + break; + } + + ungetc(c2, stream); + + if (quoted) + { + quoted = false; + continue; + } + + break; + } + else + { + if (quoted) { + pkgconf_buffer_push_byte(buffer, '\\'); + quoted = false; + } + pkgconf_buffer_push_byte(buffer, c); + } + + } + + /* Remove newline character. */ + if (pkgconf_buffer_lastc(buffer) == '\n') + pkgconf_buffer_trim_byte(buffer); + + if (pkgconf_buffer_lastc(buffer) == '\r') + pkgconf_buffer_trim_byte(buffer); + + return !(c == EOF || ferror(stream)); +} diff --git a/contrib/pkgconf/libpkgconf/fragment.c b/contrib/pkgconf/libpkgconf/fragment.c new file mode 100644 index 00000000000..2b6109039b4 --- /dev/null +++ b/contrib/pkgconf/libpkgconf/fragment.c @@ -0,0 +1,803 @@ +/* + * fragment.c + * Management of fragment lists. + * + * Copyright (c) 2012, 2013, 2014 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include +#include + +/* + * !doc + * + * libpkgconf `fragment` module + * ============================ + * + * The `fragment` module provides low-level management and rendering of fragment lists. A + * `fragment list` contains various `fragments` of text (such as ``-I /usr/include``) in a matter + * which is composable, mergeable and reorderable. + */ + +struct pkgconf_fragment_check { + char *token; + size_t len; +}; + +static inline bool +pkgconf_fragment_is_unmergeable(const char *string) +{ + static const struct pkgconf_fragment_check check_fragments[] = { + {"-framework", 10}, + {"-isystem", 8}, + {"-idirafter", 10}, + {"-pthread", 8}, + {"-Wa,", 4}, + {"-Wl,", 4}, + {"-Wp,", 4}, + {"-trigraphs", 10}, + {"-pedantic", 9}, + {"-ansi", 5}, + {"-std=", 5}, + {"-stdlib=", 8}, + {"-include", 8}, + {"-nostdinc", 9}, + {"-nostdlibinc", 12}, + {"-nobuiltininc", 13}, + {"-nodefaultlibs", 14}, + }; + + if (*string != '-') + return true; + + for (size_t i = 0; i < PKGCONF_ARRAY_SIZE(check_fragments); i++) + if (!strncmp(string, check_fragments[i].token, check_fragments[i].len)) + return true; + + /* only one pair of {-flag, arg} may be merged together */ + if (strchr(string, ' ') != NULL) + return false; + + return false; +} + +static inline bool +pkgconf_fragment_should_munge(const char *string, const char *sysroot_dir) +{ + if (*string != '/') + return false; + + if (sysroot_dir != NULL && strncmp(sysroot_dir, string, strlen(sysroot_dir))) + return true; + + return false; +} + +static inline bool +pkgconf_fragment_is_groupable(const char *string) +{ + static const struct pkgconf_fragment_check check_fragments[] = { + {"-Wl,--start-group", 17}, + {"-framework", 10}, + {"-isystem", 8}, + {"-idirafter", 10}, + {"-include", 8}, + }; + + for (size_t i = 0; i < PKGCONF_ARRAY_SIZE(check_fragments); i++) + if (!strncmp(string, check_fragments[i].token, check_fragments[i].len)) + return true; + + return false; +} + +static inline bool +pkgconf_fragment_is_terminus(const char *string) +{ + static const struct pkgconf_fragment_check check_fragments[] = { + {"-Wl,--end-group", 15}, + }; + + for (size_t i = 0; i < PKGCONF_ARRAY_SIZE(check_fragments); i++) + if (!strncmp(string, check_fragments[i].token, check_fragments[i].len)) + return true; + + return false; +} + +static inline bool +pkgconf_fragment_is_special(const char *string) +{ + if (*string != '-') + return true; + + if (!strncmp(string, "-lib:", 5)) + return true; + + return pkgconf_fragment_is_unmergeable(string); +} + +static inline void +pkgconf_fragment_munge(const pkgconf_client_t *client, char *buf, size_t buflen, const char *source, const char *sysroot_dir, unsigned int flags) +{ + *buf = '\0'; + + if (!(flags & PKGCONF_PKG_PROPF_UNINSTALLED) || (client->flags & PKGCONF_PKG_PKGF_PKGCONF1_SYSROOT_RULES)) + { + if (sysroot_dir == NULL) + sysroot_dir = pkgconf_tuple_find_global(client, "pc_sysrootdir"); + + if (sysroot_dir != NULL && pkgconf_fragment_should_munge(source, sysroot_dir)) + pkgconf_strlcat(buf, sysroot_dir, buflen); + } + + pkgconf_strlcat(buf, source, buflen); + + if (*buf == '/' && !(client->flags & PKGCONF_PKG_PKGF_DONT_RELOCATE_PATHS)) + pkgconf_path_relocate(buf, buflen); +} + +static inline char * +pkgconf_fragment_copy_munged(const pkgconf_client_t *client, const char *source, unsigned int flags) +{ + char mungebuf[PKGCONF_ITEM_SIZE]; + pkgconf_fragment_munge(client, mungebuf, sizeof mungebuf, source, client->sysroot_dir, flags); + return strdup(mungebuf); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_fragment_insert(const pkgconf_client_t *client, pkgconf_list_t *list, char type, const char *data, bool tail) + * + * Adds a `fragment` of text to a `fragment list` directly without interpreting it. + * + * :param pkgconf_client_t* client: The pkgconf client being accessed. + * :param pkgconf_list_t* list: The fragment list. + * :param char type: The type of the fragment. + * :param char* data: The data of the fragment. + * :param bool tail: Whether to place the fragment at the beginning of the list or the end. + * :return: nothing + */ +void +pkgconf_fragment_insert(const pkgconf_client_t *client, pkgconf_list_t *list, char type, const char *data, bool tail) +{ + pkgconf_fragment_t *frag; + + frag = calloc(1, sizeof(pkgconf_fragment_t)); + frag->type = type; + frag->data = pkgconf_fragment_copy_munged(client, data, 0); + + if (tail) + { + pkgconf_node_insert_tail(&frag->iter, frag, list); + return; + } + + pkgconf_node_insert(&frag->iter, frag, list); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *string, unsigned int flags) + * + * Adds a `fragment` of text to a `fragment list`, possibly modifying the fragment if a sysroot is set. + * + * :param pkgconf_client_t* client: The pkgconf client being accessed. + * :param pkgconf_list_t* list: The fragment list. + * :param char* string: The string of text to add as a fragment to the fragment list. + * :param uint flags: Parsing-related flags for the package. + * :return: nothing + */ +void +pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *string, unsigned int flags) +{ + pkgconf_list_t *target = list; + pkgconf_fragment_t *frag; + + if (*string == '\0') + return; + + if (list->tail != NULL && list->tail->data != NULL && + !(client->flags & PKGCONF_PKG_PKGF_DONT_MERGE_SPECIAL_FRAGMENTS)) + { + pkgconf_fragment_t *parent = list->tail->data; + + /* only attempt to merge 'special' fragments together */ + if (!parent->type && parent->data != NULL && + pkgconf_fragment_is_unmergeable(parent->data) && + !(parent->flags & PKGCONF_PKG_FRAGF_TERMINATED)) + { + if (pkgconf_fragment_is_groupable(parent->data)) + target = &parent->children; + + if (pkgconf_fragment_is_terminus(string)) + parent->flags |= PKGCONF_PKG_FRAGF_TERMINATED; + + PKGCONF_TRACE(client, "adding fragment as child to list @%p", target); + } + } + + frag = calloc(1, sizeof(pkgconf_fragment_t)); + if (frag == NULL) + { + PKGCONF_TRACE(client, "failed to add new fragment due to allocation failure to list @%p", target); + return; + } + + if (strlen(string) > 1 && !pkgconf_fragment_is_special(string)) + { + frag->type = *(string + 1); + frag->data = pkgconf_fragment_copy_munged(client, string + 2, flags); + + PKGCONF_TRACE(client, "added fragment {%c, '%s'} to list @%p", frag->type, frag->data, list); + } + else + { + frag->type = 0; + frag->data = pkgconf_fragment_copy_munged(client, string, flags); + + PKGCONF_TRACE(client, "created special fragment {'%s'} in list @%p", frag->data, target); + } + + pkgconf_node_insert_tail(&frag->iter, frag, target); +} + +static inline pkgconf_fragment_t * +pkgconf_fragment_lookup(pkgconf_list_t *list, const pkgconf_fragment_t *base) +{ + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY_REVERSE(list->tail, node) + { + pkgconf_fragment_t *frag = node->data; + + if (base->type != frag->type) + continue; + + if (!strcmp(base->data, frag->data)) + return frag; + } + + return NULL; +} + +static inline bool +pkgconf_fragment_can_merge_back(const pkgconf_fragment_t *base, unsigned int flags, bool is_private) +{ + (void) flags; + + if (base->type == 'l') + { + if (is_private) + return false; + + return true; + } + + if (base->type == 'F') + return false; + if (base->type == 'L') + return false; + if (base->type == 'I') + return false; + + return true; +} + +static inline bool +pkgconf_fragment_can_merge(const pkgconf_fragment_t *base, unsigned int flags, bool is_private) +{ + (void) flags; + + if (is_private) + return false; + + if (base->children.head != NULL) + return false; + + return pkgconf_fragment_is_unmergeable(base->data); +} + +static inline pkgconf_fragment_t * +pkgconf_fragment_exists(pkgconf_list_t *list, const pkgconf_fragment_t *base, unsigned int flags, bool is_private) +{ + if (!pkgconf_fragment_can_merge_back(base, flags, is_private)) + return NULL; + + if (!pkgconf_fragment_can_merge(base, flags, is_private)) + return NULL; + + return pkgconf_fragment_lookup(list, base); +} + +static inline bool +pkgconf_fragment_should_merge(const pkgconf_fragment_t *base) +{ + const pkgconf_fragment_t *parent; + + /* if we are the first fragment, that means the next fragment is the same, so it's always safe. */ + if (base->iter.prev == NULL) + return true; + + /* this really shouldn't ever happen, but handle it */ + parent = base->iter.prev->data; + if (parent == NULL) + return true; + + switch (parent->type) + { + case 'l': + case 'L': + case 'I': + return true; + default: + return !base->type || parent->type == base->type; + } +} + +/* + * !doc + * + * .. c:function:: bool pkgconf_fragment_has_system_dir(const pkgconf_client_t *client, const pkgconf_fragment_t *frag) + * + * Checks if a `fragment` contains a `system path`. System paths are detected at compile time and optionally overridden by + * the ``PKG_CONFIG_SYSTEM_INCLUDE_PATH`` and ``PKG_CONFIG_SYSTEM_LIBRARY_PATH`` environment variables. + * + * :param pkgconf_client_t* client: The pkgconf client object the fragment belongs to. + * :param pkgconf_fragment_t* frag: The fragment being checked. + * :return: true if the fragment contains a system path, else false + * :rtype: bool + */ +bool +pkgconf_fragment_has_system_dir(const pkgconf_client_t *client, const pkgconf_fragment_t *frag) +{ + const pkgconf_list_t *check_paths = NULL; + + switch (frag->type) + { + case 'L': + check_paths = &client->filter_libdirs; + break; + case 'I': + check_paths = &client->filter_includedirs; + break; + default: + return false; + } + + return pkgconf_path_match_list(frag->data, check_paths); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_fragment_copy(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_fragment_t *base, bool is_private) + * + * Copies a `fragment` to another `fragment list`, possibly removing a previous copy of the `fragment` + * in a process known as `mergeback`. + * + * :param pkgconf_client_t* client: The pkgconf client being accessed. + * :param pkgconf_list_t* list: The list the fragment is being added to. + * :param pkgconf_fragment_t* base: The fragment being copied. + * :param bool is_private: Whether the fragment list is a `private` fragment list (static linking). + * :return: nothing + */ +void +pkgconf_fragment_copy(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_fragment_t *base, bool is_private) +{ + pkgconf_fragment_t *frag; + + if ((frag = pkgconf_fragment_exists(list, base, client->flags, is_private)) != NULL) + { + if (pkgconf_fragment_should_merge(frag)) + pkgconf_fragment_delete(list, frag); + } + else if (!is_private && !pkgconf_fragment_can_merge_back(base, client->flags, is_private) && (pkgconf_fragment_lookup(list, base) != NULL)) + return; + + frag = calloc(1, sizeof(pkgconf_fragment_t)); + + frag->type = base->type; + pkgconf_fragment_copy_list(client, &frag->children, &base->children); + if (base->data != NULL) + frag->data = strdup(base->data); + + pkgconf_node_insert_tail(&frag->iter, frag, list); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_fragment_copy_list(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_list_t *base) + * + * Copies a `fragment list` to another `fragment list`, possibly removing a previous copy of the fragments + * in a process known as `mergeback`. + * + * :param pkgconf_client_t* client: The pkgconf client being accessed. + * :param pkgconf_list_t* list: The list the fragments are being added to. + * :param pkgconf_list_t* base: The list the fragments are being copied from. + * :return: nothing + */ +void +pkgconf_fragment_copy_list(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_list_t *base) +{ + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(base->head, node) + { + pkgconf_fragment_t *frag = node->data; + + pkgconf_fragment_copy(client, list, frag, true); + } +} + +/* + * !doc + * + * .. c:function:: void pkgconf_fragment_filter(const pkgconf_client_t *client, pkgconf_list_t *dest, pkgconf_list_t *src, pkgconf_fragment_filter_func_t filter_func) + * + * Copies a `fragment list` to another `fragment list` which match a user-specified filtering function. + * + * :param pkgconf_client_t* client: The pkgconf client being accessed. + * :param pkgconf_list_t* dest: The destination list. + * :param pkgconf_list_t* src: The source list. + * :param pkgconf_fragment_filter_func_t filter_func: The filter function to use. + * :param void* data: Optional data to pass to the filter function. + * :return: nothing + */ +void +pkgconf_fragment_filter(const pkgconf_client_t *client, pkgconf_list_t *dest, pkgconf_list_t *src, pkgconf_fragment_filter_func_t filter_func, void *data) +{ + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(src->head, node) + { + pkgconf_fragment_t *frag = node->data; + + if (filter_func(client, frag, data)) + pkgconf_fragment_copy(client, dest, frag, true); + } +} + +static inline char * +fragment_quote(const pkgconf_fragment_t *frag) +{ + const char *src = frag->data; + ssize_t outlen = strlen(src) + 10; + char *out, *dst; + + if (frag->data == NULL) + return NULL; + + out = dst = calloc(1, outlen); + if (out == NULL) + return NULL; + + for (; *src; src++) + { + if (((*src < ' ') || + (*src >= (' ' + (frag->children.head != NULL ? 1 : 0)) && *src < '$') || + (*src > '$' && *src < '(') || + (*src > ')' && *src < '+') || + (*src > ':' && *src < '=') || + (*src > '=' && *src < '@') || + (*src > 'Z' && *src < '\\') || +#ifndef _WIN32 + (*src == '\\') || +#endif + (*src > '\\' && *src < '^') || + (*src == '`') || + (*src > 'z' && *src < '~') || + (*src > '~'))) + *dst++ = '\\'; + + *dst++ = *src; + + if ((ptrdiff_t)(dst - out) + 2 > outlen) + { + ptrdiff_t offset = dst - out; + outlen *= 2; + + char *newout = realloc(out, outlen); + if (newout == NULL) + { + free(out); + return NULL; + } + + out = newout; + dst = out + offset; + } + } + + *dst = 0; + return out; +} + +static inline size_t +pkgconf_fragment_len(const pkgconf_fragment_t *frag) +{ + size_t len = 1; + + if (frag->type) + len += 2; + + if (frag->data != NULL) + { + pkgconf_node_t *iter; + + char *quoted = fragment_quote(frag); + len += strlen(quoted); + free(quoted); + + PKGCONF_FOREACH_LIST_ENTRY(frag->children.head, iter) + { + const pkgconf_fragment_t *child_frag = iter->data; + len += pkgconf_fragment_len(child_frag) + 1; + } + } + + return len; +} + +static size_t +fragment_render_len(const pkgconf_list_t *list, bool escape) +{ + (void) escape; + + size_t out = 1; /* trailing nul */ + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(list->head, node) + { + const pkgconf_fragment_t *frag = node->data; + out += pkgconf_fragment_len(frag); + } + + return out; +} + +static inline size_t +fragment_render_item(const pkgconf_fragment_t *frag, char *bptr, size_t bufremain) +{ + const pkgconf_node_t *iter; + char *base = bptr; + + char *quoted = fragment_quote(frag); + if (quoted == NULL) + return 0; + + if (strlen(quoted) > bufremain) + { + free(quoted); + return 0; + } + + if (frag->type) + { + *bptr++ = '-'; + *bptr++ = frag->type; + } + + if (quoted != NULL) + { + bptr += pkgconf_strlcpy(bptr, quoted, bufremain - (bptr - base)); + free(quoted); + } + + PKGCONF_FOREACH_LIST_ENTRY(frag->children.head, iter) + { + const pkgconf_fragment_t *child_frag = iter->data; + + *bptr++ = ' '; + bptr += fragment_render_item(child_frag, bptr, bufremain - (bptr - base)); + } + + return bptr - base; +} + +static void +fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape) +{ + (void) escape; + + pkgconf_node_t *node; + char *bptr = buf; + + memset(buf, 0, buflen); + + PKGCONF_FOREACH_LIST_ENTRY(list->head, node) + { + const pkgconf_fragment_t *frag = node->data; + size_t buf_remaining = buflen - (bptr - buf); + size_t written = fragment_render_item(frag, bptr, buf_remaining); + + bptr += written; + + if (node->next != NULL) + *bptr++ = ' '; + } +} + +static const pkgconf_fragment_render_ops_t default_render_ops = { + .render_len = fragment_render_len, + .render_buf = fragment_render_buf +}; + +/* + * !doc + * + * .. c:function:: size_t pkgconf_fragment_render_len(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops) + * + * Calculates the required memory to store a `fragment list` when rendered as a string. + * + * :param pkgconf_list_t* list: The `fragment list` being rendered. + * :param bool escape: Whether or not to escape special shell characters (deprecated). + * :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``. + * :return: the amount of bytes required to represent the `fragment list` when rendered + * :rtype: size_t + */ +size_t +pkgconf_fragment_render_len(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops) +{ + (void) escape; + + ops = ops != NULL ? ops : &default_render_ops; + return ops->render_len(list, true); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape, const pkgconf_fragment_render_ops_t *ops) + * + * Renders a `fragment list` into a buffer. + * + * :param pkgconf_list_t* list: The `fragment list` being rendered. + * :param char* buf: The buffer to render the fragment list into. + * :param size_t buflen: The length of the buffer. + * :param bool escape: Whether or not to escape special shell characters (deprecated). + * :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``. + * :return: nothing + */ +void +pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape, const pkgconf_fragment_render_ops_t *ops) +{ + (void) escape; + + ops = ops != NULL ? ops : &default_render_ops; + ops->render_buf(list, buf, buflen, true); +} + +/* + * !doc + * + * .. c:function:: char *pkgconf_fragment_render(const pkgconf_list_t *list) + * + * Allocate memory and render a `fragment list` into it. + * + * :param pkgconf_list_t* list: The `fragment list` being rendered. + * :param bool escape: Whether or not to escape special shell characters (deprecated). + * :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``. + * :return: An allocated string containing the rendered `fragment list`. + * :rtype: char * + */ +char * +pkgconf_fragment_render(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops) +{ + (void) escape; + + size_t buflen = pkgconf_fragment_render_len(list, true, ops); + char *buf = calloc(1, buflen); + + pkgconf_fragment_render_buf(list, buf, buflen, true, ops); + + return buf; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_fragment_delete(pkgconf_list_t *list, pkgconf_fragment_t *node) + * + * Delete a `fragment node` from a `fragment list`. + * + * :param pkgconf_list_t* list: The `fragment list` to delete from. + * :param pkgconf_fragment_t* node: The `fragment node` to delete. + * :return: nothing + */ +void +pkgconf_fragment_delete(pkgconf_list_t *list, pkgconf_fragment_t *node) +{ + pkgconf_node_delete(&node->iter, list); + + free(node->data); + free(node); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_fragment_free(pkgconf_list_t *list) + * + * Delete an entire `fragment list`. + * + * :param pkgconf_list_t* list: The `fragment list` to delete. + * :return: nothing + */ +void +pkgconf_fragment_free(pkgconf_list_t *list) +{ + pkgconf_node_t *node, *next; + + PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node) + { + pkgconf_fragment_t *frag = node->data; + + pkgconf_fragment_free(&frag->children); + free(frag->data); + free(frag); + } +} + +/* + * !doc + * + * .. c:function:: bool pkgconf_fragment_parse(const pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_list_t *vars, const char *value) + * + * Parse a string into a `fragment list`. + * + * :param pkgconf_client_t* client: The pkgconf client being accessed. + * :param pkgconf_list_t* list: The `fragment list` to add the fragment entries to. + * :param pkgconf_list_t* vars: A list of variables to use for variable substitution. + * :param uint flags: Any parsing flags to be aware of. + * :param char* value: The string to parse into fragments. + * :return: true on success, false on parse error + */ +bool +pkgconf_fragment_parse(const pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_list_t *vars, const char *value, unsigned int flags) +{ + int i, ret, argc; + char **argv; + char *repstr = pkgconf_tuple_parse(client, vars, value, flags); + + PKGCONF_TRACE(client, "post-subst: [%s] -> [%s]", value, repstr); + + ret = pkgconf_argv_split(repstr, &argc, &argv); + if (ret < 0) + { + PKGCONF_TRACE(client, "unable to parse fragment string [%s]", repstr); + free(repstr); + return false; + } + + for (i = 0; i < argc; i++) + { + PKGCONF_TRACE(client, "processing %s", argv[i]); + + if (argv[i] == NULL) + { + PKGCONF_TRACE(client, "parsed fragment string is inconsistent: argc = %d while argv[%d] == NULL", argc, i); + pkgconf_argv_free(argv); + free(repstr); + return false; + } + + pkgconf_fragment_add(client, list, argv[i], flags); + } + + pkgconf_argv_free(argv); + free(repstr); + + return true; +} diff --git a/contrib/pkgconf/libpkgconf/iter.h b/contrib/pkgconf/libpkgconf/iter.h new file mode 100644 index 00000000000..199d299f6be --- /dev/null +++ b/contrib/pkgconf/libpkgconf/iter.h @@ -0,0 +1,113 @@ +/* + * iter.h + * Linked lists and iterators. + * + * Copyright (c) 2013 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#ifndef LIBPKGCONF_ITER_H +#define LIBPKGCONF_ITER_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct pkgconf_node_ pkgconf_node_t; + +struct pkgconf_node_ { + pkgconf_node_t *prev, *next; + void *data; +}; + +typedef struct { + pkgconf_node_t *head, *tail; + size_t length; +} pkgconf_list_t; + +#define PKGCONF_LIST_INITIALIZER { NULL, NULL, 0 } + +static inline void +pkgconf_list_zero(pkgconf_list_t *list) +{ + list->head = NULL; + list->tail = NULL; + list->length = 0; +} + +static inline void +pkgconf_node_insert(pkgconf_node_t *node, void *data, pkgconf_list_t *list) +{ + pkgconf_node_t *tnode; + + node->data = data; + + if (list->head == NULL) + { + list->head = node; + list->tail = node; + list->length = 1; + return; + } + + tnode = list->head; + + node->next = tnode; + tnode->prev = node; + + list->head = node; + list->length++; +} + +static inline void +pkgconf_node_insert_tail(pkgconf_node_t *node, void *data, pkgconf_list_t *list) +{ + pkgconf_node_t *tnode; + + node->data = data; + + if (list->tail == NULL) + { + list->head = node; + list->tail = node; + list->length = 1; + return; + } + + tnode = list->tail; + + node->prev = tnode; + tnode->next = node; + + list->tail = node; + list->length++; +} + +static inline void +pkgconf_node_delete(pkgconf_node_t *node, pkgconf_list_t *list) +{ + list->length--; + + if (node->prev == NULL) + list->head = node->next; + else + node->prev->next = node->next; + + if (node->next == NULL) + list->tail = node->prev; + else + node->next->prev = node->prev; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/contrib/pkgconf/libpkgconf/libpkgconf-api.h b/contrib/pkgconf/libpkgconf/libpkgconf-api.h new file mode 100644 index 00000000000..a924c80ce83 --- /dev/null +++ b/contrib/pkgconf/libpkgconf/libpkgconf-api.h @@ -0,0 +1,19 @@ +#ifndef LIBPKGCONF_LIBPKGCONF_API_H +#define LIBPKGCONF_LIBPKGCONF_API_H + +/* Makefile.am specifies visibility using the libtool option -export-symbols-regex '^pkgconf_' + * Unfortunately, that is not available when building with meson, so use attributes instead. + */ +#if defined(PKGCONFIG_IS_STATIC) +# define PKGCONF_API +#elif defined(_WIN32) || defined(_WIN64) +# if defined(LIBPKGCONF_EXPORT) || defined(DLL_EXPORT) +# define PKGCONF_API __declspec(dllexport) +# else +# define PKGCONF_API __declspec(dllimport) +# endif +#else +# define PKGCONF_API __attribute__((visibility("default"))) +#endif + +#endif diff --git a/contrib/pkgconf/libpkgconf/libpkgconf.h b/contrib/pkgconf/libpkgconf/libpkgconf.h new file mode 100644 index 00000000000..ed0f1f996ce --- /dev/null +++ b/contrib/pkgconf/libpkgconf/libpkgconf.h @@ -0,0 +1,493 @@ +/* + * libpkgconf.h + * Global include file for everything in libpkgconf. + * + * Copyright (c) 2011, 2015 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#ifndef LIBPKGCONF__LIBPKGCONF_H +#define LIBPKGCONF__LIBPKGCONF_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* pkg-config uses ';' on win32 as ':' is part of path */ +#ifdef _WIN32 +#define PKG_CONFIG_PATH_SEP_S ";" +#else +#define PKG_CONFIG_PATH_SEP_S ":" +#endif + +#ifdef _WIN32 +#define PKG_DIR_SEP_S '\\' +#else +#define PKG_DIR_SEP_S '/' +#endif + +#ifdef _WIN32 +#define realpath(N,R) _fullpath((R),(N),_MAX_PATH) +#endif + +#define PKGCONF_BUFSIZE (65535) + +typedef enum { + PKGCONF_CMP_NOT_EQUAL, + PKGCONF_CMP_ANY, + PKGCONF_CMP_LESS_THAN, + PKGCONF_CMP_LESS_THAN_EQUAL, + PKGCONF_CMP_EQUAL, + PKGCONF_CMP_GREATER_THAN, + PKGCONF_CMP_GREATER_THAN_EQUAL +} pkgconf_pkg_comparator_t; + +#define PKGCONF_CMP_COUNT 7 + +typedef struct pkgconf_pkg_ pkgconf_pkg_t; +typedef struct pkgconf_dependency_ pkgconf_dependency_t; +typedef struct pkgconf_tuple_ pkgconf_tuple_t; +typedef struct pkgconf_fragment_ pkgconf_fragment_t; +typedef struct pkgconf_path_ pkgconf_path_t; +typedef struct pkgconf_client_ pkgconf_client_t; +typedef struct pkgconf_cross_personality_ pkgconf_cross_personality_t; +typedef struct pkgconf_queue_ pkgconf_queue_t; + +#define PKGCONF_ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) + +#define PKGCONF_FOREACH_LIST_ENTRY(head, value) \ + for ((value) = (head); (value) != NULL; (value) = (value)->next) + +#define PKGCONF_FOREACH_LIST_ENTRY_SAFE(head, nextiter, value) \ + for ((value) = (head), (nextiter) = (head) != NULL ? (head)->next : NULL; (value) != NULL; (value) = (nextiter), (nextiter) = (nextiter) != NULL ? (nextiter)->next : NULL) + +#define PKGCONF_FOREACH_LIST_ENTRY_REVERSE(tail, value) \ + for ((value) = (tail); (value) != NULL; (value) = (value)->prev) + +#define LIBPKGCONF_VERSION 20501 +#define LIBPKGCONF_VERSION_STR "2.5.1" + +struct pkgconf_queue_ { + pkgconf_node_t iter; + char *package; + + unsigned int flags; +}; + +struct pkgconf_fragment_ { + pkgconf_node_t iter; + + char type; + char *data; + + pkgconf_list_t children; + unsigned int flags; +}; + +#define PKGCONF_PKG_FRAGF_TERMINATED 0x1 + +struct pkgconf_dependency_ { + pkgconf_node_t iter; + + char *package; + pkgconf_pkg_comparator_t compare; + char *version; + pkgconf_pkg_t *parent; + pkgconf_pkg_t *match; + + unsigned int flags; + + int refcount; + pkgconf_client_t *owner; +}; + +struct pkgconf_tuple_ { + pkgconf_node_t iter; + + char *key; + char *value; + + unsigned int flags; +}; + +#define PKGCONF_PKG_TUPLEF_OVERRIDE 0x1 + +struct pkgconf_path_ { + pkgconf_node_t lnode; + + char *path; + void *handle_path; + void *handle_device; + + unsigned int flags; +}; + +#define PKGCONF_PKG_PROPF_NONE 0x00 +#define PKGCONF_PKG_PROPF_STATIC 0x01 +#define PKGCONF_PKG_PROPF_CACHED 0x02 +#define PKGCONF_PKG_PROPF_UNINSTALLED 0x08 +#define PKGCONF_PKG_PROPF_VIRTUAL 0x10 +#define PKGCONF_PKG_PROPF_ANCESTOR 0x20 +#define PKGCONF_PKG_PROPF_VISITED_PRIVATE 0x40 +#define PKGCONF_PKG_PROPF_PRELOADED 0x80 + +struct pkgconf_pkg_ { + int refcount; + char *id; + char *filename; + char *realname; + char *version; + char *description; + char *url; + char *pc_filedir; + char *license; + char *maintainer; + char *copyright; + char *why; + + pkgconf_list_t libs; + pkgconf_list_t libs_private; + pkgconf_list_t cflags; + pkgconf_list_t cflags_private; + + pkgconf_list_t required; /* this used to be requires but that is now a reserved keyword */ + pkgconf_list_t requires_private; + pkgconf_list_t conflicts; + pkgconf_list_t provides; + + pkgconf_list_t vars; + + unsigned int flags; + + pkgconf_client_t *owner; + + /* these resources are owned by the package and do not need special management, + * under no circumstance attempt to allocate or free objects belonging to these pointers + */ + pkgconf_tuple_t *orig_prefix; + pkgconf_tuple_t *prefix; + + uint64_t serial; + uint64_t identifier; + + pkgconf_node_t preload_node; +}; + +typedef bool (*pkgconf_pkg_iteration_func_t)(const pkgconf_pkg_t *pkg, void *data); +typedef void (*pkgconf_pkg_traverse_func_t)(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data); +typedef bool (*pkgconf_queue_apply_func_t)(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int maxdepth); +typedef bool (*pkgconf_error_handler_func_t)(const char *msg, const pkgconf_client_t *client, void *data); +typedef void (*pkgconf_unveil_handler_func_t)(const pkgconf_client_t *client, const char *path, const char *permissions); + +struct pkgconf_client_ { + pkgconf_list_t dir_list; + + pkgconf_list_t filter_libdirs; + pkgconf_list_t filter_includedirs; + + pkgconf_list_t global_vars; + + void *error_handler_data; + void *warn_handler_data; + void *trace_handler_data; + + pkgconf_error_handler_func_t error_handler; + pkgconf_error_handler_func_t warn_handler; + pkgconf_error_handler_func_t trace_handler; + + FILE *auditf; + + char *sysroot_dir; + char *buildroot_dir; + + unsigned int flags; + + char *prefix_varname; + + bool already_sent_notice; + + uint64_t serial; + uint64_t identifier; + + pkgconf_pkg_t **cache_table; + size_t cache_count; + + pkgconf_unveil_handler_func_t unveil_handler; + + pkgconf_list_t preloaded_pkgs; +}; + +struct pkgconf_cross_personality_ { + char *name; + + pkgconf_list_t dir_list; + + pkgconf_list_t filter_libdirs; + pkgconf_list_t filter_includedirs; + + char *sysroot_dir; + + bool want_default_static; + bool want_default_pure; +}; + +/* client.c */ +PKGCONF_API void pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality); +PKGCONF_API pkgconf_client_t * pkgconf_client_new(pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality); +PKGCONF_API void pkgconf_client_deinit(pkgconf_client_t *client); +PKGCONF_API void pkgconf_client_free(pkgconf_client_t *client); +PKGCONF_API const char *pkgconf_client_get_sysroot_dir(const pkgconf_client_t *client); +PKGCONF_API void pkgconf_client_set_sysroot_dir(pkgconf_client_t *client, const char *sysroot_dir); +PKGCONF_API const char *pkgconf_client_get_buildroot_dir(const pkgconf_client_t *client); +PKGCONF_API void pkgconf_client_set_buildroot_dir(pkgconf_client_t *client, const char *buildroot_dir); +PKGCONF_API unsigned int pkgconf_client_get_flags(const pkgconf_client_t *client); +PKGCONF_API void pkgconf_client_set_flags(pkgconf_client_t *client, unsigned int flags); +PKGCONF_API const char *pkgconf_client_get_prefix_varname(const pkgconf_client_t *client); +PKGCONF_API void pkgconf_client_set_prefix_varname(pkgconf_client_t *client, const char *prefix_varname); +PKGCONF_API pkgconf_error_handler_func_t pkgconf_client_get_warn_handler(const pkgconf_client_t *client); +PKGCONF_API void pkgconf_client_set_warn_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t warn_handler, void *warn_handler_data); +PKGCONF_API pkgconf_error_handler_func_t pkgconf_client_get_error_handler(const pkgconf_client_t *client); +PKGCONF_API void pkgconf_client_set_error_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data); +PKGCONF_API pkgconf_error_handler_func_t pkgconf_client_get_trace_handler(const pkgconf_client_t *client); +PKGCONF_API void pkgconf_client_set_trace_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t trace_handler, void *trace_handler_data); +PKGCONF_API pkgconf_unveil_handler_func_t pkgconf_client_get_unveil_handler(const pkgconf_client_t *client); +PKGCONF_API void pkgconf_client_set_unveil_handler(pkgconf_client_t *client, pkgconf_unveil_handler_func_t unveil_handler); +PKGCONF_API void pkgconf_client_dir_list_build(pkgconf_client_t *client, const pkgconf_cross_personality_t *personality); +PKGCONF_API bool pkgconf_client_preload_path(pkgconf_client_t *client, const char *path); +PKGCONF_API bool pkgconf_client_preload_from_environ(pkgconf_client_t *client, const char *env); + +/* personality.c */ +PKGCONF_API pkgconf_cross_personality_t *pkgconf_cross_personality_default(void); +PKGCONF_API pkgconf_cross_personality_t *pkgconf_cross_personality_find(const char *triplet); +PKGCONF_API void pkgconf_cross_personality_deinit(pkgconf_cross_personality_t *personality); + +#define PKGCONF_IS_MODULE_SEPARATOR(c) ((c) == ',' || isspace ((unsigned char)(c))) +#define PKGCONF_IS_OPERATOR_CHAR(c) ((c) == '<' || (c) == '>' || (c) == '!' || (c) == '=') + +#define PKGCONF_PKG_PKGF_NONE 0x0000 +#define PKGCONF_PKG_PKGF_SEARCH_PRIVATE 0x0001 +#define PKGCONF_PKG_PKGF_ENV_ONLY 0x0002 +#define PKGCONF_PKG_PKGF_NO_UNINSTALLED 0x0004 +#define PKGCONF_PKG_PKGF_SKIP_ROOT_VIRTUAL 0x0008 +#define PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS 0x0010 +#define PKGCONF_PKG_PKGF_SKIP_CONFLICTS 0x0020 +#define PKGCONF_PKG_PKGF_NO_CACHE 0x0040 +#define PKGCONF_PKG_PKGF_SKIP_ERRORS 0x0080 +#define PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE 0x0100 +#define PKGCONF_PKG_PKGF_SKIP_PROVIDES 0x0200 +#define PKGCONF_PKG_PKGF_REDEFINE_PREFIX 0x0400 +#define PKGCONF_PKG_PKGF_DONT_RELOCATE_PATHS 0x0800 +#define PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS 0x1000 +#define PKGCONF_PKG_PKGF_DONT_FILTER_INTERNAL_CFLAGS 0x2000 +#define PKGCONF_PKG_PKGF_DONT_MERGE_SPECIAL_FRAGMENTS 0x4000 +#define PKGCONF_PKG_PKGF_FDO_SYSROOT_RULES 0x8000 +#define PKGCONF_PKG_PKGF_PKGCONF1_SYSROOT_RULES 0x10000 + +#define PKGCONF_PKG_DEPF_INTERNAL 0x1 +#define PKGCONF_PKG_DEPF_PRIVATE 0x2 +#define PKGCONF_PKG_DEPF_QUERY 0x4 + +#define PKGCONF_PKG_ERRF_OK 0x0 +#define PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND 0x1 +#define PKGCONF_PKG_ERRF_PACKAGE_VER_MISMATCH 0x2 +#define PKGCONF_PKG_ERRF_PACKAGE_CONFLICT 0x4 +#define PKGCONF_PKG_ERRF_DEPGRAPH_BREAK 0x8 + +#if __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) +# define PRINTFLIKE(fmtarg, firstvararg) \ + __attribute__((__format__ (gnu_printf, fmtarg, firstvararg))) +#elif defined(__clang__) || defined(__INTEL_COMPILER) || __GNUC__ > 2 || (_GNUC__ == 2 && __GNUC_MINOR__ >= 5) +# define PRINTFLIKE(fmtarg, firstvararg) \ + __attribute__((__format__ (__printf__, fmtarg, firstvararg))) +#else +# define PRINTFLIKE(fmtarg, firstvararg) +#endif + +#if defined(__clang__) || defined(__INTEL_COMPILER) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +# define DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +# define DEPRECATED __declspec(deprecated) +#else +# define DEPRECATED +#endif + +/* parser.c */ +typedef void (*pkgconf_parser_operand_func_t)(void *data, const size_t lineno, const char *key, const char *value); +typedef void (*pkgconf_parser_warn_func_t)(void *data, const char *fmt, ...); + +PKGCONF_API void pkgconf_parser_parse(FILE *f, void *data, const pkgconf_parser_operand_func_t *ops, const pkgconf_parser_warn_func_t warnfunc, const char *filename); + +/* pkg.c */ +PKGCONF_API bool pkgconf_error(const pkgconf_client_t *client, const char *format, ...) PRINTFLIKE(2, 3); +PKGCONF_API bool pkgconf_warn(const pkgconf_client_t *client, const char *format, ...) PRINTFLIKE(2, 3); +PKGCONF_API bool pkgconf_trace(const pkgconf_client_t *client, const char *filename, size_t lineno, const char *funcname, const char *format, ...) PRINTFLIKE(5, 6); +PKGCONF_API bool pkgconf_default_error_handler(const char *msg, const pkgconf_client_t *client, void *data); + +#ifndef PKGCONF_LITE +#if defined(__GNUC__) || defined(__INTEL_COMPILER) +#define PKGCONF_TRACE(client, ...) do { \ + pkgconf_trace(client, __FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__); \ + } while (0) +#else +#define PKGCONF_TRACE(client, ...) do { \ + pkgconf_trace(client, __FILE__, __LINE__, __func__, __VA_ARGS__); \ + } while (0) +#endif +#else +#define PKGCONF_TRACE(client, ...) +#endif + +PKGCONF_API pkgconf_pkg_t *pkgconf_pkg_ref(pkgconf_client_t *client, pkgconf_pkg_t *pkg); +PKGCONF_API void pkgconf_pkg_unref(pkgconf_client_t *client, pkgconf_pkg_t *pkg); +PKGCONF_API void pkgconf_pkg_free(pkgconf_client_t *client, pkgconf_pkg_t *pkg); +PKGCONF_API pkgconf_pkg_t *pkgconf_pkg_find(pkgconf_client_t *client, const char *name); +PKGCONF_API unsigned int pkgconf_pkg_traverse(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_pkg_traverse_func_t func, void *data, int maxdepth, unsigned int skip_flags); +PKGCONF_API unsigned int pkgconf_pkg_verify_graph(pkgconf_client_t *client, pkgconf_pkg_t *root, int depth); +PKGCONF_API pkgconf_pkg_t *pkgconf_pkg_verify_dependency(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags); +PKGCONF_API const char *pkgconf_pkg_get_comparator(const pkgconf_dependency_t *pkgdep); +PKGCONF_API unsigned int pkgconf_pkg_cflags(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth); +PKGCONF_API unsigned int pkgconf_pkg_libs(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth); +PKGCONF_API pkgconf_pkg_comparator_t pkgconf_pkg_comparator_lookup_by_name(const char *name); +PKGCONF_API pkgconf_pkg_t *pkgconf_builtin_pkg_get(const char *name); + +PKGCONF_API int pkgconf_compare_version(const char *a, const char *b); +PKGCONF_API pkgconf_pkg_t *pkgconf_scan_all(pkgconf_client_t *client, void *ptr, pkgconf_pkg_iteration_func_t func); + +/* parse.c */ +PKGCONF_API pkgconf_pkg_t *pkgconf_pkg_new_from_path(pkgconf_client_t *client, const char *path, unsigned int flags); +PKGCONF_API void pkgconf_dependency_parse_str(pkgconf_client_t *client, pkgconf_list_t *deplist_head, const char *depends, unsigned int flags); +PKGCONF_API void pkgconf_dependency_parse(pkgconf_client_t *client, pkgconf_pkg_t *pkg, pkgconf_list_t *deplist_head, const char *depends, unsigned int flags); +PKGCONF_API void pkgconf_dependency_append(pkgconf_list_t *list, pkgconf_dependency_t *tail); +PKGCONF_API void pkgconf_dependency_free(pkgconf_list_t *list); +PKGCONF_API void pkgconf_dependency_free_one(pkgconf_dependency_t *dep); +PKGCONF_API pkgconf_dependency_t *pkgconf_dependency_add(pkgconf_client_t *client, pkgconf_list_t *list, const char *package, const char *version, pkgconf_pkg_comparator_t compare, unsigned int flags); +PKGCONF_API pkgconf_dependency_t *pkgconf_dependency_ref(pkgconf_client_t *client, pkgconf_dependency_t *dep); +PKGCONF_API void pkgconf_dependency_unref(pkgconf_client_t *client, pkgconf_dependency_t *dep); +PKGCONF_API pkgconf_dependency_t *pkgconf_dependency_copy(pkgconf_client_t *client, const pkgconf_dependency_t *dep); + +/* argvsplit.c */ +PKGCONF_API int pkgconf_argv_split(const char *src, int *argc, char ***argv); +PKGCONF_API void pkgconf_argv_free(char **argv); + +/* fragment.c */ +typedef struct pkgconf_fragment_render_ops_ { + size_t (*render_len)(const pkgconf_list_t *list, bool escape); + void (*render_buf)(const pkgconf_list_t *list, char *buf, size_t len, bool escape); +} pkgconf_fragment_render_ops_t; + +typedef bool (*pkgconf_fragment_filter_func_t)(const pkgconf_client_t *client, const pkgconf_fragment_t *frag, void *data); +PKGCONF_API bool pkgconf_fragment_parse(const pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_list_t *vars, const char *value, unsigned int flags); +PKGCONF_API void pkgconf_fragment_insert(const pkgconf_client_t *client, pkgconf_list_t *list, char type, const char *data, bool tail); +PKGCONF_API void pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *string, unsigned int flags); +PKGCONF_API void pkgconf_fragment_copy(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_fragment_t *base, bool is_private); +PKGCONF_API void pkgconf_fragment_copy_list(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_list_t *base); +PKGCONF_API void pkgconf_fragment_delete(pkgconf_list_t *list, pkgconf_fragment_t *node); +PKGCONF_API void pkgconf_fragment_free(pkgconf_list_t *list); +PKGCONF_API void pkgconf_fragment_filter(const pkgconf_client_t *client, pkgconf_list_t *dest, pkgconf_list_t *src, pkgconf_fragment_filter_func_t filter_func, void *data); +PKGCONF_API size_t pkgconf_fragment_render_len(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops); +PKGCONF_API void pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t len, bool escape, const pkgconf_fragment_render_ops_t *ops); +PKGCONF_API char *pkgconf_fragment_render(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops); +PKGCONF_API bool pkgconf_fragment_has_system_dir(const pkgconf_client_t *client, const pkgconf_fragment_t *frag); + +/* tuple.c */ +PKGCONF_API pkgconf_tuple_t *pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *parent, const char *key, const char *value, bool parse, unsigned int flags); +PKGCONF_API char *pkgconf_tuple_find(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key); +PKGCONF_API char *pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *list, const char *value, unsigned int flags); +PKGCONF_API void pkgconf_tuple_free(pkgconf_list_t *list); +PKGCONF_API void pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list); +PKGCONF_API void pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value); +PKGCONF_API char *pkgconf_tuple_find_global(const pkgconf_client_t *client, const char *key); +PKGCONF_API void pkgconf_tuple_free_global(pkgconf_client_t *client); +PKGCONF_API void pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv); + +/* queue.c */ +PKGCONF_API void pkgconf_queue_push(pkgconf_list_t *list, const char *package); +PKGCONF_API bool pkgconf_queue_compile(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *list); +PKGCONF_API bool pkgconf_queue_solve(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_pkg_t *world, int maxdepth); +PKGCONF_API void pkgconf_queue_free(pkgconf_list_t *list); +PKGCONF_API bool pkgconf_queue_apply(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_queue_apply_func_t func, int maxdepth, void *data); +PKGCONF_API bool pkgconf_queue_validate(pkgconf_client_t *client, pkgconf_list_t *list, int maxdepth); +PKGCONF_API void pkgconf_solution_free(pkgconf_client_t *client, pkgconf_pkg_t *world); + +/* cache.c */ +PKGCONF_API pkgconf_pkg_t *pkgconf_cache_lookup(pkgconf_client_t *client, const char *id); +PKGCONF_API void pkgconf_cache_add(pkgconf_client_t *client, pkgconf_pkg_t *pkg); +PKGCONF_API void pkgconf_cache_remove(pkgconf_client_t *client, pkgconf_pkg_t *pkg); +PKGCONF_API void pkgconf_cache_free(pkgconf_client_t *client); + +/* audit.c */ +PKGCONF_API void pkgconf_audit_set_log(pkgconf_client_t *client, FILE *auditf); +PKGCONF_API void pkgconf_audit_log(pkgconf_client_t *client, const char *format, ...) PRINTFLIKE(2, 3); +PKGCONF_API void pkgconf_audit_log_dependency(pkgconf_client_t *client, const pkgconf_pkg_t *dep, const pkgconf_dependency_t *depnode); + +/* path.c */ +PKGCONF_API void pkgconf_path_add(const char *text, pkgconf_list_t *dirlist, bool filter); +PKGCONF_API void pkgconf_path_prepend(const char *text, pkgconf_list_t *dirlist, bool filter); +PKGCONF_API size_t pkgconf_path_split(const char *text, pkgconf_list_t *dirlist, bool filter); +PKGCONF_API size_t pkgconf_path_build_from_environ(const char *envvarname, const char *fallback, pkgconf_list_t *dirlist, bool filter); +#ifdef _WIN32 +PKGCONF_API size_t pkgconf_path_build_from_registry(/* HKEY -> HANDLE -> PVOID */ void *hKey, pkgconf_list_t *dirlist, bool filter); +#endif +PKGCONF_API bool pkgconf_path_match_list(const char *path, const pkgconf_list_t *dirlist); +PKGCONF_API void pkgconf_path_free(pkgconf_list_t *dirlist); +PKGCONF_API bool pkgconf_path_relocate(char *buf, size_t buflen); +PKGCONF_API void pkgconf_path_copy_list(pkgconf_list_t *dst, const pkgconf_list_t *src); +PKGCONF_API void pkgconf_path_prepend_list(pkgconf_list_t *dst, const pkgconf_list_t *src); + +/* buffer.c */ +typedef struct pkgconf_buffer_ { + char *base; + char *end; +} pkgconf_buffer_t; + +PKGCONF_API void pkgconf_buffer_append(pkgconf_buffer_t *buffer, const char *text); +PKGCONF_API void pkgconf_buffer_push_byte(pkgconf_buffer_t *buffer, char byte); +PKGCONF_API void pkgconf_buffer_trim_byte(pkgconf_buffer_t *buffer); +PKGCONF_API void pkgconf_buffer_finalize(pkgconf_buffer_t *buffer); +static inline const char *pkgconf_buffer_str(const pkgconf_buffer_t *buffer) { + return buffer->base; +} + +static inline size_t pkgconf_buffer_len(const pkgconf_buffer_t *buffer) { + return (size_t)(ptrdiff_t)(buffer->end - buffer->base); +} + +static inline char pkgconf_buffer_lastc(const pkgconf_buffer_t *buffer) { + if (buffer->base == buffer->end) + return '\0'; + + return *(buffer->end - 1); +} + +#define PKGCONF_BUFFER_INITIALIZER { NULL, NULL } + +static inline void pkgconf_buffer_reset(pkgconf_buffer_t *buffer) { + pkgconf_buffer_finalize(buffer); + buffer->base = buffer->end = NULL; +} + +/* fileio.c */ +PKGCONF_API bool pkgconf_fgetline(pkgconf_buffer_t *buffer, FILE *stream); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/contrib/pkgconf/libpkgconf/meson.build b/contrib/pkgconf/libpkgconf/meson.build new file mode 100644 index 00000000000..5c393b0ef73 --- /dev/null +++ b/contrib/pkgconf/libpkgconf/meson.build @@ -0,0 +1,12 @@ +configure_file(input : 'config.h.meson', + output : 'config.h', + configuration : cdata) + + +install_headers('libpkgconf.h', + 'stdinc.h', + 'iter.h', + 'bsdstubs.h', + 'libpkgconf-api.h', + subdir : 'pkgconf/libpkgconf') + diff --git a/contrib/pkgconf/libpkgconf/parser.c b/contrib/pkgconf/libpkgconf/parser.c new file mode 100644 index 00000000000..3a66b8af2a8 --- /dev/null +++ b/contrib/pkgconf/libpkgconf/parser.c @@ -0,0 +1,115 @@ +/* + * parser.c + * rfc822 message parser + * + * Copyright (c) 2018 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include +#include +#include + +/* + * !doc + * + * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_new_from_file(const pkgconf_client_t *client, const char *filename, FILE *f) + * + * Parse a .pc file into a pkgconf_pkg_t object structure. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param char* filename: The filename of the package file (including full path). + * :param FILE* f: The file object to read from. + * :returns: A ``pkgconf_pkg_t`` object which contains the package data. + * :rtype: pkgconf_pkg_t * + */ +void +pkgconf_parser_parse(FILE *f, void *data, const pkgconf_parser_operand_func_t *ops, const pkgconf_parser_warn_func_t warnfunc, const char *filename) +{ + pkgconf_buffer_t readbuf = PKGCONF_BUFFER_INITIALIZER; + size_t lineno = 0; + bool continue_reading = true; + + while (continue_reading) + { + char op, *p, *key, *value; + bool warned_key_whitespace = false, warned_value_whitespace = false; + + continue_reading = pkgconf_fgetline(&readbuf, f); + lineno++; + + p = readbuf.base; + if (p == NULL) + continue; + while (*p && isspace((unsigned char)*p)) + p++; + if (*p && p != readbuf.base) + { + warnfunc(data, "%s:" SIZE_FMT_SPECIFIER ": warning: whitespace encountered while parsing key section\n", + filename, lineno); + warned_key_whitespace = true; + } + key = p; + while (*p && (isalpha((unsigned char)*p) || isdigit((unsigned char)*p) || *p == '_' || *p == '.')) + p++; + + if (!isalpha((unsigned char)*key) && + !isdigit((unsigned char)*p)) + { + pkgconf_buffer_reset(&readbuf); + continue; + } + + while (*p && isspace((unsigned char)*p)) + { + if (!warned_key_whitespace) + { + warnfunc(data, "%s:" SIZE_FMT_SPECIFIER ": warning: whitespace encountered while parsing key section\n", + filename, lineno); + warned_key_whitespace = true; + } + + /* set to null to avoid trailing spaces in key */ + *p = '\0'; + p++; + } + + op = *p; + if (*p != '\0') + { + *p = '\0'; + p++; + } + + while (*p && isspace((unsigned char)*p)) + p++; + + value = p; + p = value + (strlen(value) - 1); + while (*p && isspace((unsigned char) *p) && p > value) + { + if (!warned_value_whitespace && op == '=') + { + warnfunc(data, "%s:" SIZE_FMT_SPECIFIER ": warning: trailing whitespace encountered while parsing value section\n", + filename, lineno); + warned_value_whitespace = true; + } + + *p = '\0'; + p--; + } + if (ops[(unsigned char) op]) + ops[(unsigned char) op](data, lineno, key, value); + + pkgconf_buffer_reset(&readbuf); + } + + pkgconf_buffer_finalize(&readbuf); +} diff --git a/contrib/pkgconf/libpkgconf/path.c b/contrib/pkgconf/libpkgconf/path.c new file mode 100644 index 00000000000..dda77f5664a --- /dev/null +++ b/contrib/pkgconf/libpkgconf/path.c @@ -0,0 +1,459 @@ +/* + * path.c + * filesystem path management + * + * Copyright (c) 2016 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include +#include +#include + +#if defined(HAVE_SYS_STAT_H) && ! defined(_WIN32) +# include +# define PKGCONF_CACHE_INODES +#endif + +#ifdef _WIN32 +# define PKG_CONFIG_REG_KEY "Software\\pkgconfig\\PKG_CONFIG_PATH" +#endif + +static bool +#ifdef PKGCONF_CACHE_INODES +path_list_contains_entry(const char *text, pkgconf_list_t *dirlist, struct stat *st) +#else +path_list_contains_entry(const char *text, pkgconf_list_t *dirlist) +#endif +{ + pkgconf_node_t *n; + + PKGCONF_FOREACH_LIST_ENTRY(dirlist->head, n) + { + pkgconf_path_t *pn = n->data; + +#ifdef PKGCONF_CACHE_INODES + if (pn->handle_device == (void *)(intptr_t)st->st_dev && pn->handle_path == (void *)(intptr_t)st->st_ino) + return true; +#endif + + if (!strcmp(text, pn->path)) + return true; + } + + return false; +} + +/* + * !doc + * + * libpkgconf `path` module + * ======================== + * + * The `path` module provides functions for manipulating lists of paths in a cross-platform manner. Notably, + * it is used by the `pkgconf client` to parse the ``PKG_CONFIG_PATH``, ``PKG_CONFIG_LIBDIR`` and related environment + * variables. + */ + +static pkgconf_path_t * +prepare_path_node(const char *text, pkgconf_list_t *dirlist, bool filter) +{ + pkgconf_path_t *node; + char path[PKGCONF_ITEM_SIZE]; + + pkgconf_strlcpy(path, text, sizeof path); + pkgconf_path_relocate(path, sizeof path); + +#ifdef PKGCONF_CACHE_INODES + struct stat st; + + if (filter) + { + if (lstat(path, &st) == -1) + return NULL; + if (S_ISLNK(st.st_mode)) + { + char pathbuf[PKGCONF_ITEM_SIZE * 4]; + char *linkdest = realpath(path, pathbuf); + + if (linkdest != NULL && stat(linkdest, &st) == -1) + return NULL; + } + if (path_list_contains_entry(path, dirlist, &st)) + return NULL; + } +#else + if (filter && path_list_contains_entry(path, dirlist)) + return NULL; +#endif + + node = calloc(1, sizeof(pkgconf_path_t)); + if (node == NULL) + return NULL; + + node->path = strdup(path); + +#ifdef PKGCONF_CACHE_INODES + if (filter) { + node->handle_path = (void *)(intptr_t) st.st_ino; + node->handle_device = (void *)(intptr_t) st.st_dev; + } +#endif + + return node; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_path_add(const char *text, pkgconf_list_t *dirlist) + * + * Adds a path node to a path list. If the path is already in the list, do nothing. + * + * :param char* text: The path text to add as a path node. + * :param pkgconf_list_t* dirlist: The path list to add the path node to. + * :param bool filter: Whether to perform duplicate filtering. + * :return: nothing + */ +void +pkgconf_path_add(const char *text, pkgconf_list_t *dirlist, bool filter) +{ + pkgconf_path_t *node = prepare_path_node(text, dirlist, filter); + if (node == NULL) + return; + + pkgconf_node_insert_tail(&node->lnode, node, dirlist); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_path_prepend(const char *text, pkgconf_list_t *dirlist) + * + * Prepends a path node to a path list. If the path is already in the list, do nothing. + * + * :param char* text: The path text to add as a path node. + * :param pkgconf_list_t* dirlist: The path list to add the path node to. + * :param bool filter: Whether to perform duplicate filtering. + * :return: nothing + */ +void +pkgconf_path_prepend(const char *text, pkgconf_list_t *dirlist, bool filter) +{ + pkgconf_path_t *node = prepare_path_node(text, dirlist, filter); + if (node == NULL) + return; + + pkgconf_node_insert(&node->lnode, node, dirlist); +} + +/* + * !doc + * + * .. c:function:: size_t pkgconf_path_split(const char *text, pkgconf_list_t *dirlist) + * + * Splits a given text input and inserts paths into a path list. + * + * :param char* text: The path text to split and add as path nodes. + * :param pkgconf_list_t* dirlist: The path list to have the path nodes added to. + * :param bool filter: Whether to perform duplicate filtering. + * :return: number of path nodes added to the path list + * :rtype: size_t + */ +size_t +pkgconf_path_split(const char *text, pkgconf_list_t *dirlist, bool filter) +{ + size_t count = 0; + char *workbuf, *p, *iter; + + if (text == NULL) + return 0; + + iter = workbuf = strdup(text); + while ((p = strtok(iter, PKG_CONFIG_PATH_SEP_S)) != NULL) + { + pkgconf_path_add(p, dirlist, filter); + + count++, iter = NULL; + } + free(workbuf); + + return count; +} + +/* + * !doc + * + * .. c:function:: size_t pkgconf_path_build_from_environ(const char *envvarname, const char *fallback, pkgconf_list_t *dirlist) + * + * Adds the paths specified in an environment variable to a path list. If the environment variable is not set, + * an optional default set of paths is added. + * + * :param char* envvarname: The environment variable to look up. + * :param char* fallback: The fallback paths to use if the environment variable is not set. + * :param pkgconf_list_t* dirlist: The path list to add the path nodes to. + * :param bool filter: Whether to perform duplicate filtering. + * :return: number of path nodes added to the path list + * :rtype: size_t + */ +size_t +pkgconf_path_build_from_environ(const char *envvarname, const char *fallback, pkgconf_list_t *dirlist, bool filter) +{ + const char *data; + + data = getenv(envvarname); + if (data != NULL) + return pkgconf_path_split(data, dirlist, filter); + + if (fallback != NULL) + return pkgconf_path_split(fallback, dirlist, filter); + + /* no fallback and no environment variable, thusly no nodes added */ + return 0; +} + +/* + * !doc + * + * .. c:function:: bool pkgconf_path_match_list(const char *path, const pkgconf_list_t *dirlist) + * + * Checks whether a path has a matching prefix in a path list. + * + * :param char* path: The path to check against a path list. + * :param pkgconf_list_t* dirlist: The path list to check the path against. + * :return: true if the path list has a matching prefix, otherwise false + * :rtype: bool + */ +bool +pkgconf_path_match_list(const char *path, const pkgconf_list_t *dirlist) +{ + pkgconf_node_t *n = NULL; + char relocated[PKGCONF_ITEM_SIZE]; + const char *cpath = path; + + pkgconf_strlcpy(relocated, path, sizeof relocated); + if (pkgconf_path_relocate(relocated, sizeof relocated)) + cpath = relocated; + + PKGCONF_FOREACH_LIST_ENTRY(dirlist->head, n) + { + pkgconf_path_t *pnode = n->data; + + if (!strcmp(pnode->path, cpath)) + return true; + } + + return false; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_path_copy_list(pkgconf_list_t *dst, const pkgconf_list_t *src) + * + * Copies a path list to another path list. + * + * :param pkgconf_list_t* dst: The path list to copy to. + * :param pkgconf_list_t* src: The path list to copy from. + * :return: nothing + */ +void +pkgconf_path_copy_list(pkgconf_list_t *dst, const pkgconf_list_t *src) +{ + pkgconf_node_t *n; + + PKGCONF_FOREACH_LIST_ENTRY(src->head, n) + { + pkgconf_path_t *srcpath = n->data, *path; + + path = calloc(1, sizeof(pkgconf_path_t)); + if (path == NULL) + continue; + + path->path = strdup(srcpath->path); + +#ifdef PKGCONF_CACHE_INODES + path->handle_path = srcpath->handle_path; + path->handle_device = srcpath->handle_device; +#endif + + pkgconf_node_insert_tail(&path->lnode, path, dst); + } +} + +/* + * !doc + * + * .. c:function:: void pkgconf_path_prepend_list(pkgconf_list_t *dst, const pkgconf_list_t *src) + * + * Copies a path list to another path list. + * + * :param pkgconf_list_t* dst: The path list to copy to. + * :param pkgconf_list_t* src: The path list to copy from. + * :return: nothing + */ +void +pkgconf_path_prepend_list(pkgconf_list_t *dst, const pkgconf_list_t *src) +{ + pkgconf_node_t *n; + + PKGCONF_FOREACH_LIST_ENTRY(src->head, n) + { + pkgconf_path_t *srcpath = n->data, *path; + + path = calloc(1, sizeof(pkgconf_path_t)); + if (path == NULL) + continue; + + path->path = strdup(srcpath->path); + +#ifdef PKGCONF_CACHE_INODES + path->handle_path = srcpath->handle_path; + path->handle_device = srcpath->handle_device; +#endif + + pkgconf_node_insert(&path->lnode, path, dst); + } +} + +/* + * !doc + * + * .. c:function:: void pkgconf_path_free(pkgconf_list_t *dirlist) + * + * Releases any path nodes attached to the given path list. + * + * :param pkgconf_list_t* dirlist: The path list to clean up. + * :return: nothing + */ +void +pkgconf_path_free(pkgconf_list_t *dirlist) +{ + pkgconf_node_t *n, *tn; + + PKGCONF_FOREACH_LIST_ENTRY_SAFE(dirlist->head, tn, n) + { + pkgconf_path_t *pnode = n->data; + + free(pnode->path); + free(pnode); + } + + pkgconf_list_zero(dirlist); +} + +static char * +normpath(const char *path) +{ + if (!path) + return NULL; + + char *copy = strdup(path); + if (NULL == copy) + return NULL; + char *ptr = copy; + + for (int ii = 0; copy[ii]; ii++) + { + *ptr++ = path[ii]; + if ('/' == path[ii]) + { + ii++; + while ('/' == path[ii]) + ii++; + ii--; + } + } + *ptr = '\0'; + + return copy; +} + +/* + * !doc + * + * .. c:function:: bool pkgconf_path_relocate(char *buf, size_t buflen) + * + * Relocates a path, possibly calling normpath() on it. + * + * :param char* buf: The path to relocate. + * :param size_t buflen: The buffer length the path is contained in. + * :return: true on success, false on error + * :rtype: bool + */ +bool +pkgconf_path_relocate(char *buf, size_t buflen) +{ + char *tmpbuf; + + if ((tmpbuf = normpath(buf)) != NULL) + { + size_t tmpbuflen = strlen(tmpbuf); + if (tmpbuflen > buflen) + { + free(tmpbuf); + return false; + } + + pkgconf_strlcpy(buf, tmpbuf, buflen); + free(tmpbuf); + } + + return true; +} + +#ifdef _WIN32 +/* + * !doc + * + * .. c:function:: void pkgconf_path_build_from_registry(HKEY hKey, pkgconf_list_t *dir_list, bool filter) + * + * Adds paths to a directory list discovered from a given registry key. + * + * :param HKEY hKey: The registry key to enumerate. + * :param pkgconf_list_t* dir_list: The directory list to append enumerated paths to. + * :param bool filter: Whether duplicate paths should be filtered. + * :return: number of path nodes added to the list + * :rtype: size_t + */ +size_t +pkgconf_path_build_from_registry(void *hKey, pkgconf_list_t *dir_list, bool filter) +{ + HKEY key; + int i = 0; + size_t added = 0; + + char buf[16384]; /* per registry limits */ + DWORD bufsize = sizeof buf; + if (RegOpenKeyEx(hKey, PKG_CONFIG_REG_KEY, + 0, KEY_READ, &key) != ERROR_SUCCESS) + return 0; + + while (RegEnumValue(key, i++, buf, &bufsize, NULL, NULL, NULL, NULL) + == ERROR_SUCCESS) + { + char pathbuf[PKGCONF_ITEM_SIZE]; + DWORD type; + DWORD pathbuflen = sizeof pathbuf; + + if (RegQueryValueEx(key, buf, NULL, &type, (LPBYTE) pathbuf, &pathbuflen) + == ERROR_SUCCESS && type == REG_SZ) + { + pkgconf_path_add(pathbuf, dir_list, filter); + added++; + } + + bufsize = sizeof buf; + } + + RegCloseKey(key); + return added; +} +#endif diff --git a/contrib/pkgconf/libpkgconf/personality.c b/contrib/pkgconf/libpkgconf/personality.c new file mode 100644 index 00000000000..6c017e91277 --- /dev/null +++ b/contrib/pkgconf/libpkgconf/personality.c @@ -0,0 +1,359 @@ +/* + * personality.c + * libpkgconf cross-compile personality database + * + * Copyright (c) 2018 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include +#include +#include + +/* + * !doc + * + * libpkgconf `personality` module + * ========================= + */ + +#ifdef _WIN32 +# define strcasecmp _stricmp +#endif + +/* + * Increment each time the default personality is inited, decrement each time + * it's deinited. Whenever it is 0, then the deinit frees the personality. In + * that case an additional call to init will create it anew. + */ +static unsigned default_personality_init = 0; + +static pkgconf_cross_personality_t default_personality = { + .name = "default", +}; + +static inline void +build_default_search_path(pkgconf_list_t* dirlist) +{ +#ifdef _WIN32 + char namebuf[MAX_PATH]; + char outbuf[MAX_PATH]; + char *p; + + int sizepath = GetModuleFileName(NULL, namebuf, sizeof namebuf); + char * winslash; + namebuf[sizepath] = '\0'; + while ((winslash = strchr (namebuf, '\\')) != NULL) + { + *winslash = '/'; + } + p = strrchr(namebuf, '/'); + if (p == NULL) + pkgconf_path_split(PKG_DEFAULT_PATH, dirlist, true); + + *p = '\0'; + pkgconf_strlcpy(outbuf, namebuf, sizeof outbuf); + pkgconf_strlcat(outbuf, "/", sizeof outbuf); + pkgconf_strlcat(outbuf, "../lib/pkgconfig", sizeof outbuf); + pkgconf_path_add(outbuf, dirlist, true); + pkgconf_strlcpy(outbuf, namebuf, sizeof outbuf); + pkgconf_strlcat(outbuf, "/", sizeof outbuf); + pkgconf_strlcat(outbuf, "../share/pkgconfig", sizeof outbuf); + pkgconf_path_add(outbuf, dirlist, true); +#elif __HAIKU__ + char **paths; + size_t count; + if (find_paths(B_FIND_PATH_DEVELOP_LIB_DIRECTORY, "pkgconfig", &paths, &count) == B_OK) { + for (size_t i = 0; i < count; i++) + pkgconf_path_add(paths[i], dirlist, true); + free(paths); + paths = NULL; + } + if (find_paths(B_FIND_PATH_DATA_DIRECTORY, "pkgconfig", &paths, &count) == B_OK) { + for (size_t i = 0; i < count; i++) + pkgconf_path_add(paths[i], dirlist, true); + free(paths); + paths = NULL; + } +#else + pkgconf_path_split(PKG_DEFAULT_PATH, dirlist, true); +#endif +} + +/* + * !doc + * + * .. c:function:: const pkgconf_cross_personality_t *pkgconf_cross_personality_default(void) + * + * Returns the default cross-compile personality. + * + * Not thread safe. + * + * :rtype: pkgconf_cross_personality_t* + * :return: the default cross-compile personality + */ +pkgconf_cross_personality_t * +pkgconf_cross_personality_default(void) +{ + if (default_personality_init) { + ++default_personality_init; + return &default_personality; + } + + build_default_search_path(&default_personality.dir_list); + + pkgconf_path_split(SYSTEM_LIBDIR, &default_personality.filter_libdirs, false); + pkgconf_path_split(SYSTEM_INCLUDEDIR, &default_personality.filter_includedirs, false); + + ++default_personality_init; + return &default_personality; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_cross_personality_deinit(pkgconf_cross_personality_t *) + * + * Destroys a cross personality object and/or decreases the reference count on the + * default cross personality object. + * + * Not thread safe. + * + * :rtype: void + */ +void +pkgconf_cross_personality_deinit(pkgconf_cross_personality_t *personality) +{ + /* allow NULL parameter for API backwards compatibility */ + if (personality == NULL) + return; + + /* XXX: this hack is rather ugly, but it works for now... */ + if (personality == &default_personality && --default_personality_init > 0) + return; + + pkgconf_path_free(&personality->dir_list); + pkgconf_path_free(&personality->filter_libdirs); + pkgconf_path_free(&personality->filter_includedirs); + + if (personality->sysroot_dir != NULL) + free(personality->sysroot_dir); + + if (personality == &default_personality) + return; + + if (personality->name != NULL) + free(personality->name); + + free(personality); +} + +#ifndef PKGCONF_LITE +static bool +valid_triplet(const char *triplet) +{ + const char *c = triplet; + + for (; *c; c++) + if (!isalnum((unsigned char)*c) && *c != '-' && *c != '_') + return false; + + return true; +} + +typedef void (*personality_keyword_func_t)(pkgconf_cross_personality_t *p, const char *keyword, const size_t lineno, const ptrdiff_t offset, char *value); +typedef struct { + const char *keyword; + const personality_keyword_func_t func; + const ptrdiff_t offset; +} personality_keyword_pair_t; + +static void +personality_bool_func(pkgconf_cross_personality_t *p, const char *keyword, const size_t lineno, const ptrdiff_t offset, char *value) +{ + (void) keyword; + (void) lineno; + + bool *dest = (bool *)((char *) p + offset); + *dest = strcasecmp(value, "true") || strcasecmp(value, "yes") || *value == '1'; +} + +static void +personality_copy_func(pkgconf_cross_personality_t *p, const char *keyword, const size_t lineno, const ptrdiff_t offset, char *value) +{ + (void) keyword; + (void) lineno; + + char **dest = (char **)((char *) p + offset); + *dest = strdup(value); +} + +static void +personality_fragment_func(pkgconf_cross_personality_t *p, const char *keyword, const size_t lineno, const ptrdiff_t offset, char *value) +{ + (void) keyword; + (void) lineno; + + pkgconf_list_t *dest = (pkgconf_list_t *)((char *) p + offset); + pkgconf_path_split(value, dest, false); +} + +/* keep in alphabetical order! */ +static const personality_keyword_pair_t personality_keyword_pairs[] = { + {"DefaultSearchPaths", personality_fragment_func, offsetof(pkgconf_cross_personality_t, dir_list)}, + {"SysrootDir", personality_copy_func, offsetof(pkgconf_cross_personality_t, sysroot_dir)}, + {"SystemIncludePaths", personality_fragment_func, offsetof(pkgconf_cross_personality_t, filter_includedirs)}, + {"SystemLibraryPaths", personality_fragment_func, offsetof(pkgconf_cross_personality_t, filter_libdirs)}, + {"Triplet", personality_copy_func, offsetof(pkgconf_cross_personality_t, name)}, + {"WantDefaultPure", personality_bool_func, offsetof(pkgconf_cross_personality_t, want_default_pure)}, + {"WantDefaultStatic", personality_bool_func, offsetof(pkgconf_cross_personality_t, want_default_static)}, +}; + +static int +personality_keyword_pair_cmp(const void *key, const void *ptr) +{ + const personality_keyword_pair_t *pair = ptr; + return strcasecmp(key, pair->keyword); +} + +static void +personality_keyword_set(pkgconf_cross_personality_t *p, const size_t lineno, const char *keyword, char *value) +{ + const personality_keyword_pair_t *pair = bsearch(keyword, + personality_keyword_pairs, PKGCONF_ARRAY_SIZE(personality_keyword_pairs), + sizeof(personality_keyword_pair_t), personality_keyword_pair_cmp); + + if (pair == NULL || pair->func == NULL) + return; + + pair->func(p, keyword, lineno, pair->offset, value); +} + +static const pkgconf_parser_operand_func_t personality_parser_ops[256] = { + [':'] = (pkgconf_parser_operand_func_t) personality_keyword_set +}; + +static void personality_warn_func(void *p, const char *fmt, ...) PRINTFLIKE(2, 3); + +static void +personality_warn_func(void *p, const char *fmt, ...) +{ + va_list va; + + (void) p; + + va_start(va, fmt); + vfprintf(stderr, fmt, va); + va_end(va); +} + +static pkgconf_cross_personality_t * +load_personality_with_path(const char *path, const char *triplet, bool datadir) +{ + char pathbuf[PKGCONF_ITEM_SIZE]; + FILE *f; + pkgconf_cross_personality_t *p; + + /* if triplet is null, assume that path is a direct path to the personality file */ + if (triplet == NULL) + pkgconf_strlcpy(pathbuf, path, sizeof pathbuf); + else if (datadir) + snprintf(pathbuf, sizeof pathbuf, "%s/pkgconfig/personality.d/%s.personality", path, triplet); + else + snprintf(pathbuf, sizeof pathbuf, "%s/%s.personality", path, triplet); + + p = calloc(1, sizeof(pkgconf_cross_personality_t)); + if (p == NULL) + return NULL; + + if (triplet != NULL) + p->name = strdup(triplet); + + f = fopen(pathbuf, "r"); + if (f == NULL) { + pkgconf_cross_personality_deinit(p); + return NULL; + } + + pkgconf_parser_parse(f, p, personality_parser_ops, personality_warn_func, pathbuf); + + return p; +} + +/* + * !doc + * + * .. c:function:: pkgconf_cross_personality_t *pkgconf_cross_personality_find(const char *triplet) + * + * Attempts to find a cross-compile personality given a triplet. + * + * :rtype: pkgconf_cross_personality_t* + * :return: the default cross-compile personality + */ +pkgconf_cross_personality_t * +pkgconf_cross_personality_find(const char *triplet) +{ + pkgconf_list_t plist = PKGCONF_LIST_INITIALIZER; + pkgconf_node_t *n; + pkgconf_cross_personality_t *out = NULL; +#if ! defined(_WIN32) && ! defined(__HAIKU__) + char pathbuf[PKGCONF_ITEM_SIZE]; + const char *envvar; +#endif + + out = load_personality_with_path(triplet, NULL, false); + if (out != NULL) + return out; + + if (!valid_triplet(triplet)) + return NULL; + +#if ! defined(_WIN32) && ! defined(__HAIKU__) + envvar = getenv("XDG_DATA_HOME"); + if (envvar != NULL) + pkgconf_path_add(envvar, &plist, true); + else { + envvar = getenv("HOME"); + if (envvar != NULL) { + pkgconf_strlcpy(pathbuf, envvar, sizeof pathbuf); + pkgconf_strlcat(pathbuf, "/.local/share", sizeof pathbuf); + pkgconf_path_add(pathbuf, &plist, true); + } + } + + pkgconf_path_build_from_environ("XDG_DATA_DIRS", "/usr/local/share" PKG_CONFIG_PATH_SEP_S "/usr/share", &plist, true); + + PKGCONF_FOREACH_LIST_ENTRY(plist.head, n) + { + pkgconf_path_t *pn = n->data; + + out = load_personality_with_path(pn->path, triplet, true); + if (out != NULL) + goto finish; + } + pkgconf_path_free(&plist); +#endif + + pkgconf_path_split(PERSONALITY_PATH, &plist, true); + + PKGCONF_FOREACH_LIST_ENTRY(plist.head, n) + { + pkgconf_path_t *pn = n->data; + + out = load_personality_with_path(pn->path, triplet, false); + if (out != NULL) + goto finish; + } + +finish: + pkgconf_path_free(&plist); + return out != NULL ? out : pkgconf_cross_personality_default(); +} +#endif diff --git a/contrib/pkgconf/libpkgconf/pkg.c b/contrib/pkgconf/libpkgconf/pkg.c new file mode 100644 index 00000000000..491c0defcd6 --- /dev/null +++ b/contrib/pkgconf/libpkgconf/pkg.c @@ -0,0 +1,1935 @@ +/* + * pkg.c + * higher-level dependency graph compilation, management and manipulation + * + * Copyright (c) 2011, 2012, 2013 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include +#include +#include + +#ifndef _WIN32 +#include // open +#include // basename/dirname +#include // lstat, S_ISLNK +#include // close, readlinkat + +#include +#endif + +/* + * !doc + * + * libpkgconf `pkg` module + * ======================= + * + * The `pkg` module provides dependency resolution services and the overall `.pc` file parsing + * routines. + */ + +#ifdef _WIN32 +# undef PKG_DEFAULT_PATH +# define PKG_DEFAULT_PATH "../lib/pkgconfig;../share/pkgconfig" +# define strncasecmp _strnicmp +# define strcasecmp _stricmp +#endif + +#define PKG_CONFIG_EXT ".pc" + +static unsigned int +pkgconf_pkg_traverse_main(pkgconf_client_t *client, + pkgconf_pkg_t *root, + pkgconf_pkg_traverse_func_t func, + void *data, + int maxdepth, + unsigned int skip_flags); + +static inline bool +str_has_suffix(const char *str, const char *suffix) +{ + size_t str_len = strlen(str); + size_t suf_len = strlen(suffix); + + if (str_len < suf_len) + return false; + + return !strncasecmp(str + str_len - suf_len, suffix, suf_len); +} + +static char * +pkg_get_parent_dir(pkgconf_pkg_t *pkg) +{ + char buf[PKGCONF_ITEM_SIZE], *pathbuf; + + pkgconf_strlcpy(buf, pkg->filename, sizeof buf); +#ifndef _WIN32 + /* + * We want to resolve symlinks, since ${pcfiledir} should point to the + * parent of the file symlinked to. + */ + struct stat path_stat; + while (!lstat(buf, &path_stat) && S_ISLNK(path_stat.st_mode)) + { + /* + * Have to split the path into the dir + file components, + * in order to extract the directory file descriptor. + * + * The nomenclature here uses the + * + * ln + * + * model. + */ + char basenamebuf[PKGCONF_ITEM_SIZE]; + pkgconf_strlcpy(basenamebuf, buf, sizeof(basenamebuf)); + const char* targetfilename = basename(basenamebuf); + + char dirnamebuf[PKGCONF_ITEM_SIZE]; + pkgconf_strlcpy(dirnamebuf, buf, sizeof(dirnamebuf)); + const char* targetdir = dirname(dirnamebuf); + + const int dirfd = open(targetdir, O_DIRECTORY); + if (dirfd == -1) + break; + + char sourcebuf[PKGCONF_ITEM_SIZE]; + ssize_t len = readlinkat(dirfd, targetfilename, sourcebuf, sizeof(sourcebuf) - 1); + close(dirfd); + + if (len == -1) + break; + sourcebuf[len] = '\0'; + + memset(buf, '\0', sizeof buf); + /* + * The logic here can be a bit tricky, so here's a table: + * + * | | result + * ----------------------------------------------------------------------- + * /bar (absolute) | foo/link (relative) | /bar (absolute) + * ../bar (relative) | foo/link (relative) | foo/../bar (relative) + * /bar (absolute) | /foo/link (absolute) | /bar (absolute) + * ../bar (relative) | /foo/link (absolute) | /foo/../bar (relative) + */ + if ((sourcebuf[0] != '/') /* absolute path in wins */ + && (strcmp(targetdir, "."))) /* do not prepend "." */ + { + pkgconf_strlcat(buf, targetdir, sizeof buf); + pkgconf_strlcat(buf, "/", sizeof buf); + } + pkgconf_strlcat(buf, sourcebuf, sizeof buf); + } +#endif + + pathbuf = strrchr(buf, PKG_DIR_SEP_S); + if (pathbuf == NULL) + pathbuf = strrchr(buf, '/'); + if (pathbuf != NULL) + pathbuf[0] = '\0'; + + return strdup(buf); +} + +typedef void (*pkgconf_pkg_parser_keyword_func_t)(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value); +typedef struct { + const char *keyword; + const pkgconf_pkg_parser_keyword_func_t func; + const ptrdiff_t offset; +} pkgconf_pkg_parser_keyword_pair_t; + +static int pkgconf_pkg_parser_keyword_pair_cmp(const void *key, const void *ptr) +{ + const pkgconf_pkg_parser_keyword_pair_t *pair = ptr; + return strcasecmp(key, pair->keyword); +} + +static void +pkgconf_pkg_parser_tuple_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value) +{ + (void) keyword; + (void) lineno; + + char **dest = (char **)((char *) pkg + offset); + *dest = pkgconf_tuple_parse(client, &pkg->vars, value, pkg->flags); +} + +static void +pkgconf_pkg_parser_version_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value) +{ + (void) keyword; + (void) lineno; + char *p, *i; + size_t len; + char **dest = (char **)((char *) pkg + offset); + + /* cut at any detected whitespace */ + p = pkgconf_tuple_parse(client, &pkg->vars, value, pkg->flags); + + len = strcspn(p, " \t"); + if (len != strlen(p)) + { + i = p + (ptrdiff_t) len; + *i = '\0'; + + pkgconf_warn(client, "%s:" SIZE_FMT_SPECIFIER ": warning: malformed version field with whitespace, trimming to [%s]\n", pkg->filename, + lineno, p); + } + + *dest = p; +} + +static void +pkgconf_pkg_parser_fragment_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value) +{ + pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset); + + /* we patch client-wide sysroot dir and then patch it back when it is overridden */ + char *sysroot_dir = client->sysroot_dir; + char *pkg_sysroot_dir = pkgconf_tuple_find(client, &pkg->vars, "pc_sysrootdir"); + if (pkg_sysroot_dir != NULL) + client->sysroot_dir = pkg_sysroot_dir; + + bool ret = pkgconf_fragment_parse(client, dest, &pkg->vars, value, pkg->flags); + client->sysroot_dir = sysroot_dir; + + if (!ret) + { + pkgconf_warn(client, "%s:" SIZE_FMT_SPECIFIER ": warning: unable to parse field '%s' into an argument vector, value [%s]\n", pkg->filename, + lineno, keyword, value); + } +} + +static void +pkgconf_pkg_parser_dependency_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value) +{ + (void) keyword; + (void) lineno; + + pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset); + pkgconf_dependency_parse(client, pkg, dest, value, 0); +} + +/* a variant of pkgconf_pkg_parser_dependency_func which colors the dependency node as an "internal" dependency. */ +static void +pkgconf_pkg_parser_internal_dependency_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value) +{ + (void) keyword; + (void) lineno; + + pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset); + pkgconf_dependency_parse(client, pkg, dest, value, PKGCONF_PKG_DEPF_INTERNAL); +} + +/* a variant of pkgconf_pkg_parser_dependency_func which colors the dependency node as a "private" dependency. */ +static void +pkgconf_pkg_parser_private_dependency_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value) +{ + (void) keyword; + (void) lineno; + + pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset); + pkgconf_dependency_parse(client, pkg, dest, value, PKGCONF_PKG_DEPF_PRIVATE); +} + +/* keep this in alphabetical order */ +static const pkgconf_pkg_parser_keyword_pair_t pkgconf_pkg_parser_keyword_funcs[] = { + {"CFLAGS", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, cflags)}, + {"CFLAGS.private", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, cflags_private)}, + {"Conflicts", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, conflicts)}, + {"Copyright", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, copyright)}, + {"Description", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, description)}, + {"LIBS", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, libs)}, + {"LIBS.private", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, libs_private)}, + {"License", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, license)}, + {"Maintainer", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, maintainer)}, + {"Name", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, realname)}, + {"Provides", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, provides)}, + {"Requires", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, required)}, + {"Requires.internal", pkgconf_pkg_parser_internal_dependency_func, offsetof(pkgconf_pkg_t, requires_private)}, + {"Requires.private", pkgconf_pkg_parser_private_dependency_func, offsetof(pkgconf_pkg_t, requires_private)}, + {"URL", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, url)}, + {"Version", pkgconf_pkg_parser_version_func, offsetof(pkgconf_pkg_t, version)}, +}; + +static void +pkgconf_pkg_parser_keyword_set(void *opaque, const size_t lineno, const char *keyword, const char *value) +{ + pkgconf_pkg_t *pkg = opaque; + + const pkgconf_pkg_parser_keyword_pair_t *pair = bsearch(keyword, + pkgconf_pkg_parser_keyword_funcs, PKGCONF_ARRAY_SIZE(pkgconf_pkg_parser_keyword_funcs), + sizeof(pkgconf_pkg_parser_keyword_pair_t), pkgconf_pkg_parser_keyword_pair_cmp); + + if (pair == NULL || pair->func == NULL) + return; + + pair->func(pkg->owner, pkg, keyword, lineno, pair->offset, value); +} + +static const char * +determine_prefix(const pkgconf_pkg_t *pkg, char *buf, size_t buflen) +{ + char *pathiter; + + pkgconf_strlcpy(buf, pkg->filename, buflen); + pkgconf_path_relocate(buf, buflen); + + pathiter = strrchr(buf, PKG_DIR_SEP_S); + if (pathiter == NULL) + pathiter = strrchr(buf, '/'); + if (pathiter != NULL) + pathiter[0] = '\0'; + + pathiter = strrchr(buf, PKG_DIR_SEP_S); + if (pathiter == NULL) + pathiter = strrchr(buf, '/'); + if (pathiter == NULL) + return NULL; + + /* parent dir is not pkgconfig, can't relocate then */ + if (strcmp(pathiter + 1, "pkgconfig")) + return NULL; + + /* okay, work backwards and do it again. */ + pathiter[0] = '\0'; + pathiter = strrchr(buf, PKG_DIR_SEP_S); + if (pathiter == NULL) + pathiter = strrchr(buf, '/'); + if (pathiter == NULL) + return NULL; + + pathiter[0] = '\0'; + + return buf; +} + +/* + * Takes a real path and converts it to a pkgconf value. This means normalizing + * directory separators and escaping things (only spaces covered atm). + * + * This is useful for things like prefix/pcfiledir which might get injected + * at runtime and are not sourced from the .pc file. + * + * "C:\foo bar\baz" -> "C:/foo\ bar/baz" + * "/foo bar/baz" -> "/foo\ bar/baz" + */ +static char * +convert_path_to_value(const char *path) +{ + char *buf = calloc(1, (strlen(path) + 1) * 2); + if (buf == NULL) + return NULL; + + char *bptr = buf; + const char *i; + + for (i = path; *i != '\0'; i++) + { + if (*i == PKG_DIR_SEP_S) + *bptr++ = '/'; + else if (*i == ' ') { + *bptr++ = '\\'; + *bptr++ = *i; + } else + *bptr++ = *i; + } + + return buf; +} + +static void +remove_additional_separators(char *buf) +{ + char *p = buf; + + while (*p) { + if (*p == '/') { + char *q; + + q = ++p; + while (*q && *q == '/') + q++; + + if (p != q) + memmove (p, q, strlen (q) + 1); + } else { + p++; + } + } +} + +static void +canonicalize_path(char *buf) +{ + remove_additional_separators(buf); +} + +static bool +is_path_prefix_equal(const char *path1, const char *path2, size_t path2_len) +{ +#ifdef _WIN32 + return !_strnicmp(path1, path2, path2_len); +#else + return !strncmp(path1, path2, path2_len); +#endif +} + +static void +pkgconf_pkg_parser_value_set(void *opaque, const size_t lineno, const char *keyword, const char *value) +{ + char canonicalized_value[PKGCONF_ITEM_SIZE]; + pkgconf_pkg_t *pkg = opaque; + + (void) lineno; + + pkgconf_strlcpy(canonicalized_value, value, sizeof canonicalized_value); + canonicalize_path(canonicalized_value); + + /* Some pc files will use absolute paths for all of their directories + * which is broken when redefining the prefix. We try to outsmart the + * file and rewrite any directory that starts with the same prefix. + */ + if (pkg->owner->flags & PKGCONF_PKG_PKGF_REDEFINE_PREFIX && pkg->orig_prefix + && is_path_prefix_equal(canonicalized_value, pkg->orig_prefix->value, strlen(pkg->orig_prefix->value))) + { + char newvalue[PKGCONF_ITEM_SIZE]; + + pkgconf_strlcpy(newvalue, pkg->prefix->value, sizeof newvalue); + pkgconf_strlcat(newvalue, canonicalized_value + strlen(pkg->orig_prefix->value), sizeof newvalue); + pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, newvalue, false, pkg->flags); + } + else if (strcmp(keyword, pkg->owner->prefix_varname) || !(pkg->owner->flags & PKGCONF_PKG_PKGF_REDEFINE_PREFIX)) + pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, value, true, pkg->flags); + else + { + char pathbuf[PKGCONF_ITEM_SIZE]; + const char *relvalue = determine_prefix(pkg, pathbuf, sizeof pathbuf); + + if (relvalue != NULL) + { + char *prefix_value = convert_path_to_value(relvalue); + pkg->orig_prefix = pkgconf_tuple_add(pkg->owner, &pkg->vars, "orig_prefix", canonicalized_value, true, pkg->flags); + pkg->prefix = pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, prefix_value, false, pkg->flags); + free(prefix_value); + } + else + pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, value, true, pkg->flags); + } +} + +typedef struct { + const char *field; + const ptrdiff_t offset; +} pkgconf_pkg_validity_check_t; + +static const pkgconf_pkg_validity_check_t pkgconf_pkg_validations[] = { + {"Name", offsetof(pkgconf_pkg_t, realname)}, + {"Description", offsetof(pkgconf_pkg_t, description)}, + {"Version", offsetof(pkgconf_pkg_t, version)}, +}; + +static const pkgconf_parser_operand_func_t pkg_parser_funcs[256] = { + [':'] = pkgconf_pkg_parser_keyword_set, + ['='] = pkgconf_pkg_parser_value_set +}; + +static void pkg_warn_func(pkgconf_pkg_t *pkg, const char *fmt, ...) PRINTFLIKE(2, 3); + +static void +pkg_warn_func(pkgconf_pkg_t *pkg, const char *fmt, ...) +{ + char buf[PKGCONF_ITEM_SIZE]; + va_list va; + + va_start(va, fmt); + vsnprintf(buf, sizeof buf, fmt, va); + va_end(va); + + pkgconf_warn(pkg->owner, "%s", buf); +} + +static bool +pkgconf_pkg_validate(const pkgconf_client_t *client, const pkgconf_pkg_t *pkg) +{ + size_t i; + bool valid = true; + + for (i = 0; i < PKGCONF_ARRAY_SIZE(pkgconf_pkg_validations); i++) + { + char **p = (char **)((char *) pkg + pkgconf_pkg_validations[i].offset); + + if (*p != NULL) + continue; + + pkgconf_warn(client, "%s: warning: file does not declare a `%s' field\n", pkg->filename, pkgconf_pkg_validations[i].field); + valid = false; + } + + return valid; +} + +static void +pkg_free_object(pkgconf_pkg_t *pkg) +{ + if (pkg->flags & PKGCONF_PKG_PROPF_PRELOADED) + pkgconf_node_delete(&pkg->preload_node, &pkg->owner->preloaded_pkgs); + + if (pkg->id != NULL) + free(pkg->id); + + if (pkg->filename != NULL) + free(pkg->filename); + + if (pkg->realname != NULL) + free(pkg->realname); + + if (pkg->version != NULL) + free(pkg->version); + + if (pkg->description != NULL) + free(pkg->description); + + if (pkg->url != NULL) + free(pkg->url); + + if (pkg->pc_filedir != NULL) + free(pkg->pc_filedir); + + if (pkg->license != NULL) + free(pkg->license); + + if (pkg->maintainer != NULL) + free(pkg->maintainer); + + if (pkg->copyright != NULL) + free(pkg->copyright); + + if (pkg->why != NULL) + free(pkg->why); + + free(pkg); +} + +static void +pkg_free_lists(pkgconf_pkg_t *pkg) +{ + pkgconf_dependency_free(&pkg->required); + pkgconf_dependency_free(&pkg->requires_private); + pkgconf_dependency_free(&pkg->conflicts); + pkgconf_dependency_free(&pkg->provides); + + pkgconf_fragment_free(&pkg->cflags); + pkgconf_fragment_free(&pkg->cflags_private); + pkgconf_fragment_free(&pkg->libs); + pkgconf_fragment_free(&pkg->libs_private); + + pkgconf_tuple_free(&pkg->vars); +} + +/* + * !doc + * + * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_new_from_path(const pkgconf_client_t *client, const char *filename, unsigned int flags) + * + * Parse a .pc file into a pkgconf_pkg_t object structure. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param char* filename: The filename of the package file (including full path). + * :param FILE* f: The file object to read from. + * :param uint flags: The flags to use when parsing. + * :returns: A ``pkgconf_pkg_t`` object which contains the package data. + * :rtype: pkgconf_pkg_t * + */ +pkgconf_pkg_t * +pkgconf_pkg_new_from_path(pkgconf_client_t *client, const char *filename, unsigned int flags) +{ + pkgconf_pkg_t *pkg; + char *idptr; + FILE *f; + + /* make sure we only load .pc files */ + if (!str_has_suffix(filename, PKG_CONFIG_EXT)) + return NULL; + + f = fopen(filename, "r"); + if (f == NULL) + return NULL; + + pkg = calloc(1, sizeof(pkgconf_pkg_t)); + if (pkg == NULL) + { + fclose(f); + return NULL; + } + + pkg->owner = client; + pkg->flags = flags; + + pkg->filename = strdup(filename); + if (pkg->filename == NULL) + { + fclose(f); + pkg_free_object(pkg); + return NULL; + } + + pkg->pc_filedir = pkg_get_parent_dir(pkg); + if (pkg->pc_filedir == NULL) + { + fclose(f); + pkg_free_object(pkg); + return NULL; + } + + char *pc_filedir_value = convert_path_to_value(pkg->pc_filedir); + pkgconf_tuple_add(client, &pkg->vars, "pcfiledir", pc_filedir_value, true, pkg->flags); + free(pc_filedir_value); + + /* If pc_filedir is outside of sysroot_dir, override sysroot_dir for this + * package. + * See https://github.com/pkgconf/pkgconf/issues/213 + */ + if (client->sysroot_dir && strncmp(pkg->pc_filedir, client->sysroot_dir, strlen(client->sysroot_dir))) + pkgconf_tuple_add(client, &pkg->vars, "pc_sysrootdir", "", false, pkg->flags); + + /* make module id */ + if ((idptr = strrchr(pkg->filename, PKG_DIR_SEP_S)) != NULL) + idptr++; + else + idptr = pkg->filename; + +#ifdef _WIN32 + /* On Windows, both \ and / are allowed in paths, so we have to chop both. + * strrchr() took us to the last \ in that case, so we just have to see if + * it is followed by a /. If so, lop it off. + */ + char *mungeptr; + if ((mungeptr = strrchr(idptr, '/')) != NULL) + idptr = ++mungeptr; +#endif + + pkg->id = strdup(idptr); + if (pkg->id == NULL) + { + fclose(f); + pkg_free_lists(pkg); + pkg_free_object(pkg); + return NULL; + } + + idptr = strrchr(pkg->id, '.'); + if (idptr) + *idptr = '\0'; + + if (pkg->flags & PKGCONF_PKG_PROPF_UNINSTALLED) + { + idptr = strrchr(pkg->id, '-'); + if (idptr) + *idptr = '\0'; + } + + pkgconf_parser_parse(f, pkg, pkg_parser_funcs, (pkgconf_parser_warn_func_t) pkg_warn_func, pkg->filename); + fclose(f); + + if (!pkgconf_pkg_validate(client, pkg)) + { + pkgconf_warn(client, "%s: warning: skipping invalid file\n", pkg->filename); + pkgconf_pkg_free(client, pkg); + return NULL; + } + + pkgconf_dependency_t *dep = pkgconf_dependency_add(client, &pkg->provides, pkg->id, pkg->version, PKGCONF_CMP_EQUAL, 0); + pkgconf_dependency_unref(dep->owner, dep); + + return pkgconf_pkg_ref(client, pkg); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_pkg_free(pkgconf_client_t *client, pkgconf_pkg_t *pkg) + * + * Releases all releases for a given ``pkgconf_pkg_t`` object. + * + * :param pkgconf_client_t* client: The client which owns the ``pkgconf_pkg_t`` object, `pkg`. + * :param pkgconf_pkg_t* pkg: The package to free. + * :return: nothing + */ +void +pkgconf_pkg_free(pkgconf_client_t *client, pkgconf_pkg_t *pkg) +{ + if (pkg == NULL) + return; + + if (pkg->flags & PKGCONF_PKG_PROPF_STATIC && !(pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL)) + return; + + pkgconf_cache_remove(client, pkg); + + pkg_free_lists(pkg); + + if (pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL) + return; + + pkg_free_object(pkg); +} + +/* + * !doc + * + * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_ref(const pkgconf_client_t *client, pkgconf_pkg_t *pkg) + * + * Adds an additional reference to the package object. + * + * :param pkgconf_client_t* client: The pkgconf client object which owns the package being referenced. + * :param pkgconf_pkg_t* pkg: The package object being referenced. + * :return: The package itself with an incremented reference count. + * :rtype: pkgconf_pkg_t * + */ +pkgconf_pkg_t * +pkgconf_pkg_ref(pkgconf_client_t *client, pkgconf_pkg_t *pkg) +{ + if (pkg->owner != NULL && pkg->owner != client) + PKGCONF_TRACE(client, "WTF: client %p refers to package %p owned by other client %p", client, pkg, pkg->owner); + + pkg->refcount++; + PKGCONF_TRACE(client, "%s refcount@%p: %d", pkg->id, pkg, pkg->refcount); + + return pkg; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_pkg_unref(pkgconf_client_t *client, pkgconf_pkg_t *pkg) + * + * Releases a reference on the package object. If the reference count is 0, then also free the package. + * + * :param pkgconf_client_t* client: The pkgconf client object which owns the package being dereferenced. + * :param pkgconf_pkg_t* pkg: The package object being dereferenced. + * :return: nothing + */ +void +pkgconf_pkg_unref(pkgconf_client_t *client, pkgconf_pkg_t *pkg) +{ + if (pkg == NULL) { + PKGCONF_TRACE(client, "WTF: client %p unrefs a NULL package", client); + return; + } + + if (pkg->owner != NULL && pkg->owner != client) + PKGCONF_TRACE(client, "WTF: client %p unrefs package %p owned by other client %p", client, pkg, pkg->owner); + + pkg->refcount--; + PKGCONF_TRACE(pkg->owner, "%s refcount@%p: %d", pkg->id, pkg, pkg->refcount); + + if (pkg->refcount <= 0) + pkgconf_pkg_free(pkg->owner, pkg); +} + +static inline pkgconf_pkg_t * +pkgconf_pkg_try_specific_path(pkgconf_client_t *client, const char *path, const char *name) +{ + pkgconf_pkg_t *pkg = NULL; + char locbuf[PKGCONF_ITEM_SIZE]; + char uninst_locbuf[PKGCONF_ITEM_SIZE]; + + PKGCONF_TRACE(client, "trying path: %s for %s", path, name); + + snprintf(locbuf, sizeof locbuf, "%s%c%s" PKG_CONFIG_EXT, path, PKG_DIR_SEP_S, name); + snprintf(uninst_locbuf, sizeof uninst_locbuf, "%s%c%s-uninstalled" PKG_CONFIG_EXT, path, PKG_DIR_SEP_S, name); + + if (!(client->flags & PKGCONF_PKG_PKGF_NO_UNINSTALLED)) + pkg = pkgconf_pkg_new_from_path(client, uninst_locbuf, PKGCONF_PKG_PROPF_UNINSTALLED); + + if (pkg == NULL) + pkg = pkgconf_pkg_new_from_path(client, locbuf, 0); + + if (pkg != NULL) + PKGCONF_TRACE(client, "found%s: %s", pkg->flags & PKGCONF_PKG_PROPF_UNINSTALLED ? " (uninstalled)" : "", uninst_locbuf); + + return pkg; +} + +static pkgconf_pkg_t * +pkgconf_pkg_scan_dir(pkgconf_client_t *client, const char *path, void *data, pkgconf_pkg_iteration_func_t func) +{ + DIR *dir; + struct dirent *dirent; + pkgconf_pkg_t *outpkg = NULL; + + dir = opendir(path); + if (dir == NULL) + return NULL; + + PKGCONF_TRACE(client, "scanning dir [%s]", path); + + for (dirent = readdir(dir); dirent != NULL; dirent = readdir(dir)) + { + char filebuf[PKGCONF_ITEM_SIZE]; + pkgconf_pkg_t *pkg; + + pkgconf_strlcpy(filebuf, path, sizeof filebuf); + pkgconf_strlcat(filebuf, "/", sizeof filebuf); + pkgconf_strlcat(filebuf, dirent->d_name, sizeof filebuf); + + if (!str_has_suffix(filebuf, PKG_CONFIG_EXT)) + continue; + + PKGCONF_TRACE(client, "trying file [%s]", filebuf); + + pkg = pkgconf_pkg_new_from_path(client, filebuf, 0); + if (pkg != NULL) + { + if (func(pkg, data)) + { + outpkg = pkg; + goto out; + } + + pkgconf_pkg_unref(client, pkg); + } + } + +out: + closedir(dir); + return outpkg; +} + +/* + * !doc + * + * .. c:function:: pkgconf_pkg_t *pkgconf_scan_all(pkgconf_client_t *client, void *data, pkgconf_pkg_iteration_func_t func) + * + * Iterates over all packages found in the `package directory list`, running ``func`` on them. If ``func`` returns true, + * then stop iteration and return the last iterated package. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param void* data: An opaque pointer to data to provide the iteration function with. + * :param pkgconf_pkg_iteration_func_t func: A function which is called for each package to determine if the package matches, + * always return ``false`` to iterate over all packages. + * :return: A package object reference if one is found by the scan function, else ``NULL``. + * :rtype: pkgconf_pkg_t * + */ +pkgconf_pkg_t * +pkgconf_scan_all(pkgconf_client_t *client, void *data, pkgconf_pkg_iteration_func_t func) +{ + pkgconf_node_t *n; + pkgconf_pkg_t *pkg; + + PKGCONF_TRACE(client, "scanning preloaded list"); + PKGCONF_FOREACH_LIST_ENTRY(client->preloaded_pkgs.head, n) + { + pkg = n->data; + + /* add an additional reference to ensure preloaded packages have the same + * object ownership semantics as non-preloaded packages + */ + pkgconf_pkg_ref(client, pkg); + + if (func(pkg, data)) + return pkg; + + pkgconf_pkg_unref(client, pkg); + } + + PKGCONF_FOREACH_LIST_ENTRY(client->dir_list.head, n) + { + pkgconf_path_t *pnode = n->data; + + PKGCONF_TRACE(client, "scanning directory: %s", pnode->path); + + if ((pkg = pkgconf_pkg_scan_dir(client, pnode->path, data, func)) != NULL) + return pkg; + } + + return NULL; +} + +static pkgconf_pkg_t * +search_preload_list(pkgconf_client_t *client, const char *name) +{ + pkgconf_node_t *n; + + PKGCONF_FOREACH_LIST_ENTRY(client->preloaded_pkgs.head, n) + { + pkgconf_pkg_t *pkg = n->data; + + if (!strcmp(pkg->id, name)) + { + pkgconf_pkg_ref(client, pkg); + return pkg; + } + } + + return NULL; +} + +/* + * !doc + * + * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_find(pkgconf_client_t *client, const char *name) + * + * Search for a package. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param char* name: The name of the package `atom` to use for searching. + * :return: A package object reference if the package was found, else ``NULL``. + * :rtype: pkgconf_pkg_t * + */ +pkgconf_pkg_t * +pkgconf_pkg_find(pkgconf_client_t *client, const char *name) +{ + pkgconf_pkg_t *pkg = NULL; + pkgconf_node_t *n; + + PKGCONF_TRACE(client, "looking for: %s", name); + + /* name might actually be a filename. */ + if (str_has_suffix(name, PKG_CONFIG_EXT)) + { + if (client->unveil_handler != NULL) + client->unveil_handler(client, name, "r"); + + pkg = pkgconf_pkg_new_from_path(client, name, 0); + if (pkg != NULL) + { + PKGCONF_TRACE(client, "%s is a file", name); + + if (client->unveil_handler != NULL) + client->unveil_handler(client, pkg->pc_filedir, "r"); + + pkgconf_path_add(pkg->pc_filedir, &client->dir_list, true); + goto out; + } + } + + /* check builtins */ + if ((pkg = pkgconf_builtin_pkg_get(name)) != NULL) + { + PKGCONF_TRACE(client, "%s is a builtin", name); + return pkg; + } + + /* check cache */ + if (!(client->flags & PKGCONF_PKG_PKGF_NO_CACHE)) + { + if ((pkg = pkgconf_cache_lookup(client, name)) != NULL) + { + PKGCONF_TRACE(client, "%s is cached", name); + return pkg; + } + } + + /* check preload list */ + if ((pkg = search_preload_list(client, name)) != NULL) + { + PKGCONF_TRACE(client, "%s is preloaded", name); + return pkg; + } + + PKGCONF_FOREACH_LIST_ENTRY(client->dir_list.head, n) + { + pkgconf_path_t *pnode = n->data; + + pkg = pkgconf_pkg_try_specific_path(client, pnode->path, name); + if (pkg != NULL) + goto out; + } + +out: + pkgconf_cache_add(client, pkg); + + return pkg; +} + +/* + * !doc + * + * .. c:function:: int pkgconf_compare_version(const char *a, const char *b) + * + * Compare versions using RPM version comparison rules as described in the LSB. + * + * :param char* a: The first version to compare in the pair. + * :param char* b: The second version to compare in the pair. + * :return: -1 if the first version is less than, 0 if both versions are equal, 1 if the second version is less than. + * :rtype: int + */ +int +pkgconf_compare_version(const char *a, const char *b) +{ + char oldch1, oldch2; + char buf1[PKGCONF_ITEM_SIZE], buf2[PKGCONF_ITEM_SIZE]; + char *str1, *str2; + char *one, *two; + int ret; + bool isnum; + + /* optimization: if version matches then it's the same version. */ + if (a == NULL) + return -1; + + if (b == NULL) + return 1; + + if (!strcasecmp(a, b)) + return 0; + + pkgconf_strlcpy(buf1, a, sizeof buf1); + pkgconf_strlcpy(buf2, b, sizeof buf2); + + one = buf1; + two = buf2; + + while (*one || *two) + { + while (*one && !isalnum((unsigned char)*one) && *one != '~') + one++; + while (*two && !isalnum((unsigned char)*two) && *two != '~') + two++; + + if (*one == '~' || *two == '~') + { + if (*one != '~') + return 1; + if (*two != '~') + return -1; + + one++; + two++; + continue; + } + + if (!(*one && *two)) + break; + + str1 = one; + str2 = two; + + if (isdigit((unsigned char)*str1)) + { + while (*str1 && isdigit((unsigned char)*str1)) + str1++; + + while (*str2 && isdigit((unsigned char)*str2)) + str2++; + + isnum = true; + } + else + { + while (*str1 && isalpha((unsigned char)*str1)) + str1++; + + while (*str2 && isalpha((unsigned char)*str2)) + str2++; + + isnum = false; + } + + oldch1 = *str1; + oldch2 = *str2; + + *str1 = '\0'; + *str2 = '\0'; + + if (one == str1) + return -1; + + if (two == str2) + return (isnum ? 1 : -1); + + if (isnum) + { + int onelen, twolen; + + while (*one == '0') + one++; + + while (*two == '0') + two++; + + onelen = strlen(one); + twolen = strlen(two); + + if (onelen > twolen) + return 1; + else if (twolen > onelen) + return -1; + } + + ret = strcmp(one, two); + if (ret != 0) + return ret < 0 ? -1 : 1; + + *str1 = oldch1; + *str2 = oldch2; + + one = str1; + two = str2; + } + + if ((!*one) && (!*two)) + return 0; + + if (!*one) + return -1; + + return 1; +} + +static pkgconf_pkg_t pkg_config_virtual = { + .id = "pkg-config", + .realname = "pkg-config", + .description = "virtual package defining pkg-config API version supported", + .url = PACKAGE_BUGREPORT, + .version = PACKAGE_VERSION, + .flags = PKGCONF_PKG_PROPF_STATIC, + .vars = { + .head = &(pkgconf_node_t){ + .next = &(pkgconf_node_t){ + .next = &(pkgconf_node_t){ + .data = &(pkgconf_tuple_t){ + .key = "pc_system_libdirs", + .value = SYSTEM_LIBDIR, + } + }, + .data = &(pkgconf_tuple_t){ + .key = "pc_system_includedirs", + .value = SYSTEM_INCLUDEDIR, + } + }, + .data = &(pkgconf_tuple_t){ + .key = "pc_path", + .value = PKG_DEFAULT_PATH, + }, + }, + .tail = NULL, + } +}; + +static pkgconf_pkg_t pkgconf_virtual = { + .id = "pkgconf", + .realname = "pkgconf", + .description = "virtual package defining pkgconf API version supported", + .url = PACKAGE_BUGREPORT, + .version = PACKAGE_VERSION, + .license = "ISC", + .flags = PKGCONF_PKG_PROPF_STATIC, + .vars = { + .head = &(pkgconf_node_t){ + .next = &(pkgconf_node_t){ + .next = &(pkgconf_node_t){ + .data = &(pkgconf_tuple_t){ + .key = "pc_system_libdirs", + .value = SYSTEM_LIBDIR, + } + }, + .data = &(pkgconf_tuple_t){ + .key = "pc_system_includedirs", + .value = SYSTEM_INCLUDEDIR, + } + }, + .data = &(pkgconf_tuple_t){ + .key = "pc_path", + .value = PKG_DEFAULT_PATH, + }, + }, + .tail = NULL, + }, +}; + +typedef struct { + const char *name; + pkgconf_pkg_t *pkg; +} pkgconf_builtin_pkg_pair_t; + +/* keep these in alphabetical order */ +static const pkgconf_builtin_pkg_pair_t pkgconf_builtin_pkg_pair_set[] = { + {"pkg-config", &pkg_config_virtual}, + {"pkgconf", &pkgconf_virtual}, +}; + +static int pkgconf_builtin_pkg_pair_cmp(const void *key, const void *ptr) +{ + const pkgconf_builtin_pkg_pair_t *pair = ptr; + return strcasecmp(key, pair->name); +} + +/* + * !doc + * + * .. c:function:: pkgconf_pkg_t *pkgconf_builtin_pkg_get(const char *name) + * + * Looks up a built-in package. The package should not be freed or dereferenced. + * + * :param char* name: An atom corresponding to a built-in package to search for. + * :return: the built-in package if present, else ``NULL``. + * :rtype: pkgconf_pkg_t * + */ +pkgconf_pkg_t * +pkgconf_builtin_pkg_get(const char *name) +{ + const pkgconf_builtin_pkg_pair_t *pair = bsearch(name, pkgconf_builtin_pkg_pair_set, + PKGCONF_ARRAY_SIZE(pkgconf_builtin_pkg_pair_set), sizeof(pkgconf_builtin_pkg_pair_t), + pkgconf_builtin_pkg_pair_cmp); + + return (pair != NULL) ? pair->pkg : NULL; +} + +typedef bool (*pkgconf_vercmp_res_func_t)(const char *a, const char *b); + +typedef struct { + const char *name; + pkgconf_pkg_comparator_t compare; +} pkgconf_pkg_comparator_pair_t; + +static const pkgconf_pkg_comparator_pair_t pkgconf_pkg_comparator_names[] = { + {"!=", PKGCONF_CMP_NOT_EQUAL}, + {"(any)", PKGCONF_CMP_ANY}, + {"<", PKGCONF_CMP_LESS_THAN}, + {"<=", PKGCONF_CMP_LESS_THAN_EQUAL}, + {"=", PKGCONF_CMP_EQUAL}, + {">", PKGCONF_CMP_GREATER_THAN}, + {">=", PKGCONF_CMP_GREATER_THAN_EQUAL}, +}; + +static int pkgconf_pkg_comparator_pair_namecmp(const void *key, const void *ptr) +{ + const pkgconf_pkg_comparator_pair_t *pair = ptr; + return strcmp(key, pair->name); +} + +static bool pkgconf_pkg_comparator_lt(const char *a, const char *b) +{ + return (pkgconf_compare_version(a, b) < 0); +} + +static bool pkgconf_pkg_comparator_gt(const char *a, const char *b) +{ + return (pkgconf_compare_version(a, b) > 0); +} + +static bool pkgconf_pkg_comparator_lte(const char *a, const char *b) +{ + return (pkgconf_compare_version(a, b) <= 0); +} + +static bool pkgconf_pkg_comparator_gte(const char *a, const char *b) +{ + return (pkgconf_compare_version(a, b) >= 0); +} + +static bool pkgconf_pkg_comparator_eq(const char *a, const char *b) +{ + return (pkgconf_compare_version(a, b) == 0); +} + +static bool pkgconf_pkg_comparator_ne(const char *a, const char *b) +{ + return (pkgconf_compare_version(a, b) != 0); +} + +static bool pkgconf_pkg_comparator_any(const char *a, const char *b) +{ + (void) a; + (void) b; + + return true; +} + +static bool pkgconf_pkg_comparator_none(const char *a, const char *b) +{ + (void) a; + (void) b; + + return false; +} + +static const pkgconf_vercmp_res_func_t pkgconf_pkg_comparator_impls[] = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_any, + [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_gte, + [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_eq, + [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_ne, +}; + +/* + * !doc + * + * .. c:function:: const char *pkgconf_pkg_get_comparator(const pkgconf_dependency_t *pkgdep) + * + * Returns the comparator used in a depgraph dependency node as a string. + * + * :param pkgconf_dependency_t* pkgdep: The depgraph dependency node to return the comparator for. + * :return: A string matching the comparator or ``"???"``. + * :rtype: char * + */ +const char * +pkgconf_pkg_get_comparator(const pkgconf_dependency_t *pkgdep) +{ + if (pkgdep->compare >= PKGCONF_ARRAY_SIZE(pkgconf_pkg_comparator_names)) + return "???"; + + return pkgconf_pkg_comparator_names[pkgdep->compare].name; +} + +/* + * !doc + * + * .. c:function:: pkgconf_pkg_comparator_t pkgconf_pkg_comparator_lookup_by_name(const char *name) + * + * Look up the appropriate comparator bytecode in the comparator set (defined + * in ``pkg.c``, see ``pkgconf_pkg_comparator_names`` and ``pkgconf_pkg_comparator_impls``). + * + * :param char* name: The comparator to look up by `name`. + * :return: The comparator bytecode if found, else ``PKGCONF_CMP_ANY``. + * :rtype: pkgconf_pkg_comparator_t + */ +pkgconf_pkg_comparator_t +pkgconf_pkg_comparator_lookup_by_name(const char *name) +{ + const pkgconf_pkg_comparator_pair_t *p = bsearch(name, pkgconf_pkg_comparator_names, + PKGCONF_ARRAY_SIZE(pkgconf_pkg_comparator_names), sizeof(pkgconf_pkg_comparator_pair_t), + pkgconf_pkg_comparator_pair_namecmp); + + return (p != NULL) ? p->compare : PKGCONF_CMP_ANY; +} + +typedef struct { + pkgconf_dependency_t *pkgdep; +} pkgconf_pkg_scan_providers_ctx_t; + +typedef struct { + const pkgconf_vercmp_res_func_t rulecmp[PKGCONF_CMP_COUNT]; + const pkgconf_vercmp_res_func_t depcmp[PKGCONF_CMP_COUNT]; +} pkgconf_pkg_provides_vermatch_rule_t; + +static const pkgconf_pkg_provides_vermatch_rule_t pkgconf_pkg_provides_vermatch_rules[] = { + [PKGCONF_CMP_ANY] = { + .rulecmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + }, + .depcmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + }, + }, + [PKGCONF_CMP_LESS_THAN] = { + .rulecmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_gte, + }, + .depcmp = { + [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_gte, + }, + }, + [PKGCONF_CMP_GREATER_THAN] = { + .rulecmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_gte, + }, + .depcmp = { + [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_lte, + }, + }, + [PKGCONF_CMP_LESS_THAN_EQUAL] = { + .rulecmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_gte, + }, + .depcmp = { + [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_gt, + }, + }, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = { + .rulecmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_gte, + }, + .depcmp = { + [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_gte, + [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_gte, + [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_gte, + [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_lt, + }, + }, + [PKGCONF_CMP_EQUAL] = { + .rulecmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_gte, + [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_eq, + [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_ne + }, + .depcmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + }, + }, + [PKGCONF_CMP_NOT_EQUAL] = { + .rulecmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_gte, + [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_ne, + [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_eq + }, + .depcmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + }, + }, +}; + +/* + * pkgconf_pkg_scan_provides_vercmp(pkgdep, provider) + * + * compare a provides node against the requested dependency node. + * + * XXX: maybe handle PKGCONF_CMP_ANY in a versioned comparison + */ +static bool +pkgconf_pkg_scan_provides_vercmp(const pkgconf_dependency_t *pkgdep, const pkgconf_dependency_t *provider) +{ + const pkgconf_pkg_provides_vermatch_rule_t *rule = &pkgconf_pkg_provides_vermatch_rules[pkgdep->compare]; + + if (rule->depcmp[provider->compare] != NULL && + !rule->depcmp[provider->compare](provider->version, pkgdep->version)) + return false; + + if (rule->rulecmp[provider->compare] != NULL && + !rule->rulecmp[provider->compare](pkgdep->version, provider->version)) + return false; + + return true; +} + +/* + * pkgconf_pkg_scan_provides_entry(pkg, ctx) + * + * attempt to match a single package's Provides rules against the requested dependency node. + */ +static bool +pkgconf_pkg_scan_provides_entry(const pkgconf_pkg_t *pkg, const pkgconf_pkg_scan_providers_ctx_t *ctx) +{ + const pkgconf_dependency_t *pkgdep = ctx->pkgdep; + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(pkg->provides.head, node) + { + const pkgconf_dependency_t *provider = node->data; + if (!strcmp(provider->package, pkgdep->package)) + return pkgconf_pkg_scan_provides_vercmp(pkgdep, provider); + } + + return false; +} + +/* + * pkgconf_pkg_scan_providers(client, pkgdep, eflags) + * + * scan all available packages to see if a Provides rule matches the pkgdep. + */ +static pkgconf_pkg_t * +pkgconf_pkg_scan_providers(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags) +{ + pkgconf_pkg_t *pkg; + pkgconf_pkg_scan_providers_ctx_t ctx = { + .pkgdep = pkgdep, + }; + + pkg = pkgconf_scan_all(client, &ctx, (pkgconf_pkg_iteration_func_t) pkgconf_pkg_scan_provides_entry); + if (pkg != NULL) + { + pkgdep->match = pkgconf_pkg_ref(client, pkg); + return pkg; + } + + if (eflags != NULL) + *eflags |= PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND; + + return NULL; +} + +/* + * !doc + * + * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_verify_dependency(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags) + * + * Verify a pkgconf_dependency_t node in the depgraph. If the dependency is solvable, + * return the appropriate ``pkgconf_pkg_t`` object, else ``NULL``. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param pkgconf_dependency_t* pkgdep: The dependency graph node to solve. + * :param uint* eflags: An optional pointer that, if set, will be populated with an error code from the resolver. + * :return: On success, the appropriate ``pkgconf_pkg_t`` object to solve the dependency, else ``NULL``. + * :rtype: pkgconf_pkg_t * + */ +pkgconf_pkg_t * +pkgconf_pkg_verify_dependency(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags) +{ + pkgconf_pkg_t *pkg = NULL; + + if (eflags != NULL) + *eflags = PKGCONF_PKG_ERRF_OK; + + PKGCONF_TRACE(client, "trying to verify dependency: %s", pkgdep->package); + + if (pkgdep->match != NULL) + { + PKGCONF_TRACE(client, "cached dependency: %s -> %s@%p", pkgdep->package, pkgdep->match->id, pkgdep->match); + return pkgconf_pkg_ref(client, pkgdep->match); + } + + pkg = pkgconf_pkg_find(client, pkgdep->package); + if (pkg == NULL) + { + if (client->flags & PKGCONF_PKG_PKGF_SKIP_PROVIDES) + { + if (eflags != NULL) + *eflags |= PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND; + + return NULL; + } + + pkg = pkgconf_pkg_scan_providers(client, pkgdep, eflags); + } + else + { + if (pkg->id == NULL) + pkg->id = strdup(pkgdep->package); + + if (pkgconf_pkg_comparator_impls[pkgdep->compare](pkg->version, pkgdep->version) != true) + { + if (eflags != NULL) + *eflags |= PKGCONF_PKG_ERRF_PACKAGE_VER_MISMATCH; + } + else + pkgdep->match = pkgconf_pkg_ref(client, pkg); + } + + if (pkg != NULL && pkg->why == NULL) + pkg->why = strdup(pkgdep->package); + + return pkg; +} + +/* + * !doc + * + * .. c:function:: unsigned int pkgconf_pkg_verify_graph(pkgconf_client_t *client, pkgconf_pkg_t *root, int depth) + * + * Verify the graph dependency nodes are satisfiable by walking the tree using + * ``pkgconf_pkg_traverse()``. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param pkgconf_pkg_t* root: The root entry in the package dependency graph which should contain the top-level dependencies to resolve. + * :param int depth: The maximum allowed depth for dependency resolution. + * :return: On success, ``PKGCONF_PKG_ERRF_OK`` (0), else an error code. + * :rtype: unsigned int + */ +unsigned int +pkgconf_pkg_verify_graph(pkgconf_client_t *client, pkgconf_pkg_t *root, int depth) +{ + return pkgconf_pkg_traverse(client, root, NULL, NULL, depth, 0); +} + +static unsigned int +pkgconf_pkg_report_graph_error(pkgconf_client_t *client, pkgconf_pkg_t *parent, pkgconf_pkg_t *pkg, pkgconf_dependency_t *node, unsigned int eflags) +{ + if (eflags & PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND) + { + if (!(client->flags & PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS) & !client->already_sent_notice) + { + pkgconf_error(client, "Package %s was not found in the pkg-config search path.\n", node->package); + pkgconf_error(client, "Perhaps you should add the directory containing `%s.pc'\n", node->package); + pkgconf_error(client, "to the PKG_CONFIG_PATH environment variable\n"); + client->already_sent_notice = true; + } + + if (parent->flags & PKGCONF_PKG_PROPF_VIRTUAL) + pkgconf_error(client, "Package '%s' not found\n", node->package); + else + pkgconf_error(client, "Package '%s', required by '%s', not found\n", node->package, parent->id); + + pkgconf_audit_log(client, "%s NOT-FOUND\n", node->package); + } + else if (eflags & PKGCONF_PKG_ERRF_PACKAGE_VER_MISMATCH) + { + pkgconf_error(client, "Package dependency requirement '%s %s %s' could not be satisfied.\n", + node->package, pkgconf_pkg_get_comparator(node), node->version); + + if (pkg != NULL) + pkgconf_error(client, "Package '%s' has version '%s', required version is '%s %s'\n", + node->package, pkg->version, pkgconf_pkg_get_comparator(node), node->version); + } + + if (pkg != NULL) + pkgconf_pkg_unref(client, pkg); + + return eflags; +} + +static inline unsigned int +pkgconf_pkg_walk_list(pkgconf_client_t *client, + pkgconf_pkg_t *parent, + pkgconf_list_t *deplist, + pkgconf_pkg_traverse_func_t func, + void *data, + int depth, + unsigned int skip_flags) +{ + unsigned int eflags = PKGCONF_PKG_ERRF_OK; + pkgconf_node_t *node, *next; + + parent->flags |= PKGCONF_PKG_PROPF_ANCESTOR; + + PKGCONF_FOREACH_LIST_ENTRY_SAFE(deplist->head, next, node) + { + unsigned int eflags_local = PKGCONF_PKG_ERRF_OK; + pkgconf_dependency_t *depnode = node->data; + pkgconf_pkg_t *pkgdep; + + if (*depnode->package == '\0') + continue; + + pkgdep = pkgconf_pkg_verify_dependency(client, depnode, &eflags_local); + + eflags |= eflags_local; + if (eflags_local != PKGCONF_PKG_ERRF_OK && !(client->flags & PKGCONF_PKG_PKGF_SKIP_ERRORS)) + { + pkgconf_pkg_report_graph_error(client, parent, pkgdep, depnode, eflags_local); + continue; + } + if (pkgdep == NULL) + continue; + + if((pkgdep->flags & PKGCONF_PKG_PROPF_ANCESTOR) != 0) + { + /* In this case we have a circular reference. + * We break that by deleteing the circular node from the + * the list, so that we dont create a situation where + * memory is leaked due to circular ownership. + * i.e: A owns B owns A + * + * TODO(ariadne): Breaking circular references between Requires and Requires.private + * lists causes problems. Find a way to refactor the Requires.private list out. + */ + if (!(depnode->flags & PKGCONF_PKG_DEPF_PRIVATE) && + !(parent->flags & PKGCONF_PKG_PROPF_VIRTUAL)) + { + pkgconf_warn(client, "%s: breaking circular reference (%s -> %s -> %s)\n", + parent->id, parent->id, pkgdep->id, parent->id); + + pkgconf_node_delete(node, deplist); + pkgconf_dependency_unref(client, depnode); + } + + goto next; + } + + if (skip_flags && (depnode->flags & skip_flags) == skip_flags) + goto next; + + pkgconf_audit_log_dependency(client, pkgdep, depnode); + + eflags |= pkgconf_pkg_traverse_main(client, pkgdep, func, data, depth - 1, skip_flags); +next: + pkgconf_pkg_unref(client, pkgdep); + } + + parent->flags &= ~PKGCONF_PKG_PROPF_ANCESTOR; + + return eflags; +} + +static inline unsigned int +pkgconf_pkg_walk_conflicts_list(pkgconf_client_t *client, + pkgconf_pkg_t *root, pkgconf_list_t *deplist) +{ + unsigned int eflags; + pkgconf_node_t *node, *childnode; + + PKGCONF_FOREACH_LIST_ENTRY(deplist->head, node) + { + pkgconf_dependency_t *parentnode = node->data; + + if (*parentnode->package == '\0') + continue; + + PKGCONF_FOREACH_LIST_ENTRY(root->required.head, childnode) + { + pkgconf_pkg_t *pkgdep; + pkgconf_dependency_t *depnode = childnode->data; + + if (*depnode->package == '\0' || strcmp(depnode->package, parentnode->package)) + continue; + + pkgdep = pkgconf_pkg_verify_dependency(client, parentnode, &eflags); + if (eflags == PKGCONF_PKG_ERRF_OK) + { + pkgconf_error(client, "Version '%s' of '%s' conflicts with '%s' due to satisfying conflict rule '%s %s%s%s'.\n", + pkgdep->version, pkgdep->realname, root->realname, parentnode->package, pkgconf_pkg_get_comparator(parentnode), + parentnode->version != NULL ? " " : "", parentnode->version != NULL ? parentnode->version : ""); + + if (!(client->flags & PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS)) + { + pkgconf_error(client, "It may be possible to ignore this conflict and continue, try the\n"); + pkgconf_error(client, "PKG_CONFIG_IGNORE_CONFLICTS environment variable.\n"); + } + + pkgconf_pkg_unref(client, pkgdep); + + return PKGCONF_PKG_ERRF_PACKAGE_CONFLICT; + } + + pkgconf_pkg_unref(client, pkgdep); + } + } + + return PKGCONF_PKG_ERRF_OK; +} + +/* + * !doc + * + * .. c:function:: unsigned int pkgconf_pkg_traverse(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_pkg_traverse_func_t func, void *data, int maxdepth, unsigned int skip_flags) + * + * Walk and resolve the dependency graph up to `maxdepth` levels. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param pkgconf_pkg_t* root: The root of the dependency graph. + * :param pkgconf_pkg_traverse_func_t func: A traversal function to call for each resolved node in the dependency graph. + * :param void* data: An opaque pointer to data to be passed to the traversal function. + * :param int maxdepth: The maximum depth to walk the dependency graph for. -1 means infinite recursion. + * :param uint skip_flags: Skip over dependency nodes containing the specified flags. A setting of 0 skips no dependency nodes. + * :return: ``PKGCONF_PKG_ERRF_OK`` on success, else an error code. + * :rtype: unsigned int + */ +static unsigned int +pkgconf_pkg_traverse_main(pkgconf_client_t *client, + pkgconf_pkg_t *root, + pkgconf_pkg_traverse_func_t func, + void *data, + int maxdepth, + unsigned int skip_flags) +{ + unsigned int eflags = PKGCONF_PKG_ERRF_OK; + + if (maxdepth == 0) + return eflags; + + /* Short-circuit if we have already visited this node. + */ + if (root->serial == client->serial) + return eflags; + + root->serial = client->serial; + + if (root->identifier == 0) + root->identifier = ++client->identifier; + + PKGCONF_TRACE(client, "%s: level %d, serial %"PRIu64, root->id, maxdepth, client->serial); + + if ((root->flags & PKGCONF_PKG_PROPF_VIRTUAL) != PKGCONF_PKG_PROPF_VIRTUAL || (client->flags & PKGCONF_PKG_PKGF_SKIP_ROOT_VIRTUAL) != PKGCONF_PKG_PKGF_SKIP_ROOT_VIRTUAL) + { + if (func != NULL) + func(client, root, data); + } + + if (!(client->flags & PKGCONF_PKG_PKGF_SKIP_CONFLICTS) && root->conflicts.head != NULL) + { + PKGCONF_TRACE(client, "%s: walking 'Conflicts' list", root->id); + + eflags = pkgconf_pkg_walk_conflicts_list(client, root, &root->conflicts); + if (eflags != PKGCONF_PKG_ERRF_OK) + return eflags; + } + + PKGCONF_TRACE(client, "%s: walking 'Requires' list", root->id); + eflags = pkgconf_pkg_walk_list(client, root, &root->required, func, data, maxdepth, skip_flags); + if (eflags != PKGCONF_PKG_ERRF_OK) + return eflags; + + PKGCONF_TRACE(client, "%s: walking 'Requires.private' list", root->id); + + /* XXX: ugly */ + client->flags |= PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE; + eflags = pkgconf_pkg_walk_list(client, root, &root->requires_private, func, data, maxdepth, skip_flags); + client->flags &= ~PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE; + + if (eflags != PKGCONF_PKG_ERRF_OK) + return eflags; + + return eflags; +} + +unsigned int +pkgconf_pkg_traverse(pkgconf_client_t *client, + pkgconf_pkg_t *root, + pkgconf_pkg_traverse_func_t func, + void *data, + int maxdepth, + unsigned int skip_flags) +{ + if (root->flags & PKGCONF_PKG_PROPF_VIRTUAL) + client->serial++; + + if ((client->flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE) == 0) + skip_flags |= PKGCONF_PKG_DEPF_PRIVATE; + + return pkgconf_pkg_traverse_main(client, root, func, data, maxdepth, skip_flags); +} + +static void +pkgconf_pkg_cflags_collect(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data) +{ + pkgconf_list_t *list = data; + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(pkg->cflags.head, node) + { + pkgconf_fragment_t *frag = node->data; + pkgconf_fragment_copy(client, list, frag, false); + } +} + +static void +pkgconf_pkg_cflags_private_collect(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data) +{ + pkgconf_list_t *list = data; + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(pkg->cflags_private.head, node) + { + pkgconf_fragment_t *frag = node->data; + pkgconf_fragment_copy(client, list, frag, true); + } +} + +/* + * !doc + * + * .. c:function:: int pkgconf_pkg_cflags(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth) + * + * Walks a dependency graph and extracts relevant ``CFLAGS`` fragments. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param pkgconf_pkg_t* root: The root of the dependency graph. + * :param pkgconf_list_t* list: The fragment list to add the extracted ``CFLAGS`` fragments to. + * :param int maxdepth: The maximum allowed depth for dependency resolution. -1 means infinite recursion. + * :return: ``PKGCONF_PKG_ERRF_OK`` if successful, otherwise an error code. + * :rtype: unsigned int + */ +unsigned int +pkgconf_pkg_cflags(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth) +{ + unsigned int eflag; + unsigned int skip_flags = (client->flags & PKGCONF_PKG_PKGF_DONT_FILTER_INTERNAL_CFLAGS) == 0 ? PKGCONF_PKG_DEPF_INTERNAL : 0; + pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER; + + eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_cflags_collect, &frags, maxdepth, skip_flags); + + if (eflag == PKGCONF_PKG_ERRF_OK && client->flags & PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS) + eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_cflags_private_collect, &frags, maxdepth, skip_flags); + + if (eflag != PKGCONF_PKG_ERRF_OK) + { + pkgconf_fragment_free(&frags); + return eflag; + } + + pkgconf_fragment_copy_list(client, list, &frags); + pkgconf_fragment_free(&frags); + + return eflag; +} + +static void +pkgconf_pkg_libs_collect(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data) +{ + pkgconf_list_t *list = data; + pkgconf_node_t *node; + + if (!(client->flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE) && pkg->flags & PKGCONF_PKG_PROPF_VISITED_PRIVATE) + return; + + PKGCONF_FOREACH_LIST_ENTRY(pkg->libs.head, node) + { + pkgconf_fragment_t *frag = node->data; + pkgconf_fragment_copy(client, list, frag, (client->flags & PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE) != 0); + } + + if (client->flags & PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS) + { + PKGCONF_FOREACH_LIST_ENTRY(pkg->libs_private.head, node) + { + pkgconf_fragment_t *frag = node->data; + pkgconf_fragment_copy(client, list, frag, true); + } + } +} + +/* + * !doc + * + * .. c:function:: int pkgconf_pkg_libs(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth) + * + * Walks a dependency graph and extracts relevant ``LIBS`` fragments. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param pkgconf_pkg_t* root: The root of the dependency graph. + * :param pkgconf_list_t* list: The fragment list to add the extracted ``LIBS`` fragments to. + * :param int maxdepth: The maximum allowed depth for dependency resolution. -1 means infinite recursion. + * :return: ``PKGCONF_PKG_ERRF_OK`` if successful, otherwise an error code. + * :rtype: unsigned int + */ +unsigned int +pkgconf_pkg_libs(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth) +{ + unsigned int eflag; + + eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_libs_collect, list, maxdepth, 0); + + if (eflag != PKGCONF_PKG_ERRF_OK) + { + pkgconf_fragment_free(list); + return eflag; + } + + return eflag; +} diff --git a/contrib/pkgconf/libpkgconf/queue.c b/contrib/pkgconf/libpkgconf/queue.c new file mode 100644 index 00000000000..09c02e368f8 --- /dev/null +++ b/contrib/pkgconf/libpkgconf/queue.c @@ -0,0 +1,408 @@ +/* + * queue.c + * compilation of a list of packages into a world dependency set + * + * Copyright (c) 2012 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include +#include + +/* + * !doc + * + * libpkgconf `queue` module + * ========================= + * + * The `queue` module provides an interface that allows easily building a dependency graph from an + * arbitrary set of dependencies. It also provides support for doing "preflight" checks on the entire + * dependency graph prior to working with it. + * + * Using the `queue` module functions is the recommended way of working with dependency graphs. + */ + +/* + * !doc + * + * .. c:function:: void pkgconf_queue_push(pkgconf_list_t *list, const char *package) + * + * Pushes a requested dependency onto the dependency resolver's queue. + * + * :param pkgconf_list_t* list: the dependency resolution queue to add the package request to. + * :param char* package: the dependency atom requested + * :return: nothing + */ +void +pkgconf_queue_push(pkgconf_list_t *list, const char *package) +{ + pkgconf_queue_t *pkgq = calloc(1, sizeof(pkgconf_queue_t)); + + pkgq->package = strdup(package); + pkgconf_node_insert_tail(&pkgq->iter, pkgq, list); +} + +/* + * !doc + * + * .. c:function:: bool pkgconf_queue_compile(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *list) + * + * Compile a dependency resolution queue into a dependency resolution problem if possible, otherwise report an error. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param pkgconf_pkg_t* world: The designated root of the dependency graph. + * :param pkgconf_list_t* list: The list of dependency requests to consider. + * :return: true if the built dependency resolution problem is consistent, else false + * :rtype: bool + */ +bool +pkgconf_queue_compile(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *list) +{ + pkgconf_node_t *iter; + + PKGCONF_FOREACH_LIST_ENTRY(list->head, iter) + { + pkgconf_queue_t *pkgq; + + pkgq = iter->data; + pkgconf_dependency_parse(client, world, &world->required, pkgq->package, PKGCONF_PKG_DEPF_QUERY); + } + + return (world->required.head != NULL); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_queue_free(pkgconf_list_t *list) + * + * Release any memory related to a dependency resolution queue. + * + * :param pkgconf_list_t* list: The dependency resolution queue to release. + * :return: nothing + */ +void +pkgconf_queue_free(pkgconf_list_t *list) +{ + pkgconf_node_t *node, *tnode; + + PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, tnode, node) + { + pkgconf_queue_t *pkgq = node->data; + + free(pkgq->package); + free(pkgq); + } +} + +static void +pkgconf_queue_mark_public(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data) +{ + if (pkg->flags & PKGCONF_PKG_PROPF_VISITED_PRIVATE) + { + pkgconf_list_t *list = data; + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(list->head, node) + { + pkgconf_dependency_t *dep = node->data; + if (dep->match == pkg) + dep->flags &= ~PKGCONF_PKG_DEPF_PRIVATE; + } + + pkg->flags &= ~PKGCONF_PKG_PROPF_VISITED_PRIVATE; + + PKGCONF_TRACE(client, "%s: updated, public", pkg->id); + } +} + +static unsigned int +pkgconf_queue_collect_dependencies_main(pkgconf_client_t *client, + pkgconf_pkg_t *root, + void *data, + int maxdepth); + +static inline unsigned int +pkgconf_queue_collect_dependencies_walk(pkgconf_client_t *client, + pkgconf_list_t *deplist, + void *data, + int depth) +{ + unsigned int eflags = PKGCONF_PKG_ERRF_OK; + pkgconf_node_t *node; + pkgconf_pkg_t *world = data; + + PKGCONF_FOREACH_LIST_ENTRY_REVERSE(deplist->tail, node) + { + pkgconf_dependency_t *dep = node->data; + pkgconf_dependency_t *flattened_dep; + pkgconf_pkg_t *pkg = dep->match; + + if (*dep->package == '\0') + continue; + + if (pkg == NULL) + { + PKGCONF_TRACE(client, "WTF: unmatched dependency %p <%s>", dep, dep->package); + continue; + } + + if (pkg->serial == client->serial) + continue; + + if (client->flags & PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE) + pkg->flags |= PKGCONF_PKG_PROPF_VISITED_PRIVATE; + else + pkg->flags &= ~PKGCONF_PKG_PROPF_VISITED_PRIVATE; + + eflags |= pkgconf_queue_collect_dependencies_main(client, pkg, data, depth - 1); + + flattened_dep = pkgconf_dependency_copy(client, dep); + pkgconf_node_insert(&flattened_dep->iter, flattened_dep, &world->required); + } + + return eflags; +} + +static unsigned int +pkgconf_queue_collect_dependencies_main(pkgconf_client_t *client, + pkgconf_pkg_t *root, + void *data, + int maxdepth) +{ + unsigned int eflags = PKGCONF_PKG_ERRF_OK; + + if (maxdepth == 0) + return eflags; + + /* Short-circuit if we have already visited this node. + */ + if (root->serial == client->serial) + return eflags; + + root->serial = client->serial; + + PKGCONF_TRACE(client, "%s: collecting private dependencies, level %d", root->id, maxdepth); + + /* XXX: ugly */ + const unsigned int saved_flags = client->flags; + client->flags |= PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE; + eflags = pkgconf_queue_collect_dependencies_walk(client, &root->requires_private, data, maxdepth); + client->flags = saved_flags; + if (eflags != PKGCONF_PKG_ERRF_OK) + return eflags; + + PKGCONF_TRACE(client, "%s: collecting public dependencies, level %d", root->id, maxdepth); + + eflags = pkgconf_queue_collect_dependencies_walk(client, &root->required, data, maxdepth); + if (eflags != PKGCONF_PKG_ERRF_OK) + return eflags; + + PKGCONF_TRACE(client, "%s: finished, %s", root->id, (root->flags & PKGCONF_PKG_PROPF_VISITED_PRIVATE) ? "private" : "public"); + + return eflags; +} + +static inline unsigned int +pkgconf_queue_collect_dependencies(pkgconf_client_t *client, + pkgconf_pkg_t *root, + void *data, + int maxdepth) +{ + ++client->serial; + return pkgconf_queue_collect_dependencies_main(client, root, data, maxdepth); +} + +static inline unsigned int +pkgconf_queue_verify(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *list, int maxdepth) +{ + unsigned int result; + const unsigned int saved_flags = client->flags; + pkgconf_pkg_t initial_world = { + .id = "user:request", + .realname = "virtual world package", + .flags = PKGCONF_PKG_PROPF_STATIC | PKGCONF_PKG_PROPF_VIRTUAL, + }; + + if (!pkgconf_queue_compile(client, &initial_world, list)) + { + pkgconf_solution_free(client, &initial_world); + return PKGCONF_PKG_ERRF_DEPGRAPH_BREAK; + } + + PKGCONF_TRACE(client, "solving"); + result = pkgconf_pkg_traverse(client, &initial_world, NULL, NULL, maxdepth, 0); + if (result != PKGCONF_PKG_ERRF_OK) + { + pkgconf_solution_free(client, &initial_world); + return result; + } + + PKGCONF_TRACE(client, "flattening"); + result = pkgconf_queue_collect_dependencies(client, &initial_world, world, maxdepth); + if (result != PKGCONF_PKG_ERRF_OK) + { + pkgconf_solution_free(client, &initial_world); + return result; + } + + if (client->flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE) + { + PKGCONF_TRACE(client, "marking public deps"); + client->flags &= ~PKGCONF_PKG_PKGF_SEARCH_PRIVATE; + client->flags |= PKGCONF_PKG_PKGF_SKIP_CONFLICTS; + result = pkgconf_pkg_traverse(client, &initial_world, pkgconf_queue_mark_public, &world->required, maxdepth, 0); + client->flags = saved_flags; + if (result != PKGCONF_PKG_ERRF_OK) + { + pkgconf_solution_free(client, &initial_world); + return result; + } + } + + /* free the initial solution */ + pkgconf_solution_free(client, &initial_world); + + return PKGCONF_PKG_ERRF_OK; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_solution_free(pkgconf_client_t *client, pkgconf_pkg_t *world, int maxdepth) + * + * Removes references to package nodes contained in a solution. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param pkgconf_pkg_t* world: The root for the generated dependency graph. Should have PKGCONF_PKG_PROPF_VIRTUAL flag. + * :returns: nothing + */ +void +pkgconf_solution_free(pkgconf_client_t *client, pkgconf_pkg_t *world) +{ + (void) client; + + if (world->flags & PKGCONF_PKG_PROPF_VIRTUAL) + { + pkgconf_dependency_free(&world->required); + pkgconf_dependency_free(&world->requires_private); + } +} + +/* + * !doc + * + * .. c:function:: bool pkgconf_queue_solve(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_pkg_t *world, int maxdepth) + * + * Solves and flattens the dependency graph for the supplied dependency list. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param pkgconf_list_t* list: The list of dependency requests to consider. + * :param pkgconf_pkg_t* world: The root for the generated dependency graph, provided by the caller. Should have PKGCONF_PKG_PROPF_VIRTUAL flag. + * :param int maxdepth: The maximum allowed depth for the dependency resolver. A depth of -1 means unlimited. + * :returns: true if the dependency resolver found a solution, otherwise false. + * :rtype: bool + */ +bool +pkgconf_queue_solve(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_pkg_t *world, int maxdepth) +{ + /* if maxdepth is one, then we will not traverse deeper than our virtual package. */ + if (!maxdepth) + maxdepth = -1; + + unsigned int flags = client->flags; + client->flags |= PKGCONF_PKG_PKGF_SEARCH_PRIVATE; + + unsigned int ret = pkgconf_queue_verify(client, world, list, maxdepth); + client->flags = flags; + + return ret == PKGCONF_PKG_ERRF_OK; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_queue_apply(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_queue_apply_func_t func, int maxdepth, void *data) + * + * Attempt to compile a dependency resolution queue into a dependency resolution problem, then attempt to solve the problem and + * feed the solution to a callback function if a complete dependency graph is found. + * + * This function should not be used in new code. Use pkgconf_queue_solve instead. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param pkgconf_list_t* list: The list of dependency requests to consider. + * :param pkgconf_queue_apply_func_t func: The callback function to call if a solution is found by the dependency resolver. + * :param int maxdepth: The maximum allowed depth for the dependency resolver. A depth of -1 means unlimited. + * :param void* data: An opaque pointer which is passed to the callback function. + * :returns: true if the dependency resolver found a solution, otherwise false. + * :rtype: bool + */ +bool +pkgconf_queue_apply(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_queue_apply_func_t func, int maxdepth, void *data) +{ + bool ret = false; + pkgconf_pkg_t world = { + .id = "virtual:world", + .realname = "virtual world package", + .flags = PKGCONF_PKG_PROPF_STATIC | PKGCONF_PKG_PROPF_VIRTUAL, + }; + + /* if maxdepth is one, then we will not traverse deeper than our virtual package. */ + if (!maxdepth) + maxdepth = -1; + + if (!pkgconf_queue_solve(client, list, &world, maxdepth)) + goto cleanup; + + /* the world dependency set is flattened after it is returned from pkgconf_queue_verify */ + if (!func(client, &world, data, maxdepth)) + goto cleanup; + + ret = true; + +cleanup: + pkgconf_pkg_free(client, &world); + return ret; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_queue_validate(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_queue_apply_func_t func, int maxdepth, void *data) + * + * Attempt to compile a dependency resolution queue into a dependency resolution problem, then attempt to solve the problem. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param pkgconf_list_t* list: The list of dependency requests to consider. + * :param int maxdepth: The maximum allowed depth for the dependency resolver. A depth of -1 means unlimited. + * :returns: true if the dependency resolver found a solution, otherwise false. + * :rtype: bool + */ +bool +pkgconf_queue_validate(pkgconf_client_t *client, pkgconf_list_t *list, int maxdepth) +{ + bool retval = true; + pkgconf_pkg_t world = { + .id = "virtual:world", + .realname = "virtual world package", + .flags = PKGCONF_PKG_PROPF_STATIC | PKGCONF_PKG_PROPF_VIRTUAL, + }; + + /* if maxdepth is one, then we will not traverse deeper than our virtual package. */ + if (!maxdepth) + maxdepth = -1; + + if (pkgconf_queue_verify(client, &world, list, maxdepth) != PKGCONF_PKG_ERRF_OK) + retval = false; + + pkgconf_pkg_free(client, &world); + + return retval; +} diff --git a/contrib/pkgconf/libpkgconf/stdinc.h b/contrib/pkgconf/libpkgconf/stdinc.h new file mode 100644 index 00000000000..31284ed8826 --- /dev/null +++ b/contrib/pkgconf/libpkgconf/stdinc.h @@ -0,0 +1,75 @@ +/* + * stdinc.h + * pull in standard headers (including portability hacks) + * + * Copyright (c) 2012 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#ifndef LIBPKGCONF_STDINC_H +#define LIBPKGCONF_STDINC_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include +# include +# define PATH_DEV_NULL "nul" +# ifdef _WIN64 +# ifndef __MINGW32__ +# define SIZE_FMT_SPECIFIER "%I64u" +# else +# define SIZE_FMT_SPECIFIER "%llu" +# endif +# else +# define SIZE_FMT_SPECIFIER "%u" +# endif +# ifndef ssize_t +# ifndef __MINGW32__ +# include +# else +# include +# endif +# define ssize_t SSIZE_T +# endif +# ifndef __MINGW32__ +# include "win-dirent.h" +# else +# include +# endif +# define PKGCONF_ITEM_SIZE (_MAX_PATH + 1024) +#else +# define PATH_DEV_NULL "/dev/null" +# define SIZE_FMT_SPECIFIER "%zu" +# ifdef __HAIKU__ +# include +# endif +# include +# include +# include +# include +# ifdef PATH_MAX +# define PKGCONF_ITEM_SIZE (PATH_MAX + 1024) +# else +# define PKGCONF_ITEM_SIZE (4096 + 1024) +# endif +#endif + +#endif diff --git a/contrib/pkgconf/libpkgconf/tuple.c b/contrib/pkgconf/libpkgconf/tuple.c new file mode 100644 index 00000000000..83f6a479e77 --- /dev/null +++ b/contrib/pkgconf/libpkgconf/tuple.c @@ -0,0 +1,476 @@ +/* + * tuple.c + * management of key->value tuples + * + * Copyright (c) 2011, 2012 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include +#include + +/* + * !doc + * + * libpkgconf `tuple` module + * ========================= + * + * The `tuple` module provides key-value mappings backed by a linked list. The key-value + * mapping is mainly used for variable substitution when parsing .pc files. + * + * There are two sets of mappings: a ``pkgconf_pkg_t`` specific mapping, and a `global` mapping. + * The `tuple` module provides convenience wrappers for managing the `global` mapping, which is + * attached to a given client object. + */ + +/* + * !doc + * + * .. c:function:: void pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value) + * + * Defines a global variable, replacing the previous declaration if one was set. + * + * :param pkgconf_client_t* client: The pkgconf client object to modify. + * :param char* key: The key for the mapping (variable name). + * :param char* value: The value for the mapped entry. + * :return: nothing + */ +void +pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value) +{ + pkgconf_tuple_add(client, &client->global_vars, key, value, false, 0); +} + +static pkgconf_tuple_t * +lookup_global_tuple(const pkgconf_client_t *client, const char *key) +{ + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(client->global_vars.head, node) + { + pkgconf_tuple_t *tuple = node->data; + + if (!strcmp(tuple->key, key)) + return tuple; + } + + return NULL; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_tuple_find_global(const pkgconf_client_t *client, const char *key) + * + * Looks up a global variable. + * + * :param pkgconf_client_t* client: The pkgconf client object to access. + * :param char* key: The key or variable name to look up. + * :return: the contents of the variable or ``NULL`` + * :rtype: char * + */ +char * +pkgconf_tuple_find_global(const pkgconf_client_t *client, const char *key) +{ + pkgconf_tuple_t *tuple; + + tuple = lookup_global_tuple(client, key); + if (tuple == NULL) + return NULL; + + return tuple->value; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_tuple_free_global(pkgconf_client_t *client) + * + * Delete all global variables associated with a pkgconf client object. + * + * :param pkgconf_client_t* client: The pkgconf client object to modify. + * :return: nothing + */ +void +pkgconf_tuple_free_global(pkgconf_client_t *client) +{ + pkgconf_tuple_free(&client->global_vars); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv) + * + * Parse and define a global variable. + * + * :param pkgconf_client_t* client: The pkgconf client object to modify. + * :param char* kv: The variable in the form of ``key=value``. + * :return: nothing + */ +void +pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv) +{ + char *workbuf = strdup(kv); + char *value; + pkgconf_tuple_t *tuple; + + value = strchr(workbuf, '='); + if (value == NULL) + goto out; + + *value++ = '\0'; + + tuple = pkgconf_tuple_add(client, &client->global_vars, workbuf, value, false, 0); + if (tuple != NULL) + tuple->flags = PKGCONF_PKG_TUPLEF_OVERRIDE; + +out: + free(workbuf); +} + +static void +pkgconf_tuple_find_delete(pkgconf_list_t *list, const char *key) +{ + pkgconf_node_t *node, *next; + + PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node) + { + pkgconf_tuple_t *tuple = node->data; + + if (!strcmp(tuple->key, key)) + { + pkgconf_tuple_free_entry(tuple, list); + return; + } + } +} + +static char * +dequote(const char *value) +{ + char *buf = calloc(1, (strlen(value) + 1) * 2); + char *bptr = buf; + const char *i; + char quote = 0; + + if (*value == '\'' || *value == '"') + quote = *value; + + for (i = value; *i != '\0'; i++) + { + if (*i == '\\' && quote && *(i + 1) == quote) + { + i++; + *bptr++ = *i; + } + else if (*i != quote) + *bptr++ = *i; + } + + return buf; +} + +static const char * +find_sysroot(const pkgconf_client_t *client, pkgconf_list_t *vars) +{ + const char *sysroot_dir; + + sysroot_dir = pkgconf_tuple_find(client, vars, "pc_sysrootdir"); + if (sysroot_dir == NULL) + sysroot_dir = client->sysroot_dir; + + return sysroot_dir; +} + +static bool +should_rewrite_sysroot(const pkgconf_client_t *client, pkgconf_list_t *vars, const char *buf, unsigned int flags) +{ + const char *sysroot_dir; + + if (flags & PKGCONF_PKG_PROPF_UNINSTALLED && !(client->flags & PKGCONF_PKG_PKGF_FDO_SYSROOT_RULES)) + return false; + + sysroot_dir = find_sysroot(client, vars); + if (sysroot_dir == NULL) + return false; + + if (*buf != '/') + return false; + + if (!strcmp(sysroot_dir, "/")) + return false; + + if (strlen(buf) <= strlen(sysroot_dir)) + return false; + + if (strstr(buf + strlen(sysroot_dir), sysroot_dir) == NULL) + return false; + + return true; +} + +/* + * !doc + * + * .. c:function:: pkgconf_tuple_t *pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key, const char *value, bool parse) + * + * Optionally parse and then define a variable. + * + * :param pkgconf_client_t* client: The pkgconf client object to access. + * :param pkgconf_list_t* list: The variable list to add the new variable to. + * :param char* key: The name of the variable being added. + * :param char* value: The value of the variable being added. + * :param bool parse: Whether or not to parse the value for variable substitution. + * :return: a variable object + * :rtype: pkgconf_tuple_t * + */ +pkgconf_tuple_t * +pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key, const char *value, bool parse, unsigned int flags) +{ + char *dequote_value; + pkgconf_tuple_t *tuple = calloc(1, sizeof(pkgconf_tuple_t)); + + pkgconf_tuple_find_delete(list, key); + + dequote_value = dequote(value); + + tuple->key = strdup(key); + if (parse) + tuple->value = pkgconf_tuple_parse(client, list, dequote_value, flags); + else + tuple->value = strdup(dequote_value); + + PKGCONF_TRACE(client, "adding tuple to @%p: %s => %s (parsed? %d)", list, key, tuple->value, parse); + + pkgconf_node_insert(&tuple->iter, tuple, list); + + free(dequote_value); + + return tuple; +} + +/* + * !doc + * + * .. c:function:: char *pkgconf_tuple_find(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key) + * + * Look up a variable in a variable list. + * + * :param pkgconf_client_t* client: The pkgconf client object to access. + * :param pkgconf_list_t* list: The variable list to search. + * :param char* key: The variable name to search for. + * :return: the value of the variable or ``NULL`` + * :rtype: char * + */ +char * +pkgconf_tuple_find(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key) +{ + pkgconf_node_t *node; + pkgconf_tuple_t *global_tuple; + + global_tuple = lookup_global_tuple(client, key); + if (global_tuple != NULL && global_tuple->flags & PKGCONF_PKG_TUPLEF_OVERRIDE) + return global_tuple->value; + + PKGCONF_FOREACH_LIST_ENTRY(list->head, node) + { + pkgconf_tuple_t *tuple = node->data; + + if (!strcmp(tuple->key, key)) + return tuple->value; + } + + if (global_tuple != NULL) + return global_tuple->value; + + return NULL; +} + +/* + * !doc + * + * .. c:function:: char *pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *vars, const char *value, unsigned int flags) + * + * Parse an expression for variable substitution. + * + * :param pkgconf_client_t* client: The pkgconf client object to access. + * :param pkgconf_list_t* list: The variable list to search for variables (along side the global variable list). + * :param char* value: The ``key=value`` string to parse. + * :param uint flags: Any flags to consider while parsing. + * :return: the variable data with any variables substituted + * :rtype: char * + */ +char * +pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *vars, const char *value, unsigned int flags) +{ + char buf[PKGCONF_BUFSIZE]; + const char *ptr; + char *bptr = buf; + + if (!(client->flags & PKGCONF_PKG_PKGF_FDO_SYSROOT_RULES) && + (!(flags & PKGCONF_PKG_PROPF_UNINSTALLED) || (client->flags & PKGCONF_PKG_PKGF_PKGCONF1_SYSROOT_RULES))) + { + if (*value == '/' && client->sysroot_dir != NULL && strncmp(value, client->sysroot_dir, strlen(client->sysroot_dir))) + bptr += pkgconf_strlcpy(buf, client->sysroot_dir, sizeof buf); + } + + for (ptr = value; *ptr != '\0' && bptr - buf < PKGCONF_BUFSIZE; ptr++) + { + if (*ptr != '$' || (*ptr == '$' && *(ptr + 1) != '{')) + *bptr++ = *ptr; + else if (*(ptr + 1) == '{') + { + char varname[PKGCONF_ITEM_SIZE]; + char *vend = varname + PKGCONF_ITEM_SIZE - 1; + char *vptr = varname; + const char *pptr; + char *kv, *parsekv; + + *vptr = '\0'; + + for (pptr = ptr + 2; *pptr != '\0'; pptr++) + { + if (*pptr != '}') + { + if (vptr < vend) + *vptr++ = *pptr; + else + { + *vptr = '\0'; + break; + } + } + else + { + *vptr = '\0'; + break; + } + } + + PKGCONF_TRACE(client, "lookup tuple %s", varname); + + size_t remain = PKGCONF_BUFSIZE - (bptr - buf); + ptr += (pptr - ptr); + kv = pkgconf_tuple_find_global(client, varname); + if (kv != NULL) + { + size_t nlen = pkgconf_strlcpy(bptr, kv, remain); + if (nlen > remain) + { + pkgconf_warn(client, "warning: truncating very long variable to 64KB\n"); + + bptr = buf + (PKGCONF_BUFSIZE - 1); + break; + } + + bptr += nlen; + } + else + { + kv = pkgconf_tuple_find(client, vars, varname); + + if (kv != NULL) + { + size_t nlen; + + parsekv = pkgconf_tuple_parse(client, vars, kv, flags); + nlen = pkgconf_strlcpy(bptr, parsekv, remain); + free(parsekv); + + if (nlen > remain) + { + pkgconf_warn(client, "warning: truncating very long variable to 64KB\n"); + + bptr = buf + (PKGCONF_BUFSIZE - 1); + break; + } + + bptr += nlen; + } + } + } + } + + *bptr = '\0'; + + /* + * Sigh. Somebody actually attempted to use freedesktop.org pkg-config's broken sysroot support, + * which was written by somebody who did not understand how sysroots are supposed to work. This + * results in an incorrect path being built as the sysroot will be prepended twice, once explicitly, + * and once by variable expansion (the pkgconf approach). We could simply make ${pc_sysrootdir} blank, + * but sometimes it is necessary to know the explicit sysroot path for other reasons, so we can't really + * do that. + * + * As a result, we check to see if ${pc_sysrootdir} is prepended as a duplicate, and if so, remove the + * prepend. This allows us to handle both our approach and the broken freedesktop.org implementation's + * approach. Because a path can be shorter than ${pc_sysrootdir}, we do some checks first to ensure it's + * safe to skip ahead in the string to scan for our sysroot dir. + * + * Finally, we call pkgconf_path_relocate() to clean the path of spurious elements. + * + * New in 1.9: Only attempt to rewrite the sysroot if we are not processing an uninstalled package. + */ + if (should_rewrite_sysroot(client, vars, buf, flags)) + { + char cleanpath[PKGCONF_ITEM_SIZE]; + const char *sysroot_dir = find_sysroot(client, vars); + + pkgconf_strlcpy(cleanpath, buf + strlen(sysroot_dir), sizeof cleanpath); + pkgconf_path_relocate(cleanpath, sizeof cleanpath); + + return strdup(cleanpath); + } + + return strdup(buf); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list) + * + * Deletes a variable object, removing it from any variable lists and releasing any memory associated + * with it. + * + * :param pkgconf_tuple_t* tuple: The variable object to release. + * :param pkgconf_list_t* list: The variable list the variable object is attached to. + * :return: nothing + */ +void +pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list) +{ + pkgconf_node_delete(&tuple->iter, list); + + free(tuple->key); + free(tuple->value); + free(tuple); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_tuple_free(pkgconf_list_t *list) + * + * Deletes a variable list and any variables attached to it. + * + * :param pkgconf_list_t* list: The variable list to delete. + * :return: nothing + */ +void +pkgconf_tuple_free(pkgconf_list_t *list) +{ + pkgconf_node_t *node, *next; + + PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node) + pkgconf_tuple_free_entry(node->data, list); + + pkgconf_list_zero(list); +} diff --git a/contrib/pkgconf/libpkgconf/win-dirent.h b/contrib/pkgconf/libpkgconf/win-dirent.h new file mode 100644 index 00000000000..0734d6dc470 --- /dev/null +++ b/contrib/pkgconf/libpkgconf/win-dirent.h @@ -0,0 +1,1028 @@ +/* + * Dirent interface for Microsoft Visual Studio + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* Hide warnings about unreferenced local functions */ +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +#elif defined(_MSC_VER) +# pragma warning(disable:4505) +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of the file name without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX+1]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX+1]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + + +/* Dirent functions */ +static DIR *opendir(const char *dirname); +static _WDIR *_wopendir(const wchar_t *dirname); + +static struct dirent *readdir(DIR *dirp); +static struct _wdirent *_wreaddir(_WDIR *dirp); + +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result); +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); + +static int closedir(DIR *dirp); +static int _wclosedir(_WDIR *dirp); + +static void rewinddir(DIR* dirp); +static void _wrewinddir(_WDIR* dirp); + +static int scandir(const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)); + +static int alphasort(const struct dirent **a, const struct dirent **b); + +static int versionsort(const struct dirent **a, const struct dirent **b); + +static int strverscmp(const char *a, const char *b); + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + +/* Compatibility with older Microsoft compilers and non-Microsoft compilers */ +#if !defined(_MSC_VER) || _MSC_VER < 1400 +# define wcstombs_s dirent_wcstombs_s +# define mbstowcs_s dirent_mbstowcs_s +#endif + +/* Optimize dirent_set_errno() away on modern Microsoft compilers */ +#if defined(_MSC_VER) && _MSC_VER >= 1400 +# define dirent_set_errno _set_errno +#endif + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp); + +#if !defined(_MSC_VER) || _MSC_VER < 1400 +static int dirent_mbstowcs_s( + size_t *pReturnValue, wchar_t *wcstr, size_t sizeInWords, + const char *mbstr, size_t count); +#endif + +#if !defined(_MSC_VER) || _MSC_VER < 1400 +static int dirent_wcstombs_s( + size_t *pReturnValue, char *mbstr, size_t sizeInBytes, + const wchar_t *wcstr, size_t count); +#endif + +#if !defined(_MSC_VER) || _MSC_VER < 1400 +static void dirent_set_errno(int error); +#endif + + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR *_wopendir(const wchar_t *dirname) +{ + wchar_t *p; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno(ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + _WDIR *dirp = (_WDIR*) malloc(sizeof(struct _WDIR)); + if (!dirp) + return NULL; + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* + * Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + DWORD n = GetFullPathNameW(dirname, 0, NULL, NULL); +#else + /* WinRT */ + size_t n = wcslen(dirname); +#endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc(sizeof(wchar_t) * n + 16); + if (dirp->patt == NULL) + goto exit_closedir; + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW(dirname, n, dirp->patt, NULL); + if (n <= 0) + goto exit_closedir; +#else + /* WinRT */ + wcsncpy_s(dirp->patt, n+1, dirname, n); +#endif + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (!dirent_first(dirp)) + goto exit_closedir; + + /* Success */ + return dirp; + + /* Failure */ +exit_closedir: + _wclosedir(dirp); + return NULL; +} + +/* + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritten by + * subsequent calls to _wreaddir(). + */ +static struct _wdirent *_wreaddir(_WDIR *dirp) +{ + /* + * Read directory entry to buffer. We can safely ignore the return + * value as entry will be set to NULL in case of error. + */ + struct _wdirent *entry; + (void) _wreaddir_r(dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to NULL and returns zero. + */ +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result) +{ + /* Read next directory entry */ + WIN32_FIND_DATAW *datap = dirent_next(dirp); + if (!datap) { + /* Return NULL to indicate end of directory */ + *result = NULL; + return /*OK*/0; + } + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + size_t n = 0; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entry->d_name[n] = datap->cFileName[n]; + n++; + } + entry->d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n; + + /* File type */ + DWORD attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) + entry->d_type = DT_CHR; + else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) + entry->d_type = DT_DIR; + else + entry->d_type = DT_REG; + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof(struct _wdirent); + + /* Set result address */ + *result = entry; + return /*OK*/0; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int _wclosedir(_WDIR *dirp) +{ + if (!dirp) { + dirent_set_errno(EBADF); + return /*failure*/-1; + } + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) + FindClose(dirp->handle); + + /* Release search pattern */ + free(dirp->patt); + + /* Release directory structure */ + free(dirp); + return /*success*/0; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void _wrewinddir(_WDIR* dirp) +{ + if (!dirp) + return; + + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) + FindClose(dirp->handle); + + /* Open new search handle */ + dirent_first(dirp); +} + +/* Get first directory entry */ +static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp) +{ + if (!dirp) + return NULL; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle == INVALID_HANDLE_VALUE) + goto error; + + /* A directory entry is now waiting in memory */ + dirp->cached = 1; + return &dirp->data; + +error: + /* Failed to open directory: no directory entry in memory */ + dirp->cached = 0; + + /* Set error code */ + DWORD errorcode = GetLastError(); + switch (errorcode) { + case ERROR_ACCESS_DENIED: + /* No read access to directory */ + dirent_set_errno(EACCES); + break; + + case ERROR_DIRECTORY: + /* Directory name is invalid */ + dirent_set_errno(ENOTDIR); + break; + + case ERROR_PATH_NOT_FOUND: + default: + /* Cannot find the file */ + dirent_set_errno(ENOENT); + } + return NULL; +} + +/* Get next directory entry */ +static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp) +{ + /* Is the next directory entry already in cache? */ + if (dirp->cached) { + /* Yes, a valid directory entry found in memory */ + dirp->cached = 0; + return &dirp->data; + } + + /* No directory entry in cache */ + if (dirp->handle == INVALID_HANDLE_VALUE) + return NULL; + + /* Read the next directory entry from stream */ + if (FindNextFileW(dirp->handle, &dirp->data) == FALSE) + goto exit_close; + + /* Success */ + return &dirp->data; + + /* Failure */ +exit_close: + FindClose(dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + return NULL; +} + +/* Open directory stream using plain old C-string */ +static DIR *opendir(const char *dirname) +{ + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno(ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + struct DIR *dirp = (DIR*) malloc(sizeof(struct DIR)); + if (!dirp) + return NULL; + + /* Convert directory name to wide-character string */ + wchar_t wname[PATH_MAX + 1]; + size_t n; + int error = mbstowcs_s(&n, wname, PATH_MAX + 1, dirname, PATH_MAX+1); + if (error) + goto exit_failure; + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir(wname); + if (!dirp->wdirp) + goto exit_failure; + + /* Success */ + return dirp; + + /* Failure */ +exit_failure: + free(dirp); + return NULL; +} + +/* Read next directory entry */ +static struct dirent *readdir(DIR *dirp) +{ + /* + * Read directory entry to buffer. We can safely ignore the return + * value as entry will be set to NULL in case of error. + */ + struct dirent *entry; + (void) readdir_r(dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on success. If the end of directory stream is reached, then + * sets result to NULL and returns zero. + */ +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result) +{ + /* Read next directory entry */ + WIN32_FIND_DATAW *datap = dirent_next(dirp->wdirp); + if (!datap) { + /* No more directory entries */ + *result = NULL; + return /*OK*/0; + } + + /* Attempt to convert file name to multi-byte string */ + size_t n; + int error = wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cFileName, PATH_MAX + 1); + + /* + * If the file name cannot be represented by a multi-byte string, then + * attempt to use old 8+3 file name. This allows the program to + * access files although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file name + * unless the file system provides one. At least VirtualBox shared + * folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); + } + + if (!error) { + /* Length of file name excluding zero terminator */ + entry->d_namlen = n - 1; + + /* File attributes */ + DWORD attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) + entry->d_type = DT_CHR; + else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) + entry->d_type = DT_DIR; + else + entry->d_type = DT_REG; + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof(struct dirent); + } else { + /* + * Cannot convert file name to multi-byte string so construct + * an erroneous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_UNKNOWN; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + } + + /* Return pointer to directory entry */ + *result = entry; + return /*OK*/0; +} + +/* Close directory stream */ +static int closedir(DIR *dirp) +{ + int ok; + + if (!dirp) + goto exit_failure; + + /* Close wide-character directory stream */ + ok = _wclosedir(dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free(dirp); + return ok; + +exit_failure: + /* Invalid directory stream */ + dirent_set_errno(EBADF); + return /*failure*/-1; +} + +/* Rewind directory stream to beginning */ +static void rewinddir(DIR* dirp) +{ + if (!dirp) + return; + + /* Rewind wide-character string directory stream */ + _wrewinddir(dirp->wdirp); +} + +/* Scan directory for entries */ +static int scandir( + const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)) +{ + int result; + + /* Open directory stream */ + DIR *dir = opendir(dirname); + if (!dir) { + /* Cannot open directory */ + return /*Error*/ -1; + } + + /* Read directory entries to memory */ + struct dirent *tmp = NULL; + struct dirent **files = NULL; + size_t size = 0; + size_t allocated = 0; + while (1) { + /* Allocate room for a temporary directory entry */ + if (!tmp) { + tmp = (struct dirent*) malloc(sizeof(struct dirent)); + if (!tmp) + goto exit_failure; + } + + /* Read directory entry to temporary area */ + struct dirent *entry; + if (readdir_r(dir, tmp, &entry) != /*OK*/0) + goto exit_failure; + + /* Stop if we already read the last directory entry */ + if (entry == NULL) + goto exit_success; + + /* Determine whether to include the entry in results */ + if (filter && !filter(tmp)) + continue; + + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + /* Compute number of entries in the new table */ + size_t num_entries = size * 2 + 16; + + /* Allocate new pointer table or enlarge existing */ + void *p = realloc(files, sizeof(void*) * num_entries); + if (!p) + goto exit_failure; + + /* Got the memory */ + files = (dirent**) p; + allocated = num_entries; + } + + /* Store the temporary entry to ptr table */ + files[size++] = tmp; + tmp = NULL; + } + +exit_failure: + /* Release allocated file entries */ + for (size_t i = 0; i < size; i++) { + free(files[i]); + } + + /* Release the pointer table */ + free(files); + files = NULL; + + /* Exit with error code */ + result = /*error*/ -1; + goto exit_status; + +exit_success: + /* Sort directory entries */ + qsort(files, size, sizeof(void*), + (int (*) (const void*, const void*)) compare); + + /* Pass pointer table to caller */ + if (namelist) + *namelist = files; + + /* Return the number of directory entries read */ + result = (int) size; + +exit_status: + /* Release temporary directory entry, if we had one */ + free(tmp); + + /* Close directory stream */ + closedir(dir); + return result; +} + +/* Alphabetical sorting */ +static int alphasort(const struct dirent **a, const struct dirent **b) +{ + return strcoll((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int versionsort(const struct dirent **a, const struct dirent **b) +{ + return strverscmp((*a)->d_name, (*b)->d_name); +} + +/* Compare strings */ +static int strverscmp(const char *a, const char *b) +{ + size_t i = 0; + size_t j; + + /* Find first difference */ + while (a[i] == b[i]) { + if (a[i] == '\0') { + /* No difference */ + return 0; + } + ++i; + } + + /* Count backwards and find the leftmost digit */ + j = i; + while (j > 0 && isdigit((unsigned char)a[j-1])) { + --j; + } + + /* Determine mode of comparison */ + if (a[j] == '0' || b[j] == '0') { + /* Find the next non-zero digit */ + while (a[j] == '0' && a[j] == b[j]) { + j++; + } + + /* String with more digits is smaller, e.g 002 < 01 */ + if (isdigit((unsigned char)a[j])) { + if (!isdigit((unsigned char)b[j])) { + return -1; + } + } else if ((unsigned char)isdigit(b[j])) { + return 1; + } + } else if ((unsigned char)isdigit(a[j]) && + isdigit((unsigned char)b[j])) { + /* Numeric comparison */ + size_t k1 = j; + size_t k2 = j; + + /* Compute number of digits in each string */ + while (isdigit((unsigned char)a[k1])) { + k1++; + } + while (isdigit((unsigned char)b[k2])) { + k2++; + } + + /* Number with more digits is bigger, e.g 999 < 1000 */ + if (k1 < k2) + return -1; + else if (k1 > k2) + return 1; + } + + /* Alphabetical comparison */ + return (int) ((unsigned char) a[i]) - ((unsigned char) b[i]); +} + +/* Convert multi-byte string to wide character string */ +#if !defined(_MSC_VER) || _MSC_VER < 1400 +static int dirent_mbstowcs_s( + size_t *pReturnValue, wchar_t *wcstr, + size_t sizeInWords, const char *mbstr, size_t count) +{ + /* Older Visual Studio or non-Microsoft compiler */ + size_t n = mbstowcs(wcstr, mbstr, sizeInWords); + if (wcstr && n >= count) + return /*error*/ 1; + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) + n = sizeInWords - 1; + wcstr[n] = 0; + } + + /* Length of multi-byte string with zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + return 0; +} +#endif + +/* Convert wide-character string to multi-byte string */ +#if !defined(_MSC_VER) || _MSC_VER < 1400 +static int dirent_wcstombs_s( + size_t *pReturnValue, char *mbstr, + size_t sizeInBytes, const wchar_t *wcstr, size_t count) +{ + /* Older Visual Studio or non-Microsoft compiler */ + size_t n = wcstombs(mbstr, wcstr, sizeInBytes); + if (mbstr && n >= count) + return /*error*/1; + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + return 0; +} +#endif + +/* Set errno variable */ +#if !defined(_MSC_VER) || _MSC_VER < 1400 +static void dirent_set_errno(int error) +{ + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; +} +#endif + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/ diff --git a/contrib/pkgconf/m4/ax_check_compile_flag.m4 b/contrib/pkgconf/m4/ax_check_compile_flag.m4 new file mode 100644 index 00000000000..bd753b34d7d --- /dev/null +++ b/contrib/pkgconf/m4/ax_check_compile_flag.m4 @@ -0,0 +1,53 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 6 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS diff --git a/contrib/pkgconf/man/bomtool.1 b/contrib/pkgconf/man/bomtool.1 new file mode 100644 index 00000000000..aac6c8014ea --- /dev/null +++ b/contrib/pkgconf/man/bomtool.1 @@ -0,0 +1,100 @@ +.\" Copyright (c) 2025 pkgconf authors (see AUTHORS). +.\" +.\" Permission to use, copy, modify, and/or distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" This software is provided 'as is' and without any warranty, express or +.\" implied. In no event shall the authors be liable for any damages arising +.\" from the use of this software. +.Dd June 4, 2025 +.Dt BOMTOOL 1 +.Os +.Sh NAME +.Nm bomtool +.Nd a tool for generating SPDX-based software bills of material +.Sh SYNOPSIS +.Nm +.Op Ar options +.Ar module ... +.Sh DESCRIPTION +.Nm +is a program which generates a textual SPDX 2.0 software bill of +materials (SBOM) for a given set of pkg-config modules. +The output of this tool can then be translated into other SBOM +formats as necessary. +.Pp +The +.Ar options +are as follows: +.Bl -tag -width indent +.It Fl -about +Print the version number, the Copyright notice, and the license of the +.Nm +program to standard output and exit. +Most other options and all command line arguments are ignored. +.It Fl -version +Print the version number of the +.Nm +program to standard output and exit. +Most other options and all command line arguments are ignored. +.El +.Sh ENVIRONMENT +.Bl -tag -width indent +.It Ev PKG_CONFIG_DEBUG_SPEW +If set, print debugging messages to stderr. +.It Ev PKG_CONFIG_IGNORE_CONFLICTS +If set, ignore +.Ic Conflicts +rules in modules. +Has the same effect as the +.Fl -ignore-conflicts +option in +.Xr pkgconf 1 +. +.It Ev PKG_CONFIG_LIBDIR +A colon-separated list of low-priority directories where +.Xr pc 5 +files are looked up. +The module search path is constructed by appending this list to +.Ev PKG_CONFIG_PATH , +which enjoys higher priority. +If +.Ev PKG_CONFIG_LIBDIR +is not defined, the default list compiled into the +.Nm +program from the +.Dv PKG_DEFAULT_PATH +preprocessor macro is appended instead. +If +.Ev PKG_CONFIG_LIBDIR +is defined but empty, nothing is appended. +.It Ev PKG_CONFIG_MAXIMUM_TRAVERSE_DEPTH +Impose a limit on the allowed depth in the dependency graph. +.It Ev PKG_CONFIG_PATH +A colon-separated list of high-priority directories where +.Xr pc 5 +files are looked up. +.It Ev PKG_CONFIG_PRELOADED_FILES +Colon-separated list of +.Xr pc 5 +files which are loaded before any other pkg-config files. +These packages are given highest priority over any other +.Xr pc 5 +files that would otherwise provide a given package. +.El +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +Generating an SBOM for the package named foo: +.Dl $ bomtool foo +.Dl SPDXVersion: SPDX-2.2 +.Dl DataLicense: CC0-1.0 +.Dl SPDXID: SPDXRef-DOCUMENT +.Dl DocumentName: SBOM-SPDX-fooC641.2.3 +.Dl DocumentNamespace: https://spdx.org/spdxdocs/bomtool-2.4.3 +.Dl Creator: Tool: bomtool 2.4.3 +.Dl [...] +.Sh SEE ALSO +.Xr pc 5 , +.Xr pkgconf 1 diff --git a/contrib/pkgconf/man/pc.5 b/contrib/pkgconf/man/pc.5 new file mode 100644 index 00000000000..2aee7462b07 --- /dev/null +++ b/contrib/pkgconf/man/pc.5 @@ -0,0 +1,178 @@ +.\" Copyright (c) 2017 pkgconf authors (see AUTHORS). +.\" +.\" Permission to use, copy, modify, and/or distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" This software is provided 'as is' and without any warranty, express or +.\" implied. In no event shall the authors be liable for any damages arising +.\" from the use of this software. +.Dd December 15, 2017 +.Dt PC 5 +.Os +.Sh NAME +.Nm file.pc +.Nd pkg-config file format +.Sh DESCRIPTION +pkg-config files provide a useful mechanism for storing various information +about libraries and packages on a given system. +Information stored by +.Nm .pc +files include compiler and linker flags necessary to use a given library, as +well as any other relevant metadata. +.Pp +These +.Nm .pc +files are processed by a utility called +.Nm pkg-config , +of which +.Nm pkgconf +is an implementation. +.\" +.Ss FILE SYNTAX +The +.Nm .pc +file follows a format inspired by RFC822. +Comments are prefixed by a pound sign, hash sign or octothorpe (#), and variable +assignment is similar to POSIX shell. +Properties are defined using RFC822-style stanzas. +.\" +.Ss VARIABLES +.\" +Variable definitions start with an alphanumeric string, followed by an equal sign, +and then the value the variable should contain. +.Pp +Variable references are always written as "${variable}". +It is possible to escape literal "${" as "$${". +.\" +.Ss PROPERTIES +.\" +Properties are set using RFC822-style stanzas which consist of a keyword, followed +by a colon (:) and then the value the property should be set to. +Variable substitution is always performed regardless of property type. +.Pp +There are three types of property: +.\" +.Bl -tag -width indent +.\" +.It Literal +The property will be set to the text of the value. +.\" +.It Dependency List +The property will be set to a list of dependencies parsed from the +text. +Dependency lists are defined by this ABNF syntax: +.Bd -literal +package-list = *WSP *( package-spec *( package-sep ) ) +package-sep = WSP / "," +.\" +package-spec = package-key [ ver-op package-version ] +ver-op = "<" / "<=" / "=" / "!=" / ">=" / ">" +.Ed +.\" +.It Fragment List +The property will be set to a list of fragments parsed from the text. +The input text must be in a format that is suitable for passing to a POSIX +shell without any shell expansions after variable substitution has been done. +.\" +.El +.Ss PROPERTY KEYWORDS +.Bl -tag -width indent +.\" +.It Name +The displayed name of the package. +(mandatory; literal) +.It Version +The version of the package. +(mandatory; literal) +.It Description +A description of the package. +(mandatory; literal) +.It URL +A URL to a webpage for the package. +This is used to recommend where newer versions of the package can be acquired. +(mandatory; literal) +.It Cflags +Required compiler flags. +These flags are always used, regardless of whether static compilation is requested. +(optional; fragment list) +.It Cflags.private +Required compiler flags for static compilation. +(optional; fragment list; pkgconf extension) +.It Copyright +A copyright attestation statement. +(optional; literal; pkgconf extension) +.It Libs +Required linking flags for this package. +Libraries this package depends on for linking against it, which are not +described as dependencies should be specified here. +(optional; fragment list) +.It Libs.private +Required linking flags for this package that are only required when linking +statically. +Libraries this package depends on for linking against it statically, which are +not described as dependencies should be specified here. +(optional; fragment list) +.It License +The asserted SPDX license tag that should be applied to the given package. +(optional; literal; pkgconf extension) +.It Maintainer +The preferred contact for the maintainer. This should be in the format of a +name followed by an e-mail address or website. +(optional; literal; pkgconf extension) +.It Requires +Required dependencies that must be met for the package to be usable. +All dependencies must be satisfied or the pkg-config implementation must not use +the package. +(optional; dependency list) +.It Requires.private +Required dependencies that must be met for the package to be usable for header +inclusion and static linking. +All dependencies must be satisfied or the pkg-config implementation must not use +the package for header inclusion and static linking. +(optional; dependency list) +.It Conflicts +Dependencies that must not be met for the package to be usable. +If any package in the proposed dependency solution match any dependency in the +Conflicts list, the package being considered is not usable. +(optional; dependency list) +.It Provides +Dependencies that may be provided by an alternate package. +If a package cannot be found, the entire package collection is scanned for +providers which can match the requested dependency. +(optional; dependency list; pkgconf extension) +.El +.Ss EXTENSIONS +Features that have been marked as a pkgconf extension are only guaranteed to work +with the pkgconf implementation of pkg-config. +Other implementations may or may not support the extensions. +.Pp +Accordingly, it is suggested that +.Nm .pc +files which absolutely depend on these extensions declare a requirement on the +pkgconf virtual. +.Sh EXAMPLES +An example .pc file: +.Bd -literal +# This is a comment +prefix=/home/kaniini/pkg # this defines a variable +exec_prefix=${prefix} # defining another variable with a substitution +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: libfoo # human-readable name +Description: an example library called libfoo # human-readable description +Copyright: Copyright (c) 2022 pkgconf project authors +License: Apache-2.0 +Maintainer: the pkgconf project +Version: 1.0 +URL: http://www.pkgconf.org +Requires: libbar > 2.0.0 +Conflicts: libbaz <= 3.0.0 +Libs: -L${libdir} -lfoo +Libs.private: -lm +Cflags: -I${includedir}/libfoo +.Ed +.Sh SEE ALSO +.Xr pkgconf 1 , +.Xr pkg.m4 7 diff --git a/contrib/pkgconf/man/pkg.m4.7 b/contrib/pkgconf/man/pkg.m4.7 new file mode 100644 index 00000000000..f9210700f8f --- /dev/null +++ b/contrib/pkgconf/man/pkg.m4.7 @@ -0,0 +1,143 @@ +.\" Copyright (c) 2017 pkgconf authors (see AUTHORS). +.\" +.\" Permission to use, copy, modify, and/or distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" This software is provided 'as is' and without any warranty, express or +.\" implied. In no event shall the authors be liable for any damages arising +.\" from the use of this software. +.Dd December 5, 2017 +.Dt PKG.M4 7 +.Os +.Sh NAME +.Nm pkg.m4 +.Nd autoconf macros for using pkgconf +.Sh SYNOPSIS +.Nm PKG_PREREQ +.Nm PKG_PROG_PKG_CONFIG +.Nm PKG_CHECK_MODULES +.Nm PKG_CHECK_MODULES_STATIC +.Nm PKG_INSTALLDIR +.Nm PKG_NOARCH_INSTALLDIR +.Nm PKG_CHECK_VAR +.Nm PKG_WITH_MODULES +.Nm PKG_HAVE_WITH_MODULES +.Nm PKG_HAVE_DEFINE_WITH_MODULES +.Sh DESCRIPTION +.Nm +is a collection of autoconf macros which help to configure compiler and linker +flags for development libraries. +This allows build systems to detect other dependencies and use them with the +system toolchain. +.Sh "AUTOCONF MACROS" +.Ss "PKG_PREREQ(MIN-VERSION)" +Checks that the version of the +.Nm +autoconf macros in use is at least MIN-VERSION. +This can be used to ensure a particular +.Nm +macro will be available. +.Ss "PKG_PROG_PKG_CONFIG([MIN-VERSION])" +Checks for an implementation of +.Nm pkg-config +which is at least MIN-VERSION or newer. +.Ss "PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES [,ACTION-IF-FOUND [,ACTION-IF-NOT-FOUND]])" +.Ss "PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES [,ACTION-IF-FOUND [,ACTION-IF-NOT-FOUND]])" +Checks whether a given module set exists, and if so, defines +.Nm CFLAGS +and +.Nm LIBS +variables prefixed by +.Nm VARIABLE-PREFIX +with the output from +.Fl -cflags +and +.Fl -libs +respectively. +.Pp +The optional +.Nm ACTION-IF-FOUND +and +.Nm ACTION-IF-NOT-FOUND +arguments are shell fragments that should be executed if the module set is +found or not found. +.Pp +If +.Nm $PKG_CONFIG +is not defined, the +.Nm PKG_PROG_PKG_CONFIG +macro will be executed to locate a +.Nm pkg-config +implementation. +.Pp +The +.Nm PKG_CHECK_MODULES_STATIC +macro provides the same behaviour as +.Nm PKG_CHECK_MODULES +with static linking enabled via the +.Fl -static +flag. +.Ss "PKG_INSTALLDIR(DIRECTORY)" +Defines the variable $pkgconfigdir as the location where a package +should install pkg-config .pc files. +.Pp +By default the directory is $libdir/pkgconfig, but the default can +be changed by passing the +.Nm DIRECTORY +parameter. +.Pp +This value can be overridden with the +.Fl -with-pkgconfigdir +configure parameter. +.Ss "PKG_NOARCH_INSTALLDIR(DIRECTORY)" +Defines the variable $noarch_pkgconfigdir as the location where a package +should install pkg-config .pc files. +.Pp +By default the directory is $datadir/pkgconfig, but the default can +be changed by passing the +.Nm DIRECTORY +parameter. +.Pp +This value can be overridden with the +.Fl -with-noarch-pkgconfigdir +configure parameter. +.Ss "PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])" +Retrieves the value of the +.Nm pkg-config +variable +.Nm CONFIG-VARIABLE +from +.Nm MODULE +and stores it in the +.Nm VARIABLE +variable. +.Pp +Note that repeated usage of +.Nm VARIABLE +is not recommended as the check will be skipped if the variable is +already set. +.Ss "PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND], [DESCRIPTION], [DEFAULT])" +Prepares a "--with-" configure option using the lowercase +.Nm VARIABLE-PREFIX +name, merging the behaviour of +.Nm AC_ARG_WITH +and +.Nm PKG_CHECK_MODULES +in a single macro. +.Ss "PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES, [DESCRIPTION], [DEFAULT])" +Convenience macro to trigger +.Nm AM_CONDITIONAL +after a +.Nm PKG_WITH_MODULES check.\& +.Nm VARIABLE-PREFIX +is exported as a make variable. +.Ss "PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES, [DESCRIPTION], [DEFAULT])" +Convenience macro to trigger +.Nm AM_CONDITIONAL +and +.Nm AC_DEFINE +after a +.Nm PKG_WITH_MODULES check.\& +.Nm VARIABLE-PREFIX +is exported as a make variable. diff --git a/contrib/pkgconf/man/pkgconf-personality.5 b/contrib/pkgconf/man/pkgconf-personality.5 new file mode 100644 index 00000000000..eee70b41cf0 --- /dev/null +++ b/contrib/pkgconf/man/pkgconf-personality.5 @@ -0,0 +1,100 @@ +.\" Copyright (c) 2018 pkgconf authors (see AUTHORS). +.\" +.\" Permission to use, copy, modify, and/or distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" This software is provided 'as is' and without any warranty, express or +.\" implied. In no event shall the authors be liable for any damages arising +.\" from the use of this software. +.Dd July 19, 2018 +.Dt PKGCONF-PERSONALITY 5 +.Os +.Sh NAME +.Nm file.personality +.Nd pkgconf cross-compile personality file format +.Sh DESCRIPTION +pkgconf cross-compile personality files provide a useful mechanism for storing +various information about system toolchains. +Information stored by +.Nm .personality +files include information about paths used by a cross-compile toolchain, such as +the sysroot directory and default include and library paths. pkgconf uses this +information to determine what information is necessary to use libraries. +.\" +.Ss FILE SYNTAX +The +.Nm .personality +file follows a format inspired by RFC822. +Comments are prefixed by a pound sign, hash sign or octothorpe (#), and variable +assignment is similar to POSIX shell. +Properties are defined using RFC822-style stanzas. +.\" +.Ss PROPERTIES +.\" +Properties are set using RFC822-style stanzas which consist of a keyword, followed +by a colon (:) and then the value the property should be set to. +Variable substitution is always performed regardless of property type. +.Pp +There are three types of property: +.\" +.Bl -tag -width indent +.\" +.It Literal +The property will be set to the text of the value. +.\" +.It Fragment List +The property will be set to a list of fragments parsed from the text. +The input text must be in a format that is suitable for passing to a POSIX +shell without any shell expansions after variable substitution has been done. +Elements are delimited with a colon. +.\" +.It Boolean +The property will be set to true if the value is one of: true, yes or 1. +Otherwise it will be set to false. +.\" +.El +.Ss PROPERTY KEYWORDS +.Bl -tag -width indent +.\" +.It Triplet +The triplet used by the cross-compile toolchain. +(mandatory; literal) +.It SysrootDir +The directory used by the system root of the cross-compile toolchain. +(mandatory; literal) +.It DefaultSearchPaths +A list of directories to look for +.Xr pc 5 +files in. +(mandatory; fragment list) +.It SystemIncludePaths +A list of directories that are included by default in the search path for +include files. +(mandatory; fragment list) +.It SystemLibraryPaths +A list of directories that are included by default in the search path for +libraries. +(mandatory; fragment list) +.It WantDefaultPure +If true, pkgconf will default to preferring a pure dependency graph. +(optional; boolean; default is false) +.It WantDefaultStatic +If true, pkgconf will default to operating in static linking mode. +(optional; boolean; default is false) +.\" +.El +.Sh EXAMPLES +An example .personality file: +.Bd -literal +# This is a comment +Triplet: x86_64-pc-linux-gnu +SysrootDir: /home/kaniini/sysroot/x86_64-pc-linux-gnu +DefaultSearchPaths: /home/kaniini/sysroot/x86_64-pc-linux-gnu/lib/pkgconfig:/home/kaniini/sysroot/x86_64-pc-linux-gnu/share/pkgconfig +SystemIncludePaths: /home/kaniini/sysroot/x86_64-pc-linux-gnu/include +SystemLibraryPaths: /home/kaniini/sysroot/x86_64-pc-linux-gnu/lib +.Ed +.Sh SEE ALSO +.Xr pkgconf 1 , +.Xr pc 5 , +.Xr pkg.m4 7 diff --git a/contrib/pkgconf/man/pkgconf.1 b/contrib/pkgconf/man/pkgconf.1 new file mode 100644 index 00000000000..df286a60593 --- /dev/null +++ b/contrib/pkgconf/man/pkgconf.1 @@ -0,0 +1,758 @@ +.\" Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 pkgconf authors (see AUTHORS). +.\" +.\" Permission to use, copy, modify, and/or distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" This software is provided 'as is' and without any warranty, express or +.\" implied. In no event shall the authors be liable for any damages arising +.\" from the use of this software. +.Dd November 15, 2016 +.Dt PKGCONF 1 +.Os +.Sh NAME +.Nm pkgconf +.Nd a system for configuring build dependency information +.Sh SYNOPSIS +.Nm +.Op Ar options +.Ar module ... +.Sh DESCRIPTION +The +.Nm +program retrieves configuration information related to the +.Ar module +arguments from +.Xr pc 5 +files installed on the system and prints parts of the retrieved +information depending on the specified +.Ar options . +The most common use is printing the compiler and linker flags needed +to build software that uses the libraries given by the +.Ar module +arguments. +.Pp +The +.Xr pc 5 +files are searched for along a path constructed from the +.Fl -with-path +option, the +.Ev PKG_CONFIG_PATH +and +.Ev PKG_CONFIG_LIBDIR +environment variables, and some compiled-in default directories. +The +.Ar module +arguments correspond to the file names, but without the +.Pa .pc +filename extension. +.Pp +Several of the +.Ar options +cause immediate exit. +If multiple of these options are given, only the option with the +highest priority takes effect and those with lower priority are +silently ignored. +These options are, ordered by descending priority: +.Bl -enum +.It +No-module options: +.Fl -relocate , +.Fl -dump-personality , +.Fl -about , +.Fl -version , +.Fl -help , +.Fl -atleast-pkgconfig-version , +.Fl -list-all , +and +.Fl -list-package-names : +These options cause all arguments to be ignored. +.It +Argument-only options: +.Fl -atleast-version , +.Fl -exact-version , +and +.Fl -max-version : +These options only inspect modules explicitly specified on the +command line and do not look at dependencies. +.It +Limited-output options: +.Fl -validate , +.Fl -license , +.Fl -uninstalled , +and +.Fl -env : +These options perform dependency resolution, but exit after printing +the information requested by the highest-priority option, +ignoring other output options that may have been specified. +.El +.Pp +Several other options require at least one +.Ar module +argument, produce output, do not cause early exit, can be combined +with each other, but override and disable all +.Fl -cflags +and +.Fl -libs +options: +.Bl -enum +.It +Single-module output options: +.Fl -path , +.Fl -print-variables , +.Fl -variable : +If any of these options is specified, only the first +.Ar module +argument is used, all other arguments are silently ignored, +and no dependency resolution is attempted. +.It +Depth-one output options: +.Fl -print-provides , +.Fl -modversion , +.Fl -print-requires , +and +.Fl -print-requires-private : +If any of these options is specified, only modules +explicitly specified on the command line are inspected +and no dependency resolution is attempted. +.It +General output options: +.Fl -simulate , +.Fl -digraph , +.Fl -solution , +.Fl -fragment-tree : +These options do not limit dependency resolution. +.El +.Pp +The most important output options +.Fl -cflags +and +.Fl -libs +can be combined with each other, but are overridden and ignored if +any of the options listed above are specified. +.Pp +The complete list of +.Ar options +is as follows: +.Bl -tag -width indent +.It Fl -about +Print the version number, the Copyright notice, and the license of the +.Nm +program to standard output and exit. +Most other options and all command line arguments are ignored. +.It Fl -atleast-pkgconfig-version Ns = Ns Ar version +Exit with error if the requested +.Ar version +number is greater than the version number of the +.Nm +program, or with success otherwise. +Most other options and all command line arguments are ignored. +.It Fl -atleast-version Ns = Ns Ar version +Check the +.Ar module +arguments in the given order. +Exit with error as soon as a +.Ar module +does not exist, and exit with success as soon as the version number of a +.Ar module +is greater than or equal to the requested +.Ar version +number. +Exit with error if the version number of each +.Ar module +is less than the requested +.Ar version +number. +.It Fl -cflags , Fl -cflags-only-I , Fl -cflags-only-other +Print all compiler flags required to compile against the +.Ar module , +or only the include path +.Pq Fl I +flags, or only the compiler flags that are not include path flags, +respectively. +These options imply +.Fl -print-errors . +.It Fl -debug +Print some non-fatal warning messages to standard error output +that would otherwise silently be ignored. +This option also implies +.Fl -print-errors . +If +.Nm +was compiled without defining the preprocessor macro +.Dv PKGCONF_LITE , +this option also prints many debugging messages to standard error output. +.It Fl -define-prefix +Attempts to determine the prefix variable to use for CFLAGS and LIBS entry relocations. +This is mainly useful for platforms where framework SDKs are relocatable, such as Windows. +.It Fl -define-variable Ns = Ns Ar varname Ns = Ns Ar value +Define +.Ar varname +as +.Ar value . +Variables are used in query output, and some modules' results may change based +on the presence of a variable definition. +.It Fl -digraph +Dump the dependency resolver's solution as a graphviz +.Sq dot +file. +This can be used with graphviz to visualize module interdependencies. +This option is only available if the preprocessor macro +.Dv PKGCONF_LITE +was not defined during compilation. +.It Fl -dont-define-prefix +Disables the +.Sq define-prefix +feature. +.It Fl -dont-relocate-paths +Disables the path relocation feature. +.It Fl -dump-personality +Print some default settings to standard output, in particular +the default module search path that is used when +.Ev PKG_CONFIG_LIBDIR +is not defined, the default list of include paths that are filtered out when +.Ev PKG_CONFIG_SYSTEM_INCLUDE_PATH +is not defined, +and the default list of library paths that are filtered out when +.Ev PKG_CONFIG_SYSTEM_LIBRARY_PATH +is not defined, and exit. +Most other options and all command line arguments are ignored. +This option is only available if the preprocessor macro +.Dv PKGCONF_LITE +was not defined during compilation. +.It Fl -env Ns = Ns Ar varname +Print the requested values as variable declarations in a similar format as the +.Xr env 1 +command. +.It Fl -env-only +Initialize the module search path from +.Fl -with-path +and +.Ev PKG_CONFIG_PATH +only, ignoring +.Ev PKG_CONFIG_LIBDIR +and the compiled-in default directories. +.It Fl -errors-to-stdout +Print all error, warning, and debugging messages to standard output +instead of to standard error output. +.It Fl -exact-version Ns = Ns Ar version +Check the +.Ar module +arguments in the given order. +Exit with error as soon as a +.Ar module +does not exist, and exit with success as soon as the version number of a +.Ar module +is exactly the requested +.Ar version +number. +Exit with error if the version number of each +.Ar module +differs from the requested +.Ar version +number. +.It Fl -exists +Exit with a non-zero exit status +if the dependency resolver is unable to find all of the requested +.Ar module Ns s . +This option is active by default and cannot be disabled. +However, various other options cause +.Nm +to exit and report success or failure before all arguments have been inspected. +.It Fl -fragment-filter Ns = Ns Ar types +Filter the fragment lists for the specified +.Ar types . +.It Fl -help +Print a usage summary on standard output and exit. +Most other options and all command line arguments are ignored. +.It Fl -ignore-conflicts +Ignore +.Sq Conflicts +rules in modules. +.It Fl -keep-system-cflags , Fl -keep-system-libs +Keep CFLAGS or linker flag fragments that would be filtered due to being +included by default in the compiler. +.It Fl -libs , Fl -libs-only-L , Fl -libs-only-l , Fl -libs-only-other +Print all linker flags required to link against the +.Ar module , +or only the library path +.Pq Fl L +flags, or only the library +.Pq Fl l +flags, or only the linker flags that are neither library path +nor library flags, respectively. +These options imply +.Fl -print-errors . +.It Fl -list-all +Walk the module search path in the order of descending priority. +For each +.Xr pc 5 +file found, print one line to standard output, +containing the basename of the file without the extension, the +.Ic Name +property, a dash +.Pq Sq \- , +and the +.Ic Description +property. +This option implies +.Fl -print-errors . +All command line arguments are ignored. +.It Fl -list-package-names +Perform the same search as +.Fl -list-all , +but only print the basename of each +.Xr pc 5 +file without the extension, not the module name and the description. +This option implies +.Fl -print-errors . +All command line arguments are ignored. +.It Fl -log-file Ns = Ns Ar file +Set the name of the output +.Ar file +where information about selected modules is logged, +both about those selected by arguments and as dependencies. +For each selected module, one line is printed, +containing the basename of the +.Xr pc 5 +file without the extension, optionally an operator and version number +describing the desired range of versions, and either the actual version +number in square brackets or the string +.Qq NOT-FOUND . +If this option is not provided, the name of the output file +is instead taken from the +.Ev PKG_CONFIG_LOG +environment variable, and if that is not provided either, +this kind of logging is disabled. +.It Fl -max-version Ns = Ns Ar version +Check the +.Ar module +arguments in the given order. +Exit with error as soon as a +.Ar module +does not exist, and exit with success as soon as the version number of a +.Ar module +is less than or equal to the requested +.Ar version +number. +Exit with error if the version number of each +.Ar module +is greater than the requested +.Ar version +number. +.It Fl -maximum-traverse-depth Ns = Ns Ar depth +Impose a limit on the allowed depth in the dependency graph. +For example, a +.Ar depth +of 2 restricts the resolver from acting on child +dependencies of modules added to the resolver's solution. +This option is overridden by the +.Ev PKG_CONFIG_MAXIMUM_TRAVERSE_DEPTH +environment variable and by the options +.Fl -modversion , +.Fl -path , +.Fl -print-provides , +.Fl -print-requires , +.Fl -print-requires-private , +.Fl -print-variables , +and +.Fl -variable . +.It Fl -modversion +For each specified +.Ar module , +print the version number to standard output. +If the +.Fl -verbose +option is also specified, the name of the respective +.Ar module +and a colon is printed before each version number. +This option implies +.Fl -print-errors +and +.Fl -maximum-traverse-depth Ns =1 +and overrides and disables all +.Fl -cflags +and +.Fl -libs +flags. +.It Fl -msvc-syntax +Use MSVC syntax for +.Fl -cflags , +.Fl -env , +and +.Fl -libs +output. +This option is only available if the preprocessor macro +.Dv PKGCONF_LITE +was not defined during compilation. +.It Fl -no-cache +Skip caching packages when they are loaded into the internal resolver. +This may result in an alternate dependency graph being computed. +.It Fl -no-provides +Ignore +.Sq Provides +rules in modules when resolving dependencies. +.It Fl -no-uninstalled +Forbids the dependency resolver from considering 'uninstalled' modules as part +of a solution. +.It Fl -path +For the first +.Ar module +given on the command line, let the dependency resolver find the +.Xr pc 5 +file describing that module, print the absolute pathname of that file +to standard output, and exit immediately, +ignoring most other options and all other arguments. +.It Fl -prefix-variable Ns = Ns Ar variable +Sets the +.Sq prefix +variable used by the +.Sq define-prefix +feature. +.It Fl -print-errors +Print some messages about fatal errors to standard error output +that would otherwise be omitted. +This option is implied by many other options, but not by all. +It can be overridden with +.Fl -silence-errors . +.It Fl -print-provides +For each specified +.Ar module , +print one line to standard output containing the +.Ic Name +property, an equal sign +.Pq Sq = , +and the +.Ic Version +property. +If the +.Ar module +contains one or more +.Ic Provides +properties, print additional lines in dependency list format, one name +per line, each name optionally followed by an operator and a version. +This option implies +.Fl -maximum-traverse-depth Ns =1 +and overrides and disables all +.Fl -cflags +and +.Fl -libs +flags. +.It Fl -print-requires , Fl -print-requires-private +For each specified +.Ar module , +print the +.Ic Requires +or +.Ic Requires.private +properties, respectively, in dependency list format to standard output. +Both of these options imply +.Fl -maximum-traverse-depth Ns =1 +and override and disable all +.Fl -cflags +and +.Fl -libs +flags. +.It Fl -print-variables +For the first +.Ar module +given on the command line, print the names of all seen variables +to standard output, one per line. +Any subsequent arguments are silently ignored. +This option implies +.Fl -print-errors +and +.Fl -maximum-traverse-depth Ns =1 +and overrides and disables all +.Fl -cflags +and +.Fl -libs +flags. +.It Fl -pure +Treats the computed dependency graph as if it were pure. +This is mainly intended for use with the +.Fl -static +flag and has no effect if +.Fl -shared +is also specified. +.It Fl -relocate Ns = Ns Ar path +Relocates a path using the pkgconf_path_relocate API. +This is mainly used by the testsuite to provide a guaranteed interface +to the system's path relocation backend. +.It Fl -shared +Compute a simple dependency graph that is only suitable for shared linking. +This option overrides +.Fl -static . +.It Fl -short-errors +When printing error messages about modules that are not found +or conflict with each other, avoid printing additional, verbose +instructions explaining potential methods for solving the problem. +.It Fl -silence-errors +Do not print any error, warning, or debugging messages at all. +Overrides all of +.Fl -debug , +.Fl -errors-to-stdout , +and +.Fl -print-errors . +This option is overridden and disabled if the +.Ev PKG_CONFIG_DEBUG_SPEW +environment variable is set. +.It Fl -simulate +Simulates resolving a dependency graph based on the requested modules on the +command line. +Dumps a series of trees denoting pkgconf's resolver state. +This option is only available if the preprocessor macro +.Dv PKGCONF_LITE +was not defined during compilation. +.It Fl -solution +Print the names of the modules requested with +.Ar module +arguments and their dependencies to standard output. +This option is only available if the preprocessor macro +.Dv PKGCONF_LITE +was not defined during compilation. +.It Fl -static +Compute a deeper dependency graph and use compiler/linker flags intended for +static linking. +This option is overridden by +.Fl -shared . +.It Fl -uninstalled +Exit with a non-zero result if the dependency resolver uses an +.Sq uninstalled +module as part of its solution. +.It Fl -validate Ar package ... +Validate specific +.Sq .pc +files for correctness. +This option implies +.Fl -print-errors +and +.Fl -errors-to-stdout . +.It Fl -variable Ns = Ns Ar varname +For the first +.Ar module +given on the command line, print the value of the variable with the name +.Ar varname +to standard output. +Any subsequent arguments are silently ignored. +This option implies +.Fl -maximum-traverse-depth Ns =1 +and overrides and disables all +.Fl -cflags +and +.Fl -libs +flags. +.It Fl -verbose +This option only has an effect if +.Fl -modversion +is also specified. +It prints the name of the respective +.Ar module +and a colon before each version number. +.It Fl -version +Print the version number of the +.Nm +program to standard output and exit. +Most other options and all command line arguments are ignored. +.It Fl -with-path Ns = Ns Ar path +Prepend the directory +.Ar path +to the module search path, +giving it priority over all other directories including those from +.Ev PKG_CONFIG_PATH +and +.Ev PKG_CONFIG_LIBDIR . +.El +.Sh ENVIRONMENT +.Bl -tag -width indent +.It Ev CPATH +First supplementary colon-separated list of include paths filtered out +in the same way as +.Ev PKG_CONFIG_SYSTEM_INCLUDE_PATH . +.It Ev CPLUS_INCLUDE_PATH +Third supplementary colon-separated list of include paths filtered out +in the same way as +.Ev PKG_CONFIG_SYSTEM_INCLUDE_PATH . +.It Ev C_INCLUDE_PATH +Second supplementary colon-separated list of include paths filtered out +in the same way as +.Ev PKG_CONFIG_SYSTEM_INCLUDE_PATH . +.It Ev DESTDIR +If set to the same value as +.Ev PKG_CONFIG_SYSROOT_DIR , +behave in the same way as if +.Ev PKG_CONFIG_FDO_SYSROOT_RULES +is set. +If +.Ev PKG_CONFIG_SYSROOT_DIR +is not set or set to a different value, +.Ev DESTDIR +is ignored. +.It Ev LIBRARY_PATH +Supplementary colon-separated list of library paths filtered out +in the same way as +.Ev PKG_CONFIG_SYSTEM_LIBRARY_PATH . +.It Ev OBJC_INCLUDE_PATH +Fourth supplementary colon-separated list of include paths filtered out +in the same way as +.Ev PKG_CONFIG_SYSTEM_INCLUDE_PATH . +.It Ev PKG_CONFIG_ALLOW_SYSTEM_CFLAGS +If set, this variable has the same effect as the +.Fl -keep-system-cflags +option. +.It Ev PKG_CONFIG_ALLOW_SYSTEM_LIBS +If set, this variable has the same effect as the +.Fl -keep-system-libs +option. +.It Ev PKG_CONFIG_DEBUG_SPEW +If set, override and disable the +.Fl -silence-errors +option. +.It Ev PKG_CONFIG_DISABLE_UNINSTALLED +If set, enables the same behaviour as the +.Fl -no-uninstalled +flag. +.It Ev PKG_CONFIG_DONT_DEFINE_PREFIX +If set, this variable has the same effect as the +.Fl -dont-define-prefix +option. +.It Ev PKG_CONFIG_DONT_RELOCATE_PATHS +If set, disables the path relocation feature. +.It Ev PKG_CONFIG_FDO_SYSROOT_RULES +If set, follow the sysroot prefixing rules that freedesktop.org pkg-config uses. +.It Ev PKG_CONFIG_IGNORE_CONFLICTS +If set, ignore +.Ic Conflicts +rules in modules. +Has the same effect as the +.Fl -ignore-conflicts +option. +.It Ev PKG_CONFIG_LIBDIR +A colon-separated list of low-priority directories where +.Xr pc 5 +files are looked up. +The module search path is constructed by appending this list to +.Ev PKG_CONFIG_PATH , +which enjoys higher priority. +If +.Ev PKG_CONFIG_LIBDIR +is not defined, the default list compiled into the +.Nm +program from the +.Dv PKG_DEFAULT_PATH +preprocessor macro is appended instead. +If +.Ev PKG_CONFIG_LIBDIR +is defined but empty, nothing is appended. +.It Ev PKG_CONFIG_LOG +If set, log information about selected modules +to the file with the name stored in this variable. +For more details, see the +.Fl -log-file +command line option, which overrides this variable. +.It Ev PKG_CONFIG_MAXIMUM_TRAVERSE_DEPTH +Impose a limit on the allowed depth in the dependency graph. +This variable overrides the +.Fl -maximum-traverse-depth +option, but is overridden by the other options mentioned there. +.It Ev PKG_CONFIG_MSVC_SYNTAX +If set, use MSVC syntax for +.Fl -cflags , +.Fl -env , +and +.Fl -libs +output. +This variable has the same effect as the +.Fl -msvc-syntax +option. +If the preprocessor macro +.Dv PKGCONF_LITE +was defined during compilation, this variable is ignored. +.It Ev PKG_CONFIG_PATH +A colon-separated list of high-priority directories where +.Xr pc 5 +files are looked up. +The module search path is constructed +by prepending the directory specified with +.Fl -with-path , +if any, and unless +.Fl -env-only +is specified, by appending either +.Ev PKG_CONFIG_LIBDIR +or the compiled-in default directories with lower priority. +.It Ev PKG_CONFIG_PRELOADED_FILES +Colon-separated list of +.Xr pc 5 +files which are loaded before any other pkg-config files. +These packages are given highest priority over any other +.Xr pc 5 +files that would otherwise provide a given package. +.It Ev PKG_CONFIG_PURE_DEPGRAPH +If set, enables the same behaviour as the +.Fl -pure +flag. +.It Ev PKG_CONFIG_RELOCATE_PATHS +If set, this variable has the same effect as the +.Fl -define-prefix +option. +.It Ev PKG_CONFIG_SYSROOT_DIR +If set, this variable defines a +.Sq sysroot +directory, which will be prepended to every path variable +beginning with the prefix variable in a given +.Xr pc 5 +file. +Useful for cross compilation. +The value of this environment variable is also copied into the global variable +.Va pc_sysrootdir . +.It Ev PKG_CONFIG_SYSTEM_INCLUDE_PATH +Colon-separated list of include paths that are filtered out +and not printed by the +.Fl -cflags +and +.Fl -cflags-only-I +options because they are considered system include paths. +If not defined, the default list compiled into the +.Nm +program from the +.Dv SYSTEM_INCLUDEDIR +preprocessor macro is used instead. +This variable is a pkgconf-specific extension. +Any directories listed in the environment variables +.Ev CPATH , +.Ev C_INCLUDE_PATH , +.Ev CPLUS_INCLUDE_PATH , +and +.Ev OBJC_INCLUDE_PATH +are also filtered out. +.It Ev PKG_CONFIG_SYSTEM_LIBRARY_PATH +Colon-separated list of library paths that are filtered out +and not printed by the +.Fl -libs +and +.Fl -libs-only-L +options because they are considered system library paths. +If not defined, the default list compiled into the +.Nm +program from the +.Dv SYSTEM_LIBDIR +preprocessor macro is used instead. +This variable is a pkgconf-specific extension. +.It Ev PKG_CONFIG_TOP_BUILD_DIR +The value of the +.Va pc_top_builddir +global variable. +If this environment variable is not defined, the string +.Qq $(top_builddir) +is used as the value of +.Va pc_top_builddir . +.El +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +Displaying the CFLAGS of a package: +.Dl $ pkgconf --cflags foo +.Dl -fPIC -I/usr/include/foo +.Sh SEE ALSO +.Xr pc 5 , +.Xr pkg.m4 7 diff --git a/contrib/pkgconf/meson.build b/contrib/pkgconf/meson.build new file mode 100644 index 00000000000..30928a04c51 --- /dev/null +++ b/contrib/pkgconf/meson.build @@ -0,0 +1,199 @@ +project('pkgconf', 'c', + version : '2.5.1', + license : 'ISC', + default_options : ['c_std=c99'], + meson_version: '>=0.52', +) + +cc = meson.get_compiler('c') + +add_project_arguments( + '-D_BSD_SOURCE', + '-D_DEFAULT_SOURCE', + '-D_POSIX_C_SOURCE=200809L', + cc.get_supported_arguments( + '-Wimplicit-function-declaration', + '-Wmisleading-indentation', + ), + language : 'c', +) + +cdata = configuration_data() + +check_functions = [ + ['strlcat', 'string.h'], + ['strlcpy', 'string.h'], + ['strndup', 'string.h'], + ['strdup', 'string.h'], + ['strncasecmp', 'strings.h'], + ['strcasecmp', 'strings.h'], + ['reallocarray', 'stdlib.h'], + ['pledge', 'unistd.h'], + ['unveil', 'unistd.h'], +] + +foreach f : check_functions + name = f[0].to_upper().underscorify() + if cc.has_function(f[0], prefix : '#define _BSD_SOURCE\n#define _DEFAULT_SOURCE\n#define _POSIX_C_SOURCE 200809L\n#include <@0@>'.format(f[1])) and cc.has_header_symbol(f[1], f[0], prefix : '#define _BSD_SOURCE\n#define _DEFAULT_SOURCE\n#define _POSIX_C_SOURCE 200809L') + cdata.set('HAVE_@0@'.format(name), 1) + cdata.set('HAVE_DECL_@0@'.format(name), 1) + else + cdata.set('HAVE_DECL_@0@'.format(name), 0) + endif +endforeach + +default_path = [] +foreach f : ['libdir', 'datadir'] + default_path += [join_paths(get_option('prefix'), get_option(f), 'pkgconfig')] +endforeach + +personality_path = [] +foreach f : ['libdir', 'datadir'] + personality_path += [join_paths(get_option('prefix'), get_option(f), 'pkgconfig', 'personality.d')] +endforeach + +SYSTEM_LIBDIR = get_option('with-system-libdir') +if SYSTEM_LIBDIR != '' + cdata.set_quoted('SYSTEM_LIBDIR', SYSTEM_LIBDIR) +else + cdata.set_quoted('SYSTEM_LIBDIR', join_paths(get_option('prefix'), get_option('libdir'))) +endif +SYSTEM_INCLUDEDIR = get_option('with-system-includedir') +if SYSTEM_INCLUDEDIR != '' + cdata.set_quoted('SYSTEM_INCLUDEDIR', SYSTEM_INCLUDEDIR) +else + cdata.set_quoted('SYSTEM_INCLUDEDIR', join_paths(get_option('prefix'), get_option('includedir'))) +endif +cdata.set_quoted('PKG_DEFAULT_PATH', ':'.join(default_path)) +cdata.set_quoted('PERSONALITY_PATH', ':'.join(personality_path)) +cdata.set_quoted('PACKAGE_NAME', meson.project_name()) +cdata.set_quoted('PACKAGE_VERSION', meson.project_version()) +cdata.set_quoted('PACKAGE_BUGREPORT', 'https://todo.sr.ht/~kaniini/pkgconf') +cdata.set('abs_top_srcdir', meson.current_source_dir()) +cdata.set('abs_top_builddir', meson.current_build_dir()) + + +subdir('libpkgconf') + +libtype = get_option('default_library') +if libtype == 'static' + build_static = '-DPKGCONFIG_IS_STATIC' +else + build_static = '-DPKGCONFIG_IS_NOT_STATIC' +endif + +libpkgconf = library('pkgconf', + 'libpkgconf/argvsplit.c', + 'libpkgconf/audit.c', + 'libpkgconf/buffer.c', + 'libpkgconf/bsdstubs.c', + 'libpkgconf/cache.c', + 'libpkgconf/client.c', + 'libpkgconf/dependency.c', + 'libpkgconf/fileio.c', + 'libpkgconf/fragment.c', + 'libpkgconf/parser.c', + 'libpkgconf/path.c', + 'libpkgconf/personality.c', + 'libpkgconf/pkg.c', + 'libpkgconf/queue.c', + 'libpkgconf/tuple.c', + c_args: ['-DLIBPKGCONF_EXPORT', build_static], + install : true, + version : '7.0.0', + soversion : '7', +) + +# For other projects using libpkgconfig as a subproject +dep_libpkgconf = declare_dependency( + link_with : libpkgconf, + include_directories : include_directories('.'), +) + +# If we have a new enough meson override the dependency so that only +# `dependency('libpkgconf')` is required from the consumer +if meson.version().version_compare('>= 0.54.0') + meson.override_dependency('libpkgconf', dep_libpkgconf) +endif + +pkg = import('pkgconfig') +pkg.generate(libpkgconf, + name : 'libpkgconf', + description : 'a library for accessing and manipulating development framework configuration', + url: 'http://github.com/pkgconf/pkgconf', + filebase : 'libpkgconf', + subdirs: ['pkgconf'], + extra_cflags : build_static +) + +cli_include = include_directories('cli') + +pkgconf_exe = executable('pkgconf', + 'cli/main.c', + 'cli/getopt_long.c', + 'cli/renderer-msvc.c', + link_with : libpkgconf, + c_args : build_static, + include_directories : cli_include, + install : true) + +bomtool_exe = executable('bomtool', + 'cli/bomtool/main.c', + 'cli/getopt_long.c', + link_with : libpkgconf, + c_args : build_static, + include_directories : cli_include, + install : true) + +with_tests = get_option('tests') +kyua_exe = find_program('kyua', required : with_tests, disabler : true, native : true) +atf_sh_exe = find_program('atf-sh', required : with_tests, disabler : true, native : true) +kyuafile = configure_file(input : 'Kyuafile.in', output : 'Kyuafile', configuration : cdata) +test('kyua', kyua_exe, args : ['--config=none', 'test', '--kyuafile', kyuafile, '--build-root', meson.current_build_dir()]) +subdir('tests') + +install_man('man/bomtool.1') +install_man('man/pkgconf.1') +install_man('man/pkg.m4.7') +install_man('man/pc.5') +install_man('man/pkgconf-personality.5') +install_data('pkg.m4', install_dir: 'share/aclocal') +install_data('AUTHORS', install_dir: 'share/doc/pkgconf') +install_data('README.md', install_dir: 'share/doc/pkgconf') + +if host_machine.system() == 'windows' + conf_data = configuration_data() + conf_data.set('VERSION', meson.project_version()) + conf_data.set('EXE', pkgconf_exe.full_path()) + conf_data.set('DLL', libpkgconf.full_path()) + if host_machine.cpu() != 'x86_64' + wixl_arch = 'x86' + else + wixl_arch = 'x64' + endif + conf_data.set('WIXL_ARCH', wixl_arch) + + python = find_program('python3') + wixl = find_program('wixl', required: false, version: '>= 0.105') + msi_filename = 'pkgconf-@0@-@1@.msi'.format(wixl_arch, meson.project_version()) + + wxsfile = configure_file(input: 'pkgconf.wxs.in', output: 'pkgconf.wxs', configuration: conf_data) + + if wixl.found() + licensefile = custom_target( + 'License.rtf', + input: 'COPYING', + output: 'License.rtf', + command: [python, files('txt2rtf.py'), '@INPUT@', '@OUTPUT@'], + ) + + msi = custom_target( + msi_filename, + input: [wxsfile, licensefile, pkgconf_exe], + output: msi_filename, + command: [wixl, '--arch', wixl_arch, '--ext', 'ui', '-o', msi_filename, wxsfile], + ) + + alias_target('msi', msi) + endif +endif diff --git a/contrib/pkgconf/meson_options.txt b/contrib/pkgconf/meson_options.txt new file mode 100644 index 00000000000..e1360520bee --- /dev/null +++ b/contrib/pkgconf/meson_options.txt @@ -0,0 +1,19 @@ +option( + 'tests', + type: 'feature', + description: 'Build tests which depends upon the kyua framework', +) + +option( + 'with-system-libdir', + type: 'string', + value: '', + description: 'Specify the system library directory (default {prefix}/{libdir})' +) + +option( + 'with-system-includedir', + type: 'string', + value: '', + description: 'Specify the system include directory (default {prefix}/{includedir})' +) diff --git a/contrib/pkgconf/pkg.m4 b/contrib/pkgconf/pkg.m4 new file mode 100644 index 00000000000..ec5a70da38f --- /dev/null +++ b/contrib/pkgconf/pkg.m4 @@ -0,0 +1,350 @@ +# pkg.m4 - Macros to locate and use pkg-config. -*- Autoconf -*- +# serial 13 (pkgconf) + +dnl Copyright © 2004 Scott James Remnant . +dnl Copyright © 2012-2015 Dan Nicholson +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl This program is distributed in the hope that it will be useful, but +dnl WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, see . +dnl +dnl As a special exception to the GNU General Public License, if you +dnl distribute this file as part of a program that contains a +dnl configuration script generated by Autoconf, you may include it under +dnl the same distribution terms that you use for the rest of that +dnl program. + +dnl PKG_PREREQ(MIN-VERSION) +dnl ----------------------- +dnl Since: 0.29 +dnl +dnl Verify that the version of the pkg-config macros are at least +dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's +dnl installed version of pkg-config, this checks the developer's version +dnl of pkg.m4 when generating configure. +dnl +dnl To ensure that this macro is defined, also add: +dnl m4_ifndef([PKG_PREREQ], +dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) +dnl +dnl See the "Since" comment for each macro you use to see what version +dnl of the macros you require. +m4_defun([PKG_PREREQ], +[m4_define([PKG_MACROS_VERSION], [0.29.2]) +m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, + [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) +])dnl PKG_PREREQ + +dnl PKG_PROG_PKG_CONFIG([MIN-VERSION], [ACTION-IF-NOT-FOUND]) +dnl --------------------------------------------------------- +dnl Since: 0.16 +dnl +dnl Search for the pkg-config tool and set the PKG_CONFIG variable to +dnl first found in the path. Checks that the version of pkg-config found +dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is +dnl used since that's the first version where most current features of +dnl pkg-config existed. +dnl +dnl If pkg-config is not found or older than specified, it will result +dnl in an empty PKG_CONFIG variable. To avoid widespread issues with +dnl scripts not checking it, ACTION-IF-NOT-FOUND defaults to aborting. +dnl You can specify [PKG_CONFIG=false] as an action instead, which would +dnl result in pkg-config tests failing, but no bogus error messages. +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) +m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) +AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) +AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi +fi +if test -z "$PKG_CONFIG"; then + m4_default([$2], [AC_MSG_ERROR([pkg-config not found])]) +fi[]dnl +])dnl PKG_PROG_PKG_CONFIG + +dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ------------------------------------------------------------------- +dnl Since: 0.18 +dnl +dnl Check to see whether a particular set of modules exists. Similar to +dnl PKG_CHECK_MODULES(), but does not set variables or print errors. +dnl +dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +dnl only at the first occurrence in configure.ac, so if the first place +dnl it's called might be skipped (such as if it is within an "if", you +dnl have to call PKG_CHECK_EXISTS manually +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_default([$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + +dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +dnl --------------------------------------------- +dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting +dnl pkg_failed based on the result. +m4_define([_PKG_CONFIG], +[if test -n "$$1"; then + pkg_cv_[]$1="$$1" + elif test -n "$PKG_CONFIG"; then + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes ], + [pkg_failed=yes]) + else + pkg_failed=untried +fi[]dnl +])dnl _PKG_CONFIG + +dnl _PKG_SHORT_ERRORS_SUPPORTED +dnl --------------------------- +dnl Internal check to see if pkg-config supports short errors. +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])dnl _PKG_SHORT_ERRORS_SUPPORTED + + +dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +dnl [ACTION-IF-NOT-FOUND]) +dnl -------------------------------------------------------------- +dnl Since: 0.4.0 +dnl +dnl Note that if there is a possibility the first call to +dnl PKG_CHECK_MODULES might not happen, you should be sure to include an +dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $2]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + AC_MSG_RESULT([no]) + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + m4_default([$4], [AC_MSG_ERROR( +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT])[]dnl + ]) +elif test $pkg_failed = untried; then + AC_MSG_RESULT([no]) + m4_default([$4], [AC_MSG_FAILURE( +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see .])[]dnl + ]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + $3 +fi[]dnl +])dnl PKG_CHECK_MODULES + + +dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +dnl [ACTION-IF-NOT-FOUND]) +dnl --------------------------------------------------------------------- +dnl Since: 0.29 +dnl +dnl Checks for existence of MODULES and gathers its build flags with +dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags +dnl and VARIABLE-PREFIX_LIBS from --libs. +dnl +dnl Note that if there is a possibility the first call to +dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to +dnl include an explicit call to PKG_PROG_PKG_CONFIG in your +dnl configure.ac. +AC_DEFUN([PKG_CHECK_MODULES_STATIC], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +_save_PKG_CONFIG=$PKG_CONFIG +PKG_CONFIG="$PKG_CONFIG --static" +PKG_CHECK_MODULES($@) +PKG_CONFIG=$_save_PKG_CONFIG[]dnl +])dnl PKG_CHECK_MODULES_STATIC + + +dnl PKG_INSTALLDIR([DIRECTORY]) +dnl ------------------------- +dnl Since: 0.27 +dnl +dnl Substitutes the variable pkgconfigdir as the location where a module +dnl should install pkg-config .pc files. By default the directory is +dnl $libdir/pkgconfig, but the default can be changed by passing +dnl DIRECTORY. The user can override through the --with-pkgconfigdir +dnl parameter. +AC_DEFUN([PKG_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([pkgconfigdir], + [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, + [with_pkgconfigdir=]pkg_default) +AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +])dnl PKG_INSTALLDIR + + +dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) +dnl -------------------------------- +dnl Since: 0.27 +dnl +dnl Substitutes the variable noarch_pkgconfigdir as the location where a +dnl module should install arch-independent pkg-config .pc files. By +dnl default the directory is $datadir/pkgconfig, but the default can be +dnl changed by passing DIRECTORY. The user can override through the +dnl --with-noarch-pkgconfigdir parameter. +AC_DEFUN([PKG_NOARCH_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([noarch-pkgconfigdir], + [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, + [with_noarch_pkgconfigdir=]pkg_default) +AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +])dnl PKG_NOARCH_INSTALLDIR + + +dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, +dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ------------------------------------------- +dnl Since: 0.28 +dnl +dnl Retrieves the value of the pkg-config variable for the given module. +AC_DEFUN([PKG_CHECK_VAR], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl + +_PKG_CONFIG([$1], [variable="][$3]["], [$2]) +AS_VAR_COPY([$1], [pkg_cv_][$1]) + +AS_VAR_IF([$1], [""], [$5], [$4])dnl +])dnl PKG_CHECK_VAR + +dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES, +dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND], +dnl [DESCRIPTION], [DEFAULT]) +dnl ------------------------------------------ +dnl +dnl Prepare a "--with-" configure option using the lowercase +dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and +dnl PKG_CHECK_MODULES in a single macro. +AC_DEFUN([PKG_WITH_MODULES], +[ +m4_pushdef([with_arg], m4_tolower([$1])) + +m4_pushdef([description], + [m4_default([$5], [build with ]with_arg[ support])]) + +m4_pushdef([def_arg], [m4_default([$6], [auto])]) +m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes]) +m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no]) + +m4_case(def_arg, + [yes],[m4_pushdef([with_without], [--without-]with_arg)], + [m4_pushdef([with_without],[--with-]with_arg)]) + +AC_ARG_WITH(with_arg, + AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),, + [AS_TR_SH([with_]with_arg)=def_arg]) + +AS_CASE([$AS_TR_SH([with_]with_arg)], + [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)], + [auto],[PKG_CHECK_MODULES([$1],[$2], + [m4_n([def_action_if_found]) $3], + [m4_n([def_action_if_not_found]) $4])]) + +m4_popdef([with_arg]) +m4_popdef([description]) +m4_popdef([def_arg]) + +])dnl PKG_WITH_MODULES + +dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES, +dnl [DESCRIPTION], [DEFAULT]) +dnl ----------------------------------------------- +dnl +dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES +dnl check._[VARIABLE-PREFIX] is exported as make variable. +AC_DEFUN([PKG_HAVE_WITH_MODULES], +[ +PKG_WITH_MODULES([$1],[$2],,,[$3],[$4]) + +AM_CONDITIONAL([HAVE_][$1], + [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"]) +])dnl PKG_HAVE_WITH_MODULES + +dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES, +dnl [DESCRIPTION], [DEFAULT]) +dnl ------------------------------------------------------ +dnl +dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after +dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make +dnl and preprocessor variable. +AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES], +[ +PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4]) + +AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], + [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])]) +])dnl PKG_HAVE_DEFINE_WITH_MODULES diff --git a/contrib/pkgconf/pkgconf.wxs.in b/contrib/pkgconf/pkgconf.wxs.in new file mode 100644 index 00000000000..13ebb069a50 --- /dev/null +++ b/contrib/pkgconf/pkgconf.wxs.in @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/pkgconf/tests/Kyuafile.in b/contrib/pkgconf/tests/Kyuafile.in new file mode 100644 index 00000000000..f50b749fd71 --- /dev/null +++ b/contrib/pkgconf/tests/Kyuafile.in @@ -0,0 +1,14 @@ +syntax(2) + +test_suite('pkgconf') + +atf_test_program{name='basic'} +atf_test_program{name='requires'} +atf_test_program{name='regress'} +atf_test_program{name='parser'} +atf_test_program{name='sysroot'} +atf_test_program{name='conflicts'} +atf_test_program{name='version'} +atf_test_program{name='framework'} +atf_test_program{name='provides'} +atf_test_program{name='symlink'} diff --git a/contrib/pkgconf/tests/basic.sh b/contrib/pkgconf/tests/basic.sh new file mode 100755 index 00000000000..8debb32f2b3 --- /dev/null +++ b/contrib/pkgconf/tests/basic.sh @@ -0,0 +1,391 @@ +#!/usr/bin/env atf-sh + +. $(atf_get_srcdir)/test_env.sh + +tests_init \ + noargs \ + libs \ + libs_cflags \ + libs_cflags_version \ + libs_cflags_version_multiple \ + libs_cflags_version_alt \ + libs_cflags_version_different \ + libs_cflags_version_different_bad \ + libs_env \ + exists_nonexitent \ + nonexitent \ + exists_version \ + exists_version_bad \ + exists_version_bad2 \ + exists_version_bad3 \ + exists \ + exists2 \ + exists3 \ + exists_version_alt \ + exists_cflags \ + exists_cflags_env \ + uninstalled_bad \ + uninstalled \ + libs_intermediary \ + libs_circular1 \ + libs_circular2 \ + libs_circular_directpc \ + libs_static \ + libs_static_ordering \ + libs_metapackage \ + license_isc \ + license_noassertion \ + modversion_noflatten \ + pkg_config_path \ + nolibs \ + nocflags \ + arbitary_path \ + with_path \ + relocatable \ + single_depth_selectors \ + print_variables_env \ + variable_env \ + variable_no_recurse + +noargs_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check -s exit:1 -e ignore pkgconf +} + +libs_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L/test/lib -lfoo\n" \ + pkgconf --libs foo +} + +libs_cflags_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-fPIC -I/test/include/foo -L/test/lib -lfoo\n" \ + pkgconf --cflags --libs foo +} + +atf_test_case basic_libs_cflags_version +libs_cflags_version_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-fPIC -I/test/include/foo -L/test/lib -lfoo\n" \ + pkgconf --cflags --libs 'foo > 1.2' +} + +libs_cflags_version_multiple_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-fPIC -I/test/include/foo -L/test/lib -lbar -lfoo\n" \ + pkgconf --cflags --libs 'foo > 1.2 bar >= 1.3' +} + +libs_cflags_version_multiple_coma_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-fPIC -I/test/include/foo -L/test/lib -lbar -lfoo\n" \ + pkgconf --cflags --libs 'foo > 1.2,bar >= 1.3' +} + +libs_cflags_version_alt_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-fPIC -I/test/include/foo -L/test/lib -lfoo\n" \ + pkgconf --cflags --libs 'foo' '>' '1.2' +} + +libs_cflags_version_different_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-fPIC -I/test/include/foo -L/test/lib -lfoo\n" \ + pkgconf --cflags --libs 'foo' '!=' '1.3.0' +} + +atf_test_case basic_libs_cflags_version_different_bad +libs_cflags_version_different_bad_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -s exit:1 \ + -e inline:"Package dependency requirement 'foo != 1.2.3' could not be satisfied.\nPackage 'foo' has version '1.2.3', required version is '!= 1.2.3'\n" \ + pkgconf --cflags --libs 'foo' '!=' '1.2.3' +} + +exists_nonexitent_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -s exit:1 \ + pkgconf --exists nonexistant +} + +nonexitent_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -s exit:1 \ + pkgconf nonexistant +} + +exists_version_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + pkgconf --exists 'foo > 1.2' +} + +exists_version_bad_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -s exit:1 \ + pkgconf --exists 'foo > 1.2.3' +} + +exists_version_alt_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + pkgconf --exists 'foo' '>' '1.2' +} + +uninstalled_bad_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -s exit:1 \ + pkgconf --uninstalled 'foo' +} + +uninstalled_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + pkgconf --uninstalled 'omg' +} + +exists_version_bad2_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -s exit:1 \ + pkgconf --exists 'foo >= ' +} + +exists_version_bad3_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -s exit:1 \ + pkgconf --exists 'tilde >= 1.0.0' +} + +exists_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + pkgconf --exists 'tilde = 1.0.0~rc1' +} + +exists2_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + pkgconf --exists 'tilde <= 1.0.0' +} + +exists3_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + pkgconf --exists '' 'foo' +} + +libs_intermediary_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-lintermediary-1 -lintermediary-2 -lfoo -lbar -lbaz\n" \ + pkgconf --libs intermediary-1 intermediary-2 +} + +libs_circular2_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"circular-1: breaking circular reference (circular-1 -> circular-2 -> circular-1)\n" \ + pkgconf circular-2 --validate +} + +libs_circular1_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"circular-3: breaking circular reference (circular-3 -> circular-1 -> circular-3)\n" \ + pkgconf circular-1 --validate +} + +libs_circular_directpc_body() +{ + atf_check \ + -o inline:"-lcircular-3 -lcircular-1 -lcircular-2\n" \ + pkgconf --libs ${selfdir}/lib1/circular-3.pc +} + +libs_static_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"/libfoo.a -pthread\n" \ + pkgconf --libs static-archive-libs +} + +libs_static_ordering_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L/test/lib -lbar -lfoo\n" \ + pkgconf --libs foo bar +} + +libs_metapackage_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L/test/lib -lbar -lfoo\n" \ + pkgconf --static --libs metapackage-3 +} + +pkg_config_path_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1${PATH_SEP}${selfdir}/lib2" + atf_check \ + -o inline:"-L/test/lib -lfoo\n" \ + pkgconf --libs foo + atf_check \ + -o inline:"-L/test/lib -lbar -lfoo\n" \ + pkgconf --libs bar +} + +with_path_body() +{ + atf_check \ + -o inline:"-L/test/lib -lfoo\n" \ + pkgconf --with-path=${selfdir}/lib1 --with-path=${selfdir}/lib2 --libs foo + atf_check \ + -o inline:"-L/test/lib -lbar -lfoo\n" \ + pkgconf --with-path=${selfdir}/lib1 --with-path=${selfdir}/lib2 --libs bar +} + +nolibs_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"\n" \ + pkgconf --libs nolib +} + +nocflags_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"\n" \ + pkgconf --cflags nocflag +} + +arbitary_path_body() +{ + cp ${selfdir}/lib1/foo.pc . + atf_check \ + -o inline:"-L/test/lib -lfoo\n" \ + pkgconf --libs foo.pc +} + +relocatable_body() +{ + basedir=$(pkgconf --relocate ${selfdir}) + atf_check \ + -o inline:"${basedir}/lib-relocatable\n" \ + pkgconf --define-prefix --variable=prefix ${basedir}/lib-relocatable/lib/pkgconfig/foo.pc +} + +single_depth_selectors_body() +{ + export PKG_CONFIG_MAXIMUM_TRAVERSE_DEPTH=1 + atf_check \ + -o inline:"foo\n" \ + pkgconf --with-path=${selfdir}/lib3 --print-requires bar +} + +license_isc_body() +{ + atf_check \ + -o inline:"foo: ISC\n" \ + pkgconf --with-path=${selfdir}/lib1 --license foo +} + +license_noassertion_body() +{ + atf_check \ + -o inline:"bar: NOASSERTION\nfoo: ISC\n" \ + pkgconf --with-path=${selfdir}/lib1 --license bar +} + +modversion_noflatten_body() +{ + atf_check \ + -o inline:"1.3\n" \ + pkgconf --with-path=${selfdir}/lib1 --modversion bar +} + +exists_cflags_body() +{ + atf_check \ + -o inline:"-DHAVE_FOO\n" \ + pkgconf --with-path=${selfdir}/lib1 --cflags --exists-cflags --fragment-filter=D foo +} + +exists_cflags_env_body() +{ + atf_check \ + -o inline:"FOO_CFLAGS='-DHAVE_FOO'\n" \ + pkgconf --with-path=${selfdir}/lib1 --cflags --exists-cflags --fragment-filter=D --env=FOO foo +} + +libs_env_body() +{ + atf_check \ + -o inline:"FOO_LIBS='-L/test/lib -lfoo'\n" \ + pkgconf --with-path=${selfdir}/lib1 --libs --env=FOO foo +} + +print_variables_env_body() +{ + atf_check \ + -o inline:"FOO_CFLAGS='-fPIC -I/test/include/foo'\nFOO_LIBS='-L/test/lib -lfoo'\nFOO_INCLUDEDIR='/test/include'\nFOO_LIBDIR='/test/lib'\nFOO_EXEC_PREFIX='/test'\nFOO_PREFIX='/test'\nFOO_PCFILEDIR='${selfdir}/lib1'\n" \ + pkgconf --with-path=${selfdir}/lib1 --env=FOO --print-variables --cflags --libs foo + +} + +variable_env_body() +{ + atf_check \ + -o inline:"FOO_INCLUDEDIR='/test/include'\n" \ + pkgconf --with-path=${selfdir}/lib1 --env=FOO --variable=includedir foo +} + +variable_no_recurse_body() +{ + atf_check \ + -o inline:"/test/include\n" \ + pkgconf --with-path=${selfdir}/lib1 --variable=includedir bar +} diff --git a/contrib/pkgconf/tests/builtins.sh b/contrib/pkgconf/tests/builtins.sh new file mode 100755 index 00000000000..cbdea37e0c5 --- /dev/null +++ b/contrib/pkgconf/tests/builtins.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env atf-sh + +. $(atf_get_srcdir)/test_env.sh + +tests_init \ + modversion \ + variable \ + define_variable \ + global_variable + +modversion_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"1.0.1 \n" \ + pkgconf --modversion pkg-config +} + +variable_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"/test \n" \ + pkgconf --variable=prefix foo +} + +define_variable_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"/test2 \n" \ + pkgconf --define-variable=prefix=/test2 --variable=prefix foo +} + +global_variable_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"${selfdir}/lib1 \n" + pkgconf --exists -foo +} + +argv_parse_3_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-llib-1 -pthread /test/lib/lib2.so \n" \ + pkgconf --libs argv-parse-3 +} + +tilde_quoting_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L~ -ltilde \n" \ + pkgconf --libs tilde-quoting + atf_check \ + -o inline:"-I~ \n" \ + pkgconf --cflags tilde-quoting +} + +paren_quoting_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L\$(libdir) -ltilde \n" \ + pkgconf --libs paren-quoting +} diff --git a/contrib/pkgconf/tests/conflicts.sh b/contrib/pkgconf/tests/conflicts.sh new file mode 100755 index 00000000000..da6396f6b42 --- /dev/null +++ b/contrib/pkgconf/tests/conflicts.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env atf-sh + +. $(atf_get_srcdir)/test_env.sh + +tests_init \ + libs \ + ignore + +libs_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L/test/lib -lconflicts\n" \ + pkgconf --libs conflicts +} + +ignore_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L/test/lib -lconflicts\n" \ + pkgconf --ignore-conflicts --libs conflicts +} diff --git a/contrib/pkgconf/tests/framework.sh b/contrib/pkgconf/tests/framework.sh new file mode 100755 index 00000000000..36769233574 --- /dev/null +++ b/contrib/pkgconf/tests/framework.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env atf-sh + +. $(atf_get_srcdir)/test_env.sh + +tests_init \ + libs + +libs_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-F/test/lib -framework framework-1\n" \ + pkgconf --libs framework-1 + atf_check \ + -o inline:"-F/test/lib -framework framework-2 -framework framework-1\n" \ + pkgconf --libs framework-2 + atf_check \ + -o inline:"-F/test/lib -framework framework-2 -framework framework-1\n" \ + pkgconf --libs framework-1 framework-2 +} diff --git a/contrib/pkgconf/tests/lib-relocatable/lib/pkgconfig/foo.pc b/contrib/pkgconf/tests/lib-relocatable/lib/pkgconfig/foo.pc new file mode 100644 index 00000000000..f154eb76ccf --- /dev/null +++ b/contrib/pkgconf/tests/lib-relocatable/lib/pkgconfig/foo.pc @@ -0,0 +1,11 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: foo +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -L${libdir} -lfoo +Cflags: -fPIC -I${includedir}/foo +Cflags.private: -DFOO_STATIC diff --git a/contrib/pkgconf/tests/lib1/argv-parse-2.pc b/contrib/pkgconf/tests/lib1/argv-parse-2.pc new file mode 100644 index 00000000000..cfc0fcf904b --- /dev/null +++ b/contrib/pkgconf/tests/lib1/argv-parse-2.pc @@ -0,0 +1,10 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: argv-parse-2 +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -llib-1 -pthread ${libdir}/lib2.so +Cflags: diff --git a/contrib/pkgconf/tests/lib1/argv-parse-3.pc b/contrib/pkgconf/tests/lib1/argv-parse-3.pc new file mode 100644 index 00000000000..b115b300120 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/argv-parse-3.pc @@ -0,0 +1,11 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: argv-parse-3 +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -llib-1 \ + -pthread ${libdir}/lib2.so +Cflags: diff --git a/contrib/pkgconf/tests/lib1/argv-parse.pc b/contrib/pkgconf/tests/lib1/argv-parse.pc new file mode 100644 index 00000000000..d445ae78e7f --- /dev/null +++ b/contrib/pkgconf/tests/lib1/argv-parse.pc @@ -0,0 +1,10 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: argv-parse +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -llib-3 -llib-1 -llib-2 -lpthread +Cflags: diff --git a/contrib/pkgconf/tests/lib1/bar.pc b/contrib/pkgconf/tests/lib1/bar.pc new file mode 100644 index 00000000000..9157d06b5fd --- /dev/null +++ b/contrib/pkgconf/tests/lib1/bar.pc @@ -0,0 +1,10 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: bar +Description: Another pkg-config test +Version: 1.3 +Libs: -L${libdir} -lbar +Requires: foo diff --git a/contrib/pkgconf/tests/lib1/baz.pc b/contrib/pkgconf/tests/lib1/baz.pc new file mode 100644 index 00000000000..d9cb258020b --- /dev/null +++ b/contrib/pkgconf/tests/lib1/baz.pc @@ -0,0 +1,11 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: bar +Description: Another pkg-config test (with private Requires, ha!) +Version: 1.3 +Libs: -L${libdir} -lbaz +Libs.private: -L${libdir} -lzee +Requires.private: foo diff --git a/contrib/pkgconf/tests/lib1/billion-laughs.pc b/contrib/pkgconf/tests/lib1/billion-laughs.pc new file mode 100644 index 00000000000..ad1ee7948b4 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/billion-laughs.pc @@ -0,0 +1,13 @@ +v9=lol +v8=${v9}${v9}${v9}${v9}${v9}${v9}${v9}${v9}${v9}${v9} +v7=${v8}${v8}${v8}${v8}${v8}${v8}${v8}${v8}${v8}${v8} +v6=${v7}${v7}${v7}${v7}${v7}${v7}${v7}${v7}${v7}${v7} +v5=${v6}${v6}${v6}${v6}${v6}${v6}${v6}${v6}${v6}${v6} +v4=${v5}${v5}${v5}${v5}${v5}${v5}${v5}${v5}${v5}${v5} +v3=${v4}${v4}${v4}${v4}${v4}${v4}${v4}${v4}${v4}${v4} +v2=${v3}${v3}${v3}${v3}${v3}${v3}${v3}${v3}${v3}${v3} +v1=${v2}${v2}${v2}${v2}${v2}${v2}${v2}${v2}${v2}${v2} +v0=${v1}${v1}${v1}${v1}${v1}${v1}${v1}${v1}${v1}${v1} +Name: One Billion Laughs +Version: ${v0} +Description: Don't install this! diff --git a/contrib/pkgconf/tests/lib1/c-comment.pc b/contrib/pkgconf/tests/lib1/c-comment.pc new file mode 100644 index 00000000000..ed776a1f33e --- /dev/null +++ b/contrib/pkgconf/tests/lib1/c-comment.pc @@ -0,0 +1,17 @@ +/* + This is a C-style comment, which technically isn't allowed, but + some pkg-config files use them. + */ + +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: c-comment +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -L${libdir} -lfoo +Cflags: -fPIC -I${includedir}/foo +Cflags.private: -DFOO_STATIC +License: ISC diff --git a/contrib/pkgconf/tests/lib1/case-sensitivity.pc b/contrib/pkgconf/tests/lib1/case-sensitivity.pc new file mode 100644 index 00000000000..9618061c485 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/case-sensitivity.pc @@ -0,0 +1,7 @@ +foo=3 +Foo=4 + +Name: case-sensitivity +Description: Package for testing case-sensitivity +Version: 1 +Requires: foo < ${foo} diff --git a/contrib/pkgconf/tests/lib1/cflags-internal.pc b/contrib/pkgconf/tests/lib1/cflags-internal.pc new file mode 100644 index 00000000000..500e566df71 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/cflags-internal.pc @@ -0,0 +1,9 @@ +prefix=/test/local +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: cflags-internal +Description: A testing pkg-config file +Version: 1.2.3 +Cflags: -I${includedir}/foo diff --git a/contrib/pkgconf/tests/lib1/cflags-libs-only.pc b/contrib/pkgconf/tests/lib1/cflags-libs-only.pc new file mode 100644 index 00000000000..718e7cd9501 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/cflags-libs-only.pc @@ -0,0 +1,10 @@ +prefix=/test/local +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: cflags-libs-only +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -L${libdir} -lfoo +Cflags: -I${includedir}/foo diff --git a/contrib/pkgconf/tests/lib1/cflags-libs-private-a.pc b/contrib/pkgconf/tests/lib1/cflags-libs-private-a.pc new file mode 100644 index 00000000000..fbb7742ba2e --- /dev/null +++ b/contrib/pkgconf/tests/lib1/cflags-libs-private-a.pc @@ -0,0 +1,7 @@ +Name: cflags-libs-private-a +Version: 1 +Description: test case for issue #370 +Cflags: +Libs: +Requires.private: cflags-libs-private-b + diff --git a/contrib/pkgconf/tests/lib1/cflags-libs-private-b.pc b/contrib/pkgconf/tests/lib1/cflags-libs-private-b.pc new file mode 100644 index 00000000000..0d5e5a45bfa --- /dev/null +++ b/contrib/pkgconf/tests/lib1/cflags-libs-private-b.pc @@ -0,0 +1,7 @@ +Name: cflags-libs-private-b +Version: 1 +Description: test case for issue #370 +Cflags: +Libs: +Requires: cflags-libs-private-c + diff --git a/contrib/pkgconf/tests/lib1/cflags-libs-private-c.pc b/contrib/pkgconf/tests/lib1/cflags-libs-private-c.pc new file mode 100644 index 00000000000..7efc38e4bb9 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/cflags-libs-private-c.pc @@ -0,0 +1,6 @@ +Name: cflags-libs-private-c +Version: 1 +Description: test case for issue #370 +Cflags: +Libs: -lc + diff --git a/contrib/pkgconf/tests/lib1/cflags-whitespace-trailing.pc b/contrib/pkgconf/tests/lib1/cflags-whitespace-trailing.pc new file mode 100644 index 00000000000..2e660353b00 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/cflags-whitespace-trailing.pc @@ -0,0 +1,4 @@ +Name: CFlags Trailing Whitespace Bug +Description: Demonstrates problems with -I with spaces +Version: 1 +Cflags: -I/usr/include -I/usr/include/foo diff --git a/contrib/pkgconf/tests/lib1/cflags-whitespace.pc b/contrib/pkgconf/tests/lib1/cflags-whitespace.pc new file mode 100644 index 00000000000..4cd1cd164aa --- /dev/null +++ b/contrib/pkgconf/tests/lib1/cflags-whitespace.pc @@ -0,0 +1,4 @@ +Name: CFlags Whitespace Bug +Description: Demonstrates problems with -I with spaces and sysroot munging +Version: 1 +Cflags: -I /opt/bad/include diff --git a/contrib/pkgconf/tests/lib1/child-prefix/pkgconfig/child-prefix-1.pc b/contrib/pkgconf/tests/lib1/child-prefix/pkgconfig/child-prefix-1.pc new file mode 100644 index 00000000000..0d32d89d925 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/child-prefix/pkgconfig/child-prefix-1.pc @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=/usr +libdir=${prefix}/lib64 +includedir=${prefix}/include + +Name: child-prefix-1 +Description: child prefix 1 test data +Requires: +Version: 1.0 +Libs: -L${libdir} -lchild-prefix-1 +Cflags: -I${includedir}/child-prefix-1 diff --git a/contrib/pkgconf/tests/lib1/circular-1.pc b/contrib/pkgconf/tests/lib1/circular-1.pc new file mode 100644 index 00000000000..bbdb9f79cc8 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/circular-1.pc @@ -0,0 +1,11 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: circular-1 +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -lcircular-1 +Requires: circular-2 +Cflags: diff --git a/contrib/pkgconf/tests/lib1/circular-2.pc b/contrib/pkgconf/tests/lib1/circular-2.pc new file mode 100644 index 00000000000..2086834a6b8 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/circular-2.pc @@ -0,0 +1,11 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: circular-2 +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -lcircular-2 +Requires: circular-3 +Cflags: diff --git a/contrib/pkgconf/tests/lib1/circular-3.pc b/contrib/pkgconf/tests/lib1/circular-3.pc new file mode 100644 index 00000000000..e8b8e77a6c5 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/circular-3.pc @@ -0,0 +1,11 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: circular-3 +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -lcircular-3 +Requires: circular-1 +Cflags: diff --git a/contrib/pkgconf/tests/lib1/comments-in-fields.pc b/contrib/pkgconf/tests/lib1/comments-in-fields.pc new file mode 100644 index 00000000000..8e65a6557f4 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/comments-in-fields.pc @@ -0,0 +1,9 @@ +Name: comments-in-fields +Description: Comments in fields test case +URL: http://pkgconf.org/ +Version: 0 +Requires: # foo +Requires.private: foo +Libs: -lfoo # -lbar +Libs.private: -lbar +Cflags: -I${includedir} diff --git a/contrib/pkgconf/tests/lib1/comments.pc b/contrib/pkgconf/tests/lib1/comments.pc new file mode 100644 index 00000000000..be83b3e63b4 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/comments.pc @@ -0,0 +1,6 @@ +Name: comments +Description: Portable CD-ROM I/O library +Version: 0 +#Requires: bar-2.0 +Libs: -lfoo +Cflags: -I${includedir} diff --git a/contrib/pkgconf/tests/lib1/conflicts.pc b/contrib/pkgconf/tests/lib1/conflicts.pc new file mode 100644 index 00000000000..bec486982d6 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/conflicts.pc @@ -0,0 +1,11 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: conflicts +Description: A testing pkg-config file +Conflicts: foo <= 1.3 +Version: 1.2.3 +Libs: -L${libdir} -lconflicts +Cflags: -fPIC -I${includedir}/conflicts diff --git a/contrib/pkgconf/tests/lib1/depgraph-break.pc b/contrib/pkgconf/tests/lib1/depgraph-break.pc new file mode 100644 index 00000000000..61e00cc62bc --- /dev/null +++ b/contrib/pkgconf/tests/lib1/depgraph-break.pc @@ -0,0 +1,12 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: depgraph-break +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -L${libdir} -lfoo +Cflags: -fPIC -I${includedir}/foo +Cflags.private: -DFOO_STATIC +Requires: nonexistant diff --git a/contrib/pkgconf/tests/lib1/dos-lineendings.pc b/contrib/pkgconf/tests/lib1/dos-lineendings.pc new file mode 100644 index 00000000000..cf169f159c7 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/dos-lineendings.pc @@ -0,0 +1,10 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: dos-lineendings +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -L${libdir}/dos-lineendings -ldos-lineendings +Cflags: -fPIC -I${includedir}/dos-lineendings diff --git a/contrib/pkgconf/tests/lib1/empty-tuple.pc b/contrib/pkgconf/tests/lib1/empty-tuple.pc new file mode 100644 index 00000000000..a1794954a4f --- /dev/null +++ b/contrib/pkgconf/tests/lib1/empty-tuple.pc @@ -0,0 +1,6 @@ +xcflags= + +Name: empty-tuple +Description: testing file +Version: 1 +CFlags: ${xcflags} diff --git a/contrib/pkgconf/tests/lib1/escaped-backslash.pc b/contrib/pkgconf/tests/lib1/escaped-backslash.pc new file mode 100644 index 00000000000..6abcbb06459 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/escaped-backslash.pc @@ -0,0 +1,4 @@ +Name: escaped-backslash +Version: 1 +Description: test package for backslash escape scenario +Cflags: -IC:\\A diff --git a/contrib/pkgconf/tests/lib1/explicit-sysroot.pc b/contrib/pkgconf/tests/lib1/explicit-sysroot.pc new file mode 100644 index 00000000000..63c7ca042bd --- /dev/null +++ b/contrib/pkgconf/tests/lib1/explicit-sysroot.pc @@ -0,0 +1,7 @@ +prefix=/usr +datarootdir=${prefix}/share +pkgdatadir=${pc_sysrootdir}/${datarootdir}/test + +Name: Test +Description: Testing pc_sysrootdir auto-expansion +Version: 1.0 diff --git a/contrib/pkgconf/tests/lib1/flag-order-1.pc b/contrib/pkgconf/tests/lib1/flag-order-1.pc new file mode 100644 index 00000000000..d84175197b1 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/flag-order-1.pc @@ -0,0 +1,10 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include +cflags=-I${includedir} + +Name: flag order 1 +Description: test for flag ordering +Version: 1 +Libs: -L${libdir} -Bdynamic -lfoo -Bstatic -lbar diff --git a/contrib/pkgconf/tests/lib1/flag-order-3.pc b/contrib/pkgconf/tests/lib1/flag-order-3.pc new file mode 100644 index 00000000000..d813f50dbf4 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/flag-order-3.pc @@ -0,0 +1,10 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include +cflags=-I${includedir} + +Name: flag order 3 +Description: test for flag ordering +Version: 1 +Libs: -L${libdir} -Wl,--start-group -lfoo -lbar -Wl,--end-group diff --git a/contrib/pkgconf/tests/lib1/foo.pc b/contrib/pkgconf/tests/lib1/foo.pc new file mode 100644 index 00000000000..daef9f9fa95 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/foo.pc @@ -0,0 +1,12 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: foo +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -L${libdir} -lfoo +Cflags: -fPIC -I${includedir}/foo +Cflags.private: -DFOO_STATIC +License: ISC diff --git a/contrib/pkgconf/tests/lib1/foobar.pc b/contrib/pkgconf/tests/lib1/foobar.pc new file mode 100644 index 00000000000..e9aa8433760 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/foobar.pc @@ -0,0 +1,12 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: foobar +Description: A testing pkg-config file +Version: 3.2.1 +Libs: -L${libdir} -lfoobar +Cflags: -fPIC -I${includedir}/foobar +Cflags.private: -DFOOBAR_STATIC +License: ISC diff --git a/contrib/pkgconf/tests/lib1/fragment-collision-1.pc b/contrib/pkgconf/tests/lib1/fragment-collision-1.pc new file mode 100644 index 00000000000..201bcb0a5f9 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/fragment-collision-1.pc @@ -0,0 +1,4 @@ +Name: fragment-collision-1 +Version: 0 +Description: fragment collision test package +Cflags: -D_THREAD_SAFE -pthread diff --git a/contrib/pkgconf/tests/lib1/fragment-collision-2.pc b/contrib/pkgconf/tests/lib1/fragment-collision-2.pc new file mode 100644 index 00000000000..2e6feb4a61c --- /dev/null +++ b/contrib/pkgconf/tests/lib1/fragment-collision-2.pc @@ -0,0 +1,4 @@ +Name: fragment-collision-2 +Version: 0 +Description: fragment collision test package +CFlags: -D_FOO diff --git a/contrib/pkgconf/tests/lib1/fragment-collision-intermediary.pc b/contrib/pkgconf/tests/lib1/fragment-collision-intermediary.pc new file mode 100644 index 00000000000..1ddb6f502cf --- /dev/null +++ b/contrib/pkgconf/tests/lib1/fragment-collision-intermediary.pc @@ -0,0 +1,5 @@ +Name: fragment-collision-intermediary +Version: 0 +Description: fragment collision test package +Requires.private: fragment-collision-1, fragment-collision-2 +CFlags: -D_BAR diff --git a/contrib/pkgconf/tests/lib1/fragment-collision.pc b/contrib/pkgconf/tests/lib1/fragment-collision.pc new file mode 100644 index 00000000000..c58267513da --- /dev/null +++ b/contrib/pkgconf/tests/lib1/fragment-collision.pc @@ -0,0 +1,6 @@ +Name: fragment-collision +Version: 0 +Description: fragment collision test package +Requires: fragment-collision-2, fragment-collision-intermediary +Requires.private: fragment-collision-1 +Cflags: -D_BAZ diff --git a/contrib/pkgconf/tests/lib1/fragment-comment.pc b/contrib/pkgconf/tests/lib1/fragment-comment.pc new file mode 100644 index 00000000000..dfdd4f3b1dc --- /dev/null +++ b/contrib/pkgconf/tests/lib1/fragment-comment.pc @@ -0,0 +1,4 @@ +Name: fragment-comment +Description: Test case for issue #215 +Version: 1.0 +Cflags: kuku=\#ttt \ No newline at end of file diff --git a/contrib/pkgconf/tests/lib1/fragment-escaping-1.pc b/contrib/pkgconf/tests/lib1/fragment-escaping-1.pc new file mode 100644 index 00000000000..b147951e502 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/fragment-escaping-1.pc @@ -0,0 +1,5 @@ +Name: fragment-escaping-1 +Version: 0 +Description: fragment escaping test +Cflags: "-IC:\\D E" +Libs: "-LC:\\D E" -lE diff --git a/contrib/pkgconf/tests/lib1/fragment-escaping-2.pc b/contrib/pkgconf/tests/lib1/fragment-escaping-2.pc new file mode 100644 index 00000000000..12e03b3482f --- /dev/null +++ b/contrib/pkgconf/tests/lib1/fragment-escaping-2.pc @@ -0,0 +1,4 @@ +Name: fragment-escaping-2 +Version: 0 +Description: fragment escaping test +Cflags: '-IC:\D E' diff --git a/contrib/pkgconf/tests/lib1/fragment-escaping-3.pc b/contrib/pkgconf/tests/lib1/fragment-escaping-3.pc new file mode 100644 index 00000000000..0f002ddb084 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/fragment-escaping-3.pc @@ -0,0 +1,4 @@ +Name: fragment-escaping-3 +Version: 0 +Description: fragment escaping test +Cflags: -IC:\\D\ E diff --git a/contrib/pkgconf/tests/lib1/fragment-group-a.pc b/contrib/pkgconf/tests/lib1/fragment-group-a.pc new file mode 100644 index 00000000000..0e3966eab76 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/fragment-group-a.pc @@ -0,0 +1,4 @@ +Name: fragment-group-a +Version: 1.0 +Description: Test fixture for fragment groups +Libs: -Wl,--start-group -la -lb -Wl,--end-group diff --git a/contrib/pkgconf/tests/lib1/fragment-group-b.pc b/contrib/pkgconf/tests/lib1/fragment-group-b.pc new file mode 100644 index 00000000000..ac7dda7223a --- /dev/null +++ b/contrib/pkgconf/tests/lib1/fragment-group-b.pc @@ -0,0 +1,4 @@ +Name: fragment-group-b +Version: 1.0 +Description: Test fixture for fragment groups +Libs: -nodefaultlibs diff --git a/contrib/pkgconf/tests/lib1/fragment-group-c.pc b/contrib/pkgconf/tests/lib1/fragment-group-c.pc new file mode 100644 index 00000000000..f79965be0f1 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/fragment-group-c.pc @@ -0,0 +1,4 @@ +Name: fragment-group-c +Version: 1.0 +Description: Test fixture for fragment groups +Libs: -Wl,--start-group -la -lgcc -Wl,--end-group -Wl,--gc-sections diff --git a/contrib/pkgconf/tests/lib1/fragment-groups-2.pc b/contrib/pkgconf/tests/lib1/fragment-groups-2.pc new file mode 100644 index 00000000000..f73f70698f5 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/fragment-groups-2.pc @@ -0,0 +1,4 @@ +Name: fragment-groups-2 +Version: 1.0 +Description: Test fixture for fragment groups +Requires: fragment-group-a, fragment-group-b, fragment-group-c diff --git a/contrib/pkgconf/tests/lib1/fragment-groups.pc b/contrib/pkgconf/tests/lib1/fragment-groups.pc new file mode 100644 index 00000000000..9645f9e9719 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/fragment-groups.pc @@ -0,0 +1,4 @@ +Name: fragment-groups +Version: 1.0 +Description: Test fixture for fragment groups +Libs: -Wl,--start-group -la -lb -Wl,--end-group -nodefaultlibs -Wl,--start-group -la -lgcc -Wl,--end-group -Wl,--gc-sections diff --git a/contrib/pkgconf/tests/lib1/fragment-quoting-2.pc b/contrib/pkgconf/tests/lib1/fragment-quoting-2.pc new file mode 100644 index 00000000000..49c6ac79ce7 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/fragment-quoting-2.pc @@ -0,0 +1,10 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: fragment-quoting +Description: A testing pkg-config file +Version: 1.2.3 +Cflags: -fPIC -I${includedir}/foo -DQUOTED="${prefix}/share/doc" +Cflags.private: -DFOO_STATIC diff --git a/contrib/pkgconf/tests/lib1/fragment-quoting-3.pc b/contrib/pkgconf/tests/lib1/fragment-quoting-3.pc new file mode 100644 index 00000000000..4ff59fc37e0 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/fragment-quoting-3.pc @@ -0,0 +1,10 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: fragment-quoting +Description: A testing pkg-config file +Version: 1.2.3 +Cflags: -fPIC -I${includedir}/foo -DQUOTED=\"${prefix}/share/doc\" +Cflags.private: -DFOO_STATIC diff --git a/contrib/pkgconf/tests/lib1/fragment-quoting-5.pc b/contrib/pkgconf/tests/lib1/fragment-quoting-5.pc new file mode 100644 index 00000000000..22df0b3afba --- /dev/null +++ b/contrib/pkgconf/tests/lib1/fragment-quoting-5.pc @@ -0,0 +1,10 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: fragment-quoting-5 +Description: A testing pkg-config file +Version: 1.2.3 +Cflags: -fPIC -I${includedir}/foo -DQUOTED='${prefix}/share/doc' +Cflags.private: -DFOO_STATIC diff --git a/contrib/pkgconf/tests/lib1/fragment-quoting-7.pc b/contrib/pkgconf/tests/lib1/fragment-quoting-7.pc new file mode 100644 index 00000000000..902e2f634e7 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/fragment-quoting-7.pc @@ -0,0 +1,4 @@ +Name: fragment-quoting-7 +Description: A testing pkg-config file, this time from Go +Version: 1.2.3 +Cflags: -Dhello=10 -Dworld=+32 -DDEFINED_FROM_PKG_CONFIG=hello\ world diff --git a/contrib/pkgconf/tests/lib1/fragment-quoting.pc b/contrib/pkgconf/tests/lib1/fragment-quoting.pc new file mode 100644 index 00000000000..5d8e70b57f8 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/fragment-quoting.pc @@ -0,0 +1,10 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: fragment-quoting +Description: A testing pkg-config file +Version: 1.2.3 +Cflags: -fPIC -I${includedir}/foo -DQUOTED='"${prefix}/share/doc"' +Cflags.private: -DFOO_STATIC diff --git a/contrib/pkgconf/tests/lib1/framework-1.pc b/contrib/pkgconf/tests/lib1/framework-1.pc new file mode 100644 index 00000000000..fcaa0081483 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/framework-1.pc @@ -0,0 +1,9 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: framework-1 +Description: Another pkg-config test +Version: 1.3 +Libs: -F${libdir} -framework framework-1 diff --git a/contrib/pkgconf/tests/lib1/framework-2.pc b/contrib/pkgconf/tests/lib1/framework-2.pc new file mode 100644 index 00000000000..793655a6cc3 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/framework-2.pc @@ -0,0 +1,10 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: framework-2 +Description: Another pkg-config test +Version: 1.3 +Libs: -F${libdir} -framework framework-2 +Requires: framework-1 diff --git a/contrib/pkgconf/tests/lib1/idirafter-ordering.pc b/contrib/pkgconf/tests/lib1/idirafter-ordering.pc new file mode 100644 index 00000000000..c42f7022736 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/idirafter-ordering.pc @@ -0,0 +1,4 @@ +Name: Bad +Description: Demonstrates problems with -idirafter in old pkg-config; see also https://bugs.freedesktop.org/show_bug.cgi?id=23480 +Version: 1 +Cflags: -I/opt/bad/include1 -idirafter -I/opt/bad/include2 -I/opt/bad/include3 diff --git a/contrib/pkgconf/tests/lib1/idirafter.pc b/contrib/pkgconf/tests/lib1/idirafter.pc new file mode 100644 index 00000000000..6c37c8bffed --- /dev/null +++ b/contrib/pkgconf/tests/lib1/idirafter.pc @@ -0,0 +1,4 @@ +Name: Bad +Description: Demonstrates problems with -idirafter in both old pkg-config and current pkgconf; see also https://bugs.freedesktop.org/show_bug.cgi?id=97337 +Version: 1 +Cflags: -idirafter /opt/bad/include -idirafter /opt/bad2/include diff --git a/contrib/pkgconf/tests/lib1/incomplete.pc b/contrib/pkgconf/tests/lib1/incomplete.pc new file mode 100644 index 00000000000..40f0d91b9fc --- /dev/null +++ b/contrib/pkgconf/tests/lib1/incomplete.pc @@ -0,0 +1,4 @@ +Name: incomplete +Description: incomplete package without CFLAGS/LIBS entries (freedesktop #54271) +Version: 1.2.3 + diff --git a/contrib/pkgconf/tests/lib1/intermediary-1.pc b/contrib/pkgconf/tests/lib1/intermediary-1.pc new file mode 100644 index 00000000000..38ee4830ca3 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/intermediary-1.pc @@ -0,0 +1,10 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: intermediary-1 +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -lintermediary-1 -lfoo -lbar -lbaz +Cflags: diff --git a/contrib/pkgconf/tests/lib1/intermediary-2.pc b/contrib/pkgconf/tests/lib1/intermediary-2.pc new file mode 100644 index 00000000000..188ce6b0955 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/intermediary-2.pc @@ -0,0 +1,10 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: intermediary-2 +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -lintermediary-2 -lfoo -lbar -lbaz +Cflags: diff --git a/contrib/pkgconf/tests/lib1/isystem.pc b/contrib/pkgconf/tests/lib1/isystem.pc new file mode 100644 index 00000000000..98416b54ff3 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/isystem.pc @@ -0,0 +1,4 @@ +Name: Bad +Description: Demonstrates problems with -isystem in both old pkg-config and current pkgconf; see also https://bugs.freedesktop.org/show_bug.cgi?id=72584 +Version: 1 +Cflags: -isystem /opt/bad/include -isystem /opt/bad2/include diff --git a/contrib/pkgconf/tests/lib1/malformed-1.pc b/contrib/pkgconf/tests/lib1/malformed-1.pc new file mode 100644 index 00000000000..4f4a700f017 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/malformed-1.pc @@ -0,0 +1,2 @@ +prefix=/usr\nName: GKrellM\nDescription: Extensible GTK system monitoring application\nVersion: 2.3.7\nRequires: gtk+-2.0 >= 2.4.0\nCflags: -I/usr/include\n + diff --git a/contrib/pkgconf/tests/lib1/malformed-quoting.pc b/contrib/pkgconf/tests/lib1/malformed-quoting.pc new file mode 100644 index 00000000000..b8a324edadd --- /dev/null +++ b/contrib/pkgconf/tests/lib1/malformed-quoting.pc @@ -0,0 +1,4 @@ +Name: malformed-quoting +Version: 1 +Description: None. +Cflags: '-I/ABC diff --git a/contrib/pkgconf/tests/lib1/malformed-version.pc b/contrib/pkgconf/tests/lib1/malformed-version.pc new file mode 100644 index 00000000000..41222cc358b --- /dev/null +++ b/contrib/pkgconf/tests/lib1/malformed-version.pc @@ -0,0 +1,5 @@ +Name: malformed-version +Version: 3.922 2018-03-17 +Description: None. +Cflags: -Ifoo +Libs: -lbar diff --git a/contrib/pkgconf/tests/lib1/metapackage-1.pc b/contrib/pkgconf/tests/lib1/metapackage-1.pc new file mode 100644 index 00000000000..bb59db1a740 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/metapackage-1.pc @@ -0,0 +1,6 @@ +Name: metapackage-1 +Version: 0.1 +Description: metapackage for testing purposes +Requires.private: metapackage-2 +Libs: -lmetapackage-1 +Cflags: -I/metapackage-1 diff --git a/contrib/pkgconf/tests/lib1/metapackage-2.pc b/contrib/pkgconf/tests/lib1/metapackage-2.pc new file mode 100644 index 00000000000..d8bf7d6b37a --- /dev/null +++ b/contrib/pkgconf/tests/lib1/metapackage-2.pc @@ -0,0 +1,5 @@ +Name: metapackage-2 +Version: 0.1 +Description: metapackage for testing purposes +Libs: -lmetapackage-2 +Cflags: -I/metapackage-2 diff --git a/contrib/pkgconf/tests/lib1/metapackage-3.pc b/contrib/pkgconf/tests/lib1/metapackage-3.pc new file mode 100644 index 00000000000..111954a04fc --- /dev/null +++ b/contrib/pkgconf/tests/lib1/metapackage-3.pc @@ -0,0 +1,4 @@ +Name: metapackage-3 +Version: 0.1 +Description: metapackage for testing purposes +Requires.private: bar diff --git a/contrib/pkgconf/tests/lib1/metapackage.pc b/contrib/pkgconf/tests/lib1/metapackage.pc new file mode 100644 index 00000000000..3ce0db354a3 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/metapackage.pc @@ -0,0 +1,4 @@ +Name: metapackage +Version: 0.1 +Description: metapackage for testing purposes +Requires: metapackage-1, metapackage-2 diff --git a/contrib/pkgconf/tests/lib1/missing-require.pc b/contrib/pkgconf/tests/lib1/missing-require.pc new file mode 100644 index 00000000000..ede148d85e8 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/missing-require.pc @@ -0,0 +1,11 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: missing-require +Description: A testing pkg-config file +Version: 1.2.3 +Requires.private: missing +Libs: -L${libdir} -lfoo +Cflags: -fPIC -I${includedir}/foo diff --git a/contrib/pkgconf/tests/lib1/multiline-bogus.pc b/contrib/pkgconf/tests/lib1/multiline-bogus.pc new file mode 100644 index 00000000000..c7f00653c7c --- /dev/null +++ b/contrib/pkgconf/tests/lib1/multiline-bogus.pc @@ -0,0 +1,9 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: multiline +Description: A multiline +description blah blah blah +Version: 1.2.3 diff --git a/contrib/pkgconf/tests/lib1/multiline.pc b/contrib/pkgconf/tests/lib1/multiline.pc new file mode 100644 index 00000000000..75b5902f894 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/multiline.pc @@ -0,0 +1,9 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: multiline +Description: A multiline \ + description +Version: 1.2.3 diff --git a/contrib/pkgconf/tests/lib1/no-trailing-newline.pc b/contrib/pkgconf/tests/lib1/no-trailing-newline.pc new file mode 100644 index 00000000000..eaf2f8f2191 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/no-trailing-newline.pc @@ -0,0 +1,10 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: no-trailing-newline +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -L${libdir}/no-trailing-newline -lno-trailing-newline +Cflags: -I${includedir}/no-trailing-newline diff --git a/contrib/pkgconf/tests/lib1/nocflag.pc b/contrib/pkgconf/tests/lib1/nocflag.pc new file mode 100644 index 00000000000..0a3d7524cf4 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/nocflag.pc @@ -0,0 +1,9 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: foo +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -L${libdir} -lfoo diff --git a/contrib/pkgconf/tests/lib1/nolib.pc b/contrib/pkgconf/tests/lib1/nolib.pc new file mode 100644 index 00000000000..b47e30c27a8 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/nolib.pc @@ -0,0 +1,9 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: foo +Description: A testing pkg-config file +Version: 1.2.3 +Cflags: -fPIC -I${includedir}/foo diff --git a/contrib/pkgconf/tests/lib1/omg-sysroot-uninstalled.pc b/contrib/pkgconf/tests/lib1/omg-sysroot-uninstalled.pc new file mode 100644 index 00000000000..7e391b951e1 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/omg-sysroot-uninstalled.pc @@ -0,0 +1,10 @@ +prefix=${pc_sysrootdir}/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: omg-sysroot +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -L${libdir} -lomg +Cflags: -fPIC -I${includedir}/omg diff --git a/contrib/pkgconf/tests/lib1/omg-uninstalled.pc b/contrib/pkgconf/tests/lib1/omg-uninstalled.pc new file mode 100644 index 00000000000..17d8078aa68 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/omg-uninstalled.pc @@ -0,0 +1,10 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: omg +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -L${libdir} -lomg +Cflags: -fPIC -I${includedir}/omg diff --git a/contrib/pkgconf/tests/lib1/orphaned-requires-private.pc b/contrib/pkgconf/tests/lib1/orphaned-requires-private.pc new file mode 100644 index 00000000000..c62bab4459e --- /dev/null +++ b/contrib/pkgconf/tests/lib1/orphaned-requires-private.pc @@ -0,0 +1,8 @@ +Name: orphaned-requires-private +Version: 1 +Description: A testing module for pkgconf +URL: http://www.pkgconf.org/ +Requires: foo +Requires.private: orphaned-foo +Cflags: -DUNREACHABLE +Libs: diff --git a/contrib/pkgconf/tests/lib1/paren-quoting.pc b/contrib/pkgconf/tests/lib1/paren-quoting.pc new file mode 100644 index 00000000000..08ed49d0258 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/paren-quoting.pc @@ -0,0 +1,5 @@ +Name: paren-quoting +Description: Another pkg-config test +Version: 1.3 +Libs: -L$(libdir) -ltilde +Cflags: -I$(includedir) diff --git a/contrib/pkgconf/tests/lib1/pcfiledir.pc b/contrib/pkgconf/tests/lib1/pcfiledir.pc new file mode 100644 index 00000000000..fa6ada923c4 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/pcfiledir.pc @@ -0,0 +1,8 @@ +prefix=${pcfiledir} +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: foo +Description: A testing pkg-config file with a ${pcfiledir} +Version: 1.2.3 diff --git a/contrib/pkgconf/tests/lib1/prefix-foo1.pc b/contrib/pkgconf/tests/lib1/prefix-foo1.pc new file mode 100644 index 00000000000..7a6e9008964 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/prefix-foo1.pc @@ -0,0 +1,12 @@ +prefix=/test/bar +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: prefix-foo1 +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -L${libdir} -lfoo1 +Cflags: -fPIC -I${includedir}/foo -DBAR +Cflags.private: -DFOO_STATIC + diff --git a/contrib/pkgconf/tests/lib1/prefix-foo2.pc b/contrib/pkgconf/tests/lib1/prefix-foo2.pc new file mode 100644 index 00000000000..0546975e230 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/prefix-foo2.pc @@ -0,0 +1,12 @@ +prefix=/test/bar +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: prefix-foo2 +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -L${libdir} -lfoo2 +Cflags: -fPIC -I${includedir}/foo -DFOO +Cflags.private: -DFOO_STATIC + diff --git a/contrib/pkgconf/tests/lib1/private-libs-duplication.pc b/contrib/pkgconf/tests/lib1/private-libs-duplication.pc new file mode 100644 index 00000000000..7f0f5734360 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/private-libs-duplication.pc @@ -0,0 +1,7 @@ +Name: private-libs-duplication +Description: test +Version: 1.0 +Requires: baz bar +Libs: -lprivate +Libs.private: -lfoo + diff --git a/contrib/pkgconf/tests/lib1/provides-request-simple.pc b/contrib/pkgconf/tests/lib1/provides-request-simple.pc new file mode 100644 index 00000000000..7e12c2d40dd --- /dev/null +++ b/contrib/pkgconf/tests/lib1/provides-request-simple.pc @@ -0,0 +1,6 @@ +Name: provides-request-simple +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -lfoo +Cflags: -I/usr/include/foo +Requires: provides-test-foo diff --git a/contrib/pkgconf/tests/lib1/provides.pc b/contrib/pkgconf/tests/lib1/provides.pc new file mode 100644 index 00000000000..0480c7375c8 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/provides.pc @@ -0,0 +1,6 @@ +Name: provides +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -lfoo +Cflags: -I/usr/include/foo +Provides: provides-test-foo = 1.0.0, provides-test-bar > 1.1.0, provides-test-baz >= 1.1.0, provides-test-quux < 1.2.0, provides-test-moo <= 1.2.0, provides-test-meow != 1.3.0 diff --git a/contrib/pkgconf/tests/lib1/quotes.pc b/contrib/pkgconf/tests/lib1/quotes.pc new file mode 100644 index 00000000000..3213fbe5a1e --- /dev/null +++ b/contrib/pkgconf/tests/lib1/quotes.pc @@ -0,0 +1,10 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: quotes +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -L${libdir} -lfoo +Cflags: -DQUOTED=\"bla\" -DA=\"escaped\ string\'\ \literal\" -DB="\1\$" -DC='bla' diff --git a/contrib/pkgconf/tests/lib1/requires-internal-2.pc b/contrib/pkgconf/tests/lib1/requires-internal-2.pc new file mode 100644 index 00000000000..84677205cf3 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/requires-internal-2.pc @@ -0,0 +1,9 @@ +prefix=/test/local +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: requires-internal +Description: A testing pkg-config file +Version: 1.2.3 +Requires.internal: cflags-internal diff --git a/contrib/pkgconf/tests/lib1/requires-internal-collision.pc b/contrib/pkgconf/tests/lib1/requires-internal-collision.pc new file mode 100644 index 00000000000..2b257cf4f9e --- /dev/null +++ b/contrib/pkgconf/tests/lib1/requires-internal-collision.pc @@ -0,0 +1,10 @@ +prefix=/test/local +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: requires-internal +Description: A testing pkg-config file +Version: 1.2.3 +Requires.internal: cflags-internal +Requires.private: cflags-internal diff --git a/contrib/pkgconf/tests/lib1/requires-internal-missing.pc b/contrib/pkgconf/tests/lib1/requires-internal-missing.pc new file mode 100644 index 00000000000..8c55a1ddfa6 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/requires-internal-missing.pc @@ -0,0 +1,10 @@ +prefix=/test/local +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: requires-internal +Description: A testing pkg-config file +Version: 1.2.3 +Requires.internal: static-libs, missing +Requires.private: foo diff --git a/contrib/pkgconf/tests/lib1/requires-internal.pc b/contrib/pkgconf/tests/lib1/requires-internal.pc new file mode 100644 index 00000000000..b7d5a05d93b --- /dev/null +++ b/contrib/pkgconf/tests/lib1/requires-internal.pc @@ -0,0 +1,10 @@ +prefix=/test/local +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: requires-internal +Description: A testing pkg-config file +Version: 1.2.3 +Requires.internal: static-libs +Requires.private: foo diff --git a/contrib/pkgconf/tests/lib1/spaces-in-paths.pc b/contrib/pkgconf/tests/lib1/spaces-in-paths.pc new file mode 100644 index 00000000000..490624e84c6 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/spaces-in-paths.pc @@ -0,0 +1,7 @@ +prefix=/test\ with\ spaces +includedir=${prefix}/include + +Name: spaces-in-paths +Version: 1 +Description: test package for properly expanding spaces in variables +Cflags: -I${includedir} -I${includedir}/subdir diff --git a/contrib/pkgconf/tests/lib1/static-archive-libs.pc b/contrib/pkgconf/tests/lib1/static-archive-libs.pc new file mode 100644 index 00000000000..95f7bfba92a --- /dev/null +++ b/contrib/pkgconf/tests/lib1/static-archive-libs.pc @@ -0,0 +1,10 @@ +prefix=/test/local +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: static-archive-libs +Description: A testing pkg-config file +Version: 1.2.3 +Libs: /libfoo.a -pthread +Cflags: -I${includedir}/foo diff --git a/contrib/pkgconf/tests/lib1/static-libs.pc b/contrib/pkgconf/tests/lib1/static-libs.pc new file mode 100644 index 00000000000..075b331ff19 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/static-libs.pc @@ -0,0 +1,12 @@ +prefix=/test/local +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: static-libs +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -lbar +Libs.private: -lbar-private +Cflags: -I${includedir}/foo +Requires.private: foo diff --git a/contrib/pkgconf/tests/lib1/sysroot-dir-2.pc b/contrib/pkgconf/tests/lib1/sysroot-dir-2.pc new file mode 100644 index 00000000000..31f3ffdb067 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/sysroot-dir-2.pc @@ -0,0 +1,7 @@ +prefix=${pc_sysrootdir}/usr + +Name: sysroot-dir +Description: Package for testing whether sysroot dirs are repeated or not +Version: 1 +CFlags: -I${prefix}/include +Libs: -L${prefix}/lib -lfoo diff --git a/contrib/pkgconf/tests/lib1/sysroot-dir-3.pc b/contrib/pkgconf/tests/lib1/sysroot-dir-3.pc new file mode 100644 index 00000000000..8c53a453a44 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/sysroot-dir-3.pc @@ -0,0 +1,9 @@ +prefix=/sysroot/usr +includedir=${prefix}/include +libdir=${prefix}/lib + +Name: sysroot-dir +Description: Package for testing whether sysroot dirs are repeated or not +Version: 1 +CFlags: -I${includedir} +Libs: -L${libdir} -lfoo diff --git a/contrib/pkgconf/tests/lib1/sysroot-dir-4.pc b/contrib/pkgconf/tests/lib1/sysroot-dir-4.pc new file mode 100644 index 00000000000..524dc7c17cf --- /dev/null +++ b/contrib/pkgconf/tests/lib1/sysroot-dir-4.pc @@ -0,0 +1,9 @@ +prefix=${pc_sysrootdir}/usr +includedir=${prefix}/include +libdir=${prefix}/lib + +Name: sysroot-dir +Description: Package for testing whether sysroot dirs are repeated or not +Version: 1 +CFlags: -I${includedir} +Libs: -L${libdir} -lfoo diff --git a/contrib/pkgconf/tests/lib1/sysroot-dir-5.pc b/contrib/pkgconf/tests/lib1/sysroot-dir-5.pc new file mode 100644 index 00000000000..8c53a453a44 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/sysroot-dir-5.pc @@ -0,0 +1,9 @@ +prefix=/sysroot/usr +includedir=${prefix}/include +libdir=${prefix}/lib + +Name: sysroot-dir +Description: Package for testing whether sysroot dirs are repeated or not +Version: 1 +CFlags: -I${includedir} +Libs: -L${libdir} -lfoo diff --git a/contrib/pkgconf/tests/lib1/sysroot-dir.pc b/contrib/pkgconf/tests/lib1/sysroot-dir.pc new file mode 100644 index 00000000000..8334c501972 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/sysroot-dir.pc @@ -0,0 +1,5 @@ +Name: sysroot-dir +Description: Package for testing whether sysroot dirs are repeated or not +Version: 1 +CFlags: -I/sysroot/include +Libs: -L/sysroot/lib -lfoo diff --git a/contrib/pkgconf/tests/lib1/tilde-quoting.pc b/contrib/pkgconf/tests/lib1/tilde-quoting.pc new file mode 100644 index 00000000000..4d37978c748 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/tilde-quoting.pc @@ -0,0 +1,5 @@ +Name: tilde +Description: Another pkg-config test +Version: 1.3 +Libs: -L~ -ltilde +Cflags: -I~ diff --git a/contrib/pkgconf/tests/lib1/tilde.pc b/contrib/pkgconf/tests/lib1/tilde.pc new file mode 100644 index 00000000000..a4002ecd29e --- /dev/null +++ b/contrib/pkgconf/tests/lib1/tilde.pc @@ -0,0 +1,11 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: Tilde version test +Description: Test package for checking rpmvercmp ~ handling +Version: 1.0.0~rc1 +Requires: +Libs: -L${libdir} -ltilde +Cflags: -I${includedir} diff --git a/contrib/pkgconf/tests/lib1/truncated.pc b/contrib/pkgconf/tests/lib1/truncated.pc new file mode 100644 index 00000000000..4977bc62c0e --- /dev/null +++ b/contrib/pkgconf/tests/lib1/truncated.pc @@ -0,0 +1 @@ +~ \ No newline at end of file diff --git a/contrib/pkgconf/tests/lib1/tuple-quoting.pc b/contrib/pkgconf/tests/lib1/tuple-quoting.pc new file mode 100644 index 00000000000..666ba4a7a46 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/tuple-quoting.pc @@ -0,0 +1,7 @@ +prefix="/test" +libdir=${prefix}/lib + +Name: tuple-quoting +Description: Another pkg-config test +Version: 1.3 +Libs: -L${libdir} -lfoo diff --git a/contrib/pkgconf/tests/lib1/typelibdir.pc b/contrib/pkgconf/tests/lib1/typelibdir.pc new file mode 100644 index 00000000000..16e46c4729f --- /dev/null +++ b/contrib/pkgconf/tests/lib1/typelibdir.pc @@ -0,0 +1,11 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include +typelibdir=${libdir}/typelibdir + +Name: test +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -llib-1 +Cflags: diff --git a/contrib/pkgconf/tests/lib1/unavailable-provider.pc b/contrib/pkgconf/tests/lib1/unavailable-provider.pc new file mode 100644 index 00000000000..a8eed6648b5 --- /dev/null +++ b/contrib/pkgconf/tests/lib1/unavailable-provider.pc @@ -0,0 +1,11 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: unavailable_provider +Description: Provides an otherwise unavailable package +Version: 1.2.3 +Provides: unavailable = 1.2.3 +Libs: -lunavailable +Cflags: diff --git a/contrib/pkgconf/tests/lib1/utf8.pc b/contrib/pkgconf/tests/lib1/utf8.pc new file mode 100644 index 00000000000..6507ec0f25b --- /dev/null +++ b/contrib/pkgconf/tests/lib1/utf8.pc @@ -0,0 +1,10 @@ +prefix=/tëst +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: utf8 +Description: Library installed in a prefix with UTF-8 +Version: 0 +Libs: -L${libdir} -lutf8 +Cflags: -I${includedir} diff --git a/contrib/pkgconf/tests/lib1/variable-whitespace.pc b/contrib/pkgconf/tests/lib1/variable-whitespace.pc new file mode 100644 index 00000000000..8d8eab22cde --- /dev/null +++ b/contrib/pkgconf/tests/lib1/variable-whitespace.pc @@ -0,0 +1,11 @@ + +prefix=/test +exec_prefix= ${prefix} +libdir= ${prefix}/lib64 +includedir=${prefix}/include + +Name: variable-whitespace +Description: A test for variable whitespace +Version: 2.12.3 +Libs: -lvariable-whitespace -L${libdir} +Cflags: -I${includedir} diff --git a/contrib/pkgconf/tests/lib2/foo.pc b/contrib/pkgconf/tests/lib2/foo.pc new file mode 100644 index 00000000000..44588f24352 --- /dev/null +++ b/contrib/pkgconf/tests/lib2/foo.pc @@ -0,0 +1,10 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: foo +Description: A testing pkg-config file +Version: 1.2.3 +Libs: -L${libdir} -lfoo +Cflags: -fPIC -I${includedir}/foo diff --git a/contrib/pkgconf/tests/lib3/bar.pc b/contrib/pkgconf/tests/lib3/bar.pc new file mode 100644 index 00000000000..9157d06b5fd --- /dev/null +++ b/contrib/pkgconf/tests/lib3/bar.pc @@ -0,0 +1,10 @@ +prefix=/test +exec_prefix=${prefix} +libdir=${prefix}/lib +includedir=${prefix}/include + +Name: bar +Description: Another pkg-config test +Version: 1.3 +Libs: -L${libdir} -lbar +Requires: foo diff --git a/contrib/pkgconf/tests/meson.build b/contrib/pkgconf/tests/meson.build new file mode 100644 index 00000000000..56406c3bb0c --- /dev/null +++ b/contrib/pkgconf/tests/meson.build @@ -0,0 +1,23 @@ +configure_file(input: 'Kyuafile.in', output: 'Kyuafile', configuration: cdata) +configure_file(input: 'test_env.sh.in', output: 'test_env.sh', configuration: cdata) + + +tests = [ + 'basic', + 'builtins', + 'conflicts', + 'framework', + 'parser', + 'provides', + 'regress', + 'requires', + 'symlink', + 'sysroot', + 'version', +] + + +# yuck +foreach test : tests + test_file = configure_file(input: test + '.sh', output: test, copy: true) +endforeach diff --git a/contrib/pkgconf/tests/parser.sh b/contrib/pkgconf/tests/parser.sh new file mode 100755 index 00000000000..9d9f9c6ff4b --- /dev/null +++ b/contrib/pkgconf/tests/parser.sh @@ -0,0 +1,372 @@ +#!/usr/bin/env atf-sh + +. $(atf_get_srcdir)/test_env.sh + +tests_init \ + comments \ + comments_in_fields \ + dos \ + no_trailing_newline \ + argv_parse \ + bad_option \ + argv_parse_3 \ + tilde_quoting \ + paren_quoting \ + multiline_field \ + multiline_bogus_header \ + escaped_backslash \ + flag_order_1 \ + flag_order_2 \ + flag_order_3 \ + flag_order_4 \ + quoted \ + variable_whitespace \ + fragment_escaping_1 \ + fragment_escaping_2 \ + fragment_escaping_3 \ + fragment_quoting \ + fragment_quoting_2 \ + fragment_quoting_3 \ + fragment_quoting_5 \ + fragment_quoting_7 \ + fragment_comment \ + msvc_fragment_quoting \ + msvc_fragment_render_cflags \ + tuple_dequote \ + version_with_whitespace \ + version_with_whitespace_2 \ + version_with_whitespace_diagnostic \ + fragment_groups \ + fragment_groups_composite \ + fragment_tree \ + truncated \ + c_comment + +comments_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-lfoo\n" \ + pkgconf --libs comments +} + +comments_in_fields_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-lfoo\n" \ + pkgconf --libs comments-in-fields +} + +dos_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L/test/lib/dos-lineendings -ldos-lineendings\n" \ + pkgconf --libs dos-lineendings +} + +no_trailing_newline_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-I/test/include/no-trailing-newline\n" \ + pkgconf --cflags no-trailing-newline +} + +argv_parse_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-llib-3 -llib-1 -llib-2 -lpthread\n" \ + pkgconf --libs argv-parse +} + +bad_option_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -e ignore \ + -s eq:1 \ + pkgconf --exists -foo +} + +argv_parse_3_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-llib-1 -pthread /test/lib/lib2.so\n" \ + pkgconf --libs argv-parse-3 +} + +tilde_quoting_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L~ -ltilde\n" \ + pkgconf --libs tilde-quoting + atf_check \ + -o inline:"-I~\n" \ + pkgconf --cflags tilde-quoting +} + +paren_quoting_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L\$(libdir) -ltilde\n" \ + pkgconf --libs paren-quoting +} + +multiline_field_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -e ignore \ + -o match:"multiline description" \ + pkgconf --list-all +} + +multiline_bogus_header_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -s eq:0 \ + pkgconf --exists multiline-bogus +} + +escaped_backslash_body() +{ + atf_check \ + -e ignore \ + -o inline:"-IC:\\\\\\\\A\n" \ + pkgconf --with-path=${selfdir}/lib1 --cflags escaped-backslash +} + +quoted_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-DQUOTED=\\\"bla\\\" -DA=\\\"escaped\\ string\\\'\\ literal\\\" -DB=\\\\\\1\$ -DC=bla\n" \ + pkgconf --cflags quotes +} + +flag_order_1_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L/test/lib -Bdynamic -lfoo -Bstatic -lbar\n" \ + pkgconf --libs flag-order-1 +} + +flag_order_2_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L/test/lib -Bdynamic -lfoo -Bstatic -lbar -lfoo\n" \ + pkgconf --libs flag-order-1 foo +} + +flag_order_3_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L/test/lib -Wl,--start-group -lfoo -lbar -Wl,--end-group\n" \ + pkgconf --libs flag-order-3 +} + +flag_order_4_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L/test/lib -Wl,--start-group -lfoo -lbar -Wl,--end-group -lfoo\n" \ + pkgconf --libs flag-order-3 foo +} + +variable_whitespace_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-I/test/include\n" \ + pkgconf --cflags variable-whitespace +} + +fragment_quoting_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-fPIC -I/test/include/foo -DQUOTED=\\\"/test/share/doc\\\"\n" \ + pkgconf --cflags fragment-quoting +} + +fragment_quoting_2_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-fPIC -I/test/include/foo -DQUOTED=/test/share/doc\n" \ + pkgconf --cflags fragment-quoting-2 +} + +fragment_quoting_3_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-fPIC -I/test/include/foo -DQUOTED=\\\"/test/share/doc\\\"\n" \ + pkgconf --cflags fragment-quoting-3 +} + +fragment_quoting_5_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-fPIC -I/test/include/foo -DQUOTED=/test/share/doc\n" \ + pkgconf --cflags fragment-quoting-5 +} + +fragment_quoting_7_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-Dhello=10 -Dworld=+32 -DDEFINED_FROM_PKG_CONFIG=hello\\ world\n" \ + pkgconf --cflags fragment-quoting-7 +} + +fragment_escaping_1_body() +{ + atf_check \ + -o inline:"-IC:\\\\\\\\D\\ E\n" \ + pkgconf --with-path="${selfdir}/lib1" --cflags fragment-escaping-1 +} + +fragment_escaping_2_body() +{ + atf_check \ + -o inline:"-IC:\\\\\\\\D\\ E\n" \ + pkgconf --with-path="${selfdir}/lib1" --cflags fragment-escaping-2 +} + +fragment_escaping_3_body() +{ + atf_check \ + -o inline:"-IC:\\\\\\\\D\\ E\n" \ + pkgconf --with-path="${selfdir}/lib1" --cflags fragment-escaping-3 +} + +fragment_quoting_7a_body() +{ + set -x + + test_cflags=$(pkgconf --with-path=${selfdir}/lib1 --cflags fragment-quoting-7) + echo $test_cflags +# test_cflags='-Dhello=10 -Dworld=+32 -DDEFINED_FROM_PKG_CONFIG=hello\\ world' + + cat > test.c <<- __TESTCASE_END__ + int main(int argc, char *argv[]) { return DEFINED_FROM_PKG_CONFIG; } + __TESTCASE_END__ + cc -o test-fragment-quoting-7 ${test_cflags} ./test.c + atf_check -e 42 ./test-fragment-quoting-7 + rm -f test.c test-fragment-quoting-7 + + set +x +} + + +fragment_comment_body() +{ + atf_check \ + -o inline:'kuku=\#ttt\n' \ + pkgconf --with-path="${selfdir}/lib1" --cflags fragment-comment +} + +msvc_fragment_quoting_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:'/libpath:"C:\D E" E.lib \n' \ + pkgconf --libs --msvc-syntax fragment-escaping-1 +} + +msvc_fragment_render_cflags_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:'/I/test/include/foo /DFOO_STATIC \n' \ + pkgconf --cflags --static --msvc-syntax foo +} + +tuple_dequote_body() +{ + atf_check \ + -o inline:'-L/test/lib -lfoo\n' \ + pkgconf --with-path="${selfdir}/lib1" --libs tuple-quoting +} + +version_with_whitespace_body() +{ + atf_check \ + -o inline:'3.922\n' \ + pkgconf --with-path="${selfdir}/lib1" --modversion malformed-version +} + +version_with_whitespace_2_body() +{ + atf_check \ + -o inline:'malformed-version = 3.922\n' \ + pkgconf --with-path="${selfdir}/lib1" --print-provides malformed-version +} + +version_with_whitespace_diagnostic_body() +{ + atf_check \ + -o match:warning \ + pkgconf --with-path="${selfdir}/lib1" --validate malformed-version +} + +fragment_groups_body() +{ + atf_check \ + -o inline:'-Wl,--start-group -la -lb -Wl,--end-group -nodefaultlibs -Wl,--start-group -la -lgcc -Wl,--end-group -Wl,--gc-sections\n' \ + pkgconf --with-path="${selfdir}/lib1" --libs fragment-groups +} + +fragment_groups_composite_body() +{ + atf_check \ + -o inline:'-Wl,--start-group -la -lb -Wl,--end-group -nodefaultlibs -Wl,--start-group -la -lgcc -Wl,--end-group -Wl,--gc-sections\n' \ + pkgconf --with-path="${selfdir}/lib1" --libs fragment-groups-2 +} + +truncated_body() +{ + atf_check \ + -o match:warning -s exit:1 \ + pkgconf --with-path="${selfdir}/lib1" --validate truncated +} + +c_comment_body() +{ + atf_check \ + -o match:warning \ + pkgconf --with-path="${selfdir}/lib1" --validate c-comment +} + +fragment_tree_body() +{ + atf_check \ + -o inline:"'-Wl,--start-group' [untyped] + '-la' [type l] + '-lb' [type l] + '-Wl,--end-group' [untyped] + +'-nodefaultlibs' [untyped] +'-Wl,--start-group' [untyped] + '-la' [type l] + '-lgcc' [type l] + '-Wl,--end-group' [untyped] + +'-Wl,--gc-sections' [untyped] + +" \ + pkgconf --with-path="${selfdir}/lib1" --fragment-tree fragment-groups-2 +} + diff --git a/contrib/pkgconf/tests/provides.sh b/contrib/pkgconf/tests/provides.sh new file mode 100755 index 00000000000..b699dbf563f --- /dev/null +++ b/contrib/pkgconf/tests/provides.sh @@ -0,0 +1,310 @@ +#!/usr/bin/env atf-sh + +. $(atf_get_srcdir)/test_env.sh + +tests_init \ + simple \ + foo \ + bar \ + baz \ + quux \ + moo \ + meow \ + indirect_dependency_node + +simple_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" +OUTPUT="provides-test-foo = 1.0.0 +provides-test-bar > 1.1.0 +provides-test-baz >= 1.1.0 +provides-test-quux < 1.2.0 +provides-test-moo <= 1.2.0 +provides-test-meow != 1.3.0 +provides = 1.2.3 +" + atf_check \ + -o inline:"${OUTPUT}" \ + pkgconf --print-provides provides + atf_check \ + -o inline:"-lfoo\n" \ + pkgconf --libs provides-request-simple + atf_check \ + -e ignore \ + -s exit:1 \ + pkgconf --no-provides --libs provides-request-simple +} + +foo_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o ignore \ + pkgconf --libs provides-test-foo + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-foo = 1.0.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-foo >= 1.0.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-foo <= 1.0.0' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-foo != 1.0.0' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-foo > 1.0.0' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-foo < 1.0.0' +} + +bar_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o ignore \ + pkgconf --libs provides-test-bar + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-bar = 1.1.1' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-bar >= 1.1.1' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-bar <= 1.1.1' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-bar != 1.1.0' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-bar != 1.1.1' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-bar > 1.1.1' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-bar <= 1.1.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-bar <= 1.2.0' +} + +baz_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o ignore \ + pkgconf --libs provides-test-baz + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-baz = 1.1.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-baz >= 1.1.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-baz <= 1.1.0' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-baz != 1.1.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-baz != 1.0.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-baz > 1.1.1' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-baz > 1.1.0' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-baz < 1.1.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-baz < 1.2.0' +} + +quux_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o ignore \ + pkgconf --libs provides-test-quux + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-quux = 1.1.9' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-quux >= 1.1.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-quux >= 1.1.9' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-quux >= 1.2.0' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-quux <= 1.2.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-quux <= 1.1.9' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-quux != 1.2.0' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-quux != 1.1.0' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-quux != 1.0.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-quux > 1.1.9' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-quux > 1.2.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-quux < 1.1.0' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-quux > 1.2.0' +} + +moo_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o ignore \ + pkgconf --libs provides-test-moo + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-moo = 1.2.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-moo >= 1.1.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-moo >= 1.2.0' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-moo >= 1.2.1' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-moo <= 1.2.0' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-moo != 1.1.0' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-moo != 1.0.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-moo > 1.1.9' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-moo > 1.2.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-moo < 1.1.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-moo < 1.2.0' +} + +meow_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o ignore \ + pkgconf --libs provides-test-meow + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-meow = 1.3.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-meow != 1.3.0' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-meow > 1.2.9' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-meow < 1.3.1' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-meow < 1.3.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-meow > 1.3.0' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-meow >= 1.3.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-meow >= 1.3.1' + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --libs 'provides-test-meow <= 1.3.0' + atf_check \ + -o ignore \ + pkgconf --libs 'provides-test-meow < 1.2.9' +} + +indirect_dependency_node_body() +{ + atf_check \ + -o inline:'1.2.3\n' \ + pkgconf --with-path="${selfdir}/lib1" --modversion 'provides-test-meow' + atf_check \ + -s exit:1 \ + -e ignore \ + pkgconf --with-path="${selfdir}/lib1" --modversion 'provides-test-meow = 1.3.0' +} diff --git a/contrib/pkgconf/tests/regress.sh b/contrib/pkgconf/tests/regress.sh new file mode 100755 index 00000000000..b264f3593f9 --- /dev/null +++ b/contrib/pkgconf/tests/regress.sh @@ -0,0 +1,354 @@ +#!/usr/bin/env atf-sh + +. $(atf_get_srcdir)/test_env.sh + +tests_init \ + case_sensitivity \ + depgraph_break_1 \ + depgraph_break_2 \ + depgraph_break_3 \ + define_variable \ + define_variable_override \ + variable \ + keep_system_libs \ + libs \ + libs_only \ + libs_never_mergeback \ + cflags_only \ + cflags_never_mergeback \ + incomplete_libs \ + incomplete_cflags \ + isystem_munge_order \ + isystem_munge_sysroot \ + idirafter_munge_order \ + idirafter_munge_sysroot \ + idirafter_ordering \ + modversion_common_prefix \ + modversion_fullpath \ + modversion_provides \ + modversion_uninstalled \ + modversion_one_word_expression \ + modversion_two_word_expression \ + modversion_three_word_expression \ + modversion_one_word_expression_no_space \ + modversion_one_word_expression_no_space_zero \ + pcpath \ + virtual_variable \ + fragment_collision \ + malformed_1 \ + malformed_quoting \ + explicit_sysroot \ + empty_tuple \ + solver_requires_private_debounce \ + billion_laughs \ + define_prefix_child_prefix_1 \ + define_prefix_child_prefix_1_env + +# sysroot_munge \ + +case_sensitivity_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"3\n" \ + pkgconf --variable=foo case-sensitivity + atf_check \ + -o inline:"4\n" \ + pkgconf --variable=Foo case-sensitivity +} + +depgraph_break_1_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check -s exit:1 -e ignore \ + pkgconf --exists --print-errors 'foo > 0.6.0 foo < 0.8.0' +} + +depgraph_break_2_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check -s exit:1 -e ignore \ + pkgconf --exists --print-errors 'nonexisting foo <= 3' +} + +depgraph_break_3_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check -s exit:1 -e ignore \ + pkgconf --exists --print-errors 'depgraph-break' +} + +define_variable_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check -o inline:"\\\${libdir}/typelibdir\n" \ + pkgconf --variable=typelibdir --define-variable='libdir=\${libdir}' typelibdir +} + +define_variable_override_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check -o inline:"/test\n" \ + pkgconf --variable=prefix --define-variable='prefix=/test' typelibdir +} + +variable_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"/test/include\n" \ + pkgconf --variable=includedir foo +} + +keep_system_libs_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + eval export "$LIBRARY_PATH_ENV"="/test/local/lib" + atf_check \ + -o inline:"\n" \ + pkgconf --libs-only-L cflags-libs-only + + atf_check \ + -o inline:"-L/test/local/lib\n" \ + pkgconf --libs-only-L --keep-system-libs cflags-libs-only +} + +libs_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L/test/local/lib -lfoo\n" \ + pkgconf --libs cflags-libs-only +} + +libs_only_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L/test/local/lib -lfoo\n" \ + pkgconf --libs-only-L --libs-only-l cflags-libs-only +} + +libs_never_mergeback_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L/test/bar/lib -lfoo1\n" \ + pkgconf --libs prefix-foo1 + atf_check \ + -o inline:"-L/test/bar/lib -lfoo1 -lfoo2\n" \ + pkgconf --libs prefix-foo1 prefix-foo2 +} + +cflags_only_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-I/test/local/include/foo\n" \ + pkgconf --cflags-only-I --cflags-only-other cflags-libs-only +} + +cflags_never_mergeback_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-I/test/bar/include/foo -DBAR -fPIC -DFOO\n" \ + pkgconf --cflags prefix-foo1 prefix-foo2 +} + +incomplete_libs_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"\n" \ + pkgconf --libs incomplete +} + +incomplete_cflags_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"\n" \ + pkgconf --cflags incomplete +} + +isystem_munge_order_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-isystem /opt/bad/include -isystem /opt/bad2/include\n" \ + pkgconf --cflags isystem +} + +isystem_munge_sysroot_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" PKG_CONFIG_SYSROOT_DIR="${selfdir}" + atf_check \ + -o match:"-isystem ${selfdir}/opt/bad/include" \ + pkgconf --cflags isystem +} + +idirafter_munge_order_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-idirafter /opt/bad/include -idirafter /opt/bad2/include\n" \ + pkgconf --cflags idirafter +} + +idirafter_munge_sysroot_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" PKG_CONFIG_SYSROOT_DIR="${selfdir}" + atf_check \ + -o match:"-idirafter ${selfdir}/opt/bad/include" \ + pkgconf --cflags idirafter +} + +idirafter_ordering_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-I/opt/bad/include1 -idirafter -I/opt/bad/include2 -I/opt/bad/include3\n" \ + pkgconf --cflags idirafter-ordering +} + +pcpath_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib2" + atf_check \ + -o inline:"-fPIC -I/test/include/foo\n" \ + pkgconf --cflags ${selfdir}/lib3/bar.pc +} + +sysroot_munge_body() +{ + sed "s|/sysroot/|${selfdir}/|g" ${selfdir}/lib1/sysroot-dir.pc > ${selfdir}/lib1/sysroot-dir-selfdir.pc + export PKG_CONFIG_PATH="${selfdir}/lib1" PKG_CONFIG_SYSROOT_DIR="${selfdir}" + atf_check \ + -o inline:"-L${selfdir}/lib -lfoo\n" \ + pkgconf --libs sysroot-dir-selfdir +} + +virtual_variable_body() +{ + atf_check -s exit:0 \ + pkgconf --exists pkg-config + atf_check -s exit:0 \ + pkgconf --exists pkgconf + + atf_check -o inline:"${pcpath}\n" \ + pkgconf --variable=pc_path pkg-config + atf_check -o inline:"${pcpath}\n" \ + pkgconf --variable=pc_path pkgconf +} + +fragment_collision_body() +{ + atf_check -o inline:"-D_BAZ -D_BAR -D_FOO -D_THREAD_SAFE -pthread\n" \ + pkgconf --with-path="${selfdir}/lib1" --cflags fragment-collision +} + +malformed_1_body() +{ + atf_check -s exit:1 -o ignore \ + pkgconf --validate --with-path="${selfdir}/lib1" malformed-1 +} + +malformed_quoting_body() +{ + atf_check -s exit:0 -o ignore \ + pkgconf --validate --with-path="${selfdir}/lib1" malformed-quoting +} + +explicit_sysroot_body() +{ + export PKG_CONFIG_SYSROOT_DIR=${selfdir} + atf_check -o inline:"${selfdir}/usr/share/test\n" \ + pkgconf --with-path="${selfdir}/lib1" --variable=pkgdatadir explicit-sysroot +} + +empty_tuple_body() +{ + atf_check -o inline:"\n" \ + pkgconf --with-path="${selfdir}/lib1" --cflags empty-tuple +} + +solver_requires_private_debounce_body() +{ + atf_check -o inline:"-I/metapackage-1 -I/metapackage-2 -lmetapackage-1 -lmetapackage-2\n" \ + pkgconf --with-path="${selfdir}/lib1" --cflags --libs metapackage +} + +billion_laughs_body() +{ + atf_check -o inline:"warning: truncating very long variable to 64KB\nwarning: truncating very long variable to 64KB\nwarning: truncating very long variable to 64KB\nwarning: truncating very long variable to 64KB\nwarning: truncating very long variable to 64KB\n" \ + pkgconf --with-path="${selfdir}/lib1" --validate billion-laughs +} + +modversion_common_prefix_body() +{ + atf_check -o inline:"foo: 1.2.3\nfoobar: 3.2.1\n" \ + pkgconf --with-path="${selfdir}/lib1" --modversion --verbose foo foobar +} + +modversion_fullpath_body() +{ + atf_check -o inline:"1.2.3\n" \ + pkgconf --modversion "${selfdir}/lib1/foo.pc" +} + +modversion_provides_body() +{ + atf_check -o inline:"1.2.3\n" \ + pkgconf --with-path="${selfdir}/lib1" --modversion unavailable +} + +modversion_uninstalled_body() +{ + atf_check -o inline:"1.2.3\n" \ + pkgconf --with-path="${selfdir}/lib1" --modversion omg +} + +modversion_one_word_expression_body() +{ + atf_check -o inline:"1.2.3\n" \ + pkgconf --with-path="${selfdir}/lib1" --modversion "foo > 1.0" +} + +modversion_two_word_expression_body() +{ + atf_check -o inline:"1.2.3\n" \ + pkgconf --with-path="${selfdir}/lib1" --modversion foo "> 1.0" +} + +modversion_three_word_expression_body() +{ + atf_check -o inline:"1.2.3\n" \ + pkgconf --with-path="${selfdir}/lib1" --modversion foo ">" 1.0 +} + +modversion_one_word_expression_no_space_body() +{ + atf_check -o inline:"1.2.3\n" \ + pkgconf --with-path="${selfdir}/lib1" --modversion "foo >1.0" +} + +modversion_one_word_expression_no_space_zero_body() +{ + atf_check -o inline:"1.2.3\n" \ + pkgconf --with-path="${selfdir}/lib1" --modversion "foo >0.5" +} + +define_prefix_child_prefix_1_body() +{ + atf_check -o inline:"-I${selfdir}/lib1/include/child-prefix-1 -L${selfdir}/lib1/lib64 -lchild-prefix-1\n" \ + pkgconf --with-path="${selfdir}/lib1/child-prefix/pkgconfig" --define-prefix --cflags --libs child-prefix-1 +} + +define_prefix_child_prefix_1_env_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1/child-prefix/pkgconfig" + export PKG_CONFIG_RELOCATE_PATHS=1 + atf_check -o inline:"-I${selfdir}/lib1/include/child-prefix-1 -L${selfdir}/lib1/lib64 -lchild-prefix-1\n" \ + pkgconf --cflags --libs child-prefix-1 +} diff --git a/contrib/pkgconf/tests/requires.sh b/contrib/pkgconf/tests/requires.sh new file mode 100755 index 00000000000..7a95a66cd23 --- /dev/null +++ b/contrib/pkgconf/tests/requires.sh @@ -0,0 +1,176 @@ +#!/usr/bin/env atf-sh + +. $(atf_get_srcdir)/test_env.sh + +tests_init \ + libs \ + libs_cflags \ + libs_static \ + libs_static_pure \ + cflags_libs_private \ + argv_parse2 \ + static_cflags \ + private_duplication \ + private_duplication_digraph \ + foo_bar \ + bar_foo \ + foo_metapackage_3 \ + libs_static2 \ + missing \ + requires_internal \ + requires_internal_missing \ + requires_internal_collision \ + orphaned_requires_private + +libs_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L/test/lib -lbar -lfoo\n" \ + pkgconf --libs bar +} + +libs_cflags_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-fPIC -I/test/include/foo -L/test/lib -lbaz\n" \ + pkgconf --libs --cflags baz +} + +libs_static_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L/test/lib -lbaz -L/test/lib -lzee -lfoo\n" \ + pkgconf --static --libs baz +} + +libs_static_pure_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-L/test/lib -lbaz -lfoo\n" \ + pkgconf --static --pure --libs baz +} + +argv_parse2_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-llib-1 -pthread /test/lib/lib2.so\n" \ + pkgconf --static --libs argv-parse-2 +} + +static_cflags_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-fPIC -I/test/include/foo -DFOO_STATIC\n" \ + pkgconf --static --cflags baz +} + +private_duplication_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-lprivate -lbaz -lzee -lbar -lfoo\n" \ + pkgconf --static --libs-only-l private-libs-duplication +} + +private_duplication_digraph_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o 'match:"user:request" -> "private-libs-duplication"' \ + -o 'match:"private-libs-duplication" -> "bar"' \ + -o 'match:"private-libs-duplication" -> "baz"' \ + -o 'match:"bar" -> "foo"' \ + -o 'match:"baz" -> "foo"' \ + pkgconf --static --libs-only-l private-libs-duplication --digraph +} + +bar_foo_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-lbar -lfoo\n" \ + pkgconf --static --libs-only-l bar foo +} + +foo_bar_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-lbar -lfoo\n" \ + pkgconf --static --libs-only-l foo bar +} + +foo_metapackage_3_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-lbar -lfoo\n" \ + pkgconf --static --libs-only-l foo metapackage-3 +} + +libs_static2_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -o inline:"-lbar -lbar-private -L/test/lib -lfoo\n" \ + pkgconf --static --libs static-libs +} + +missing_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --cflags missing-require +} + +requires_internal_body() +{ + atf_check \ + -o inline:"-lbar -lbar-private -L/test/lib -lfoo\n" \ + pkgconf --with-path="${selfdir}/lib1" --static --libs requires-internal +} + +requires_internal_missing_body() +{ + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --with-path="${selfdir}/lib1" --static --libs requires-internal-missing +} + +requires_internal_collision_body() +{ + atf_check \ + -o inline:"-I/test/local/include/foo\n" \ + pkgconf --with-path="${selfdir}/lib1" --cflags requires-internal-collision +} + +orphaned_requires_private_body() +{ + atf_check \ + -s exit:1 \ + -e ignore \ + -o ignore \ + pkgconf --with-path="${selfdir}/lib1" --cflags --libs orphaned-requires-private +} + +cflags_libs_private_body() +{ + atf_check \ + -o inline:"\n" \ + pkgconf --with-path="${selfdir}/lib1" --libs cflags-libs-private-a + + atf_check \ + -o inline:"-lc\n" \ + pkgconf --with-path="${selfdir}/lib1" --static --libs cflags-libs-private-a +} diff --git a/contrib/pkgconf/tests/symlink.sh b/contrib/pkgconf/tests/symlink.sh new file mode 100755 index 00000000000..7f49655755e --- /dev/null +++ b/contrib/pkgconf/tests/symlink.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env atf-sh + +. $(atf_get_srcdir)/test_env.sh + +tests_init \ + pcfiledir_symlink_absolute \ + pcfiledir_symlink_relative + +# - We need to create a temporary subtree, since symlinks are not preserved +# in "make dist". +# - ${srcdir} is relative and since we need to compare paths, we would have +# to portably canonicalize it again, which is hard. Instead, just keep +# the whole thing nested. +pcfiledir_symlink_absolute_body() +{ + mkdir -p tmp/child + cp -f "${selfdir}/lib1/pcfiledir.pc" tmp/child/ + ln -f -s "${PWD}/tmp/child/pcfiledir.pc" tmp/pcfiledir.pc # absolute + ln -f -s tmp/pcfiledir.pc pcfiledir.pc + + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix pcfiledir.pc + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix tmp/pcfiledir.pc + atf_check \ + -o inline:"tmp/child\n" \ + pkgconf --variable=prefix tmp/child/pcfiledir.pc + + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix "${PWD}/pcfiledir.pc" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix "${PWD}/tmp/pcfiledir.pc" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix "${PWD}/tmp/child/pcfiledir.pc" + + export PKG_CONFIG_PATH="." + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + export PKG_CONFIG_PATH="${PWD}" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + + export PKG_CONFIG_PATH="tmp" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + export PKG_CONFIG_PATH="${PWD}/tmp" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + + export PKG_CONFIG_PATH="tmp/child" + atf_check \ + -o inline:"tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + export PKG_CONFIG_PATH="${PWD}/tmp/child" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix pcfiledir +} + +pcfiledir_symlink_relative_body() +{ + mkdir -p tmp/child + cp -f "${selfdir}/lib1/pcfiledir.pc" tmp/child/ + ln -f -s child/pcfiledir.pc tmp/pcfiledir.pc # relative + ln -f -s tmp/pcfiledir.pc pcfiledir.pc + + atf_check \ + -o inline:"tmp/child\n" \ + pkgconf --variable=prefix pcfiledir.pc + atf_check \ + -o inline:"tmp/child\n" \ + pkgconf --variable=prefix tmp/pcfiledir.pc + atf_check \ + -o inline:"tmp/child\n" \ + pkgconf --variable=prefix tmp/child/pcfiledir.pc + + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix "${PWD}/pcfiledir.pc" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix "${PWD}/tmp/pcfiledir.pc" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix "${PWD}/tmp/child/pcfiledir.pc" + + export PKG_CONFIG_PATH="." + atf_check \ + -o inline:"tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + export PKG_CONFIG_PATH="${PWD}" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + + export PKG_CONFIG_PATH="tmp" + atf_check \ + -o inline:"tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + export PKG_CONFIG_PATH="${PWD}/tmp" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + + export PKG_CONFIG_PATH="tmp/child" + atf_check \ + -o inline:"tmp/child\n" \ + pkgconf --variable=prefix pcfiledir + export PKG_CONFIG_PATH="${PWD}/tmp/child" + atf_check \ + -o inline:"${PWD}/tmp/child\n" \ + pkgconf --variable=prefix pcfiledir +} diff --git a/contrib/pkgconf/tests/sysroot.sh b/contrib/pkgconf/tests/sysroot.sh new file mode 100755 index 00000000000..9d61eaea311 --- /dev/null +++ b/contrib/pkgconf/tests/sysroot.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env atf-sh + +. $(atf_get_srcdir)/test_env.sh + +tests_init \ + cflags \ + variable \ + do_not_eat_slash \ + do_not_duplicate_sysroot_dir \ + uninstalled \ + uninstalled_pkgconf1 \ + uninstalled_fdo \ + uninstalled_fdo_pc_sysrootdir + +do_not_eat_slash_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + export PKG_CONFIG_SYSROOT_DIR="/" + atf_check \ + -o inline:"-fPIC -I/test/include/foo\n" \ + pkgconf --cflags baz +} + +cflags_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + export PKG_CONFIG_SYSROOT_DIR="${SYSROOT_DIR}" + atf_check \ + -o inline:"-fPIC -I${SYSROOT_DIR}/test/include/foo\n" \ + pkgconf --cflags baz +} + +variable_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + export PKG_CONFIG_SYSROOT_DIR="${SYSROOT_DIR}" + atf_check \ + -o inline:"${SYSROOT_DIR}/test\n" \ + pkgconf --variable=prefix foo + atf_check \ + -o inline:"${SYSROOT_DIR}/test/include\n" \ + pkgconf --variable=includedir foo +} + +do_not_duplicate_sysroot_dir_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + export PKG_CONFIG_SYSROOT_DIR="/sysroot" + + atf_check \ + -o inline:"-I/sysroot/usr/include\n" \ + pkgconf --cflags sysroot-dir-2 + + atf_check \ + -o inline:"-I/sysroot/usr/include\n" \ + pkgconf --cflags sysroot-dir-3 + + atf_check \ + -o inline:"-I/sysroot/usr/include\n" \ + pkgconf --cflags sysroot-dir-5 + + export PKG_CONFIG_SYSROOT_DIR="${SYSROOT_DIR}" + + atf_check \ + -o inline:"-I${SYSROOT_DIR}/usr/include\n" \ + pkgconf --cflags sysroot-dir-4 +} + +uninstalled_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + export PKG_CONFIG_SYSROOT_DIR="/sysroot" + + atf_check \ + -o inline:"-L/test/lib -lomg\n" \ + pkgconf --libs omg +} + +uninstalled_pkgconf1_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + export PKG_CONFIG_SYSROOT_DIR="/sysroot" + export PKG_CONFIG_PKGCONF1_SYSROOT_RULES="1" + + atf_check \ + -o inline:"-L/sysroot/test/lib -lomg\n" \ + pkgconf --libs omg +} + +uninstalled_fdo_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + export PKG_CONFIG_SYSROOT_DIR="/sysroot" + export PKG_CONFIG_FDO_SYSROOT_RULES="1" + + atf_check \ + -o inline:"-L/test/lib -lomg\n" \ + pkgconf --libs omg +} + +uninstalled_fdo_pc_sysrootdir_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + export PKG_CONFIG_SYSROOT_DIR="/sysroot" + export PKG_CONFIG_FDO_SYSROOT_RULES="1" + + atf_check \ + -o inline:"-L/sysroot/test/lib -lomg\n" \ + pkgconf --libs omg-sysroot +} diff --git a/contrib/pkgconf/tests/test_env.sh.in b/contrib/pkgconf/tests/test_env.sh.in new file mode 100644 index 00000000000..d2b6136f18c --- /dev/null +++ b/contrib/pkgconf/tests/test_env.sh.in @@ -0,0 +1,50 @@ +srcdir="$(atf_get_srcdir)" +export PATH="$srcdir/..:${PATH}" + +#--- begin windows kludge --- +# When building with Visual Studio, binaries are in a subdirectory named after the configration... +# and the configuration is not known unless you're in the IDE, or something. +# So just guess. This won't work well if you build more than one configuration. +the_configuration="" +for configuration in Debug Release RelWithDebInfo +do + if test -d "$srcdir/../$configuration" + then + if test "$the_configuration" != "" + then + echo "test_env.sh: FAIL: more than one configuration found" + exit 1 + fi + the_configuration=$configuration + export PATH="$srcdir/../${configuration}:${PATH}" + fi +done +#--- end kludge --- + +selfdir="@abs_top_srcdir@/tests" +LIBRARY_PATH_ENV="LIBRARY_PATH" +PATH_SEP=":" +SYSROOT_DIR="${selfdir}" +case "$(uname -s)" in +Haiku) LIBRARY_PATH_ENV="BELIBRARIES";; +esac + +prefix="@prefix@" +exec_prefix="@exec_prefix@" +datarootdir="@datarootdir@" +pcpath="@PKG_DEFAULT_PATH@" + +tests_init() +{ + TESTS="$@" + export TESTS + for t ; do + atf_test_case $t + done +} + +atf_init_test_cases() { + for t in ${TESTS}; do + atf_add_test_case $t + done +} diff --git a/contrib/pkgconf/tests/version.sh b/contrib/pkgconf/tests/version.sh new file mode 100755 index 00000000000..0172c0437da --- /dev/null +++ b/contrib/pkgconf/tests/version.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env atf-sh + +. $(atf_get_srcdir)/test_env.sh + +tests_init \ + atleast \ + exact \ + max + +atleast_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + pkgconf --atleast-version 1.0 foo + atf_check \ + -s exit:1 \ + pkgconf --atleast-version 2.0 foo +} + +exact_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -s exit:1 \ + pkgconf --exact-version 1.0 foo + atf_check \ + pkgconf --exact-version 1.2.3 foo +} + +max_body() +{ + export PKG_CONFIG_PATH="${selfdir}/lib1" + atf_check \ + -s exit:1 \ + pkgconf --max-version 1.0 foo + atf_check \ + pkgconf --max-version 2.0 foo +} diff --git a/contrib/pkgconf/txt2rtf.py b/contrib/pkgconf/txt2rtf.py new file mode 100644 index 00000000000..111ba374214 --- /dev/null +++ b/contrib/pkgconf/txt2rtf.py @@ -0,0 +1,35 @@ +def text_to_rtf(input_file: str, output_file: str) -> None: + with open(input_file, "r", encoding="utf-8") as file: + text_content = file.read() + + text_content = text_content.replace("\\", "\\\\") + text_content = text_content.replace("{", "\\{") + text_content = text_content.replace("}", "\\}") + + text_content = text_content.replace("\n", "\\par\n") + + rtf_content = "{\\rtf1\\ansi\\ansicpg1252\\cocoartf2580\\cocoasubrtf220\n" + rtf_content += "{\\fonttbl\\f0\\fswiss\\fcharset0 Helvetica;}\n" + rtf_content += "\\vieww12000\\viewh15840\\viewkind0\n" + rtf_content += "\\pard\\tx720\\tx1440\\tx2160\\tx2880\\tx3600\\tx4320\\pardirnatural\\partightenfactor0\n" + rtf_content += "\\f0\\fs24 " + rtf_content += text_content + rtf_content += "\n}" + + with open(output_file, "w", encoding="utf-8") as file: + file.write(rtf_content) + + print(f"Conversion complete! RTF file saved as: {output_file}") + + +if __name__ == "__main__": + import sys + + if len(sys.argv) != 3: + print(f"Usage: python {sys.argv[0]} input.txt output.rtf") + sys.exit(1) + + input_file = sys.argv[1] + output_file = sys.argv[2] + + text_to_rtf(input_file, output_file)