Compare commits

..

13 Commits

Author SHA1 Message Date
Ariadne Conill 20db9ffcbe cli: if --exists is specified, require the full dependency graph to validate 2023-01-22 10:46:27 +00:00
Ariadne Conill 5436648cd4 pkgconf 1.8.1. 2023-01-22 10:02:10 +00:00
Ariadne Conill 66046df940 libpkgconf 1.8.1 2023-01-22 10:01:42 +00:00
Ariadne Conill 81e1785db2 add NEWS for 1.8.1 2023-01-22 10:01:23 +00:00
wi24rd 2b850d2648 Update sum value of types of property. 2023-01-22 09:59:37 +00:00
Eli Schwartz c87a6c2ec1 meson: remove useless command that isn't needed
The tests/*.sh are executable in the source tree, and don't need to be
chmodded after being copied to the build tree.
2023-01-22 09:58:55 +00:00
Doug Freed 1f282295ff doc: update libpkgconf-pkg docs to match 2023-01-22 09:57:45 +00:00
Ariadne Conill e3957c6155 tests: fix test regressions caused by tilde changes 2023-01-22 09:57:39 +00:00
Doug Freed f58d54e77b pkg: make pkgconf_compare_version consistent
The code taken from rpmvercmp in pkg-config returns -1 if a is less than
b, 0 if a is equal to b, and 1 if a is greater than b. This matches the
expectations of the comparison operators that use this function.
However, the tilde handling, the NULL handling, and the docstring all do
the opposite.

This fixes the tilde handling, the NULL handling, and the docstring to
match the behavior of the rpmvercmp code and the expectations of the
comparison operators.
2023-01-22 09:57:34 +00:00
Timo Röhling 2837bbcc4c Ignore whitespace indentation
Fixes #265
2023-01-22 09:57:29 +00:00
Ariadne Conill 0b5f360bce tests: add regression test for billion-laughs 2023-01-22 09:57:08 +00:00
Ariadne Conill 81cc9b3e6d tuple: test for, and stop string processing, on truncation
otherwise a buffer overflow occurs.
this has been a bug in pkgconf since the beginning, it seems.
instead of disclosing the bug correctly, a "hotshot" developer
decided to blog about it instead.  sigh.

https://nullprogram.com/blog/2023/01/18/
2023-01-22 09:55:45 +00:00
Ariadne Conill 4b0264de8b add billion-laughs test fixture 2023-01-22 09:53:49 +00:00
70 changed files with 1266 additions and 3448 deletions

View File

@ -35,13 +35,13 @@ jobs:
# the code assumes msvc style printf atm
export CFLAGS=-D__USE_MINGW_ANSI_STDIO=0
meson -Dtests=disabled _build
meson -Dtests=false _build
meson compile -C _build
debian-meson:
runs-on: ubuntu-latest
container:
image: debian:testing
image: debian:bullseye
steps:
- name: Checkout
uses: actions/checkout@v2
@ -53,41 +53,17 @@ jobs:
- name: Build
run: |
meson _build -Dwerror=true
meson _build
meson compile -C _build
- name: Run tests
run: |
meson test -v -C _build
debian-meson-asan:
runs-on: ubuntu-latest
container:
image: debian:testing
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Update system and add dependencies
run: |
apt-get update
apt-get install -y kyua atf-sh build-essential meson
- name: Build
run: |
meson _build -Db_sanitize=address
meson compile -C _build
- name: Run tests
run: |
meson test -v -C _build
env:
ASAN_OPTIONS: "exitcode=7"
debian-autotools:
runs-on: ubuntu-latest
container:
image: debian:testing
image: debian:bullseye
steps:
- name: Checkout
uses: actions/checkout@v2
@ -122,7 +98,7 @@ jobs:
- name: Build
run: |
meson _build -Dwerror=true
meson _build
meson compile -C _build
- name: Run tests

49
.gitignore vendored
View File

@ -1,19 +1,13 @@
Makefile
Makefile.in
.dirstamp
stamp-h*
*.h.in~
*.o
*.og
*.op
*.lo
*.gcno
*.gcda
*.gcov
core
.gdb_history
.libs
.deps
*.exe
@ -22,8 +16,8 @@ core
*.orig
*.patch
*.rej
/tests/run.sh
# autoconf stuff
/aclocal.m4
/autom4te.cache/
/buildsys.mk
@ -32,47 +26,8 @@ core
/config.log
/config.status
/configure
/compile
/config.guess
/config.sub
/depcomp
/install-sh
/missing
# programs
/bomtool
/pkgconf
/pkgconf-gcov
/pkgconf-profile
# libtool
/libtool
/ltmain.sh
/libpkgconf.la
/m4/libtool.m4
/m4/lt~obsolete.m4
/m4/ltoptions.m4
/m4/ltsugar.m4
/m4/ltversion.m4
# pkgconfig file
/libpkgconf.pc
# autoheader
/libpkgconf/config.h
/libpkgconf/config.h.in
# tests
/Kyuafile
/tests/Kyuafile
/tests/basic
/tests/builtins
/tests/conflicts
/tests/framework
/tests/parser
/tests/provides
/tests/regress
/tests/requires
/tests/sysroot
/tests/test_env.sh
/tests/version
Makefile

View File

@ -1,75 +0,0 @@
pipeline:
debian-meson:
image: debian:testing
commands:
- apt-get update
- apt-get install -y kyua atf-sh build-essential meson
- meson _build -Dwerror=true
- meson compile -C _build
- meson test -v -C _build
when:
matrix:
IMAGE: debian
BUILD: meson
debian-meson-asan:
image: debian:testing
environment:
- ASAN_OPTIONS="exitcode=7"
commands:
- apt-get update
- apt-get install -y kyua atf-sh build-essential meson
- meson _build -Db_sanitize=address
- meson compile -C _build
- meson test -v -C _build
when:
matrix:
IMAGE: debian
BUILD: meson
debian-autotools:
image: debian:testing
commands:
- apt-get update
- apt-get install -y kyua atf-sh build-essential autoconf libtool
- ./autogen.sh
- ./configure
- make -j
- make distcheck
when:
matrix:
IMAGE: debian
BUILD: autotools
alpine-meson:
image: alpine
commands:
- apk add -U --no-cache kyua atf build-base meson
- meson _build -Dwerror=true
- meson compile -C _build
- meson test -v -C _build
when:
matrix:
IMAGE: alpine
BUILD: meson
alpine-autotools:
image: alpine
commands:
- apk add -U --no-cache kyua atf build-base autoconf automake libtool xz gzip
- ./autogen.sh
- ./configure
- make -j
- make distcheck
when:
matrix:
IMAGE: alpine
BUILD: autotools
matrix:
IMAGE:
- debian
- alpine
BUILD:
- meson
- autotools

View File

@ -11,7 +11,7 @@ nodist_pkgconfig_DATA = libpkgconf.pc
AM_CFLAGS = -DPERSONALITY_PATH=\"$(personality_dir)\" -DPKG_DEFAULT_PATH=\"$(pkg_default_dir)\" -DSYSTEM_INCLUDEDIR=\"$(system_includedir)\" -DSYSTEM_LIBDIR=\"$(system_libdir)\"
bin_PROGRAMS = pkgconf bomtool
bin_PROGRAMS = pkgconf
lib_LTLIBRARIES = libpkgconf.la
EXTRA_DIST = pkg.m4 \
@ -27,8 +27,6 @@ EXTRA_DIST = pkg.m4 \
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 \
@ -64,7 +62,6 @@ EXTRA_DIST = pkg.m4 \
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 \
@ -92,10 +89,6 @@ EXTRA_DIST = pkg.m4 \
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 \
@ -106,10 +99,6 @@ EXTRA_DIST = pkg.m4 \
tests/lib1/tuple-quoting.pc \
tests/lib1/empty-tuple.pc \
tests/lib1/orphaned-requires-private.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 \
$(test_scripts) \
doc/conf.py \
doc/extract.py \
@ -138,8 +127,7 @@ test_scripts= tests/meson.build \
tests/sysroot.sh \
tests/version.sh
test_sh = $(filter-out tests/meson.build, $(test_scripts))
check_SCRIPTS = $(test_sh:.sh=)
check_SCRIPTS= $(test_scripts:.sh=)
SUFFIXES= .sh
@ -159,7 +147,7 @@ libpkgconf_la_SOURCES = \
libpkgconf/path.c \
libpkgconf/personality.c \
libpkgconf/parser.c
libpkgconf_la_LDFLAGS = -no-undefined -version-info 5:0:0 -export-symbols-regex '^pkgconf_'
libpkgconf_la_LDFLAGS = -no-undefined -version-info 3:0:0 -export-symbols-regex '^pkgconf_'
dist_man_MANS = \
man/pkgconf.1 \
@ -172,17 +160,11 @@ pkgconf_SOURCES = \
cli/main.c \
cli/getopt_long.c \
cli/renderer-msvc.c
pkgconf_CPPFLAGS = -I$(top_srcdir)/libpkgconf -I$(top_srcdir)/cli
pkgconf_CPPFLAGS = -Ilibpkgconf -Icli
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

View File

@ -39,7 +39,7 @@ 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.2.0"' >> $@
@echo '#define PACKAGE_VERSION "1.6.2"' >> $@
@echo '#define PACKAGE PACKAGE_NAME " " PACKAGE_VERSION' >> $@
@echo '#define HAVE_STRLCPY' >> $@
@echo '#define HAVE_STRLCAT' >> $@

215
NEWS
View File

@ -1,227 +1,14 @@
Changes from previous version of pkgconf
========================================
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:
Changes from 1.8.0 to 1.8.1:
----------------------------
* 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
<https://gitea.treehouse.systems/ariadne/pkgconf>. 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:
----------------------------

View File

@ -3,9 +3,9 @@
`pkgconf` is a program which helps to configure compiler and linker flags for
development libraries. It is similar to pkg-config from freedesktop.org.
`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.
`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.
## using `pkgconf` with autotools
@ -41,7 +41,7 @@ to improve this behaviour.
As of the 1.1 series, pkgconf also fully implements support for `Provides` rules,
while pkg-config does not. pkg-config only provides the `--print-provides` functionality
as a stub. There are other intentional implementation differences in pkgconf's dependency
resolver versus pkg-config's dependency resolver in terms of completeness and correctness,
resolver verses pkg-config's dependency resolver in terms of completeness and correctness,
such as, for example, how `Conflicts` rules are processed.
## linker flags optimization
@ -50,7 +50,7 @@ As previously mentioned, pkgconf makes optimizations to the linker flags in both
case of static and shared linking in order to avoid overlinking binaries and also
simplifies the `CFLAGS` and `LIBS` output of the pkgconf tool for improved readability.
This functionality depends on the pkg-config module properly declaring its dependency
This functionality depends on the pkg-config module properly declaring it's dependency
tree instead of using `Libs` and `Cflags` fields to directly link against other modules
which have pkg-config metadata files installed.
@ -71,25 +71,16 @@ do let us know, but also make sure that the .pc files are valid and follow the r
the [pkg-config tutorial][fd-tut], as most likely fixing them to follow the specified
rules will solve the problem.
Additionally, **we do not consider pkgconf doing what you tell it to do, in cases for
which pkg-config fails to do so, to be a bug**.
Additionally, **we do not consider pkgconf doing what you tell it to do, when pkg-config
fails to do so, to be a bug**.
If, for example, you use environment variables such as `PKG_CONFIG_SYSTEM_[INCLUDE|LIBRARY]_PATH`
and then find yourself surprised that `pkgconf` is stripping `-I` and `-L` flags relating
to those paths, it is not a `pkgconf` problem -- `pkgconf` is doing exactly what you told
to those paths, it's not a `pkgconf` problem -- `pkgconf` is doing exactly what you told
it to do.
We will reject bugs like this, and if someone insists on fixing such a non-bug, this
constitutes a violation of our [Code of Conduct](CODE_OF_CONDUCT.md), which may be
addressed by banning from this repository.
## 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.
We will reject bugs like this, and if you choose to violate our [Code of Conduct](CODE_OF_CONDUCT.md)
by demanding we fix your non-bug, we will fix the problem by banning you from the repo instead.
## compiling `pkgconf` and `libpkgconf` on UNIX
@ -112,16 +103,17 @@ flags like so:
$ make
$ sudo make install
## compiling `pkgconf` and `libpkgconf` with Meson (usually for Windows)
## compiling `pkgconf` and `libpkgconf` with CMake (usually for Windows)
pkgconf is compiled using CMake on Windows. In theory, you could also use CMake to build
on UNIX, but this is not recommended at this time as it pkgconf is typically built much earlier
than CMake.
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 it pkgconf is typically built
much earlier than Meson.
$ meson setup build -Dtests=disabled
$ meson compile -C build
$ meson install -C build
$ mkdir build
$ cd build
$ cmake ..
$ make
$ sudo make install
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
@ -137,8 +129,8 @@ to make this determination themselves.
## release tarballs
Release tarballs are available at <https://distfiles.ariadne.space/pkgconf/>.
Please only use the tarballs from distfiles.ariadne.space.
Release tarballs are available at <https://distfiles.dereferenced.org/pkgconf/>.
Please only use the tarballs from distfiles.dereferenced.org.
## contacts

View File

@ -1,365 +0,0 @@
/*
* 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 <libpkgconf/stdinc.h>
#include <libpkgconf/libpkgconf.h>
#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->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: <text>%s</text>\n", pkg->copyright);
if (pkg->description != NULL)
printf("PackageSummary: <text>%s</text>\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;
}

View File

@ -70,14 +70,11 @@
#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)
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;
@ -88,7 +85,7 @@ FILE *error_msgout = NULL;
FILE *logfile_out = NULL;
static bool
error_handler(const char *msg, const pkgconf_client_t *client, void *data)
error_handler(const char *msg, const pkgconf_client_t *client, const void *data)
{
(void) client;
(void) data;
@ -254,71 +251,32 @@ apply_provides(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int
#ifndef PKGCONF_LITE
static void
print_digraph_node(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data)
print_digraph_node(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *unused)
{
pkgconf_node_t *node;
(void) client;
pkgconf_pkg_t **last_seen = data;
(void) unused;
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;
}
printf("\"%s\" [fontname=Sans fontsize=8]\n", pkg->id);
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);
printf("\"%s\" -- \"%s\" [fontname=Sans fontsize=8]\n", dep->package, pkg->id);
}
}
static bool
apply_digraph(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int maxdepth)
apply_digraph(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int maxdepth)
{
int eflag;
pkgconf_list_t *list = data;
pkgconf_pkg_t *last_seen = NULL;
pkgconf_node_t *iter;
printf("digraph deptree {\n");
printf("graph 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);
eflag = pkgconf_pkg_traverse(client, world, print_digraph_node, unused, maxdepth, 0);
if (eflag != PKGCONF_PKG_ERRF_OK)
return false;
@ -326,62 +284,23 @@ apply_digraph(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int ma
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")\n", pkg->id, pkg->identifier);
}
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)
apply_modversion(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int maxdepth)
{
pkgconf_node_t *queue_iter;
pkgconf_list_t *pkgq = data;
pkgconf_node_t *iter;
(void) client;
(void) unused;
(void) maxdepth;
PKGCONF_FOREACH_LIST_ENTRY(pkgq->head, queue_iter)
PKGCONF_FOREACH_LIST_ENTRY(world->required.head, iter)
{
pkgconf_node_t *world_iter;
pkgconf_queue_t *queue_node = queue_iter->data;
pkgconf_dependency_t *dep = iter->data;
pkgconf_pkg_t *pkg = dep->match;
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(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;
}
if (pkg->version != NULL)
printf("%s\n", pkg->version);
}
return true;
@ -427,6 +346,16 @@ apply_path(pkgconf_client_t *client, pkgconf_pkg_t *world, void *unused, int max
return true;
}
static void
print_variable(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *variable)
{
const char *var;
var = pkgconf_tuple_find(client, &pkg->vars, variable);
if (var != NULL)
printf("%s", var);
}
static bool
apply_variable(pkgconf_client_t *client, pkgconf_pkg_t *world, void *variable, int maxdepth)
{
@ -437,12 +366,11 @@ apply_variable(pkgconf_client_t *client, pkgconf_pkg_t *world, void *variable, i
{
pkgconf_dependency_t *dep = iter->data;
pkgconf_pkg_t *pkg = dep->match;
const char *var;
var = pkgconf_tuple_find(client, &pkg->vars, variable);
if (iter->prev != NULL)
printf(" ");
if (var != NULL)
printf("%s%s", iter->prev != NULL ? " " : "", var);
print_variable(client, pkg, variable);
}
printf("\n");
@ -487,8 +415,7 @@ apply_env(pkgconf_client_t *client, pkgconf_pkg_t *world, void *env_prefix_p, in
char workbuf[PKGCONF_ITEM_SIZE];
for (it = want_env_prefix; *it != '\0'; it++)
if (!isalpha((unsigned char)*it) &&
!isdigit((unsigned char)*it))
if (!isalpha(*it) && !isdigit(*it))
return false;
snprintf(workbuf, sizeof workbuf, "%s_CFLAGS", want_env_prefix);
@ -666,32 +593,6 @@ apply_simulate(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int m
}
#endif
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)
{
@ -723,7 +624,6 @@ usage(void)
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");
@ -790,14 +690,12 @@ usage(void)
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("\nfiltering output:\n\n");
#ifndef PKGCONF_LITE
@ -903,13 +801,8 @@ main(int argc, char *argv[])
char *logfile_arg = NULL;
char *want_env_prefix = NULL;
unsigned int want_client_flags = PKGCONF_PKG_PKGF_NONE;
pkgconf_cross_personality_t *personality = NULL;
pkgconf_cross_personality_t *personality;
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,
};
want_flags = 0;
@ -943,7 +836,6 @@ main(int argc, char *argv[])
{ "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, },
@ -991,8 +883,6 @@ main(int argc, char *argv[])
{ "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 },
{ NULL, 0, NULL, 0 }
};
@ -1004,6 +894,12 @@ main(int argc, char *argv[])
}
#endif
#ifndef PKGCONF_LITE
personality = deduce_personality(argv);
#else
personality = pkgconf_cross_personality_default();
#endif
while ((ret = pkg_getopt_long_only(argc, argv, "", options, NULL)) != -1)
{
switch (ret)
@ -1033,7 +929,7 @@ main(int argc, char *argv[])
logfile_arg = pkg_optarg;
break;
case 42:
pkgconf_path_prepend(pkg_optarg, &dir_list, true);
pkgconf_path_add(pkg_optarg, &dir_list, true);
break;
case 43:
pkgconf_client_set_prefix_varname(&pkg_client, pkg_optarg);
@ -1052,26 +948,15 @@ main(int argc, char *argv[])
personality = pkgconf_cross_personality_find(pkg_optarg);
break;
#endif
case 55:
verbosity++;
break;
case '?':
case ':':
ret = EXIT_FAILURE;
goto out;
return EXIT_FAILURE;
break;
default:
break;
}
}
if (personality == NULL) {
#ifndef PKGCONF_LITE
personality = deduce_personality(argv);
#else
personality = pkgconf_cross_personality_default();
#endif
}
pkgconf_path_copy_list(&personality->dir_list, &dir_list);
pkgconf_path_free(&dir_list);
@ -1116,33 +1001,24 @@ main(int argc, char *argv[])
if ((want_flags & PKG_ABOUT) == PKG_ABOUT)
{
about();
ret = EXIT_SUCCESS;
goto out;
return EXIT_SUCCESS;
}
if ((want_flags & PKG_VERSION) == PKG_VERSION)
{
version();
ret = EXIT_SUCCESS;
goto out;
return EXIT_SUCCESS;
}
if ((want_flags & PKG_HELP) == PKG_HELP)
{
usage</