Compare commits

..

337 Commits

Author SHA1 Message Date
Ariadne Conill
0c2743f6e3 libpkgconf: fragment: constify quote_spans
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-20 13:24:21 -08:00
Ariadne Conill
65237e9d91 libpkgconf: fragment: remove unnecessary ternary
We always want to escape spaces because all strings are fully quoted for safety.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-20 13:23:51 -08:00
Ariadne Conill
a950adc6ef libpkgconf: fragment: rework fragment_quote to use pkgconf_buffer_escape
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-20 13:13:48 -08:00
Ariadne Conill
be8459e5fe libpkgconf: buffer: add pkgconf_buffer_escape
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-20 13:02:26 -08:00
Ariadne Conill
130c149072 libpkgconf: fragment: align escaped character set with original pkg-config
Ref: https://github.com/pkgconf/pkgconf/issues/406
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-20 12:39:08 -08:00
Antonin Décimo
2a7c05b9c7 pkg.m4: add missing quotes in PKG_WITH_MODULES 2026-01-20 12:10:35 -08:00
Ariadne Conill
ba33d22032 tests: port version option tests out of kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 21:34:33 -08:00
Ariadne Conill
e574c85325 tests: harness: add AtLeastVersion, ExactVersion and MaxVersion options
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 21:29:06 -08:00
Ariadne Conill
4a49b50e3b cli: constify required module version params
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 21:28:47 -08:00
Ariadne Conill
cc432b332c tests: move conflicts tests out of kyua, add more
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 21:18:30 -08:00
Ariadne Conill
9bac6b3a1d tests: harness: fix sorting of flag names
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 21:15:07 -08:00
Ariadne Conill
9cd3629f85 libpkgconf: queue: check final solutions for conflicts before returning OK
Otherwise, surfaced conflicts will be detected too late.
Closes: https://github.com/pkgconf/pkgconf/issues/436

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 21:04:22 -08:00
Ariadne Conill
18e32f2fb6 libpkgconf: pkg: make pkgconf_pkg_walk_conflicts_list public
It is needed by the solver to check the solution for conflicts.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 21:03:07 -08:00
Ariadne Conill
07dfbfffee libpkgconf: pkg: track conflict rule origins
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 20:50:18 -08:00
Ariadne Conill
5e26791ba5 libpkgconf: queue: surface conflicts as part of the solution
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 20:44:48 -08:00
Ariadne Conill
b1dd65e45d cli: core: rewrite query nodes instead of separately checking for --required-module-version
This allows us to let the solver deal with it, removing a duplicate
code path.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 19:55:02 -08:00
Ariadne Conill
192bc8c923 tests: move relevant query building regress tests out of kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 19:47:09 -08:00
Ariadne Conill
da019f2059 tests: move relevant libs query regress tests out of kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 19:41:34 -08:00
Ariadne Conill
52c2b0c2cb tests: move cflags ordering tests out of kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 19:28:52 -08:00
Ariadne Conill
c8018139cc tests: move malformed parsing regression tests out of kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 19:17:24 -08:00
Ariadne Conill
b08efccad8 tests: port modversion prefix test out of kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 19:08:41 -08:00
Ariadne Conill
b785d620fe tests: harness: allow verbosity level to be specified
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 19:03:26 -08:00
Ariadne Conill
1984ff2da6 tests: move idirafter and isystem sysroot tests out of kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 17:20:09 -08:00
Ariadne Conill
5b2c0a1110 tests: port define-prefix regress tests out of kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 16:34:59 -08:00
Ariadne Conill
9e802e5d76 tests: harness: clean up variable defines in destructor
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 16:16:46 -08:00
Ariadne Conill
d1bfa530be tests: migrate define-variable tests out of kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 16:14:46 -08:00
Ariadne Conill
f702beddf9 tests: harness: add DefineVariable keyword
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-18 16:07:34 -08:00
Elizabeth Kiara Regina Ashford
f887c83082 buffer: Style fixes
Signed-off-by: Elizabeth Kiara Regina Ashford <elizabeth.jennifer.myers@gmail.com>
2026-01-17 17:06:02 -08:00
Elizabeth Kiara Regina Ashford
cceebf098f buffer: fix undefined behaviour in variable argument function
When arguments are passed through ..., the C standard mandates that char
is promoted to int (other types are promoted as well, but that's outside
the scope of this commit). This happens with any argument passed through
va_start as well. Therefore, the last argument before variable arguments
in C must be a promoted type. Otherwise, the behaviour is undefined
(probably fine on most ABIs, but stricter ABIs might break).

Signed-off-by: Elizabeth Kiara Regina Ashford <elizabeth.jennifer.myers@gmail.com>
2026-01-17 17:06:02 -08:00
Elizabeth Kiara Regina Ashford
97e2d0433e buffer: Add pkgconf_buffer_copy and pkgconf_buffer_contains_byte
Signed-off-by: Elizabeth Kiara Regina Ashford <elizabeth.jennifer.myers@gmail.com>
2026-01-17 17:06:02 -08:00
Ariadne Conill
18dbb91e7a configure: warn loudly when --with-system-libdir and --with-system-includedir are not set
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-14 11:05:09 -08:00
Ariadne Conill
67c63bbba4 tests: skip shell-quoted-output on windows
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-03 15:39:06 -08:00
Ariadne Conill
06b04a2968 tests: skip backslash escaping tests on windows
Windows has slightly different behavior here.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-03 15:33:23 -08:00
Ariadne Conill
bbdf8ccee1 tests: move additional parser tests out of kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-03 15:19:59 -08:00
Ariadne Conill
55d5937800 tests: move several parser tests away from kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-03 14:24:50 -08:00
Ariadne Conill
450bccfef9 tests: add regression test for undefined pc_sysrootdir
Ref: https://github.com/pkgconf/pkgconf/issues/437
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-03 00:19:37 -08:00
Ariadne Conill
aaa89a4489 libpkgconf: client: ${pc_sysrootdir} should default to empty string, not /
If PKG_CONFIG_SYSROOT_DIR is not set, then pc_sysrootdir would be defined as /
which causes problems on Windows.

Fixes: https://github.com/pkgconf/pkgconf/issues/437
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-03 00:15:10 -08:00
Ariadne Conill
3bc327655b tests: move some flag ordering tests over from kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-02 23:47:55 -08:00
Ariadne Conill
964d595977 tests: port sysroot tests away from kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2026-01-02 23:30:35 -08:00
Ariadne Conill
93e085d144 tests: port some tuple-related tests from the kyua regress suite
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-30 16:26:15 -08:00
Ariadne Conill
ebba516806 tests: port requires.internal solver tests and query reordering tests from kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-30 15:17:30 -08:00
Ariadne Conill
08bc7159d3 tests: move keep-system-libs tests out of kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-30 14:09:41 -08:00
Ariadne Conill
c7b788699e tests: harness: add empty match strategy
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-30 14:02:12 -08:00
Ariadne Conill
34cf8ae094 tests: port remaining solver regression tests out of kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-30 13:13:32 -08:00
Ariadne Conill
20145bcfc1 tests: move more regression tests out of kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-30 13:07:01 -08:00
Ariadne Conill
222c5b5153 tests: port some ordering tests out of the kyua regress suite
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-30 11:22:15 -08:00
Ariadne Conill
0a44b9fa60 meson: delete dropped kyua test suites
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-30 11:03:07 -08:00
Ariadne Conill
2ab26a0b23 tests: delete kyua framework ordering tests
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-30 11:02:14 -08:00
Ariadne Conill
0c7acae717 tests: port framework ordering tests out of kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-30 11:01:09 -08:00
Ariadne Conill
45648f2562 tests: delete kyua provides suite
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-30 10:56:00 -08:00
Ariadne Conill
b5046faab0 tests: solver: port over provides-bar tests
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-30 10:50:26 -08:00
Ariadne Conill
b62019892e tests: harness: run tests in canonicalized order
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 22:18:50 -08:00
Ariadne Conill
039e596439 tests: port some provides tests out of kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 22:08:25 -08:00
Ariadne Conill
16cdb57887 tests: harness: PKG_PROVIDES should be print-provides
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 21:52:56 -08:00
Ariadne Conill
69dafb8ef1 meson: drop basic and builtin kyua test suites
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 21:21:32 -08:00
Ariadne Conill
9ae7adfcce tests: drop builtins suite, never hooked up in kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 21:21:32 -08:00
Ariadne Conill
d243ccd02c tests: drop basic testsuite from kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 21:19:01 -08:00
Ariadne Conill
7103f16ec9 tests: basic: port remaining solver tests out of kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 21:17:58 -08:00
Ariadne Conill
8ea545f5a3 tests: basic: port remaining non-solver tests out of kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 21:06:04 -08:00
Ariadne Conill
9061bd0fcf tests: harness: PKG_VARIABLES should be print-variables
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 21:01:25 -08:00
Ariadne Conill
c4f94af0bf tests: move pkg_config_path tests from kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 20:44:08 -08:00
Ariadne Conill
c2ded48007 tests: harness: clean up variable substitution
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 20:42:32 -08:00
Ariadne Conill
1af0659abc tests: harness: finalize want_variable
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 20:24:15 -08:00
Ariadne Conill
cdd2f4caec tests: move define-prefix test out of kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 20:22:25 -08:00
Ariadne Conill
965894e1c9 tests: harness: do %TEST_FIXTURES_DIR% subst on expected output
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 20:21:12 -08:00
Ariadne Conill
5ac9edec05 libpkgconf: buffer: add PKGCONF_BUFFER_FROM_STR() helper macro
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 20:20:09 -08:00
Ariadne Conill
f69ed723c0 tests: move arbitrary_path test out of kyua
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 20:06:07 -08:00
Ariadne Conill
eacbbbf54e tests: harness: substitute %TEST_FIXTURES_DIR% in query/environment values
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 20:05:00 -08:00
Ariadne Conill
8230b86da9 libpkgconf: buffer: add pkgconf_buffer_subst
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 19:52:57 -08:00
Ariadne Conill
a4ade8586d tests: convert sbom-related tests
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 19:19:15 -08:00
Ariadne Conill
6867afe86e tests: harness: fix up pkgconf_buffer lifecycle in buffersets
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 19:04:05 -08:00
Ariadne Conill
330a6f7761 meson: run solver tests
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 19:01:14 -08:00
Ariadne Conill
de33da1e41 test: harness: allow specifying multiple match clauses
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 19:00:05 -08:00
Ariadne Conill
9d7e6b2799 tests: solver: port some solver-related tests over
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 18:43:47 -08:00
Ariadne Conill
10be94af88 tests: basic: migrate many more tests to be declarative
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 18:28:56 -08:00
Ariadne Conill
a1e8f100c1 tests: harness: allow defining a fragment filter declaratively
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 18:10:12 -08:00
Ariadne Conill
ac2f34ed11 tests: remove now redundant tests from the kyua testsuite
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 17:55:39 -08:00
Ariadne Conill
73780a4239 tests: harness: finalize want-env-prefix buffer when destroying a test case
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 15:56:10 -08:00
Ariadne Conill
e2ba86341d tests: basic: convert libs_env, malformed-empty-package and a few exists tests
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 15:53:24 -08:00
Ariadne Conill
6f1009103a tests: harness: allow running individual test cases
This works nicely with --debug to debug individual test scenarios.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 15:43:13 -08:00
Ariadne Conill
03a0f5a25d tests: harness: allow running pkgconf engine in debug mode
This is something kyua cannot do: run a test scenario in debug mode.
2025-12-29 15:38:25 -08:00
Ariadne Conill
b8d8a5c8fd tests: harness: wire up WantEnvPrefix setting
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 15:24:22 -08:00
Ariadne Conill
7bbdf301e7 tests: basic: port over another set of tests
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 15:12:44 -08:00
Ariadne Conill
ecbedee413 cli: core: do not assume argv list is NULL terminated
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 15:03:25 -08:00
Ariadne Conill
077a1995d7 meson: fix test fixtures dir
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 14:54:24 -08:00
Ariadne Conill
e187002cb6 tests: harness: do not format too many things at once
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 14:51:27 -08:00
Ariadne Conill
f7689804f0 build: use $(top_srcdir) when referring to fixtures
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 14:46:01 -08:00
Ariadne Conill
000be0b6d1 tests: harness: provide annotated debug information on test failures
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 14:45:35 -08:00
Ariadne Conill
34713f899f tests: basic: port libs, libs-cflags and libs-cflags-version from old test suite
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 14:24:18 -08:00
Ariadne Conill
0376cc9142 github: run non-kyua tests on Windows
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 14:14:24 -08:00
Ariadne Conill
9a687744e8 tests: harness: avoid basename(3)
Windows does not have it.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 14:08:26 -08:00
Ariadne Conill
8210926404 tests: harness: do not shadow environ
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 14:03:23 -08:00
Ariadne Conill
c4a7081727 meson: integrate new test harness
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 13:55:38 -08:00
Ariadne Conill
7f6849d0f8 tests: harness: log when opendir(3) fails
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 13:55:04 -08:00
Ariadne Conill
2017526750 tests: start porting to the new test harness
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 13:46:24 -08:00
Ariadne Conill
0192269d62 tests: harness: allow fixtures directory to be specified
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 13:30:52 -08:00
Ariadne Conill
a7b6ef193f meson: do not drop bomtool_exe declaration with spdxtool one
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 13:12:41 -08:00
Ariadne Conill
cce39cb495 tests: harness: handle NULL case from pkgconf_buffer_freeze
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 13:10:41 -08:00
Ariadne Conill
4cba08e2bf tests: harness: remove spurious cast
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 13:08:55 -08:00
Ariadne Conill
0cd4a5c238 tests: add new harness
Ref: https://github.com/pkgconf/pkgconf/issues/406
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-29 13:06:47 -08:00
Ariadne Conill
521bb4e6e8 cli: core: use pkgconf_buffer_str_or_empty when setting builtin variables
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-27 15:13:08 -08:00
Ariadne Conill
db6bcbdcbe libpkgconf: buffer: add pkgconf_buffer_contains and pkgconf_buffer_match
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-27 15:07:50 -08:00
Ariadne Conill
a0933c8b1b cli: core: use pkgconf_output_puts for stderr messages
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-24 21:06:04 -08:00
Ariadne Conill
dfd20e49fc cli: core: move dump_personality back
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-24 20:51:22 -08:00
Ariadne Conill
ff07f6b7eb cli: core: refactor dependency query parsing 2025-12-24 20:48:05 -08:00
Ariadne Conill
d0823a14fc libpkgconf: queue: add pkgconf_queue_push_dependency
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-24 20:38:23 -08:00
Ariadne Conill
bb79dad5fe libpkgconf: client: clear client in deinit rather than in init
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-24 20:10:27 -08:00
Ariadne Conill
38b67e8c3b libpkgconf: client: zero out the client struct when initializing it
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-24 16:33:58 -08:00
Ariadne Conill
98784f1d42 libpkgconf: add cross personality pointer to client struct
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-24 16:05:17 -08:00
Ariadne Conill
fd604729c4 cli: core: use pkgconf_client_getenv wrapper for mocking
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-24 15:48:06 -08:00
Ariadne Conill
be0687f637 libpkgconf: use pkgconf_client_getenv wrapper for mocking
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-24 15:47:53 -08:00
Ariadne Conill
50da3bde52 libpkgconf: client: add pkgconf_client_getenv wrapper
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-24 15:39:35 -08:00
Ariadne Conill
eab5f5da93 cli: chase pkgconf_client_init changes
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-24 15:35:09 -08:00
Ariadne Conill
c7d8ebc439 libpkgconf: client: make environment variable lookups mockable
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-24 15:34:54 -08:00
Ariadne Conill
a592567215 cli: core: only zero out the end of the package name if actually necessary
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-24 15:22:12 -08:00
Ariadne Conill
1cbe6de04f cli: core: use last_argc everywhere instead of pkg_optind
This way the test runner does not use getopt_long.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-24 15:18:54 -08:00
Ariadne Conill
8459b05bbf cli: refactor main for testing purposes
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-23 16:46:39 -08:00
Ariadne Conill
c7069a080b cli: {bomtool,spdxtool}: chase pkgconf_client_init change 2025-12-23 16:35:11 -08:00
Ariadne Conill
f2b7af94e5 libpkgconf: client: add client_data pointer to client structure
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-23 16:03:49 -08:00
Ariadne Conill
41d320f679 cli: cleanups from output refactoring
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-23 15:01:32 -08:00
Ariadne Conill
6682bcb78d cli: use pkgconf_output_putbuf for printing CFLAGS
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-23 14:58:05 -08:00
Ariadne Conill
62473d1481 libpkgconf: output: add pkgconf_output_putbuf
This copies a pkgconf_buffer_t to a pkgconf_output_t.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-23 14:57:44 -08:00
Ariadne Conill
53db712972 cli: use pkgconf_output_fmt in most places
This way the C testsuite will be able to capture output as appropriate.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-23 14:51:56 -08:00
Ariadne Conill
0a0e74ec2e libpkgconf: output: fix length argument to fwrite
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-23 14:20:03 -08:00
Ariadne Conill
82e66bf096 libpkgconf: client: set the default output object in client init
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-23 14:10:48 -08:00
Ariadne Conill
5951b644bb libpkgconf: client: add pkgconf_client_set_output
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-23 14:09:10 -08:00
Ariadne Conill
38151d1726 libpkgconf: output: add implementation
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-23 13:31:53 -08:00
Ariadne Conill
def161ad50 libpkgconf: buffer: add pkgconf_buffer_append_vfmt
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-23 13:31:40 -08:00
Ariadne Conill
cf246dba4f build: add libpkgconf pkgconf_output files
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-23 13:31:40 -08:00
Ariadne Conill
232cbc0cc9 libpkgconf: add pkgconf_output API declarations
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-23 13:31:40 -08:00
Ariadne Conill
0c38d0afd6 libpkgconf: pkg: fix formatting of version matching truth table
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-23 10:33:05 -08:00
Ariadne Conill
be9f756e3d cli: include synthesized virtuals (from provides) in --list-all and --list-package-names output
Closes: https://github.com/pkgconf/pkgconf/issues/435
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-23 10:29:13 -08:00
Ariadne Conill
d619fc6f10 github: update funding links for pkgconf
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-22 13:07:45 -08:00
Ariadne Conill
9378236fe3 pkg.m4: stop pointing people at freedesktop pkg-config
Freedesktop pkg-config is no longer maintained, the last commit was in 2021.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-22 12:38:55 -08:00
Ariadne Conill
213cccf9dc libpkgconf: pkg: tolerate missing requires.internal dependency nodes if unnecessary
Requires.internal is intended to be a weaker version of requires.private,
where a dependency node does not need to be satisfied if link libraries are
not requested.

Closes: https://github.com/pkgconf/pkgconf/issues/434
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-21 13:32:20 -08:00
Ariadne Conill
0ed38cec79 libpkgconf: introduce PKGCONF_PKG_PKGF_REQUIRE_INTERNAL flag
Ref: https://github.com/pkgconf/pkgconf/issues/434
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-21 13:06:07 -08:00
Ariadne Conill
74bb9a2301 tests: add additional requires.internal tests
Ref: https://github.com/pkgconf/pkgconf/issues/434
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-21 12:57:06 -08:00
Ariadne Conill
ab3bddd80b man/pc.5: add documentation for requires.internal
Ref: https://github.com/pkgconf/pkgconf/issues/318
Ref: https://github.com/pkgconf/pkgconf/issues/434
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-21 12:41:00 -08:00
Ariadne Conill
a32da62aa7 spdxtool: util: clean up strftime buffer usage
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 23:46:04 -08:00
Ariadne Conill
36ac187ad5 spdxtool: util: manually specify ISO8601 format string elements instead of %F and %Z
These do not work on the Microsoft C runtime library.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 23:40:53 -08:00
Ariadne Conill
0fc3ba9d95 spdxtool: drop spdxtool_util_spdx_id_add
Not needed or used by anything.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 16:03:21 -08:00
Ariadne Conill
8f9161514b libpkgconf: bsdstubs: use libpkgconf/stdinc.h
This saves us some more _WIN32 pain.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 15:37:29 -08:00
Ariadne Conill
c863ef3f82 libpkgconf: clean up _WIN32 include mess quite a bit
Ref: https://github.com/pkgconf/pkgconf/issues/406
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 15:25:17 -08:00
Ariadne Conill
530fc89d13 libpkgconf: tuple: use pc_sysrootdir variable consistently
In one case we were unconditionally using the value of PKG_CONFIG_SYSROOT_DIR
rather than checking the package-specific value.  This caused issues where the
sysroot was prepended where it should not be.

Closes: https://github.com/pkgconf/pkgconf/issues/307
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 14:43:03 -08:00
Ariadne Conill
c8bfa9fee0 libpkgconf: pkg: do not override pc_sysrootdir when emulating pkgconf 1.x
In pkgconf 1.x, we unconditionally prepend PKG_CONFIG_SYSROOT_DIR if
it is not already present.  Emulate this by not overriding pc_sysrootdir
in cases where the .pc file is outside the sysroot.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 14:43:03 -08:00
Ariadne Conill
dc0805a3ad tests: sysroot: add test for sysroot deduplication
If ${pc_sysrootdir} is expanded in another variable, then it
may be included twice.

Ref: https://github.com/pkgconf/pkgconf/issues/307
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 14:43:03 -08:00
Ariadne Conill
9590e052bb spdxtool: use SIZE_FMT_SPECIFIER in numeric URI generator
This resolves a compiler warning when building on Windows.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 13:44:38 -08:00
Ariadne Conill
b689fedcd9 spdxtool: fix isspace usage
ISO C expects parameters to ctype.h macros to be unsigned.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 13:32:42 -08:00
Ariadne Conill
0658ba7721 libpkgconf: pkg: add lint for duplicated dependency-list fields
Duplicating fields does not have consistent behavior across pkg-config
implementations, so we add a lint for it.

Ref: https://github.com/microsoft/vcpkg/issues/48837
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 01:52:49 -08:00
Ariadne Conill
008f963693 spdxtool: util: use a static counter instead of looping over possible IDs
Looping over possible IDs makes generating URIs an O(k) operation,
verses an O(1) operation.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 01:34:02 -08:00
Ariadne Conill
018983a78f spdxtool: serialize: fix docstrings
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 01:20:39 -08:00
Ariadne Conill
4e2fde48f6 spdxtool: serialize: further refactoring
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 01:12:47 -08:00
Ariadne Conill
e2ae592b89 spdxtool: serialize: clean up spdxtool_serialize_add_ch_with_comma
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 00:47:50 -08:00
Ariadne Conill
ecd1a20915 spdxtool: serialize: light refactoring using new pkgconf_buffer_t functions
Also drop spdxtool_serialize_add_string_with_comma because it is no longer
needed.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 00:39:12 -08:00
Ariadne Conill
3a24e69a8f spdxtool: use pkgconf_buffer_t functions to generate URIs
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 00:37:35 -08:00
Ariadne Conill
51dc5f6b8d libpkgconf: buffer: add pkgconf_buffer_freeze
This is a convenience function that strdups a buffer and then resets it.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 00:22:17 -08:00
Ariadne Conill
5f6681478e libpkgconf: buffer: add pkgconf_buffer_[v]join
This is a convenience function for combining multiple strings into a buffer
with a delimiter.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 00:19:22 -08:00
Ariadne Conill
741ee356b9 cli: declare logging FILE pointers as static
Found using sparse.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-20 00:00:41 -08:00
Ariadne Conill
a779abe2a4 libpkgconf: fragment: combine lexed arguments where a flag is expected to have a value
Previously, pkgconf --fragment-tree would generate inconsistent output
for Cflags/Libs strings where whitespace was present between a flag and
its argument.

Closes: https://github.com/pkgconf/pkgconf/issues/411
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-19 23:44:36 -08:00
Ariadne Conill
9e96c090dc tests: add test for flag whitespace parsing
Ref: https://github.com/pkgconf/pkgconf/issues/411
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-19 23:19:19 -08:00
Ariadne Conill
a3da8f5cd4 tests: add fixtures for whitespace between flag tests
Ref: https://github.com/pkgconf/pkgconf/issues/411
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-19 23:14:23 -08:00
Ariadne Conill
e07ba0af64 libpkgconf: client: fix potential memory leak in pkgconf_trace under memory pressure
If pkgconf_trace was unable to allocate the final error message buffer, the intermediate
buffer would not be freed.

Found with clang static analyzer.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-19 22:56:04 -08:00
Ariadne Conill
44d8d567d3 libpkgconf: client: fix uninitialized return variable when preloading from environment
An empty PKG_CONFIG_PRELOADED_FILES setting would result in an uninitialized
return value.

Found by clang static analyzer.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-19 22:52:31 -08:00
Jonathan Gray
31c71208e3 libpkgconf: pkg: fix unintended bitwise and
found with sparse: 'warning: dubious: !x & !y'
2025-12-19 22:27:13 -08:00
Ariadne Conill
62da037c5b github: upload MSI artifacts to distfiles.ariadne.space as well
Fixes: https://github.com/pkgconf/pkgconf/issues/422
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-07 21:51:37 -08:00
Ariadne Conill
e733223092 tests: add test for variable overrides from environment
Ref: https://github.com/pkgconf/pkgconf/issues/426
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-07 21:45:36 -08:00
Ariadne Conill
3dbec89d3d libpkgconf: pkg: allow environment variables to override tuple contents
This was a feature in original pkg-config that was overlooked.

Fixes: https://github.com/pkgconf/pkgconf/issues/426
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-07 21:44:00 -08:00
Ariadne Conill
6cfe4ee587 tests: add duplicate-tuple-upsert regression test
Ref: https://github.com/pkgconf/pkgconf/issues/424
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-07 21:20:40 -08:00
Ariadne Conill
4c4a4242dc tests: add duplicate-tuple fixture
Ref: https://github.com/pkgconf/pkgconf/issues/424
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-07 21:19:01 -08:00
Ariadne Conill
e80c5f0c9a libpkgconf: tuple: fix upserting tuples which reference themselves
In the MingW MSYS2 package for OpenSSL, we have the following in the
libcrypto.pc file:

   ...
   prefix=/mingw64
   prefix=${prefix}
   ...

Previously the second assignment was evaluating as an empty string
because the earlier tuple was being deleted too early during the
upsert.

Fixes: https://github.com/pkgconf/pkgconf/issues/424
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-07 21:17:31 -08:00
Ariadne Conill
1f42fa99fe github: install GNU tar when releasing from tag
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-07 21:09:08 -08:00
Ariadne Conill
502fcdf71c github: install GNU tar when building on Alpine
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-07 21:07:51 -08:00
Ariadne Conill
2e3b56aa4b libpkgconf: fragment: drop pkgconf_fragment_render API
Replaced by pkgconf_fragment_render_buf, which has clearer reference
ownership semantics.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-07 21:00:18 -08:00
Ariadne Conill
1d9142c4c7 cli: use pkgconf_fragment_render_buf for rendering fragments
This is an improvement for memory safety as the underlying memory
reference ownership is clearer.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-07 20:59:06 -08:00
Ariadne Conill
fff4302b05 libpkgconf: buffer: add pkgconf_buffer_str_or_empty
This returns the underlying buffer allocation if it is not empty,
otherwise returns an empty allocation.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-07 20:57:02 -08:00
Ariadne Conill
1fcc01aa4f libpkgconf: buffer: add pkgconf_buffer_fputs
This dumps a pkgconf_buffer_t to the given stdio FILE stream
with puts(3)-like semantics.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-07 20:54:02 -08:00
Ariadne Conill
8bae84d6a4 libpkgconf: fragment: rework fragment API to use pkgconf_buffer_t
This improves the memory safety of internal pkgconf APIs by
using Pascal-style strings (pkgconf_buffer_t).

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-07 20:41:46 -08:00
Ariadne Conill
90f9186e10 cli: use a single fragment list for all collected CFLAGS and linker flags
This cleans up --cflags and --libs handling a bit, making the code less
repetitive.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-02 14:01:55 -08:00
Ariadne Conill
467328ec8b cli: fix --newlines with both --cflags and --libs
In the future we should collapse this down to a single fragment list...

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-02 13:44:51 -08:00
Ariadne Conill
92cb09751a man/pkgconf.1: document --newlines option
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-02 13:39:52 -08:00
Ariadne Conill
5d6c90faaf cli: add --newlines option
Closes: https://github.com/pkgconf/pkgconf/issues/412
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-02 13:39:35 -08:00
Ariadne Conill
f3aac2724a cli: properly align --source and --license-file options
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-02 13:29:21 -08:00
Ariadne Conill
42b6a6a04c libpkgconf: fragment: allow user to specify inter-fragment delimiter
Ref: https://github.com/pkgconf/pkgconf/issues/412
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-02 13:27:51 -08:00
Ariadne Conill
e0b3ca8330 cli: fix memory leak with --dump-personality
Previously, we would call dump_personality() and immediately exit.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-02 13:15:56 -08:00
Ariadne Conill
ce401e3c45 tests: add personality handling tests
Tests for --dump-personality as well as builtin variables that are influenced
by pkgconf-personality(5).

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-02 13:09:50 -08:00
Ariadne Conill
7c46095d0b tests: add fixture for i386-linux-gnu personality
This will be used to validate --dump-personality and other personality-related
features.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-02 12:58:33 -08:00
Ariadne Conill
98d0b9342e cli: make --dump-personality output consistent with pkgconf-personality(5)
Previously, --dump-personality used whitespace to separate the path list, while the
specification requires a colon as the separator.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-02 11:37:38 -08:00
Ariadne Conill
16cebd6262 libpkgconf: buffer: null out buffer references when freeing it
This allows for pkgconf_buffer_finalize() to reset a buffer, not simply free the
underlying resources.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-02 11:26:12 -08:00
Ariadne Conill
da8cb80179 pkg.m4: bump serial
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-02 10:59:54 -08:00
Ariadne Conill
ad0d8b3944 Revert "pkg.m4: Abort by default if no pkgconf"
This reverts commit c583d49a3780c71c0d20a662a7cf562f6d01c171.
2025-12-02 10:59:19 -08:00
Ariadne Conill
0a3cd05bcd libpkgconf: buffer: add pkgconf_buffer_append_fmt
Roughly similar to asprintf(3) but with pkgconf_buffer.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-02 10:38:24 -08:00
Ariadne Conill
4e20b02177 libpkgconf: personality: do not filter PKG_DEFAULT_PATH
Otherwise the search path list would be empty in CI when building
with autoconf.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-02 01:19:25 -08:00
Ariadne Conill
e1e788c8cc cli: rework dump_personality to use pkgconf buffers
We already had a helper function, path_list_to_buffer, which we can re-use to
avoid repeatitive code.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-02 01:00:17 -08:00
Ariadne Conill
2bfd36beb4 cli: dynamically generate built-in paths from selected pkgconf personality
Previously pkgconf would dump the hardcoded defaults, even if they had been changed
by the use of a pkgconf personality.

Now the data in the pkgconf personality is used to register the built-in package data.

Fixes: https://github.com/pkgconf/pkgconf/issues/418
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-02 00:54:03 -08:00
Ariadne Conill
0fb2ce907b spdxtool: remove errant uses of pkgconf_buffer_trim_byte
This was a workaround for a pkgconf_buffer bug which was fixed in
commit 75b57db56291a28eed881a9ad48742713a7c635b.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-02 00:50:33 -08:00
Ariadne Conill
75b57db562 libpkgconf: buffer: fix pkgconf_buffer_append
Previously, pkgconf_buffer_append would append a nul byte to the buffer
because buffer->end was set to include the nul byte added by pkgconf_strlcpy.

(Buffers internally are a multiple of PAGE_SIZE, so this was not a security problem.)

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-02 00:44:51 -08:00
Ariadne Conill
ccd916d0a8 libpkgconf: pkg: remove old static pkg-config virtual objects
These are no longer used (or even compiled).  They were left behind for reference.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-01 23:59:39 -08:00
Ariadne Conill
ef935a6349 cli: register built-in packages for pkg-config and pkgconf
The variable data will eventually be dynamically generated instead of just using
hardcoded data.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-01 23:58:52 -08:00
Ariadne Conill
c7b21f5755 libpkgconf: client: log when preloading packages into a client
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-01 23:54:50 -08:00
Ariadne Conill
2a17e025ab libpkgconf: client: fix PKGCONF_LIST_FOREACH_ENTRY_SAFE ordering when iterating preloads
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-01 23:53:27 -08:00
Ariadne Conill
bff83a117b libpkgconf: add pkgconf_client_preload_one for preloading a pkgconf_pkg_t
This allows us to programmatically add "built-in" packages in the client.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-01 23:31:34 -08:00
Ariadne Conill
9baf542f84 libpkgconf: remove pkgconf_builtin_pkg_get
Now that we have preloaded packages, we can handle the builtin packages as
preloads.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-12-01 23:16:47 -08:00
Ariadne Conill
a1e09753bc libpkgconf: parser: add function to allow for parsing a pkgconf_buffer_t directly
This will be used to overhaul the built-in package support by synthesizing a .pc
file on the fly for built-ins.

Ref: https://github.com/pkgconf/pkgconf/issues/418
2025-11-02 01:27:38 -08:00
Ariadne Conill
c598ef7218 spdxtool: use software_sbomType instead of sbomType
sbomType is not in the SPDX 3.0.1 vocabulary.
2025-11-01 23:56:00 -07:00
Ariadne Conill
bbe8ab6593 spdxtool: do not serialize missing license-related document elements
If a .pc file without licensing information was processed, we would get nodes like
pkgconf:(null), which is undesirable.
2025-11-01 15:54:15 -07:00
Tuukka Pasanen
4307d691f9 Add tests for SPDX Lite profile 3.0 generator
Add some basic tests for spdxtool to make sure that
 SPDX Lite profile 3.0 does not break in future
2025-11-01 15:31:55 -07:00
Tuukka Pasanen
f0826ce2d2 Add SPDX Lite profile 3.0 generator
Add SPDX Lite profile 3.0 generator named
spdxtool to pkdconf. It outputs JSON-LD RDF
tree and does only depend pkgconf and pkgconf
.pc file to generate SBOM.

It superseeds bomtool which is SPDX 2.x generator
name bomtool. But as SPDX version 2.x is still widely
used new tool was created instead of upgrading old one.
2025-11-01 15:31:55 -07:00
Tuukka Pasanen
e77e36c15c libpkgconf.pc.in: Add License.file-tag
Add License.file-tag to libpkgconf.pc.in.
2025-11-01 15:21:32 -07:00
Tuukka Pasanen
c9d54312ee tests: Add testing for License.file-tag
Add some simple testcases for License.file-tag reading
2025-11-01 15:21:32 -07:00
Tuukka Pasanen
136562c699 man/pc.5: Add documentation for License.file-tag to pc.5 man page
Add more documentation to pc.5 man page to make sure that
there is clear description how to use License.file-tag
2025-11-01 15:21:32 -07:00
Tuukka Pasanen
ba731727d3 man/pkgconf.1: Mention License.file-tag in pkgconf.1 man page
Add similar mentioning of License.file-tag than License-tag has in
pkgconf.1 man page.
2025-11-01 15:21:32 -07:00
Tuukka Pasanen
589d8028c4 cli: Add License.file-tag to cli
Add License.file-tag printing to cli.
2025-11-01 15:21:32 -07:00
Tuukka Pasanen
d0481ddafd libpkgconf: Add License.file-tag
Add License.file-tag for determining where to find
correct license for package. License.file should
be in URI format.
2025-11-01 15:21:32 -07:00
Ariadne Conill
6b68231882 bomtool: add PackageDownloadLocation for packages with source tags 2025-10-22 13:55:07 -07:00
Ariadne Conill
4e5fcd810a autogen: run libtoolize first
Fixes #428
2025-10-22 13:41:19 -07:00
Ariadne Conill
15650665b3 libpkgconf: ensure source-tag is freed when package is freed 2025-10-22 13:38:31 -07:00
Tuukka Pasanen
fe35ffd555 libpkgconf.pc.in: Add Source-tag
Add Source tag to libpkgconf.pc.in.
2025-10-22 13:32:32 -07:00
Tuukka Pasanen
115ce623a3 tests: Add testing for Source-tag
Add some simple testing for Source tag reading
in test cases
2025-10-22 13:32:32 -07:00
Tuukka Pasanen
7ba494a0ed man/pc.5: Add documentation for Source-tag to pc.5 man page
Add more documentation to pc.5 man page to make sure that
there is clear description how to use Source-tag
2025-10-22 13:32:32 -07:00
Tuukka Pasanen
790ac89795 man/pkgconf.1: Small mention of Source-tag to pkgconf.1 man page
Add similar mentioning of Source-tag than License-tag has to
pkgconf.1 man page.
2025-10-22 13:32:32 -07:00
Tuukka Pasanen
e21de9c671 cli: Add Source-tag to cli
Add Source tag printing to cli.
2025-10-22 13:32:32 -07:00
Tuukka Pasanen
a04e269553 libpkgconf: Add Source-tag
Add Source-tag for determining where to find
correct source code for the package. Source tag
should be URI.
2025-10-22 13:32:32 -07:00
Undefine
2c9a0d2d7b cli: unveil only after building the client dir list
This fixes an issue where pkgconf can't access directories in the
PKG_CONFIG_PATH environment variable.
2025-10-22 13:24:04 -07:00
Undefine
706b3cbdd8 cli: ignore unveil ENOENT errors on defaults path 2025-10-22 13:24:04 -07:00
mrgrouse
88ef76a600 build: add buffer.c to SRCS in Makefile.lite 2025-09-03 09:59:16 -07:00
Ariadne Conill
e6e945835a cli: use append for PKG_CONFIG_LOG
This was an oversight when originally implementing PKG_CONFIG_LOG
functionality.  In original pkg-config, it is opened as an append
log.

Fixes: 8ccc108 ("main: add a stub for PKG_CONFIG_LOG (ref #88)")
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-08-18 10:27:21 -07:00
Marc-André Lureau
1c0ad34c02 github: add some extra jobs to publish tarball/msi on github
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2025-08-18 10:22:05 -07:00
Marc-André Lureau
499083eba9 build: fix -Werror=unused-variable
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2025-08-18 10:22:05 -07:00
Ariadne Conill
de3ffe21cf libpkgconf: downgrade to readlink(3) if readlinkat(3) is unavailable
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
Fixes: https://github.com/pkgconf/pkgconf/issues/420
2025-07-30 12:06:38 -07:00
Ariadne Conill
c060c5af3f build: check for readlinkat presence
Related: https://github.com/pkgconf/pkgconf/issues/420
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-07-30 11:59:38 -07:00
Mike L
1550bf53b3 build: add -D_DARWIN_C_SOURCE 2025-06-25 15:30:57 -07:00
Ariadne Conill
aa6b5c4ca9 libpkgconf: dependency: make sure buf_sz is at least 1 byte 2025-06-24 16:31:21 -07:00
Ariadne Conill
4fc570f91d pkgconf 2.5.1. 2025-06-24 15:49:00 -07:00
Ariadne Conill
46059c36d7 libpkgconf: dependency: skip parsing of empty strings
Closes: https://github.com/pkgconf/pkgconf/issues/413
Closes: https://github.com/pkgconf/pkgconf/issues/414
Co-authored-by: Jonas Kvinge <jonas@jkvinge.net>
2025-06-24 15:29:56 -07:00
Christoph Reiter
c52f77854f pkgconf_trace: fix error return handling
In both cases the return value was incremented before checking
for the error case.
2025-06-24 15:20:22 -07:00
Ariadne Conill
204ad5e48a pkgconf 2.5.0.
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-06-18 21:56:01 -07:00
Ariadne Conill
b4547a01f9 github: add actions workflow for pkgconf releases
Ref: https://github.com/pkgconf/pkgconf/issues/404
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-06-18 21:29:57 -07:00
Ariadne Conill
70748388e8 chore: delete obsolete woodpecker CI configuration
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-06-18 20:33:18 -07:00
Ariadne Conill
a0fc46a805 libpkgconf: clean up PRINTFLIKE and DEPRECATED macros
On MingW, the wrong format string checker would be used.  So prefer gnu_printf
where it is known to be available.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-06-18 14:51:37 -07:00
Kai Pastor
fea9592aa4 personality.c: _WIN32 does not want_default_static 2025-06-16 08:40:36 -07:00
Ingo Schwarze
efdeb71de5 man/pkgconf.1: improve the beginning of the DESCRIPTION
Replace the first paragraph with an adequate description of what the
program actually does, mention the main use case, and avoid vague
wordings like "helps to" and "allow" and misleading wordings like
"development libraries" (this is perfectly adequate for production
builds, too) and "detect" (nothing is automatically detected,
everything has to be specified explicitly in pc(5) configuration
files).

Add a second paragraph introducing the module search path
because that concept is really crucial for the program.

After that, add three paragraphs explaining the classification of
options, in particular how they behave with respect to early exit
and dependency resolution.  Admittedly, this requires rather lengthy
text, which is clearly undesirable at this place in a manual page.
But the atrocious user interface design only leaves the choice of
either bothering the reader with a long, complicated description,
or not describing how the program behaves at all.

Postponing the description of these interactions until after the
option list would not be better.  Understanding the classification
up front helps to understand the descriptions and purposes of the
individual options.

What is still sorely missing is a precise definition of what the
three different versions of the crucial term "dependency" mean,
and how resolution actually works.
2025-06-05 09:24:12 -07:00
Ingo Schwarze
5729e1dd15 man/pkgconf.1: document --dump-personality --help --solution 2025-06-05 09:24:12 -07:00
Ingo Schwarze
11ea807fcd man/pkgconf.1: correctly describe the module version check options
The options --atleast-version, --exact-version, and --max-version
short-circuit to both success and failure, which implies that the
result depends on the the order of the arguments and that a simple
description is not possible.  In particular, it is not possible
to describe the behaviour with any sentence of the form
"(succeed|fail) if (any|each) argument satisfies (condition)".
2025-06-05 09:24:12 -07:00
Ingo Schwarze
5c3b5b1da9 man/pkgconf.1: --log-file does not print to stdout 2025-06-05 09:24:12 -07:00
Ingo Schwarze
d3e941554b man/pkgconf.1: mention that --list-all exits early 2025-06-05 09:24:12 -07:00
Ingo Schwarze
f0be161cc8 man/pkgconf.1: mention that --atleast-pkgconfig-version exits early 2025-06-05 09:24:12 -07:00
Ingo Schwarze
509c34fa45 man/pkgconf.1: mention that PKG_CONFIG_SYSROOT_DIR sets pc_sysrootdir 2025-06-05 09:24:12 -07:00
Ingo Schwarze
8886d83900 man/pkgconf.1: describe DESTDIR more precisely 2025-06-05 09:24:12 -07:00
Ingo Schwarze
5401935844 man/pkgconf.1: document PKG_CONFIG_ALLOW_SYSTEM_{CFLAGS,LIBS} 2025-06-05 09:24:12 -07:00
Ariadne Conill
505f310250 meson: add bomtool to build targets
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-06-04 12:50:56 -07:00
Ariadne Conill
ac8cc8006d man: add bomtool manpage
Closes: https://github.com/pkgconf/pkgconf/issues/408
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-06-04 12:43:54 -07:00
Ariadne Conill
5b02ec3787 man/pkgconf.1: document PKG_CONFIG_PRELOADED_FILES
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-06-04 11:59:05 -07:00
Ariadne Conill
d6be0d38d5 libpkgconf: client: unref the preloaded list on client deinit
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-06-04 11:53:20 -07:00
Ariadne Conill
f748308ba0 cli: add support for PKG_CONFIG_PRELOADED_FILES
This allows for preloading external files outside PKG_CONFIG_PATH, which will
be prioritized over the files located inside PKG_CONFIG_PATH.

For example:

~/pkgconf $ PKG_CONFIG_PRELOADED_FILES=tests/lib1/foo.pc ./pkgconf --list-all
foo                            foo - A testing pkg-config file

Fixes: https://github.com/pkgconf/pkgconf/issues/391
2025-06-04 11:29:11 -07:00
Ariadne Conill
1a28204a96 libpkgconf: client: avoid environ keyword which is macro on Win32
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-06-04 11:22:44 -07:00
Ariadne Conill
a3af684d16 libpkgconf: pkg: ensure pkgconf_pkg_new_from_path only loads .pc files
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-06-04 11:18:41 -07:00
Ariadne Conill
9665d214cb libpkgconf: client: add pkgconf_client_preload_from_environ()
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-06-04 11:15:52 -07:00
Ariadne Conill
0a93248351 libpkgconf: pkg: add functionality for searching and scanning the preloaded package list
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-06-04 10:58:27 -07:00
Ariadne Conill
d477016924 libpkgconf: pkg: remove freed packages from the preload list
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-06-04 10:42:11 -07:00
Ariadne Conill
01eb0cf19d libpkgconf: client: add pkgconf_client_preload_path() for preloading packages
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-06-04 10:39:09 -07:00
Ariadne Conill
3be7f2cef1 libpkgconf: pkg: add tracking infrastructure for preloaded packages
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-06-04 10:34:23 -07:00
Ariadne Conill
aa5813ac5e libpkgconf: path: refactor windows registry PKG_CONFIG_PATH support
Now we add to the search list rather than falling back to the registry
after the search list fails to find a package.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-31 00:17:11 -07:00
Ariadne Conill
c6f973bd5b build: add pkgconf.wxs.in and txt2rtf.py to source files list
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-30 23:27:31 -07:00
Marc-André Lureau
6fe9458829 ci: enable back MSI build
Drop mingw32 arch build, see:
https://github.com/msys2/MINGW-packages/issues/24419
2025-05-30 23:18:17 -07:00
Ariadne Conill
4dad43adbf github: disable msi build for now, broken
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-29 16:04:04 -07:00
Marc-André Lureau
a1a255ee7d ci: build MSI on msys2, publish artefacts
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2025-05-29 15:50:14 -07:00
Marc-André Lureau
86d38119c0 meson: add MSI build target
It would be nice to provide a release MSI for Windows users.

Requires wixl.

Tested on Fedora, with mingw64 cross-build.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2025-05-29 15:50:14 -07:00
Marc-André Lureau
e49cbdfcb3 stdinc: fix -Wformat warning on win64
../libpkgconf/client.c: In function 'pkgconf_trace':
../libpkgconf/client.c:381:47: warning: format '%u' expects argument of type 'unsigned int', but argument 5 has type 'size_t' {aka 'long long unsigned int'} [-Wformat=]
  381 |         len = snprintf(errbuf, sizeof errbuf, "%s:" SIZE_FMT_SPECIFIER " [%s]: ", filename, lineno, funcname);
      |                                               ^~~~~                                         ~~~~~~
      |                                                                                             |
      |                                                                                             size_t {aka long long unsigned int}

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2025-05-29 15:50:14 -07:00
Ariadne Conill
205bc7bf42 libpkgconf: client: remove PKGCONF_BUFSIZE from logging subsystem
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-29 13:13:17 -07:00
Ariadne Conill
fd7069a209 libpkgconf: dependency: move static parsing buffer off the stack
Also remove the 64KB limit for dependency list strings.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-29 11:13:39 -07:00
Ariadne Conill
25cbcbc351 README: simplify, improve tone to be less grumpy
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-29 10:47:47 -07:00
Ariadne Conill
5925cc4376 github: remove V=1 from analysis step
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-29 10:23:58 -07:00
Ariadne Conill
ad75dec33b github: enable GCC static analysis tests with -Werror
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-29 10:21:21 -07:00
Ariadne Conill
61610c65cb libpkgconf: pkg: refactor pkgconf_pkg_new_from_file into pkgconf_pkg_new_from_path
Previously, files would be closed by side effect, which is a somewhat bad API
design that trips up various static analysis tools.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-29 10:08:00 -07:00
Ariadne Conill
2983d31188 libpkgconf: path: gracefully handle memory alloc failures
Found-by: GCC -fanalyzer
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-28 23:21:24 -07:00
Ariadne Conill
10d37b6160 libpkgconf: dependency: handle memory allocation failures
Found-by: GCC -fanalyzer
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-28 23:18:42 -07:00
Ariadne Conill
1fd56779af libpkgconf: argvsplit: use calloc for all buffers, handle memory alloc failures
Found-by: GCC -fanalyzer
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-28 23:15:52 -07:00
Ariadne Conill
735fd05427 libpkgconf: fragment: robustly catch and handle memory allocation failures
Found-by: GCC -fanalyzer
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-28 23:11:34 -07:00
Ariadne Conill
7fbb8bf280 libpkgconf: pkg: refactor pkgconf_pkg_free into micro-operations
Then use pkg_free_lists and pkg_free_object to clean up package objects
which have not been fully initialized.

Found-by: GCC -fanalyzer
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-28 22:59:43 -07:00
Ariadne Conill
e4e3b45b78 libpkgconf: pkg: gracefully handle NULL parameter to pkgconf_pkg_unref()
This could happen when pkgconf_pkg_new_from_file() fails due to error.

Found-by: GCC -fanalyzer
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-28 22:32:06 -07:00
Ariadne Conill
859402ef3d libpkgconf: pkg: allow pkgconf_pkg_new_from_file to return NULL on malloc error
Other errors can already cause pkgconf_pkg_new_from_file() to return NULL, so
this doesn't break API.

Found-by: GCC -fanalyzer
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-28 22:30:34 -07:00
Ariadne Conill
e3f9e6943a libpkgconf: client: propagate malloc failures upwards
Otherwise a NULL dereference can happen when malloc fails in pkgconf_client_new().

Found-by: GCC -fanalyzer
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-28 22:18:39 -07:00
Ariadne Conill
5ecd34e44b libpkgconf: personality: rework non-default personality loading
If there is an error loading the personality file (for example, the personality file
associated with the requested triplet does not exist), then resources would be leaked.

Found-by: GCC -fanalyzer
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-28 22:12:57 -07:00
Ariadne Conill
187f2604a5 libpkgconf: personality: make cross-personality triplet identifier non-const
Dynamically loaded personality files use a malloced string to store the triplet,
and so we need to drop the const to allow for that string to be freed later.

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-28 22:08:08 -07:00
Jonathan Gray
a61b00f129 man/pkgconf.1: fix spelling of overridden 2025-05-28 21:34:37 -07:00
Ariadne Conill
f2396064bc man/pkgconf.1: fix description of PKG_CONFIG_SYSROOT_DIR
Fixes: ef59baf ("Document environment variables")
Closes: https://github.com/pkgconf/pkgconf/issues/388
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-28 21:33:45 -07:00
Ingo Schwarze
c34379adaf man/pkgconf.1: better describe PKG_CONFIG_TOP_BUILD_DIR
In particular, mention the default value and make it more explicit
in which case the default is used.
2025-05-21 11:37:52 -07:00
Ingo Schwarze
c5953937e8 man/pkgconf.1: better describe --print-variables and --variable
In particular, mention that only the first "module" argument is used
and clarify what exactly is printed.
2025-05-21 11:37:52 -07:00
Ingo Schwarze
adc549918d man/pkgconf.1: rudimentary documentation of two more env vars
Specifically, PKG_CONFIG_DONT_DEFINE_PREFIX and PKG_CONFIG_RELOCATE_PATHS.
2025-05-21 11:37:52 -07:00
Ingo Schwarze
e607da2e0a man/pkgconf.1: clarify the interaction of --static, --pure, and --shared 2025-05-21 11:37:52 -07:00
Ingo Schwarze
83c7ca08ef man/pkgconf.1: rudimentary documentation of PKG_CONFIG_IGNORE_CONFLICTS 2025-05-21 11:37:52 -07:00
Ingo Schwarze
5ad8a5bed7 man/pkgconf.1: mention which options imply --print-errors 2025-05-21 11:37:52 -07:00
Ingo Schwarze
e785ee470b man/pkgconf.1: fix incorrect description of PKG_CONFIG_DEBUG_SPEW
It does *not* enable any additional debug logging, only --debug does that.
This variable only overrides --silence-errors.
2025-05-21 11:37:52 -07:00
Ingo Schwarze
a7443fe535 man/pkgconf.1: document PKG_CONFIG_MAXIMUM_TRAVERSE_DEPTH
Also mention that it overrides --maximum-traverse-depth
as well as which options override it.
2025-05-21 11:37:52 -07:00
Ingo Schwarze
cab9dcbfcf man/pkgconf.1: rudimentary documentation of --msvc-syntax
Slightly improve the description of PKG_CONFIG_MSVC_SYNTAX, too.
2025-05-21 11:37:52 -07:00
Ingo Schwarze
4e7ecc377a man/pkgconf.1: document some environment variables defining paths
Document CPATH, C_INCLUDE_PATH, CPLUS_INCLUDE_PATH, OBJC_INCLUDE_PATH,
and LIBRARY_PATH.

Improve the description of PKG_CONFIG_SYSTEM_INCLUDE_PATH
and PKG_CONFIG_SYSTEM_LIBRARY_PATH to mention their syntax,
their default values, and what they actually do.
2025-05-21 11:37:52 -07:00
Ariadne Conill
a2f0af2f29 cli: squash unused variable warning in cli-specific unveil_handler
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-17 23:52:34 -07:00
Ariadne Conill
2ad2eaa4e3 build: bump libpkgconf SOVERSION to 7
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-17 23:50:39 -07:00
Ariadne Conill
5a18878451 libpkgconf: add support for late-breaking unveil(2) notifications
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-17 23:49:54 -07:00
Ariadne Conill
5d363cc715 cli: add cli-side unveil(2) support
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-17 23:33:48 -07:00
Ingo Schwarze
6546c4ba6f man/pkgconf.1: document four more command line options
The options --about, --list-package-names, --short-errors,
and --log-file were not documented yet.  While documenting --log-file,
adjust the description of PKG_CONFIG_LOG, too.
2025-05-11 00:05:56 -07:00
Ingo Schwarze
14d122810d man/pkgconf.1: properly document the module search path
Specifically, document --env-only, --list-all, --with-path,
PKG_CONFIG_LIBDIR, and PKG_CONFIG_PATH, all of which were
already mentioned, but very little was said about them,
and much of what *was* said was outdated.
2025-05-08 15:17:16 -07:00
Ariadne Conill
7502a55de1 libpkgconf: bsdstubs: align pledge and unveil stubs with openbsd-portable
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-08 13:48:26 -07:00
Ariadne Conill
4deef17348 cli: defer --with-path processing until libpkgconf is fully initialized
Previously, we would modify the cross-compilation personality object
in order to inject paths provided by --with-path.

This would cause the paths provided by --with-path to be clobbered when
using PKG_CONFIG_LIBDIR to explicitly initialize the search path list.

Fixes: c468682 ("cli: implement --personality")
Related: https://github.com/pkgconf/pkgconf/issues/400
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-08 13:12:21 -07:00
Ariadne Conill
7b8865d1ba libpkgconf: path: add pkgconf_path_prepend_list
Previously when processing --with-path entries we were adding to the tail
of the search list.  In reality, we expect --with-path entries to be treated
in an equivalent way to PKG_CONFIG_PATH.

Accordingly, add a variant of pkgconf_path_copy_list which prepends instead.

Related: https://github.com/pkgconf/pkgconf/issues/400
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-08 13:07:30 -07:00
Ingo Schwarze
265a8a4309 man/pkgconf.1: two instances where .Va -> .Ar was forgotten 2025-05-07 13:31:53 -07:00
Ingo Schwarze
bd49b7007a man/pkgconf.1: sort the ENVIRONMENT section and use .Ev 2025-05-07 13:31:53 -07:00
Ingo Schwarze
d1a5b4f23f man/pkgconf.1: correct the description of --path 2025-05-07 13:31:53 -07:00
Ingo Schwarze
31d73e40f2 man/pkgconf.1: document --modversion and --verbose 2025-05-07 13:31:53 -07:00
Ingo Schwarze
bdede95f70 man/pkgconf.1: mark features not compiled in with PKGCONF_LITE 2025-05-07 13:31:53 -07:00
Ingo Schwarze
5123e18075 man/pkgconf.1: fix the markup of two instances of "module"
They were erroneously marked up with .Cm instead of .Ar.
2025-05-07 13:31:53 -07:00
Ingo Schwarze
32cd9afbde man/pkgconf.1: fix documentation bug regarding --*-version
One single matching module is sufficient for success.
2025-05-07 13:31:53 -07:00
Ingo Schwarze
bd1b791a6a man/pkgconf.1: do not use all caps for the arguments of .Ar macros
Very little text change, only minimal wording and markup improvements
in the immediate vicinity.
2025-05-07 13:31:53 -07:00
Ingo Schwarze
e49ae60253 man/pkgconf.1: add the missing EXIT STATUS section 2025-05-06 13:29:04 -07:00
Ingo Schwarze
c3090718d9 man/pkgconf.1: improve the description of --version 2025-05-06 13:29:04 -07:00
Ingo Schwarze
db4b02122d man/pkgconf.1: improve the description of --print-requires 2025-05-06 13:29:04 -07:00
Ingo Schwarze
18332eff9d man/pkgconf.1: improve the description of --print-provides 2025-05-06 13:29:04 -07:00
Ingo Schwarze
2932604c67 man/pkgconf.1: improve the description of --exists 2025-05-06 13:29:04 -07:00
Ingo Schwarze
4c9afed422 man/pkgconf.1: improve the description of --atleast-pkgconfig-version 2025-05-06 13:29:04 -07:00
Ariadne Conill
d7efcda039 bsdstubs: fix build with MSVC
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-05 11:17:21 -07:00
Ingo Schwarze
50d0a3ae64 man/pkgconf.1: sort options list; no text or markup change 2025-05-05 10:57:53 -07:00
Ingo Schwarze
6414ec2926 man/pkgconf.1: better document --silence-errors, which was very incomplete 2025-05-03 22:55:14 -07:00
Ingo Schwarze
a1508c5f03 man/pkgconf.1: document --print-errors; it was missing 2025-05-03 22:55:14 -07:00
Ingo Schwarze
9e911529d4 man/pkgconf.1: improve the description of --errors-to-stdout 2025-05-03 22:55:14 -07:00
Ingo Schwarze
71881ffe21 man/pkgconf.1: warning and debugging messages go to stderr
Sorry for misreading the code earlier.
2025-05-03 22:55:14 -07:00
Ingo Schwarze
293cd1d5a3 man/pkgconf.1: improve the description of --libs 2025-05-03 22:55:14 -07:00
Ingo Schwarze
210014a991 man/pkgconf.1: document --debug, which was missing 2025-05-03 22:55:14 -07:00
Ingo Schwarze
4c2eaf5e01 man/pkgconf.1: improve the description of --cflags 2025-05-03 22:55:14 -07:00
Ingo Schwarze
d7bdb5e050 man/pkgconf.1: use conventional syntax for the synopsis line 2025-05-03 22:55:14 -07:00
Ariadne Conill
c6974e244d cli: pledge: also allow unveil(2) in the syscall set
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-03 22:28:42 -07:00
Ariadne Conill
2e1bfc2fbf cli: use pledge(2) to restrict syscalls where available
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-03 22:22:21 -07:00
Ariadne Conill
230b45c00c build: add stub functions for pledge and unveil
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-05-03 22:09:04 -07:00
Filipe Laíns
664b53d5c4 build: add -D_POSIX_C_SOURCE=200809L
Signed-off-by: Filipe Laíns <lains@riseup.net>
2025-04-12 16:08:22 -07:00
Ariadne Conill
1d37e711ce pkgconf 2.4.3. 2025-03-07 16:37:15 -08:00
Ariadne Conill
933e925a16 tests: add test for fragment tree topological correctness
Fixes: https://github.com/pkgconf/pkgconf/issues/385
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-03-07 16:07:00 -08:00
Ariadne Conill
c440e2da49 libpkgconf: fragment: properly group fragments together
Previously, fragment groupings could be overzealous.

Related: https://github.com/pkgconf/pkgconf/issues/385
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-03-07 12:39:52 -08:00
Ariadne Conill
4a62f61217 cli: add --fragment-tree visualization tool
Related: https://github.com/pkgconf/pkgconf/issues/385
Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
2025-03-07 12:24:25 -08:00
255 changed files with 9542 additions and 3972 deletions

4
.github/FUNDING.yml vendored
View File

@ -1,3 +1 @@
github: kaniini
patreon: kaniini
liberapay: kaniini
open_collective: pkgconf

135
.github/workflows/release-tarballs.yml vendored Normal file
View File

@ -0,0 +1,135 @@
name: Generate and upload release artifacts
on:
push:
tags:
- 'pkgconf-*'
jobs:
tarball:
runs-on: ubuntu-latest
container:
image: alpine:latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
repository: pkgconf/pkgconf
- name: Update system and add dependencies
run: |
apk update
apk add kyua atf build-base autoconf automake libtool xz gzip tar openssh-client
- name: Bootstrap autotools
run: |
sh autogen.sh
./configure
- name: Run tests and generate dist tarballs
run: |
make distcheck -j$(nproc)
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: tarball
path: pkgconf-*.tar.*
msi:
name: Build MSI
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include: [
{ msystem: MINGW64, arch: x86_64},
]
steps:
- uses: actions/checkout@v4
- name: setup-msys2
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.msystem }}
update: true
install: >-
mingw-w64-${{ matrix.arch }}-meson
mingw-w64-${{ matrix.arch }}-ninja
mingw-w64-${{ matrix.arch }}-gcc
mingw-w64-${{ matrix.arch }}-msitools
- name: Build
shell: msys2 {0}
run: |
# the code assumes msvc style printf atm
export CFLAGS=-D__USE_MINGW_ANSI_STDIO=0
meson _build --buildtype=release -Dtests=disabled -Db_lto=true
meson compile -C _build msi
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: msi
path: _build/pkgconf-*.msi
release:
name: Create Release
runs-on: ubuntu-latest
permissions:
contents: write
needs: [tarball, msi]
steps:
- name: Download tarball artifact
uses: actions/download-artifact@v4
with:
name: tarball
path: dist
- name: Download msi artifact
uses: actions/download-artifact@v4
with:
name: msi
path: dist
- name: Create Release
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
dist/pkgconf-*.tar.*
dist/pkgconf-*.msi
publish:
name: Publish on distfiles.ariadne.space
runs-on: ubuntu-latest
permissions:
contents: write
needs: [tarball, msi]
steps:
- name: Download tarball artifact
uses: actions/download-artifact@v4
with:
name: tarball
path: dist
- name: Download msi artifact
uses: actions/download-artifact@v4
with:
name: msi
path: dist
- name: Upload dist tarballs to distfiles.ariadne.space
env:
DISTFILES_PRIVATE_KEY: ${{ secrets.DISTFILES_PRIVATE_KEY }}
DISTFILES_HOST_KEYS: ${{ secrets.DISTFILES_HOST_KEYS }}
run: |
mkdir -p $HOME/.ssh
# Ensure the private key is read-only so SSH doesn't reject it.
umask 277
(echo "$DISTFILES_PRIVATE_KEY" | base64 -d) > $HOME/.ssh/id_ed25519
umask 077
(echo "$DISTFILES_HOST_KEYS" | base64 -d) > $HOME/.ssh/known_hosts
# Upload the tarballs.
scp -i "$HOME/.ssh/id_ed25519" -o "UserKnownHostsFile=$HOME/.ssh/known_hosts" dist/* kaniini@distfiles.ariadne.space:distfiles/pkgconf/
# Delete the key material.
rm -rf $HOME/.ssh

View File

@ -13,7 +13,6 @@ jobs:
matrix:
include: [
{ msystem: MINGW64, arch: x86_64},
{ msystem: MINGW32, arch: i686}
]
steps:
- name: Checkout code
@ -28,6 +27,7 @@ jobs:
mingw-w64-${{ matrix.arch }}-meson
mingw-w64-${{ matrix.arch }}-ninja
mingw-w64-${{ matrix.arch }}-gcc
mingw-w64-${{ matrix.arch }}-msitools
- name: Build
shell: msys2 {0}
@ -35,8 +35,22 @@ jobs:
# the code assumes msvc style printf atm
export CFLAGS=-D__USE_MINGW_ANSI_STDIO=0
meson -Dtests=disabled _build
meson compile -C _build
meson _build
meson compile -C _build msi
- name: Run tests
shell: msys2 {0}
run: |
meson test -v -C _build
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: build-output
path: |
_build/*.exe
_build/*.dll
_build/*.msi
debian-meson:
runs-on: ubuntu-latest
@ -140,7 +154,7 @@ jobs:
- name: Update system and add dependencies
run: |
apk update
apk add kyua atf build-base autoconf automake libtool xz gzip
apk add kyua atf build-base autoconf automake libtool xz gzip tar
- name: Build
run: |
@ -151,3 +165,23 @@ jobs:
- name: Run tests
run: |
make distcheck
analysis:
runs-on: ubuntu-latest
container:
image: alpine
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Update system and add dependencies
run: |
apk update
apk add kyua atf build-base autoconf automake libtool xz gzip
- name: Build
run: |
export CFLAGS="-Wall -Werror -O2 -ggdb3 -fanalyzer"
./autogen.sh
./configure
make -j9 CFLAGS="$CFLAGS"

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

@ -12,15 +12,187 @@ 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
bin_PROGRAMS = pkgconf bomtool spdxtool
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 \
t/basic/arbitrary-path.test \
t/basic/builtin-pkg-config-exists.test \
t/basic/builtin-pkg-config-pc_path-variable.test \
t/basic/builtin-pkgconf-exists.test \
t/basic/builtin-pkgconf-pc_path-variable.test \
t/basic/define-prefix.test \
t/basic/define-prefix-child-prefix-env.test \
t/basic/define-prefix-child-prefix.test \
t/basic/direct-query-with-pkg_config_path.test \
t/basic/env-tuple.test \
t/basic/exists-cflags-env.test \
t/basic/exists-cflags.test \
t/basic/exists-nonexistent.test \
t/basic/exists-version-greater-than-with-tilde.test \
t/basic/exists-version-greater-than.test \
t/basic/exists-version-less-than-with-tilde.test \
t/basic/exists-version-malformed.test \
t/basic/exists-version-minimum.test \
t/basic/exists-version-with-tilde.test \
t/basic/incomplete-cflags.test \
t/basic/incomplete-libs.test \
t/basic/keep-system-libs.test \
t/basic/keep-system-libs-2.test \
t/basic/libs.test \
t/basic/libs-cflags.test \
t/basic/libs-cflags-version.test \
t/basic/libs-cflags-version-multiple.test \
t/basic/libs-cflags-version-multiple-comma.test \
t/basic/libs-cflags-version-not.test \
t/basic/libs-cflags-version-not-provided.test \
t/basic/libs-env.test \
t/basic/libs-only.test \
t/basic/malformed-empty-package.test \
t/basic/modversion-fullpath.test \
t/basic/modversion-noflatten.test \
t/basic/modversion-uninstalled.test \
t/basic/modversion-verbose-prefix.test \
t/basic/noargs.test \
t/basic/nocflags.test \
t/basic/nolibs.test \
t/basic/pkg-config-path.test \
t/basic/print-variables-env.test \
t/basic/single-depth-selectors.test \
t/basic/static-archive-libs.test \
t/basic/uninstalled-not.test \
t/basic/uninstalled.test \
t/basic/variable-env.test \
t/basic/variable-no-recursion.test \
t/basic/variable.test \
t/basic/version-atleast-1.test \
t/basic/version-atleast-2.test \
t/basic/version-exact-1.test \
t/basic/version-exact-2.test \
t/basic/version-max-1.test \
t/basic/version-max-2.test \
t/ordering/cflags-never-mergeback.test \
t/ordering/cflags-only.test \
t/ordering/flag-order-1.test \
t/ordering/flag-order-2.test \
t/ordering/flag-order-3.test \
t/ordering/flag-order-4.test \
t/ordering/fragment-collision.test \
t/ordering/framework-1.test \
t/ordering/framework-2.test \
t/ordering/framework-3.test \
t/ordering/idirafter-munge-ordering.test \
t/ordering/idirafter-ordering.test \
t/ordering/isystem-munge-ordering.test \
t/ordering/libs-never-mergeback.test \
t/parser/argv-parse-1.test \
t/parser/argv-parse-2.test \
t/parser/argv-parse-3.test \
t/parser/c-comments-should-warn.test \
t/parser/comments-in-fields.test \
t/parser/comments.test \
t/parser/dos-lineendings.test \
t/parser/escaped-backslashes-in-output.test \
t/parser/fragment-comment.test \
t/parser/fragment-groups-composite.test \
t/parser/fragment-groups.test \
t/parser/malformed-no-newlines.test \
t/parser/malformed-quoting.test \
t/parser/multiline-bogus-header.test \
t/parser/multiline-field.test \
t/parser/no-trailing-newline.test \
t/parser/paren-quoting.test \
t/parser/shell-quoted-output.test \
t/parser/tilde-quoting-1.test \
t/parser/tilde-quoting-2.test \
t/parser/truncated-files-should-fail-to-parse.test \
t/parser/tuple-dequote.test \
t/parser/variable-whitespace.test \
t/solver/circular-reference-1.test \
t/solver/circular-reference-2.test \
t/solver/circular-reference-directpc.test \
t/solver/conflicts-solution-error.test \
t/solver/conflicts-solution-ignore.test \
t/solver/conflicts-solution-is-fine-standalone.test \
t/solver/depgraph-break-1.test \
t/solver/depgraph-break-2.test \
t/solver/depgraph-break-3.test \
t/solver/libs-intermediary.test \
t/solver/libs-metapackage.test \
t/solver/libs-static-ordering.test \
t/solver/missing-required-dep.test \
t/solver/modversion-provides.test \
t/solver/private-libs-duplication-digraph.test \
t/solver/private-libs-duplication.test \
t/solver/provides-bar-any.test \
t/solver/provides-bar-versioned-equal.test \
t/solver/provides-bar-versioned-greater-than-equal.test \
t/solver/provides-bar-versioned-greater-than.test \
t/solver/provides-bar-versioned-less-than-equal.test \
t/solver/provides-bar-versioned-less-than-new.test \
t/solver/provides-bar-versioned-less-than-old.test \
t/solver/provides-bar-versioned-not-equal-old.test \
t/solver/provides-bar-versioned-not-equal.test \
t/solver/provides-foo-any.test \
t/solver/provides-foo-versioned-exact.test \
t/solver/provides-foo-versioned-greater-than-equal.test \
t/solver/provides-foo-versioned-greater-than.test \
t/solver/provides-foo-versioned-less-than-equal.test \
t/solver/provides-foo-versioned-less-than.test \
t/solver/provides-foo-versioned-not-equal.test \
t/solver/provides-indirect-dependency-versioned.test \
t/solver/provides-indirect-dependency.test \
t/solver/provides-print-simple.test \
t/solver/provides-request-simple.test \
t/solver/provides-request-simple-no-provides.test \
t/solver/query-no-space-0.test \
t/solver/query-no-space-1.test \
t/solver/query-order-1.test \
t/solver/query-order-2.test \
t/solver/requires-internal-collision.test \
t/solver/requires-internal-missing-nonstatic-cflags-libs.test \
t/solver/requires-internal-missing-nonstatic.test \
t/solver/requires-internal-missing-static-cflags.test \
t/solver/requires-internal-missing.test \
t/solver/requires-internal.test \
t/solver/requires-private-debounce.test \
t/solver/requires-private-missing.test \
t/sbom/license-file-bar.test \
t/sbom/license-file-foo.test \
t/sbom/license-isc.test \
t/sbom/license-noassertion.test \
t/sbom/source-tag-bar.test \
t/sbom/source-tag-foo.test \
t/sysroot/cflags-rewriting.test \
t/sysroot/idirafter-munge-sysroot.test \
t/sysroot/isystem-munge-sysroot.test \
t/sysroot/no-pc_sysrootdir-rewriting-2.test \
t/sysroot/no-pc_sysrootdir-rewriting-3.test \
t/sysroot/no-pc_sysrootdir-rewriting-4.test \
t/sysroot/no-pc_sysrootdir-rewriting-5.test \
t/sysroot/no-pc_sysrootdir-rewriting-variable.test \
t/sysroot/skip-rewriting-when-sysroot-dir-is-root-dir.test \
t/sysroot/undefined-pc_sysrootdir-should-be-empty.test \
t/sysroot/uninstalled-rewriting-fdo-rules-with-pc_sysrootdir.test \
t/sysroot/uninstalled-rewriting-fdo-rules.test \
t/sysroot/uninstalled-rewriting-pkgconf-1-rules.test \
t/sysroot/uninstalled-rewriting.test \
t/sysroot/variable-rewriting-1.test \
t/sysroot/variable-rewriting-2.test \
t/tuple/billion-laughs.test \
t/tuple/case-sensitivity-1.test \
t/tuple/case-sensitivity-2.test \
t/tuple/define-variable-override.test \
t/tuple/define-variable.test \
t/tuple/duplicate-upsert.test \
t/tuple/empty-tuple.test \
tests/lib-relocatable/lib/pkgconfig/foo.pc \
tests/lib1/argv-parse-2.pc \
tests/lib1/billion-laughs.pc \
@ -123,6 +295,20 @@ EXTRA_DIST = pkg.m4 \
tests/lib1/cflags-libs-private-c.pc \
tests/lib1/truncated.pc \
tests/lib1/c-comment.pc \
tests/lib1/duplicate-tuple.pc \
tests/lib1/flag-whitespace.pc \
tests/lib1/flag-whitespace-2.pc \
tests/lib-sbom/meta_package.pc \
tests/lib-sbom/test1.pc \
tests/lib-sbom/test2.pc \
tests/lib-sbom/test3.pc \
tests/lib-sbom/test4.pc \
tests/lib-sbom/test5.pc \
tests/lib-sbom/test6.pc \
tests/lib-sbom-files/basic.json \
tests/lib-sbom-files/meta_package.json \
tests/lib-sbom-files/with_dependency.json \
tests/personality-data/i386-linux-gnu.personality \
tests/meson.build \
$(test_scripts) \
doc/conf.py \
@ -140,17 +326,13 @@ EXTRA_DIST = pkg.m4 \
doc/libpkgconf-queue.rst \
doc/libpkgconf-tuple.rst
test_scripts= tests/basic.sh \
tests/builtins.sh \
tests/conflicts.sh \
tests/framework.sh \
test_scripts= \
tests/parser.sh \
tests/provides.sh \
tests/personality.sh \
tests/regress.sh \
tests/requires.sh \
tests/symlink.sh \
tests/sysroot.sh \
tests/version.sh
tests/spdxtool.sh \
tests/symlink.sh
test_sh = $(test_scripts)
check_SCRIPTS = ${test_sh:.sh=}
@ -171,12 +353,14 @@ libpkgconf_la_SOURCES = \
libpkgconf/tuple.c \
libpkgconf/dependency.c \
libpkgconf/queue.c \
libpkgconf/output.c \
libpkgconf/path.c \
libpkgconf/personality.c \
libpkgconf/parser.c
libpkgconf_la_LDFLAGS = -no-undefined -version-info 6:0:0 -export-symbols-regex '^pkgconf_'
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 \
@ -185,12 +369,19 @@ dist_man_MANS = \
pkgconf_LDADD = libpkgconf.la
pkgconf_SOURCES = \
cli/main.c \
cli/core.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
cli/core.h \
cli/getopt_long.h \
cli/renderer-msvc.h \
cli/spdxtool/core.h \
cli/spdxtool/serialize.h \
cli/spdxtool/simplelicensing.h \
cli/spdxtool/software.h \
cli/spdxtool/util.h
bomtool_LDADD = libpkgconf.la
bomtool_SOURCES = \
@ -198,6 +389,25 @@ bomtool_SOURCES = \
cli/getopt_long.c
bomtool_CPPFLAGS = -I$(top_srcdir)/libpkgconf -I$(top_srcdir)/cli -I$(top_srcdir)/cli/bomtool
spdxtool_LDADD = libpkgconf.la
spdxtool_SOURCES = \
cli/spdxtool/main.c \
cli/spdxtool/core.c \
cli/spdxtool/software.c \
cli/spdxtool/serialize.c \
cli/spdxtool/simplelicensing.c \
cli/spdxtool/util.c \
cli/getopt_long.c
spdxtool_CPPFLAGS = -I$(top_srcdir)/libpkgconf -I$(top_srcdir)/cli -I$(top_srcdir)/cli/spdxtool
noinst_PROGRAMS = test-runner
test_runner_LDADD = libpkgconf.la
test_runner_SOURCES = \
cli/core.c \
cli/getopt_long.c \
cli/renderer-msvc.c \
tests/test-runner.c
dist_doc_DATA = README.md AUTHORS
m4datadir = $(datadir)/aclocal
@ -206,7 +416,14 @@ m4data_DATA = pkg.m4
CLEANFILES = $(EXTRA_PROGRAMS) \
$(check_SCRIPTS)
check: pkgconf $(check_SCRIPTS)
check: pkgconf test-runner $(check_SCRIPTS)
$(builddir)/test-runner$(EXEEXT) --test-fixtures=$(top_srcdir)/tests $(top_srcdir)/t/basic
$(builddir)/test-runner$(EXEEXT) --test-fixtures=$(top_srcdir)/tests $(top_srcdir)/t/ordering
$(builddir)/test-runner$(EXEEXT) --test-fixtures=$(top_srcdir)/tests $(top_srcdir)/t/parser
$(builddir)/test-runner$(EXEEXT) --test-fixtures=$(top_srcdir)/tests $(top_srcdir)/t/solver
$(builddir)/test-runner$(EXEEXT) --test-fixtures=$(top_srcdir)/tests $(top_srcdir)/t/sbom
$(builddir)/test-runner$(EXEEXT) --test-fixtures=$(top_srcdir)/tests $(top_srcdir)/t/sysroot
$(builddir)/test-runner$(EXEEXT) --test-fixtures=$(top_srcdir)/tests $(top_srcdir)/t/tuple
kyua --config=none test --kyuafile='$(top_builddir)/Kyuafile' \
--build-root='$(top_builddir)'

View File

@ -16,11 +16,13 @@ SRCS = \
libpkgconf/argvsplit.c \
libpkgconf/audit.c \
libpkgconf/bsdstubs.c \
libpkgconf/buffer.c \
libpkgconf/cache.c \
libpkgconf/client.c \
libpkgconf/dependency.c \
libpkgconf/fileio.c \
libpkgconf/fragment.c \
libpkgconf/output.c \
libpkgconf/parser.c \
libpkgconf/path.c \
libpkgconf/personality.c \
@ -28,7 +30,8 @@ SRCS = \
libpkgconf/queue.c \
libpkgconf/tuple.c \
cli/getopt_long.c \
cli/main.c
cli/main.c \
cli/core.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 =
@ -39,7 +42,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.4.2"' >> $@
@echo '#define PACKAGE_VERSION "2.5.1"' >> $@
@echo '#define PACKAGE PACKAGE_NAME " " PACKAGE_VERSION' >> $@
@echo '#define HAVE_STRLCPY' >> $@
@echo '#define HAVE_STRLCAT' >> $@

46
NEWS
View File

@ -1,6 +1,52 @@
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:
----------------------------

View File

@ -1,88 +1,75 @@
# 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 similar to pkg-config from freedesktop.org.
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.
## using `pkgconf` with autotools
## release tarballs
Implementations of pkg-config, such as pkgconf, are typically used with the
PKG_CHECK_MODULES autoconf macro. As far as we know, pkgconf is
compatible with all known variations of this macro. pkgconf detects at
runtime whether or not it was started as 'pkg-config', and if so, attempts
to set program options such that its behaviour is similar.
Release tarballs are available on [distfiles.ariadne.space][distfiles].
In terms of the autoconf macro, it is possible to specify the PKG_CONFIG
environment variable, so that you can test pkgconf without overwriting your
pkg-config binary. Some other build systems may also respect the PKG_CONFIG
environment variable.
[distfiles]: https://distfiles.ariadne.space/pkgconf/
To set the environment variable on the bourne shell and clones (i.e. bash), you
can run:
## build system setup
$ export PKG_CONFIG=/usr/bin/pkgconf
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:
## comparison of `pkgconf` and `pkg-config` dependency resolvers
$ apk add autoconf automake libtool build-base
$ sh ./autogen.sh
pkgconf builds an acyclic directed dependency graph. This 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. As a result of building
a directed dependency graph designed for the specific problem domain provided
by the user, more accurate dependencies can be determined.
## pkgconf-lite
Current release versions of pkg-config, on the other hand, build a database of all
known pkg-config files on the system before attempting to resolve dependencies, which
is a considerably slower and less efficient design. Efforts have been made recently
to improve this behaviour.
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:
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,
such as, for example, how `Conflicts` rules are processed.
$ 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
As previously mentioned, pkgconf makes optimizations to the linker flags in both the
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.
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.
Doing so is discouraged by the [freedesktop tutorial][fd-tut] anyway.
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
## compatibility with pkg-config
## bug compatibility with original pkg-config
I really hate that I have to have this section, I like being a nice person, but we
unfortunately have to say this because otherwise we get passive-aggressive people who
try to argue with us about what pkg-config compatibility means.
We do not provide bug-level compatibility with 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.
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**.
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
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
@ -114,7 +101,6 @@ flags like so:
## 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.
@ -123,8 +109,8 @@ much earlier than Meson.
$ 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
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
@ -135,11 +121,6 @@ to make this determination themselves.
$ ln -sf pkgconf /usr/bin/pkg-config
## release tarballs
Release tarballs are available at <https://distfiles.ariadne.space/pkgconf/>.
Please only use the tarballs from distfiles.ariadne.space.
## contacts
You can report bugs at <https://github.com/pkgconf/pkgconf/issues>.

View File

@ -80,8 +80,8 @@ parse_options "$@"
cd $TOP_DIR
run_or_die $LIBTOOLIZE --install
run_or_die $ACLOCAL
run_or_die $AUTOHEADER
run_or_die $AUTOCONF
run_or_die $LIBTOOLIZE --install
run_or_die $AUTOMAKE --add-missing

View File

@ -31,7 +31,15 @@ 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 FILE *error_msgout = NULL;
static const char *
environ_lookup_handler(const pkgconf_client_t *client, const char *key)
{
(void) client;
return getenv(key);
}
static bool
error_handler(const char *msg, const pkgconf_client_t *client, void *data)
@ -136,6 +144,9 @@ write_sbom_package(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *unused)
if (pkg->description != NULL)
printf("PackageSummary: <text>%s</text>\n", pkg->description);
if (pkg->source != NULL)
printf("PackageDownloadLocation: %s\n", pkg->source);
printf("\n\n");
}
@ -282,7 +293,7 @@ main(int argc, char *argv[])
}
}
pkgconf_client_init(&pkg_client, error_handler, NULL, personality);
pkgconf_client_init(&pkg_client, error_handler, NULL, personality, NULL, environ_lookup_handler);
/* we have determined what features we want most likely. in some cases, we override later. */
pkgconf_client_set_flags(&pkg_client, want_client_flags);

1371
cli/core.c Normal file

File diff suppressed because it is too large Load Diff

99
cli/core.h Normal file
View File

@ -0,0 +1,99 @@
/*
* core.h
* core, printer functions
*
* Copyright (c) 2011-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.
*/
#ifndef __CLI_CORE_H
#define __CLI_CORE_H
#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)
#define PKG_DUMP_SOURCE (((uint64_t) 1) << 49)
#define PKG_DUMP_LICENSE_FILE (((uint64_t) 1) << 50)
#define PKG_NEWLINES (((uint64_t) 1) << 51)
typedef struct {
pkgconf_client_t pkg_client;
pkgconf_fragment_render_ops_t *want_render_ops;
uint64_t want_flags;
int verbosity;
int maximum_traverse_depth;
size_t maximum_package_count;
const char *want_variable;
const char *want_fragment_filter;
const char *want_env_prefix;
char *required_pkgconfig_version;
const char *required_exact_module_version;
const char *required_max_module_version;
const char *required_module_version;
FILE *error_msgout;
FILE *logfile_out;
bool opened_error_msgout;
} pkgconf_cli_state_t;
extern void path_list_to_buffer(const pkgconf_list_t *list, pkgconf_buffer_t *buffer, char delim);
extern int pkgconf_cli_run(pkgconf_cli_state_t *state, int argc, char *argv[], int last_argc);
extern void pkgconf_cli_state_reset(pkgconf_cli_state_t *state);
#endif

1525
cli/main.c

File diff suppressed because it is too large Load Diff

View File

@ -104,68 +104,50 @@ msvc_renderer_render_len(const pkgconf_list_t *list, bool escape)
}
static void
msvc_renderer_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape)
msvc_renderer_render_buf(const pkgconf_list_t *list, pkgconf_buffer_t *buf, bool escape, char delim)
{
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;
pkgconf_buffer_append_fmt(buf, "/%c", frag->type);
break;
case 'L':
cnt = pkgconf_strlcpy(bptr, "/libpath:", buf_remaining);
bptr += cnt;
buf_remaining -= cnt;
pkgconf_buffer_append(buf, "/libpath:");
break;
}
escape = fragment_should_quote(frag);
if (escape)
*bptr++ = '"';
pkgconf_buffer_push_byte(buf, '"');
cnt = pkgconf_strlcpy(bptr, frag->data, buf_remaining);
bptr += cnt;
buf_remaining -= cnt;
pkgconf_buffer_append(buf, frag->data);
if (frag->type == 'l')
{
cnt = pkgconf_strlcpy(bptr, ".lib", buf_remaining);
bptr += cnt;
}
pkgconf_buffer_append(buf, ".lib");
if (escape)
*bptr++ = '"';
pkgconf_buffer_push_byte(buf, '"');
*bptr++ = ' ';
pkgconf_buffer_push_byte(buf, delim);
}
*bptr = '\0';
}
static const pkgconf_fragment_render_ops_t msvc_renderer_ops = {
static 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 *
pkgconf_fragment_render_ops_t *
msvc_renderer_get(void)
{
return &msvc_renderer_ops;

View File

@ -18,6 +18,6 @@
#include <libpkgconf/libpkgconf.h>
const pkgconf_fragment_render_ops_t *msvc_renderer_get(void);
pkgconf_fragment_render_ops_t *msvc_renderer_get(void);
#endif

690
cli/spdxtool/core.c Normal file
View File

@ -0,0 +1,690 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 The FreeBSD Foundation
*
* Portions of this software were developed by
* Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
* the FreeBSD Foundation
*/
#include <stdlib.h>
#include <string.h>
#include "util.h"
#include "serialize.h"
#include "core.h"
#include "software.h"
#include "simplelicensing.h"
/*
* !doc
*
* .. c:function:: spdxtool_core_agent_t *spdxtool_core_agent_new(pkgconf_client_t *client, char *creation_info_id, char *name)
*
* Create new /Core/Agent struct
*
* :param pkgconf_client_t *client: The pkgconf client being accessed.
* :param char *creation_info_id: CreationInfo spdxId
* :param char *name: Name of agent
* :return: NULL if some problem occurs and Agent struct if not
*/
spdxtool_core_agent_t *
spdxtool_core_agent_new(pkgconf_client_t *client, char *creation_info_id, char *name)
{
char spdx_id_name[1024];
char *spdx_id = NULL;
spdxtool_core_agent_t *agent_struct = NULL;
if(!client || !creation_info_id || !name)
{
return NULL;
}
agent_struct = calloc(1, sizeof(spdxtool_core_agent_t));
if(!agent_struct)
{
pkgconf_error(client, "Memory exhausted! Can't create agent struct.");
return NULL;
}
agent_struct->type = "Agent";
memset(spdx_id_name, 0x00, 1024);
strncpy(spdx_id_name, name, 1023);
spdxtool_util_string_correction(spdx_id_name);
spdx_id = spdxtool_util_get_spdx_id_string(client, agent_struct->type, spdx_id_name);
agent_struct->creation_info = creation_info_id;
agent_struct->spdx_id = spdx_id;
agent_struct->name = name;
return agent_struct;
}
/*
* !doc
*
* .. c:function:: void spdxtool_core_agent_free(spdxtool_core_agent_t *agent_struct)
*
* Free /Core/Agent struct
*
* :param spdxtool_core_agent_t *agent_struct: Agent struct to be freed.
* :return: nothing
*/
void
spdxtool_core_agent_free(spdxtool_core_agent_t *agent_struct)
{
if(!agent_struct)
{
return;
}
if(agent_struct->creation_info)
{
free(agent_struct->creation_info);
agent_struct->creation_info = NULL;
}
if(agent_struct->spdx_id)
{
free(agent_struct->spdx_id);
agent_struct->spdx_id = NULL;
}
if(agent_struct->name)
{
free(agent_struct->name);
agent_struct->name = NULL;
}
free(agent_struct);
}
/*
* !doc
*
* .. c:function:: void spdxtool_core_agent_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, spdxtool_core_agent_t *agent_struct, bool last)
*
* Serialize /Core/Agent struct to JSON
*
* :param pkgconf_client_t *client: The pkgconf client being accessed.
* :param pkgconf_buffer_t *buffer: Buffer where struct is serialized
* :param spdxtool_core_agent_t *agent_struct: Agent struct to be serialized
* :param bool last: Is this last Agent struct or does it need comma at the end. True comma False not
* :return: nothing
*/
void
spdxtool_core_agent_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, spdxtool_core_agent_t *agent_struct, bool last)
{
(void) client;
spdxtool_serialize_obj_start(buffer, 2);
spdxtool_serialize_parm_and_string(buffer, "@type", agent_struct->type, 3, 1);
spdxtool_serialize_parm_and_string(buffer, "creationInfo", agent_struct->creation_info, 3, 1);
spdxtool_serialize_parm_and_string(buffer, "spdxId", agent_struct->spdx_id, 3, 1);
spdxtool_serialize_parm_and_string(buffer, "name", agent_struct->name, 3, 0);
spdxtool_serialize_obj_end(buffer, 2, last);
}
/*
* !doc
*
* .. c:function:: spdxtool_core_creation_info_t *spdxtool_core_creation_info_new(pkgconf_client_t *client, char *agent_id, char *id)
*
* Create new /Core/CreationInfo struct
*
* :param pkgconf_client_t *client: The pkgconf client being accessed.
* :param char *agent_id: Agent spdxId
* :param char *id: Id for creation info
* :param char *time: If NULL current time is used if not then
* this time string is used. Time string should be
* in ISO8601 format: YYYY-MM-DDTHH:MM:SSZ
* :return: NULL if some problem occurs and CreationInfo struct if not
*/
spdxtool_core_creation_info_t *
spdxtool_core_creation_info_new(pkgconf_client_t *client, char *agent_id, char *id, char *time)
{
spdxtool_core_creation_info_t *creation_struct = NULL;
if(!client || !agent_id || !id)
{
return NULL;
}
creation_struct = calloc(1, sizeof(spdxtool_core_creation_info_t));
if(!creation_struct)
{
pkgconf_error(client, "Memory exhausted! Can't create agent struct.");
return NULL;
}
creation_struct->type = "CreationInfo";
creation_struct->id = id;
if(!time)
{
creation_struct->created = spdxtool_util_get_current_iso8601_time();
}
else
{
creation_struct->created = time;
}
creation_struct->created_by = agent_id;
creation_struct->created_using="pkgconf spdxtool";
creation_struct->spec_version = spdxtool_util_get_spdx_version(client);
return creation_struct;
}
/*
* !doc
*
* .. c:function:: void spdxtool_core_creation_info_free(spdxtool_core_creation_info_t *creation_struct)
*
* Free /Core/CreationInfo struct
*
* :param spdxtool_core_creation_info_t *creation_struct: CreationInfo struct to be freed.
* :return: nothing
*/
void
spdxtool_core_creation_info_free(spdxtool_core_creation_info_t *creation_struct)
{
if(!creation_struct)
{
return;
}
if(creation_struct->id)
{
free(creation_struct->id);
creation_struct->id = NULL;
}
if(creation_struct->created)
{
free(creation_struct->created);
creation_struct->created = NULL;
}
if(creation_struct->created_by)
{
free(creation_struct->created_by);
creation_struct->created_by = NULL;
}
free(creation_struct);
}
/*
* !doc
*
* .. c:function:: void spdxtool_core_creation_info_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, spdxtool_core_creation_info_t *creation_struct, bool last)
*
* Serialize /Core/CreationInfo struct to JSON
*
* :param pkgconf_client_t *client: The pkgconf client being accessed.
* :param pkgconf_buffer_t *buffer: Buffer where struct is serialized
* :param spdxtool_core_creation_info_t *creation_struct: CreationInfo struct to be serialized
* :param bool last: Is this last CreationInfo struct or does it need comma at the end. True comma False not
* :return: nothing
*/
void
spdxtool_core_creation_info_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, spdxtool_core_creation_info_t *creation_struct, bool last)
{
(void) client;
spdxtool_serialize_obj_start(buffer, 2);
spdxtool_serialize_parm_and_string(buffer, "@type", creation_struct->type, 3, true);
spdxtool_serialize_parm_and_string(buffer, "@id", creation_struct->id, 3, true);
spdxtool_serialize_parm_and_string(buffer, "created", creation_struct->created, 3, true);
spdxtool_serialize_parm_and_char(buffer, "createdBy", '[', 3, false);
spdxtool_serialize_string(buffer, creation_struct->created_by, 4, false);
spdxtool_serialize_array_end(buffer, 3, true);
spdxtool_serialize_parm_and_string(buffer, "specVersion", (char *)creation_struct->spec_version, 3, false);
spdxtool_serialize_obj_end(buffer, 2, last);
}
/*
* !doc
*
* .. c:function:: spdxtool_core_creation_info_t *spdxtool_core_creation_info_new(pkgconf_client_t *client, char *agent_id, char *id)
*
* Create new /Core/SpdxDocument struct
* In SPDX Lite SBOM there can be only one SpdxDocument
*
* :param pkgconf_client_t *client: The pkgconf client being accessed.
* :param char *spdx_id: Id of this SpdxDocument
* :param char *creation_id: Id for creation info
* :return: NULL if some problem occurs and SpdxDocument struct if not
*/
spdxtool_core_spdx_document_t *
spdxtool_core_spdx_document_new(pkgconf_client_t *client, char *spdx_id, char *creation_id)
{
spdxtool_core_spdx_document_t *spdx_struct = NULL;
if(!client || !spdx_id || !creation_id)
{
return NULL;
}
(void)client;
spdx_struct = calloc(1, sizeof(spdxtool_core_spdx_document_t));
if(!spdx_struct)
{
pkgconf_error(client, "Memory exhausted! Can't create spdx_document struct.");
return NULL;
}
spdx_struct->type = "SpdxDocument";
spdx_struct->spdx_id = spdx_id;
spdx_struct->creation_info = creation_id;
return spdx_struct;
}
/*
* !doc
*
* .. c:function:: void spdxtool_core_spdx_document_free(spdxtool_core_spdx_document_t *spdx_struct)
*
* Free /Core/SpdxDocument struct
*
* :param spdxtool_core_spdx_document_t *spdx_struct: SpdxDocument struct to be freed.
* :return: nothing
*/
void
spdxtool_core_spdx_document_free(spdxtool_core_spdx_document_t *spdx_struct)
{
pkgconf_node_t *iter = NULL;
pkgconf_node_t *iter_last = NULL;
if(!spdx_struct)
{
return;
}
if(spdx_struct->spdx_id)
{
free(spdx_struct->spdx_id);
spdx_struct->spdx_id = NULL;
}
if(spdx_struct->creation_info)
{
free(spdx_struct->creation_info);
spdx_struct->creation_info = NULL;
}
if(spdx_struct->agent)
{
free(spdx_struct->agent);
spdx_struct->agent = NULL;
}
iter_last = NULL;
PKGCONF_FOREACH_LIST_ENTRY(spdx_struct->rootElement.head, iter)
{
if(iter_last)
{
free(iter_last);
iter_last = NULL;
}
spdxtool_software_sbom_t *sbom = iter->data;
spdxtool_software_sbom_free(sbom);
iter->data = NULL;
iter_last = iter;
}
if(iter_last)
{
free(iter_last);
iter_last = NULL;
}
iter_last = NULL;
PKGCONF_FOREACH_LIST_ENTRY(spdx_struct->element.head, iter)
{
if(iter_last)
{
free(iter_last);
iter_last = NULL;
}
free(iter->data);
iter_last = iter;
}
if(iter_last)
{
free(iter_last);
iter_last = NULL;
}
iter_last = NULL;
PKGCONF_FOREACH_LIST_ENTRY(spdx_struct->licenses.head, iter)
{
if(iter_last)
{
free(iter_last);
iter_last = NULL;
}
spdxtool_simplelicensing_license_expression_t *expression = iter->data;
spdxtool_simplelicensing_licenseExpression_free(expression);
iter->data = NULL;
iter_last = iter;
}
if(iter_last)
{
free(iter_last);
iter_last = NULL;
}
free(spdx_struct);
}
/*
* !doc
*
* .. c:function:: bool spdxtool_core_spdx_document_is_license(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx_struct, char *license)
*
* Find out if specific license is already there.
*
* :param pkgconf_client_t *client: The pkgconf client being accessed.
* :param spdxtool_core_spdx_document_t *spdx_struct: SpdxDocument struct being used.
* :param char *license: SPDX name of license
* :return: true is license is there and false if not
*/
bool
spdxtool_core_spdx_document_is_license(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx_struct, char *license)
{
pkgconf_node_t *iter = NULL;
spdxtool_simplelicensing_license_expression_t *expression = NULL;
(void) client;
if(!license)
{
return false;
}
PKGCONF_FOREACH_LIST_ENTRY(spdx_struct->licenses.head, iter)
{
expression = iter->data;
if (!strcmp(expression->license_expression, license))
{
return true;
}
}
return false;
}
/*
* !doc
*
* .. c:function:: void spdxtool_core_spdx_document_add_license(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx_struct, char *license)
*
* Add license to SpdxDocument and make sure that specific license is not already there.
*
* :param pkgconf_client_t *client: The pkgconf client being accessed.
* :param spdxtool_core_spdx_document_t *spdx_struct: SpdxDocument struct being used.
* :param char *license: SPDX name of license
* :return: nothing
*/
void
spdxtool_core_spdx_document_add_license(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx_struct, char *license)
{
pkgconf_node_t *node = NULL;
if(!license)
{
return;
}
if(spdxtool_core_spdx_document_is_license(client, spdx_struct, license))
{
return;
}
node = calloc(1, sizeof(pkgconf_node_t));
if(!node)
{
pkgconf_error(client, "Memory exhausted! Cant't add license to spdx_document.");
return;
}
spdxtool_simplelicensing_license_expression_t *expression = spdxtool_simplelicensing_licenseExpression_new(client, license);
pkgconf_node_insert_tail(node, expression, &spdx_struct->licenses);
}
/*
* !doc
*
* .. c:function:: void spdxtool_core_spdx_document_add_element(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx_struct, char *element)
*
* Add element spdxId to SpdxDocument
*
* :param pkgconf_client_t *client: The pkgconf client being accessed.
* :param spdxtool_core_spdx_document_t *spdx_struct: SpdxDocument struct being used.
* :param char *element: spdxId of element
* :return: nothing
*/
void
spdxtool_core_spdx_document_add_element(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx_struct, char *element)
{
pkgconf_node_t *node = NULL;
(void) client;
if(!element)
{
return;
}
node = calloc(1, sizeof(pkgconf_node_t));
if(!node)
{
pkgconf_error(NULL, "Memory exhausted! Can't add spdx_id's to spdx_document.");
return;
}
pkgconf_node_insert_tail(node, element, &spdx_struct->element);
}
/*
* !doc
*
* .. c:function:: void spdxtool_core_creation_info_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, spdxtool_core_creation_info_t *creation_struct, bool last)
*
* Serialize /Core/SpdxDocument struct to JSON
*
* :param pkgconf_client_t *client: The pkgconf client being accessed.
* :param pkgconf_buffer_t *buffer: Buffer where struct is serialized
* :param spdxtool_core_spdx_document_t *spdx_struct: SpdxDocument struct to be serialized
* :param bool last: Is this last CreationInfo struct or does it need comma at the end. True comma False not
* :return: nothing
*/
void
spdxtool_core_spdx_document_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, spdxtool_core_spdx_document_t *spdx_struct, bool last)
{
pkgconf_node_t *iter = NULL;
bool is_next = false;
PKGCONF_FOREACH_LIST_ENTRY(spdx_struct->rootElement.head, iter)
{
spdxtool_software_sbom_t *sbom = NULL;
sbom = iter->data;
spdxtool_software_sbom_serialize(client, buffer, sbom, true);
}
spdxtool_serialize_obj_start(buffer, 2);
spdxtool_serialize_parm_and_string(buffer, "@type", spdx_struct->type, 3, true);
spdxtool_serialize_parm_and_string(buffer, "creationInfo", spdx_struct->creation_info, 3, true);
spdxtool_serialize_parm_and_string(buffer, "spdxId", spdx_struct->spdx_id, 3, true);
spdxtool_serialize_parm_and_char(buffer, "rootElement", '[', 3, false);
PKGCONF_FOREACH_LIST_ENTRY(spdx_struct->rootElement.head, iter)
{
spdxtool_software_sbom_t *sbom = NULL;
sbom = iter->data;
is_next = false;
if(iter->next)
{
is_next = true;
}
spdxtool_serialize_string(buffer, sbom->spdx_id, 4, is_next);
}
spdxtool_serialize_array_end(buffer, 3, true);
spdxtool_serialize_parm_and_char(buffer, "element", '[', 3, false);
spdxtool_serialize_string(buffer, spdx_struct->agent, 4, true);
PKGCONF_FOREACH_LIST_ENTRY(spdx_struct->element.head, iter)
{
char *spdx_id = NULL;
spdx_id = iter->data;
spdxtool_serialize_string(buffer, spdx_id, 4, true);
}
PKGCONF_FOREACH_LIST_ENTRY(spdx_struct->rootElement.head, iter)
{
spdxtool_software_sbom_t *sbom = NULL;
sbom = iter->data;
is_next = false;
if(iter->next)
{
is_next = true;
}
spdxtool_serialize_string(buffer, sbom->spdx_id, 4, true);
spdxtool_serialize_string(buffer, pkgconf_tuple_find(client, &sbom->rootElement->vars, "spdxId"), 4, is_next);
}
spdxtool_serialize_array_end(buffer, 3, false);
spdxtool_serialize_obj_end(buffer, 2, last);
}
/*
* !doc
*
* .. c:function:: spdxtool_core_relationship_t *spdxtool_core_relationship_new(pkgconf_client_t *client, char *creation_info_id, char *spdx_id, char *from, char *to, char *relationship_type)
*
* Create new /Core/Relationship struct
*
* :param pkgconf_client_t *client: The pkgconf client being accessed.
* :param char *creation_id: Id for creation info
* :param char *spdx_id: Id of this SpdxDocument
* :param char *from: from spdxId
* :param char *to: to spdxId
* :param char *relationship_type: These can be found on SPDX documentation
* :return: NULL if some problem occurs and SpdxDocument struct if not
*/
spdxtool_core_relationship_t *
spdxtool_core_relationship_new(pkgconf_client_t *client, char *creation_info_id, char *spdx_id, char *from, char *to, char *relationship_type)
{
spdxtool_core_relationship_t *relationship = NULL;
if(!client || !creation_info_id || !spdx_id || !from || !to || !relationship_type)
{
return NULL;
}
(void) client;
relationship = calloc(1, sizeof(spdxtool_core_relationship_t));
if(!relationship)
{
pkgconf_error(client, "Memory exhausted! Can't create relationship struct.");
return NULL;
}
relationship->type = "Relationship";
relationship->creation_info = creation_info_id;
relationship->spdx_id = spdx_id;
relationship->from = from;
relationship->to = to;
relationship->relationship_type = relationship_type;
return relationship;
}
/*
* !doc
*
* .. c:function:: void spdxtool_core_relationship_free(spdxtool_core_relationship_t *relationship_struct)
*
* Free /Core/Relationship struct
*
* :param spdxtool_core_relationship_t *relationship_struct: Relationship struct to be freed.
* :return: nothing
*/
void
spdxtool_core_relationship_free(spdxtool_core_relationship_t *relationship_struct)
{
if(!relationship_struct)
{
return;
}
if(relationship_struct->spdx_id)
{
free(relationship_struct->spdx_id);
relationship_struct->spdx_id = NULL;
}
if(relationship_struct->creation_info)
{
free(relationship_struct->creation_info);
relationship_struct->creation_info = NULL;
}
if(relationship_struct->from)
{
free(relationship_struct->from);
relationship_struct->from = NULL;
}
if(relationship_struct->to)
{
free(relationship_struct->to);
relationship_struct->to = NULL;
}
if(relationship_struct->relationship_type)
{
free(relationship_struct->relationship_type);
relationship_struct->relationship_type = NULL;
}
free(relationship_struct);
}
/*
* !doc
*
* .. c:function:: void spdxtool_core_creation_info_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, spdxtool_core_creation_info_t *creation_struct, bool last)
*
* Serialize /Core/Relationship struct to JSON
*
* :param pkgconf_client_t *client: The pkgconf client being accessed.
* :param pkgconf_buffer_t *buffer: Buffer where struct is serialized
* :param spdxtool_core_relationship_t *relationship_struct: Relationship struct to be serialized
* :param bool last: Is this last CreationInfo struct or does it need comma at the end. True comma False not
* :return: nothing
*/
void
spdxtool_core_relationship_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, spdxtool_core_relationship_t *relationship_struct, bool last)
{
(void) client;
spdxtool_serialize_obj_start(buffer, 2);
spdxtool_serialize_parm_and_string(buffer, "@type", relationship_struct->type, 3, true);
spdxtool_serialize_parm_and_string(buffer, "creationInfo", relationship_struct->creation_info, 3, true);
spdxtool_serialize_parm_and_string(buffer, "spdxId", relationship_struct->spdx_id, 3, true);
spdxtool_serialize_parm_and_string(buffer, "from", relationship_struct->from, 3, true);
spdxtool_serialize_parm_and_char(buffer, "to", '[', 3, false);
spdxtool_serialize_string(buffer, relationship_struct->to, 4, false);
spdxtool_serialize_array_end(buffer, 3, true);
spdxtool_serialize_parm_and_string(buffer, "relationshipType", relationship_struct->relationship_type, 3, false);
spdxtool_serialize_obj_end(buffer, 2, last);
}

73
cli/spdxtool/core.h Normal file
View File

@ -0,0 +1,73 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 The FreeBSD Foundation
*
* Portions of this software were developed by
* Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
* the FreeBSD Foundation
*/
#ifndef CLI__SPDXTOOL__CORE_H
#define CLI__SPDXTOOL__CORE_H
#include <stdlib.h>
#include "util.h"
#ifdef __cplusplus
extern "C" {
#endif
spdxtool_core_agent_t *
spdxtool_core_agent_new(pkgconf_client_t *client, char *creation_id, char *name);
void
spdxtool_core_agent_free(spdxtool_core_agent_t *agent_struct);
void
spdxtool_core_agent_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, spdxtool_core_agent_t *agent_struct, bool last);
spdxtool_core_creation_info_t *
spdxtool_core_creation_info_new(pkgconf_client_t *client, char *agent_id, char *id, char *time);
void
spdxtool_core_creation_info_free(spdxtool_core_creation_info_t *creation_struct);
void
spdxtool_core_creation_info_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, spdxtool_core_creation_info_t *creation_struct, bool last);
spdxtool_core_spdx_document_t *
spdxtool_core_spdx_document_new(pkgconf_client_t *client, char *spdx_id, char *creation_id);
bool
spdxtool_core_spdx_document_is_license(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx_struct, char *license);
void
spdxtool_core_spdx_document_add_license(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx_struct, char *license);
void
spdxtool_core_spdx_document_add_element(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx_struct, char *element);
void
spdxtool_core_spdx_document_free(spdxtool_core_spdx_document_t *spdx_struct);
void
spdxtool_core_spdx_document_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, spdxtool_core_spdx_document_t *spdx_struct, bool last);
spdxtool_core_relationship_t *
spdxtool_core_relationship_new(pkgconf_client_t *client, char *creation_info_id, char *spdx_id, char *from, char *to, char *relationship_type);
void
spdxtool_core_relationship_free(spdxtool_core_relationship_t *relationship_struct);
void
spdxtool_core_relationship_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, spdxtool_core_relationship_t *relationship_struct, bool last);
#ifdef __cplusplus
}
#endif
#endif

356
cli/spdxtool/main.c Normal file
View File

@ -0,0 +1,356 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 The FreeBSD Foundation
*
* Portions of this software were developed by
* Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
* the FreeBSD Foundation
*/
#include "libpkgconf/config.h"
#include <libpkgconf/stdinc.h>
#include <libpkgconf/libpkgconf.h>
#include "getopt_long.h"
#include "util.h"
#include "core.h"
#include "software.h"
#include "serialize.h"
#include "simplelicensing.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 = "3.0.1";
static const char *bom_license = "CC0-1.0";
static const char *xsd_any_uri_default_base = "https://github.com/pkgconf/pkgconf";
static int maximum_traverse_depth = 2000;
static pkgconf_client_t pkg_client;
static uint64_t want_flags;
static size_t maximum_package_count = 0;
// static int maximum_traverse_depth = 2000;
static FILE *error_msgout = NULL;
static const char *
environ_lookup_handler(const pkgconf_client_t *client, const char *key)
{
(void) client;
return getenv(key);
}
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 void
write_jsonld_end(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_buffer_t *buffer)
{
(void) client;
(void) world;
spdxtool_serialize_array_end(buffer, 1, false);
spdxtool_serialize_obj_end(buffer, 0, false);
}
static void
write_jsonld_header(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_buffer_t *buffer)
{
(void) client;
(void) world;
spdxtool_serialize_obj_start(buffer, 0);
spdxtool_serialize_parm_and_string(buffer, "@context", "https://spdx.org/rdf/3.0.1/spdx-context.jsonld", 1, true);
spdxtool_serialize_parm_and_char(buffer, "@graph", '[', 1, false);
}
static void
generate_spdx_package(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *ptr)
{
(void) client;
pkgconf_node_t *node = NULL;
spdxtool_core_spdx_document_t *document = (spdxtool_core_spdx_document_t *)ptr;
char *package_spdx = NULL;
char spdx_id_name[1024];
if (pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL)
return;
spdxtool_software_sbom_t *sbom = spdxtool_software_sbom_new(client, spdxtool_util_get_spdx_id_string(client, "software_Sbom", pkg->realname), strdup(document->creation_info), strdup("build"));
sbom->spdx_document = document;
sbom->rootElement = pkg;
package_spdx = spdxtool_util_get_spdx_id_string(client, "Package", pkg->realname);
pkgconf_tuple_add(client, &pkg->vars, "spdxId", package_spdx, false, 0);
free(package_spdx);
package_spdx = NULL;
pkgconf_tuple_add(client, &pkg->vars, "creationInfo", document->creation_info, false, 0);
pkgconf_tuple_add(client, &pkg->vars, "agent", document->agent, false, 0);
if (pkg->license != NULL)
{
snprintf(spdx_id_name, 1024, "%s/hasDeclaredLicense", pkg->realname);
package_spdx = spdxtool_util_get_spdx_id_string(client, "Relationship", spdx_id_name);
pkgconf_tuple_add(client, &pkg->vars, "hasDeclaredLicense", package_spdx, false, 0);
free(package_spdx);
package_spdx = NULL;
snprintf(spdx_id_name, 1024, "%s/hasConcludedLicense", pkg->realname);
package_spdx = spdxtool_util_get_spdx_id_string(client, "Relationship", spdx_id_name);
pkgconf_tuple_add(client, &pkg->vars, "hasConcludedLicense", package_spdx, false, 0);
free(package_spdx);
package_spdx = NULL;
spdxtool_core_spdx_document_add_license(client, document, pkg->license);
}
node = calloc(1, sizeof(pkgconf_node_t));
if(!node)
{
pkgconf_error(NULL, "Memory exhausted!");
return;
}
pkgconf_node_insert_tail(node, sbom, &document->rootElement);
}
static bool
generate_spdx(pkgconf_client_t *client, pkgconf_pkg_t *world, char *creation_time, char *creation_id, char *agent_name)
{
int eflag;
pkgconf_buffer_t buffer = PKGCONF_BUFFER_INITIALIZER;
char *agent_name_string = "Default";
char *creation_id_string = "_:creationinfo_1";
char *creation_time_string = NULL;
if(agent_name)
{
agent_name_string = agent_name;
}
if(creation_time)
{
creation_time_string = strdup(creation_time);
}
if(creation_id)
{
creation_id_string = creation_id;
}
spdxtool_core_agent_t *agent = spdxtool_core_agent_new(client, strdup(creation_id_string), strdup(agent_name_string));
spdxtool_core_creation_info_t *creation = spdxtool_core_creation_info_new(&pkg_client, strdup(agent->spdx_id), strdup(creation_id_string), creation_time_string);
spdxtool_core_spdx_document_t *document = spdxtool_core_spdx_document_new(&pkg_client, spdxtool_util_get_spdx_id_int(&pkg_client, "spdxDocument"), strdup(creation_id_string));
document->agent = strdup(agent->spdx_id);
eflag = pkgconf_pkg_traverse(client, world, generate_spdx_package, document, maximum_traverse_depth, 0);
if (eflag != PKGCONF_PKG_ERRF_OK)
return false;
write_jsonld_header(client, world, &buffer);
spdxtool_core_agent_serialize(client, &buffer, agent, true);
spdxtool_core_creation_info_serialize(client, &buffer, creation, true);
spdxtool_simplelicensing_licenseExpression_serialize(client, &buffer, document, true);
spdxtool_core_spdx_document_serialize(client, &buffer, document, false);
write_jsonld_end(client, world, &buffer);
spdxtool_core_spdx_document_free(document);
spdxtool_core_agent_free(agent);
spdxtool_core_creation_info_free(creation);
printf("%s\n", pkgconf_buffer_str(&buffer));
pkgconf_buffer_finalize(&buffer);
return true;
}
static int
version(void)
{
printf("spdxtool %s\n", PACKAGE_VERSION);
return EXIT_SUCCESS;
}
static int
about(void)
{
printf("spdxtool (%s %s)\n\n", PACKAGE_NAME, PACKAGE_VERSION);
printf("SPDX-License-Identifier: BSD-2-Clause\n\n");
printf("Copyright (c) 2025 The FreeBSD Foundation\n\n");
printf("Portions of this software were developed by\n");
printf("Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from\n");
printf("the FreeBSD Foundation\n\n");
printf("Report bugs at <%s>.\n", PACKAGE_BUGREPORT);
return EXIT_SUCCESS;
}
static int
usage(void)
{
printf("usage: spdxtool [modules]\n");
printf("\nOptions:\n");
printf(" --agent-name Set agent name [default: 'Default']\n");
printf(" --creation-time Use string as creation time (Should be in ISO8601 format) [default: current time]\n");
printf(" --creation-id Use string as creation id [default: '_:creationinfo_1']\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();
char *creation_time = NULL;
char *creation_id = NULL;
char *agent_name = NULL;
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[] =
{
{ "agent-name", required_argument, NULL, 100, },
{ "creation-time", required_argument, NULL, 101, },
{ "creation-id", required_argument, NULL, 102, },
{ "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 100:
agent_name = pkg_optarg;
break;
case 101:
creation_time = pkg_optarg;
break;
case 102:
creation_id = pkg_optarg;
break;
case '?':
case ':':
return EXIT_FAILURE;
default:
break;
}
}
pkgconf_client_init(&pkg_client, error_handler, NULL, personality, NULL, environ_lookup_handler);
/* 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 (!pkgconf_queue_solve(&pkg_client, &pkgq, &world, maximum_traverse_depth))
{
ret = EXIT_FAILURE;
goto out;
}
spdxtool_util_set_uri_root(&pkg_client, xsd_any_uri_default_base);
spdxtool_util_set_spdx_license(&pkg_client, bom_license);
spdxtool_util_set_spdx_version(&pkg_client, spdx_version);
if (!generate_spdx(&pkg_client, &world, creation_time, creation_id, agent_name))
{
ret = EXIT_FAILURE;
goto out;
}
ret = EXIT_SUCCESS;
out:
pkgconf_solution_free(&pkg_client, &world);
pkgconf_queue_free(&pkgq);
pkgconf_cross_personality_deinit(personality);
pkgconf_client_deinit(&pkg_client);
return ret;
}

206
cli/spdxtool/serialize.c Normal file
View File

@ -0,0 +1,206 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 The FreeBSD Foundation
*
* Portions of this software were developed by
* Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
* the FreeBSD Foundation
*/
#include <stdlib.h>
#include <string.h>
#include "util.h"
#include "core.h"
static inline void
serialize_add_indent(pkgconf_buffer_t *buffer, unsigned int level)
{
for (; level; level--)
pkgconf_buffer_append(buffer, " ");
}
static inline void
serialize_next_line(pkgconf_buffer_t *buffer, bool more)
{
if (more)
pkgconf_buffer_push_byte(buffer, ',');
pkgconf_buffer_push_byte(buffer, '\n');
}
static inline void
serialize_begin_object(pkgconf_buffer_t *buffer, char ch, unsigned int level)
{
serialize_add_indent(buffer, level);
pkgconf_buffer_push_byte(buffer, ch);
serialize_next_line(buffer, false);
}
static inline void
serialize_end_object(pkgconf_buffer_t *buffer, char ch, unsigned int level, bool more)
{
serialize_add_indent(buffer, level);
pkgconf_buffer_push_byte(buffer, ch);
serialize_next_line(buffer, more);
}
/*
* !doc
*
* .. c:function:: void spdxtool_serialize_parm_and_string(pkgconf_buffer_t *buffer, char *parm, char *string, unsigned int level, bool more)
*
* Add paramter, string and optional comma to buffer
*
* :param pkgconf_buffer_t *buffer: Buffer to add.
* :param char *param: Parameter name
* :param char *string: String to add
* :param unsigned int level: Indent level
* :param bool more: true if more fields are expected, else false
* :return: nothing
*/
void
spdxtool_serialize_parm_and_string(pkgconf_buffer_t *buffer, char *parm, char *string, unsigned int level, bool more)
{
serialize_add_indent(buffer, level);
pkgconf_buffer_append_fmt(buffer, "\"%s\": \"%s\"", parm, string);
serialize_next_line(buffer, more);
}
/*
* !doc
*
* .. c:function:: void spdxtool_serialize_parm_and_char(pkgconf_buffer_t *buffer, char *parm, char ch, unsigned int level, bool more)
*
* Add paramter, char and optional comma to buffer
*
* :param pkgconf_buffer_t *buffer: Buffer to add.
* :param char *param: Parameter name
* :param char ch: Char to add
* :param unsigned int level: Indent level
* :param bool more: true if more fields are expected, else false
* :return: nothing
*/
void
spdxtool_serialize_parm_and_char(pkgconf_buffer_t *buffer, char *parm, char ch, unsigned int level, bool more)
{
serialize_add_indent(buffer, level);
pkgconf_buffer_append_fmt(buffer, "\"%s\": %c", parm, ch);
serialize_next_line(buffer, more);
}
/*
* !doc
*
* .. c:function:: void spdxtool_serialize_parm_and_int(pkgconf_buffer_t *buffer, char *parm, int integer, unsigned int level, bool more)
*
* Add paramter, integer and optional comma to buffer
*
* :param pkgconf_buffer_t *buffer: Buffer to add.
* :param char *param: Parameter name
* :param int integer: Int to add
* :param unsigned int level: Indent level
* :param bool more: true if more fields are expected, else false
* :return: nothing
*/
void
spdxtool_serialize_parm_and_int(pkgconf_buffer_t *buffer, char *parm, int integer, unsigned int level, bool more)
{
serialize_add_indent(buffer, level);
pkgconf_buffer_append_fmt(buffer, "\"%s\": %d", parm, integer);
serialize_next_line(buffer, more);
}
/*
* !doc
*
* .. c:function:: void spdxtool_serialize_string(pkgconf_buffer_t *buffer, char *string, unsigned int level, bool more)
*
* Add just string.
*
* :param pkgconf_buffer_t *buffer: Buffer to add.
* :param char *param: Parameter name
* :param char *ch: String to add
* :param unsigned int: level Indent level
* :param bool more: true if more fields are expected, else false
* :return: nothing
*/
void
spdxtool_serialize_string(pkgconf_buffer_t *buffer, char *string, unsigned int level, bool more)
{
serialize_add_indent(buffer, level);
pkgconf_buffer_push_byte(buffer, '"');
pkgconf_buffer_append(buffer, string);
pkgconf_buffer_push_byte(buffer, '"');
serialize_next_line(buffer, more);
}
/*
* !doc
*
* .. c:function:: void spdxtool_serialize_obj_start(pkgconf_buffer_t *buffer, unsigned int level)
*
* Start JSON object to buffer
*
* :param pkgconf_buffer_t *buffer: Buffer to add.
* :param unsigned int level: Indent level
* :return: nothing
*/
void
spdxtool_serialize_obj_start(pkgconf_buffer_t *buffer, unsigned int level)
{
serialize_begin_object(buffer, '{', level);
}
/*
* !doc
*
* .. c:function:: void spdxtool_serialize_obj_end(pkgconf_buffer_t *buffer, unsigned int level, bool more)
*
* End JSON object to buffer
*
* :param pkgconf_buffer_t *buffer: Buffer to add.
* :param unsigned int level: Level which is added
* :param bool more: true if more fields are expected, else false
* :return: nothing
*/
void
spdxtool_serialize_obj_end(pkgconf_buffer_t *buffer, unsigned int level, bool more)
{
serialize_end_object(buffer, '}', level, more);
}
/*
* !doc
*
* .. c:function:: void spdxtool_serialize_obj_start(pkgconf_buffer_t *buffer, unsigned int level)
*
* Start JSON array to buffer
*
* :param pkgconf_buffer_t *buffer: Buffer to add.
* :param unsigned int level: Level which is added
* :return: nothing
*/
void
spdxtool_serialize_array_start(pkgconf_buffer_t *buffer, unsigned int level)
{
serialize_begin_object(buffer, '[', level);
}
/*
* !doc
*
* .. c:function:: void spdxtool_serialize_array_end(pkgconf_buffer_t *buffer, unsigned int level, bool more)
*
* End JSON array to buffer
*
* :param pkgconf_buffer_t *buffer: Buffer to add.
* :param unsigned int level: Level which is added
* :param bool more: true if more fields are expected, else false
* :return: nothing
*/
void
spdxtool_serialize_array_end(pkgconf_buffer_t *buffer, unsigned int level, bool more)
{
serialize_end_object(buffer, ']', level, more);
}

50
cli/spdxtool/serialize.h Normal file
View File

@ -0,0 +1,50 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 The FreeBSD Foundation
*
* Portions of this software were developed by
* Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
* the FreeBSD Foundation
*/
#include <stdlib.h>
#include <string.h>
#include "util.h"
#ifndef CLI__SPDXTOOL__SERIALIZE_H
#define CLI__SPDXTOOL__SERIALIZE_H
#ifdef __cplusplus
extern "C" {
#endif
void
spdxtool_serialize_parm_and_string(pkgconf_buffer_t *buffer, char *parm, char *string, unsigned int level, bool comma);
void
spdxtool_serialize_parm_and_char(pkgconf_buffer_t *buffer, char *parm, char ch, unsigned int level, bool comma);
void
spdxtool_serialize_parm_and_int(pkgconf_buffer_t *buffer, char *parm, int integer, unsigned int level, bool comma);
void
spdxtool_serialize_string(pkgconf_buffer_t *buffer, char *string, unsigned int level, bool comma);
void
spdxtool_serialize_obj_start(pkgconf_buffer_t *buffer, unsigned int level);
void
spdxtool_serialize_obj_end(pkgconf_buffer_t *buffer, unsigned int level, bool comma);
void
spdxtool_serialize_array_start(pkgconf_buffer_t *buffer, unsigned int level);
void
spdxtool_serialize_array_end(pkgconf_buffer_t *buffer, unsigned int level, bool comma);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,120 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 The FreeBSD Foundation
*
* Portions of this software were developed by
* Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
* the FreeBSD Foundation
*/
#include <stdlib.h>
#include <string.h>
#include "util.h"
#include "serialize.h"
#include "software.h"
#include "core.h"
/*
* !doc
*
* .. c:function:: spdxtool_simplelicensing_license_expression_t *spdxtool_simplelicensing_licenseExpression_new(pkgconf_client_t *client, char *license)
*
* Create new /SimpleLicensing/SimpleLicensingText struct
*
* :param pkgconf_client_t *client: The pkgconf client being accessed.
* :param char *license: SPDX name of license
* :return: NULL if some problem occurs and SimpleLicensingText struct if not
*/
spdxtool_simplelicensing_license_expression_t *
spdxtool_simplelicensing_licenseExpression_new(pkgconf_client_t *client, char *license)
{
spdxtool_simplelicensing_license_expression_t *expression = NULL;
if(!client || !license)
{
return NULL;
}
expression = calloc(1, sizeof(spdxtool_simplelicensing_license_expression_t));
if(!expression)
{
pkgconf_error(client, "Memory exhausted! Can't create simplelicense_expression struct!");
return NULL;
}
expression->type = "simplelicensing_LicenseExpression";
expression->license_expression = strdup(license);
expression->spdx_id = spdxtool_util_get_spdx_id_string(client, expression->type, license);
return expression;
}
/*
* !doc
*
* .. c:function:: void spdxtool_simplelicensing_licenseExpression_free(spdxtool_simplelicensing_license_expression_t *expression_struct)
*
* Free /SimpleLicensing/SimpleLicensingText struct
*
* :param spdxtool_simplelicensing_license_expression_t *expression_struct: SimpleLicensingText struct to be freed.
* :return: nothing
*/
void
spdxtool_simplelicensing_licenseExpression_free(spdxtool_simplelicensing_license_expression_t *expression_struct)
{
if(!expression_struct)
{
return;
}
if(expression_struct->spdx_id)
{
free(expression_struct->spdx_id);
expression_struct->spdx_id = NULL;
}
if(expression_struct->license_expression)
{
free(expression_struct->license_expression);
expression_struct->license_expression = NULL;
}
free(expression_struct);
}
/*
* !doc
*
* .. c:function:: void spdxtool_simplelicensing_licenseExpression_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, spdxtool_core_spdx_document_t *spdx_struct, bool last)
*
* Serialize /SimpleLicensing/SimpleLicensingText struct to JSON
*
* :param pkgconf_client_t *client: The pkgconf client being accessed.
* :param pkgconf_buffer_t *buffer: Buffer where struct is serialized
* :param spdxtool_core_spdx_document_t *spdx_struct: SimpleLicensingText struct to be serialized
* :param bool last: Is this last CreationInfo struct or does it need comma at the end. True comma False not
* :return: nothing
*/
void
spdxtool_simplelicensing_licenseExpression_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, spdxtool_core_spdx_document_t *spdx_struct, bool last)
{
pkgconf_node_t *iter = NULL;
(void) last;
PKGCONF_FOREACH_LIST_ENTRY(spdx_struct->licenses.head, iter)
{
spdxtool_simplelicensing_license_expression_t *expression = iter->data;
spdxtool_serialize_obj_start(buffer, 2);
spdxtool_serialize_parm_and_string(buffer, "@type", "simplelicensing_LicenseExpression", 3, true);
spdxtool_serialize_parm_and_string(buffer, "creationInfo", spdx_struct->creation_info, 3, true);
spdxtool_serialize_parm_and_string(buffer, "spdxId",expression->spdx_id, 3, true);
spdxtool_serialize_parm_and_string(buffer, "simplelicensing_licenseExpression", expression->license_expression, 3, false);
spdxtool_serialize_obj_end(buffer, 2, true);
spdxtool_core_spdx_document_add_element(client, spdx_struct, strdup(expression->spdx_id));
}
}

View File

@ -0,0 +1,38 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 The FreeBSD Foundation
*
* Portions of this software were developed by
* Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
* the FreeBSD Foundation
*/
#include <stdlib.h>
#include <string.h>
#include "util.h"
#ifndef CLI__SPDXTOOL__SIMPLELICENSING_H
#define CLI__SPDXTOOL__SIMPLELICENSING_H
#ifdef __cplusplus
extern "C" {
#endif
spdxtool_simplelicensing_license_expression_t *
spdxtool_simplelicensing_licenseExpression_new(pkgconf_client_t *client, char *license);
void
spdxtool_simplelicensing_licenseExpression_free(spdxtool_simplelicensing_license_expression_t *expression_struct);
void
spdxtool_simplelicensing_licenseExpression_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, spdxtool_core_spdx_document_t *spdx_struct, bool last);
#ifdef __cplusplus
}
#endif
#endif

241
cli/spdxtool/software.c Normal file
View File

@ -0,0 +1,241 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 The FreeBSD Foundation
*
* Portions of this software were developed by
* Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
* the FreeBSD Foundation
*/
#include <stdlib.h>
#include <string.h>
#include "util.h"
#include "serialize.h"
#include "software.h"
#include "core.h"
/*
* !doc
*
* .. c:function:: spdxtool_software_sbom_t *spdxtool_software_sbom_new(pkgconf_client_t *client, char *spdx_id, char *creation_id, char *sbom_type)
*
* Create new /Software/Sbom struct
*
* :param pkgconf_client_t *client: The pkgconf client being accessed.
* :param char *spdx_id: spdxId for this SBOM element
* :param char *creation_id: id for CreationInfo
* :param char *sbom_type: Sbom types can be found SPDX documention
* :return: NULL if some problem occurs and Sbom struct if not
*/
spdxtool_software_sbom_t *
spdxtool_software_sbom_new(pkgconf_client_t *client, char *spdx_id, char *creation_id, char *sbom_type)
{
spdxtool_software_sbom_t *sbom_struct = NULL;
if(!client || !spdx_id || !creation_id || !sbom_type)
{
return NULL;
}
(void)client;
sbom_struct = calloc(1, sizeof(spdxtool_software_sbom_t));
if(!sbom_struct)
{
pkgconf_error(client, "Memory exhausted! Can't create sbom struct.");
return NULL;
}
sbom_struct->type="software_Sbom";
sbom_struct->spdx_id = spdx_id;
sbom_struct->creation_info = creation_id;
sbom_struct->sbom_type = sbom_type;
return sbom_struct;
}
/*
* !doc
*
* .. c:function:: void spdxtool_software_sbom_free(spdxtool_software_sbom_t *sbom_struct)
*
* Free /Software/Sbom struct
*
* :param spdxtool_software_sbom_t *sbom_struct: Sbom struct to be freed.
* :return: nothing
*/
void
spdxtool_software_sbom_free(spdxtool_software_sbom_t *sbom_struct)
{
if(!sbom_struct)
{
return;
}
if(sbom_struct->spdx_id)
{
free(sbom_struct->spdx_id);
sbom_struct->spdx_id = NULL;
}
if(sbom_struct->creation_info)
{
free(sbom_struct->creation_info);
sbom_struct->creation_info = NULL;
}
if(sbom_struct->sbom_type)
{
free(sbom_struct->sbom_type);
sbom_struct->sbom_type = NULL;
}
free(sbom_struct);
}
/*
* !doc
*
* .. c:function:: void spdxtool_software_sbom_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, spdxtool_software_sbom_t *sbom_struct, bool last)
*
* Serialize /Software/Sbom struct to JSON
*
* :param pkgconf_client_t *client: The pkgconf client being accessed.
* :param pkgconf_buffer_t *buffer: Buffer where struct is serialized
* :param spdxtool_software_sbom_t *sbom_struct: SimpleLicensingText struct to be serialized
* :param bool last: Is this last CreationInfo struct or does it need comma at the end. True comma False not
* :return: nothing
*/
void
spdxtool_software_sbom_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, spdxtool_software_sbom_t *sbom_struct, bool last)
{
pkgconf_node_t *node = NULL;
char *value;
spdxtool_serialize_obj_start(buffer, 2);
spdxtool_serialize_parm_and_string(buffer, "@type", sbom_struct->type, 3, true);
spdxtool_serialize_parm_and_string(buffer, "creationInfo", sbom_struct->creation_info, 3, true);
spdxtool_serialize_parm_and_string(buffer, "spdxId", sbom_struct->spdx_id, 3, true);
spdxtool_serialize_parm_and_char(buffer, "software_sbomType", '[', 3, false);
spdxtool_serialize_string(buffer, sbom_struct->sbom_type, 4, false);
spdxtool_serialize_array_end(buffer, 3, true);
spdxtool_serialize_parm_and_char(buffer, "rootElement", '[', 3, false);
spdxtool_serialize_string(buffer, pkgconf_tuple_find(client, &sbom_struct->rootElement->vars, "spdxId"), 4, false);
spdxtool_serialize_array_end(buffer, 3, true);
spdxtool_serialize_parm_and_char(buffer, "element", '[', 3, false);
PKGCONF_FOREACH_LIST_ENTRY(sbom_struct->rootElement->required.head, node)
{
pkgconf_dependency_t *dep = node->data;
pkgconf_pkg_t *match = dep->match;
char *spdx_id_relation = NULL;
char tmp_str[1024];
// New relation spdxId
snprintf(tmp_str, 1024, "%s/dependsOn/%s", sbom_struct->rootElement->realname, match->realname);
spdx_id_relation = spdxtool_util_get_spdx_id_string(client, "Releationship", tmp_str);
spdxtool_serialize_string(buffer, spdx_id_relation, 4, true);
spdxtool_core_spdx_document_add_element(client, sbom_struct->spdx_document, strdup(spdx_id_relation));
free(spdx_id_relation);
}
value = pkgconf_tuple_find(client, &sbom_struct->rootElement->vars, "hasDeclaredLicense");
if (value != NULL)
{
spdxtool_serialize_string(buffer, value, 4, true);
spdxtool_core_spdx_document_add_element(client, sbom_struct->spdx_document, strdup(value));
}
value = pkgconf_tuple_find(client, &sbom_struct->rootElement->vars, "hasConcludedLicense");
if (value != NULL)
{
spdxtool_serialize_string(buffer, value, 4, false);
spdxtool_core_spdx_document_add_element(client, sbom_struct->spdx_document, strdup(value));
}
spdxtool_serialize_array_end(buffer, 3, false);
spdxtool_serialize_obj_end(buffer, 2, last);
spdxtool_software_package_serialize(client, buffer, sbom_struct->rootElement, false);
}
/*
* !doc
*
* .. c:function:: void spdxtool_software_sbom_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, spdxtool_software_sbom_t *sbom_struct, bool last)
*
* Serialize /Software/Package struct to JSON
* This is bit special case as it takes pkgconf pkg struct and serializes that one
* There is not new or free for this stype
*
* :param pkgconf_client_t *client: The pkgconf client being accessed.
* :param pkgconf_buffer_t *buffer: Buffer where struct is serialized
* :param pkgconf_pkg_t *pkg: Pkgconf package
* :param bool last: Is this last CreationInfo struct or does it need comma at the end. True comma False not
* :return: nothing
*/
void
spdxtool_software_package_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, pkgconf_pkg_t *pkg, bool last)
{
spdxtool_core_relationship_t *relationship_struct = NULL;
char *spdx_id_license = NULL, *tuple_license;
pkgconf_node_t *node = NULL;
(void) last;
spdxtool_serialize_obj_start(buffer, 2);
spdxtool_serialize_parm_and_string(buffer, "@type", "software_Package", 3, true);
spdxtool_serialize_parm_and_string(buffer, "creationInfo", pkgconf_tuple_find(client, &pkg->vars, "creationInfo"), 3, true);
spdxtool_serialize_parm_and_string(buffer, "spdxId", pkgconf_tuple_find(client, &pkg->vars, "spdxId"), 3, true);
spdxtool_serialize_parm_and_string(buffer, "name", pkg->realname, 3, true);
spdxtool_serialize_parm_and_char(buffer, "originatedBy", '[', 3, false);
spdxtool_serialize_string(buffer, pkgconf_tuple_find(client, &pkg->vars, "agent"), 4, false);
spdxtool_serialize_array_end(buffer, 3, true);
spdxtool_serialize_parm_and_string(buffer, "software_copyrightText", "NOASSERTION", 3, true);
spdxtool_serialize_parm_and_string(buffer, "software_packageVersion", pkg->version, 3, false);
spdxtool_serialize_obj_end(buffer, 2, true);
/* These are must so add them right a way */
spdx_id_license = spdxtool_util_get_spdx_id_string(client, "simplelicensing_LicenseExpression", pkg->license);
tuple_license = pkgconf_tuple_find(client, &pkg->vars, "hasDeclaredLicense");
if (tuple_license != NULL)
{
relationship_struct = spdxtool_core_relationship_new(client, strdup(pkgconf_tuple_find(client, &pkg->vars, "creationInfo")), strdup(tuple_license), strdup(pkgconf_tuple_find(client, &pkg->vars, "spdxId")), strdup(spdx_id_license), strdup("hasDeclaredLicense"));
spdxtool_core_relationship_serialize(client, buffer, relationship_struct, true);
spdxtool_core_relationship_free(relationship_struct);
}
tuple_license = pkgconf_tuple_find(client, &pkg->vars, "hasConcludedLicense");
if (tuple_license != NULL)
{
relationship_struct = spdxtool_core_relationship_new(client, strdup(pkgconf_tuple_find(client, &pkg->vars, "creationInfo")), strdup(tuple_license), strdup(pkgconf_tuple_find(client, &pkg->vars, "spdxId")), spdx_id_license, strdup("hasConcludedLicense"));
spdxtool_core_relationship_serialize(client, buffer, relationship_struct, true);
spdxtool_core_relationship_free(relationship_struct);
}
PKGCONF_FOREACH_LIST_ENTRY(pkg->required.head, node)
{
pkgconf_dependency_t *dep = node->data;
pkgconf_pkg_t *match = dep->match;
char *spdx_id_relation = NULL;
char *spdx_id_package = NULL;
char tmp_str[1024];
// New relation spdxId
snprintf(tmp_str, 1024, "%s/dependsOn/%s", pkg->realname, match->realname);
spdx_id_relation = spdxtool_util_get_spdx_id_string(client, "Releationship", tmp_str);
// new package spdxId which will be hopefully come later on
spdx_id_package = spdxtool_util_get_spdx_id_string(client, "Package", match->realname);
relationship_struct = spdxtool_core_relationship_new(client, strdup(pkgconf_tuple_find(client, &pkg->vars, "creationInfo")), spdx_id_relation, strdup(pkgconf_tuple_find(client, &pkg->vars, "spdxId")), spdx_id_package, strdup("dependsOn"));
spdxtool_core_relationship_serialize(client, buffer, relationship_struct, true);
spdxtool_core_relationship_free(relationship_struct);
}
}

37
cli/spdxtool/software.h Normal file
View File

@ -0,0 +1,37 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 The FreeBSD Foundation
*
* Portions of this software were developed by
* Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
* the FreeBSD Foundation
*/
#ifndef CLI__SPDXTOOL_SOFTWARE_H
#define CLI__SPDXTOOL_SOFTWARE_H
#include <stdlib.h>
#include "util.h"
#ifdef __cplusplus
extern "C" {
#endif
spdxtool_software_sbom_t *
spdxtool_software_sbom_new(pkgconf_client_t *client, char *spdx_id, char *creation_id, char *sbom_type);
void
spdxtool_software_sbom_free(spdxtool_software_sbom_t *sbom_struct);
void
spdxtool_software_sbom_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, spdxtool_software_sbom_t *sbom_struct, bool last);
void
spdxtool_software_package_serialize(pkgconf_client_t *client, pkgconf_buffer_t *buffer, pkgconf_pkg_t *pkg, bool last);
#ifdef __cplusplus
}
#endif
#endif

255
cli/spdxtool/util.c Normal file
View File

@ -0,0 +1,255 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 The FreeBSD Foundation
*
* Portions of this software were developed by
* Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
* the FreeBSD Foundation
*/
#include <libpkgconf/libpkgconf.h>
#include <libpkgconf/stdinc.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>
/*
* !doc
*
* .. c:function:: void spdxtool_util_set_key(pkgconf_client_t *client, const char *key, const char *key_value, const char *key_default)
*
* Set key wit default value. Default value is used if key is NULL
*
* :param pkgconf_client_t* client: The pkgconf client being accessed.
* :param const char *key: Key to be preserved
* :param const char *key_value: Value for key
* :param const char *key_default: Default value if key_value is NULL
* :return: nothing
*/
void
spdxtool_util_set_key(pkgconf_client_t *client, const char *key, const char *key_value, const char *key_default)
{
PKGCONF_TRACE(client, "set uri_root to: %s", key_value != NULL ? key_value : key_default);
pkgconf_tuple_add_global(client, key, key_value != NULL ? key_value : key_default);
}
/*
* !doc
*
* .. c:function:: void spdxtool_util_set_uri_root(pkgconf_client_t *client, const char *uri_root)
*
* Set URI/URL root for spdxId. Type for this is 'xsd:anyURI' which means
* it can they may be absolute or relative.
*
* :param pkgconf_client_t* client: The pkgconf client being accessed.
* :param const char *uri_root: URI root.
* :return: nothing
*/
void
spdxtool_util_set_uri_root(pkgconf_client_t *client, const char *uri_root)
{
spdxtool_util_set_key(client, "spdx_uri_root", uri_root, "https://github.com/pkgconf/pkgconf");
}
/*
* !doc
*
* .. c:function:: const char *spdxtool_util_get_uri_root(pkgconf_client_t *client)
*
* Get current URI
*
* :param pkgconf_client_t* client: The pkgconf client being accessed.
* :return: URI or NULL
*/
const char *
spdxtool_util_get_uri_root(pkgconf_client_t *client)
{
return pkgconf_tuple_find_global(client, "spdx_uri_root");
}
/*
* !doc
*
* .. c:function:: void spdxtool_util_set_spdx_version(pkgconf_client_t *client, const char *spdx_version)
*
* Set current SPDX SBOM Spec version. If not set it's 3.0.1
*
* :param pkgconf_client_t* client: The pkgconf client being accessed.
* :param char* spdx_version: SPDX specification version.
* :return: nothing
*/
void
spdxtool_util_set_spdx_version(pkgconf_client_t *client, const char *spdx_version)
{
spdxtool_util_set_key(client, "spdx_version", spdx_version, "3.0.1");
}
/*
* !doc
*
* .. c:function:: const char *spdxtool_util_get_spdx_version(pkgconf_client_t *client)
*
* Get SPDX SBOM specification version in use
*
* :param pkgconf_client_t* client: The pkgconf client being accessed.
* :return: Spec version
*/
const char *
spdxtool_util_get_spdx_version(pkgconf_client_t *client)
{
return pkgconf_tuple_find_global(client, "spdx_version");
}
/*
* !doc
*
* .. c:function:: void spdxtool_util_set_spdx_license(pkgconf_client_t *client, const char *spdx_license)
*
* Under which license SBOM is released. License string should be in
* SPDX style. Something like: FreeBSD-DOC, CC0-1.0 or MIT
*
* :param pkgconf_client_t* client: The pkgconf client being accessed.
* :param const char *spdx_license: SPDX compatible license
* :return: nothing
*/
void
spdxtool_util_set_spdx_license(pkgconf_client_t *client, const char *spdx_license)
{
spdxtool_util_set_key(client, "spdx_license", spdx_license, "CC0-1.0");
}
/*
* !doc
*
* .. c:function:: const char *spdxtool_util_get_spdx_license(pkgconf_client_t *client)
*
* Get license which SBOM is release in
*
* :param pkgconf_client_t* client: The pkgconf client being accessed.
* :return: SPDX license
*/
const char *
spdxtool_util_get_spdx_license(pkgconf_client_t *client)
{
return pkgconf_tuple_find_global(client, "spdx_license");
}
static size_t last_id = 0;
/*
* !doc
*
* .. c:function:: char *spdxtool_util_get_spdx_id_int(pkgconf_client_t *client, char *part)
*
* Get spdxId with current URI.
* URI is lookg like https://test/part/1
*
* :param pkgconf_client_t* client: The pkgconf client being accessed.
* :param char *part: Addition subdir part before number
* :return: URI
*/
char *
spdxtool_util_get_spdx_id_int(pkgconf_client_t *client, char *part)
{
const char *global_xsd_any_uri = spdxtool_util_get_uri_root(client);
pkgconf_buffer_t current_uri = PKGCONF_BUFFER_INITIALIZER;
pkgconf_buffer_join(&current_uri, '/', global_xsd_any_uri, part, NULL);
pkgconf_buffer_append_fmt(&current_uri, "/" SIZE_FMT_SPECIFIER, ++last_id);
return pkgconf_buffer_freeze(&current_uri);
}
/*
* !doc
*
* .. c:function:: char *spdxtool_util_get_spdx_id_string(pkgconf_client_t *client, char *part, char *string_id)
*
* Get string id URI
* looks something like: https://test/part/string_id
*
* :param pkgconf_client_t* client: The pkgconf client being accessed.
* :param char* part: subdir part of URI.
* :param char* string_id: String ID.
* :return: URI
*/
char *
spdxtool_util_get_spdx_id_string(pkgconf_client_t *client, char *part, char *string_id)
{
const char *global_xsd_any_uri = spdxtool_util_get_uri_root(client);
pkgconf_buffer_t current_uri = PKGCONF_BUFFER_INITIALIZER;
pkgconf_buffer_join(&current_uri, '/', global_xsd_any_uri, part, string_id, NULL);
return pkgconf_buffer_freeze(&current_uri);
}
/*
* !doc
*
* .. c:function:: char *spdxtool_util_get_iso8601_time(time_t *wanted_time)
*
* Get ISO8601 time string
*
* :param time_t *wanted_time: Time to be converted
* :return: Time string in ISO8601 format
*/
char *
spdxtool_util_get_iso8601_time(time_t *wanted_time)
{
char buf[PKGCONF_ITEM_SIZE];
struct tm *tm_info = gmtime(wanted_time);
if(!wanted_time)
return NULL;
/* ISO8601 time with Z at the end */
strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%SZ", tm_info);
return strdup(buf);
}
/*
* !doc
*
* .. c:function:: char *spdxtool_util_get_current_iso8601_time()
*
* Get ISO8601 current timestamp
*
* :return: Time string in ISO8601 format
*/
char *
spdxtool_util_get_current_iso8601_time()
{
time_t now;
time(&now);
return spdxtool_util_get_iso8601_time(&now);
}
/*
* !doc
*
* .. c:function:: char *spdxtool_util_string_correction(char *str)
*
* Lowercase string and change spaces to '_'
*
* :param char *str: String to be converted
* :return: Converted string
*/
char *
spdxtool_util_string_correction(char *str)
{
char *ptr = str;
/* Lowecase string and make spaces '_' */
for ( ; *ptr; ++ptr)
{
*ptr = tolower(*ptr);
if(isspace((unsigned char) *ptr))
{
*ptr = '_';
}
}
return str;
}

123
cli/spdxtool/util.h Normal file
View File

@ -0,0 +1,123 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 The FreeBSD Foundation
*
* Portions of this software were developed by
* Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
* the FreeBSD Foundation
*/
#ifndef CLI__SPDXTOOL__UTIL_H
#define CLI__SPDXTOOL__UTIL_H
#include <libpkgconf/libpkgconf.h>
#include <time.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct spdxtool_core_agent_
{
int refcount;
char *spdx_id;
char *type;
char *creation_info;
char *name;
} spdxtool_core_agent_t;
typedef struct spdxtool_core_creation_info_
{
int refcount;
char *id;
char *type;
char *created;
char *created_by;
char *created_using;
const char *spec_version;
} spdxtool_core_creation_info_t;
typedef struct spdxtool_core_spdx_document
{
int refcount;
char *type;
char *spdx_id;
char *creation_info;
char *agent;
pkgconf_list_t licenses;
pkgconf_list_t element;
pkgconf_list_t rootElement;
} spdxtool_core_spdx_document_t;
typedef struct spdxtool_software_sbom_
{
int refcount;
char *spdx_id;
char *type;
char *creation_info;
char *sbom_type;
spdxtool_core_spdx_document_t *spdx_document;
pkgconf_pkg_t *rootElement;
} spdxtool_software_sbom_t;
typedef struct spdxtool_simplelicensing_license_expression_
{
int refcount;
char *type;
char *spdx_id;
char *license_expression;
} spdxtool_simplelicensing_license_expression_t;
typedef struct spdxtool_core_relationship_
{
int refcount;
char *type;
char *spdx_id;
char *creation_info;
char *from;
char *to;
char *relationship_type;
} spdxtool_core_relationship_t;
void
spdxtool_util_set_key(pkgconf_client_t *client, const char *key, const char *key_value, const char *key_default);
void
spdxtool_util_set_uri_root(pkgconf_client_t *client, const char *uri_root);
const char *
spdxtool_util_get_uri_root(pkgconf_client_t *client);
void
spdxtool_util_set_spdx_version(pkgconf_client_t *client, const char *spdx_version);
const char *
spdxtool_util_get_spdx_version(pkgconf_client_t *client);
void
spdxtool_util_set_spdx_license(pkgconf_client_t *client, const char *spdx_license);
const char *
spdxtool_util_get_spdx_license(pkgconf_client_t *client);
char *
spdxtool_util_get_spdx_id_int(pkgconf_client_t *client, char *part);
char *
spdxtool_util_get_spdx_id_string(pkgconf_client_t *client, char *part, char *string_id);
char *
spdxtool_util_get_iso8601_time(time_t *wanted_time);
char *
spdxtool_util_get_current_iso8601_time();
char *
spdxtool_util_string_correction(char *str);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -12,7 +12,7 @@ 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.4.2],[https://github.com/pkgconf/pkgconf/issues/new])
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"])
@ -23,7 +23,9 @@ AX_CHECK_COMPILE_FLAG([-std=gnu99], [CFLAGS="$CFLAGS -std=gnu99"], [
])
AC_CONFIG_HEADERS([libpkgconf/config.h])
AC_CHECK_DECLS([strlcpy, strlcat, strndup], [], [], [[#include <string.h>]])
AC_CHECK_DECLS([reallocarray])
AC_CHECK_DECLS([pledge, unveil], [], [], [[#include <unistd.h>]])
AC_CHECK_DECLS([readlinkat], [], [], [[#include <unistd.h>]])
AC_CHECK_DECLS([reallocarray], [], [], [[#include <stdlib.h>]])
AC_CHECK_HEADERS([sys/stat.h])
AM_INIT_AUTOMAKE([foreign dist-xz subdir-objects])
AM_SILENT_RULES([yes])
@ -46,13 +48,21 @@ 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}")
[SYSTEM_LIBDIR="$withval"], [
AC_MSG_WARN([--with-system-libdir is not set, assuming ${libdir}])
AC_MSG_WARN([It is important that this value be properly set for correct behavior!])
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}")
[SYSTEM_INCLUDEDIR="$withval"], [
AC_MSG_WARN([--with-system-includedir is not set, assuming ${includedir}])
AC_MSG_WARN([It is important that this value be properly set for correct behavior!])
SYSTEM_INCLUDEDIR="${includedir}"
])
AC_SUBST([SYSTEM_INCLUDEDIR])
@ -63,3 +73,14 @@ AC_PROG_LN_S
AC_CONFIG_FILES([Makefile Kyuafile libpkgconf.pc tests/Kyuafile tests/test_env.sh])
AC_OUTPUT
cat << EOF
Build configuration:
Default personality search paths: ${PERSONALITY_PATH}
Default package search paths: ${PKG_DEFAULT_PATH}
Default include search paths: ${SYSTEM_INCLUDEDIR}
Default library search paths: ${SYSTEM_LIBDIR}
Incorrect paths may result in unexpected behavior from pkgconf.
EOF

View File

@ -7,6 +7,8 @@ Name: libpkgconf
Description: a library for accessing and manipulating development framework configuration
URL: https://gitea.treehouse.systems/ariadne/pkgconf
License: ISC
License.file: https://raw.githubusercontent.com/pkgconf/pkgconf/refs/heads/master/COPYING
Source: https://github.com/pkgconf/pkgconf
Version: @PACKAGE_VERSION@
CFlags: -I${includedir}/pkgconf
Libs: -L${libdir} -lpkgconf

View File

@ -59,7 +59,10 @@ pkgconf_argv_free(char **argv)
int
pkgconf_argv_split(const char *src, int *argc, char ***argv)
{
char *buf = malloc(strlen(src) + 1);
char *buf = calloc(1, strlen(src) + 1);
if (buf == NULL)
return -1;
const char *src_iter;
char *dst_iter;
int argc_count = 0;
@ -70,9 +73,13 @@ pkgconf_argv_split(const char *src, int *argc, char ***argv)
src_iter = src;
dst_iter = buf;
memset(buf, 0, strlen(src) + 1);
*argv = calloc(argv_size, sizeof (void *));
if (*argv == NULL)
{
free(buf);
return -1;
}
(*argv)[argc_count] = dst_iter;
while (*src_iter)

View File

@ -17,11 +17,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#include <libpkgconf/stdinc.h>
#include <libpkgconf/bsdstubs.h>
#include <libpkgconf/config.h>
@ -121,6 +117,28 @@ strndup(const char *src, size_t len)
}
#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)
{
@ -158,3 +176,15 @@ 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);
}

View File

@ -26,6 +26,8 @@ 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
}

View File

@ -32,6 +32,18 @@ target_allocation_size(size_t target_size)
return 4096 + (4096 * (target_size / 4096));
}
#if 0
static void
buffer_debug(pkgconf_buffer_t *buffer)
{
for (char *c = buffer->base; c <= buffer->end; c++) {
fprintf(stderr, "%02x ", (unsigned char) *c);
}
fprintf(stderr, "\n");
}
#endif
void
pkgconf_buffer_append(pkgconf_buffer_t *buffer, const char *text)
{
@ -48,7 +60,41 @@ pkgconf_buffer_append(pkgconf_buffer_t *buffer, const char *text)
pkgconf_strlcpy(newend, text, needed);
buffer->base = newbase;
buffer->end = newend + needed;
buffer->end = newend + (needed - 1);
*buffer->end = '\0';
}
void
pkgconf_buffer_append_vfmt(pkgconf_buffer_t *buffer, const char *fmt, va_list src_va)
{
va_list va;
char *buf;
size_t needed;
va_copy(va, src_va);
needed = vsnprintf(NULL, 0, fmt, va) + 1;
va_end(va);
buf = malloc(needed);
va_copy(va, src_va);
vsnprintf(buf, needed, fmt, va);
va_end(va);
pkgconf_buffer_append(buffer, buf);
free(buf);
}
void
pkgconf_buffer_append_fmt(pkgconf_buffer_t *buffer, const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
pkgconf_buffer_append_vfmt(buffer, fmt, va);
va_end(va);
}
void
@ -84,4 +130,117 @@ void
pkgconf_buffer_finalize(pkgconf_buffer_t *buffer)
{
free(buffer->base);
buffer->base = buffer->end = NULL;
}
void
pkgconf_buffer_fputs(pkgconf_buffer_t *buffer, FILE *out)
{
if (pkgconf_buffer_len(buffer) != 0)
fputs(pkgconf_buffer_str(buffer), out);
fputc('\n', out);
}
void
pkgconf_buffer_vjoin(pkgconf_buffer_t *buffer, char delim, va_list src_va)
{
va_list va;
const char *arg;
va_copy(va, src_va);
while ((arg = va_arg(va, const char *)) != NULL)
{
if (pkgconf_buffer_str(buffer) != NULL)
pkgconf_buffer_push_byte(buffer, delim);
pkgconf_buffer_append(buffer, arg);
}
va_end(va);
}
// NOTE: due to C's rules regarding promotion in variable args and permissible variables, delim must
// be an int here.
void
pkgconf_buffer_join(pkgconf_buffer_t *buffer, int delim, ...)
{
va_list va;
va_start(va, delim);
pkgconf_buffer_vjoin(buffer, (char)delim, va);
va_end(va);
}
bool
pkgconf_buffer_contains(const pkgconf_buffer_t *haystack, const pkgconf_buffer_t *needle)
{
const char *haystack_str = pkgconf_buffer_str_or_empty(haystack);
const char *needle_str = pkgconf_buffer_str_or_empty(needle);
return strstr(haystack_str, needle_str) != NULL;
}
bool
pkgconf_buffer_contains_byte(const pkgconf_buffer_t *haystack, char needle)
{
const char *haystack_str = pkgconf_buffer_str_or_empty(haystack);
return strchr(haystack_str, needle) != NULL;
}
bool
pkgconf_buffer_match(const pkgconf_buffer_t *haystack, const pkgconf_buffer_t *needle)
{
const char *haystack_str = pkgconf_buffer_str_or_empty(haystack);
const char *needle_str = pkgconf_buffer_str_or_empty(needle);
if (pkgconf_buffer_len(haystack) != pkgconf_buffer_len(needle))
return false;
return memcmp(haystack_str, needle_str, pkgconf_buffer_len(haystack)) == 0;
}
void
pkgconf_buffer_subst(pkgconf_buffer_t *dest, const pkgconf_buffer_t *src, const char *pattern, const char *value)
{
const char *iter = src->base;
size_t pattern_len = strlen(pattern);
if (!pkgconf_buffer_len(src))
return;
if (!pattern_len)
{
pkgconf_buffer_append(dest, pkgconf_buffer_str(src));
return;
}
while (iter < src->end)
{
if ((size_t)(src->end - iter) >= pattern_len && !memcmp(iter, pattern, pattern_len))
{
pkgconf_buffer_append(dest, value);
iter += pattern_len;
}
else
pkgconf_buffer_push_byte(dest, *iter++);
}
}
void
pkgconf_buffer_escape(pkgconf_buffer_t *dest, const pkgconf_buffer_t *src, const pkgconf_span_t *spans, size_t nspans)
{
const char *p = pkgconf_buffer_str(src);
if (!pkgconf_buffer_len(src))
return;
for (; *p; p++)
{
if (pkgconf_span_contains((unsigned char) *p, spans, nspans))
pkgconf_buffer_push_byte(dest, '\\');
pkgconf_buffer_push_byte(dest, *p);
}
}

View File

@ -60,17 +60,22 @@ trace_path_list(const pkgconf_client_t *client, const char *desc, pkgconf_list_t
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);
pkgconf_path_build_from_environ(client, "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;
if (getenv("PKG_CONFIG_LIBDIR") != NULL)
#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 (pkgconf_client_getenv(client, "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);
(void) pkgconf_path_build_from_environ(client, "PKG_CONFIG_LIBDIR", NULL, &dir_list, true);
prepend_list = &dir_list;
}
@ -82,7 +87,7 @@ pkgconf_client_dir_list_build(pkgconf_client_t *client, const pkgconf_cross_pers
/*
* !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)
* .. 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, void *client_data, pkgconf_environ_lookup_handler_func_t environ_lookup_handler)
*
* Initialise a pkgconf client object.
*
@ -90,11 +95,16 @@ pkgconf_client_dir_list_build(pkgconf_client_t *client, const pkgconf_cross_pers
* :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
* :param void* client_data: user data associated with the client
* :param pkgconf_environ_lookup_handler_func_t environ_lookup_handler: the lookup handler to use for environment variables
* :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)
pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality, void *client_data, pkgconf_environ_lookup_handler_func_t environ_lookup_handler)
{
client->personality = personality;
client->client_data = client_data;
client->environ_lookup_handler = environ_lookup_handler;
client->error_handler_data = error_handler_data;
client->error_handler = error_handler;
client->auditf = NULL;
@ -106,6 +116,9 @@ pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error
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);
@ -113,36 +126,38 @@ pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error
pkgconf_client_set_buildroot_dir(client, NULL);
pkgconf_client_set_prefix_varname(client, NULL);
if(getenv("PKG_CONFIG_SYSTEM_LIBRARY_PATH") == NULL)
if(pkgconf_client_getenv(client, "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);
pkgconf_path_build_from_environ(client, "PKG_CONFIG_SYSTEM_LIBRARY_PATH", NULL, &client->filter_libdirs, false);
if(getenv("PKG_CONFIG_SYSTEM_INCLUDE_PATH") == NULL)
if(pkgconf_client_getenv(client, "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);
pkgconf_path_build_from_environ(client, "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);
pkgconf_path_build_from_environ(client, "BELIBRARIES", NULL, &client->filter_libdirs, false);
#else
pkgconf_path_build_from_environ("LIBRARY_PATH", NULL, &client->filter_libdirs, false);
pkgconf_path_build_from_environ(client, "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);
pkgconf_path_build_from_environ(client, "CPATH", NULL, &client->filter_includedirs, false);
pkgconf_path_build_from_environ(client, "C_INCLUDE_PATH", NULL, &client->filter_includedirs, false);
pkgconf_path_build_from_environ(client, "CPLUS_INCLUDE_PATH", NULL, &client->filter_includedirs, false);
pkgconf_path_build_from_environ(client, "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);
pkgconf_path_build_from_environ(client, "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);
client->output = pkgconf_output_default();
}
/*
@ -155,17 +170,34 @@ pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error
* :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
* :param void* client_data: user data associated with the client
* :param pkgconf_environ_lookup_handler_func_t environ_lookup_handler: the lookup handler to use for environment variables
* :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_new(pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality, void *client_data, pkgconf_environ_lookup_handler_func_t environ_lookup_handler)
{
pkgconf_client_t *out = calloc(1, sizeof(pkgconf_client_t));
pkgconf_client_init(out, error_handler, error_handler_data, personality);
if (out == NULL)
return NULL;
pkgconf_client_init(out, error_handler, error_handler_data, personality, client_data, environ_lookup_handler);
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, tn, n)
{
pkgconf_pkg_t *pkg = n->data;
pkgconf_pkg_unref(client, pkg);
}
}
/*
* !doc
*
@ -181,6 +213,8 @@ 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);
@ -196,6 +230,8 @@ pkgconf_client_deinit(pkgconf_client_t *client)
pkgconf_tuple_free_global(client);
pkgconf_path_free(&client->dir_list);
pkgconf_cache_free(client);
memset(client, '\0', sizeof(*client));
}
/*
@ -256,7 +292,7 @@ pkgconf_client_set_sysroot_dir(pkgconf_client_t *client, const char *sysroot_dir
PKGCONF_TRACE(client, "set sysroot_dir to: %s", client->sysroot_dir != NULL ? client->sysroot_dir : "<default>");
pkgconf_tuple_add_global(client, "pc_sysrootdir", client->sysroot_dir != NULL ? client->sysroot_dir : "/");
pkgconf_tuple_add_global(client, "pc_sysrootdir", client->sysroot_dir != NULL ? client->sysroot_dir : "");
}
/*
@ -318,14 +354,32 @@ pkgconf_client_set_buildroot_dir(pkgconf_client_t *client, const char *buildroot
bool
pkgconf_error(const pkgconf_client_t *client, const char *format, ...)
{
char errbuf[PKGCONF_BUFSIZE];
char *errbuf;
ssize_t msgsize = 0;
bool ret;
va_list va;
va_start(va, format);
vsnprintf(errbuf, sizeof errbuf, format, va);
msgsize = vsnprintf(NULL, 0, format, va);
va_end(va);
return client->error_handler(errbuf, client, client->error_handler_data);
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;
}
/*
@ -343,14 +397,32 @@ pkgconf_error(const pkgconf_client_t *client, const char *format, ...)
bool
pkgconf_warn(const pkgconf_client_t *client, const char *format, ...)
{
char errbuf[PKGCONF_BUFSIZE];
char *errbuf;
ssize_t msgsize = 0;
bool ret;
va_list va;
va_start(va, format);
vsnprintf(errbuf, sizeof errbuf, format, va);
msgsize = vsnprintf(NULL, 0, format, va);
va_end(va);
return client->warn_handler(errbuf, client, client->warn_handler_data);
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;
}
/*
@ -371,22 +443,56 @@ pkgconf_warn(const pkgconf_client_t *client, const char *format, ...)
bool
pkgconf_trace(const pkgconf_client_t *client, const char *filename, size_t lineno, const char *funcname, const char *format, ...)
{
char errbuf[PKGCONF_BUFSIZE];
size_t len;
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;
len = snprintf(errbuf, sizeof errbuf, "%s:" SIZE_FMT_SPECIFIER " [%s]: ", filename, lineno, funcname);
snprintf(prefix, sizeof prefix, "%s:" SIZE_FMT_SPECIFIER " [%s]:", filename, lineno, funcname);
va_start(va, format);
vsnprintf(errbuf + len, sizeof(errbuf) - len, format, va);
errlen = vsnprintf(NULL, 0, format, va);
va_end(va);
pkgconf_strlcat(errbuf, "\n", sizeof errbuf);
if (errlen < 0)
return false;
return client->trace_handler(errbuf, client, client->trace_handler_data);
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)
{
free(errbuf);
return false;
}
finallen++;
finalbuf = calloc(1, finallen);
if (finalbuf == NULL)
{
free(errbuf);
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;
}
/*
@ -412,6 +518,14 @@ pkgconf_default_error_handler(const char *msg, const pkgconf_client_t *client, v
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
*
@ -571,6 +685,45 @@ pkgconf_client_set_error_handler(pkgconf_client_t *client, pkgconf_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
@ -613,3 +766,131 @@ pkgconf_client_set_trace_handler(pkgconf_client_t *client, pkgconf_error_handler
}
}
#endif
/*
* !doc
*
* .. c:function:: bool pkgconf_client_preload_one(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
*
* Adds a package to the preloaded packages set.
*
* :param pkgconf_client_t* client: The client object for preloading.
* :param pkgconf_pkg_t* pkg: The package to preload.
* :return: true on success, false on error
* :rtype: bool
*/
bool
pkgconf_client_preload_one(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
{
PKGCONF_TRACE(client, "preloading pkg %s@%p", pkg->id, pkg);
pkg->flags |= PKGCONF_PKG_PROPF_PRELOADED;
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_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;
return pkgconf_client_preload_one(client, pkg);
}
/*
* !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 = true;
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;
}
/*
* !doc
*
* .. c:function:: void pkgconf_client_set_output(pkgconf_client_t *client, pkgconf_output_t *output)
*
* Sets the client's output object. This is mainly a convenience function for clients
* to use.
*
* :param pkgconf_client_t* client: The client object to set the output object for.
* :param pkgconf_output_t* output: The output object to use.
* :return: nothing
*/
void
pkgconf_client_set_output(pkgconf_client_t *client, pkgconf_output_t *output)
{
client->output = output;
}
/*
* !doc
*
* .. c:function:: const char *pkgconf_client_getenv(const pkgconf_client_t *client, const char *key)
*
* Looks up an environmental variable which may be mocked, otherwise fetches
* from the main environment.
*
* :param pkgconf_client_t* client: yhe client object to use for looking up environmental variables.
* :param char* key: the environmental variable to look up.
* :return: the environmental variable contents else NULL
* :rtype: const char*
*/
const char *
pkgconf_client_getenv(const pkgconf_client_t *client, const char *key)
{
if (client != NULL && client->environ_lookup_handler != NULL)
return client->environ_lookup_handler(client, key);
return getenv(key);
}

View File

@ -24,6 +24,12 @@
/* 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

View File

@ -131,6 +131,9 @@ pkgconf_dependency_addraw(pkgconf_client_t *client, pkgconf_list_t *list, const
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)
@ -208,6 +211,9 @@ pkgconf_dependency_free_one(pkgconf_dependency_t *dep)
if (dep->version != NULL)
free(dep->version);
if (dep->why != NULL)
free(dep->why);
free(dep);
}
@ -304,19 +310,29 @@ pkgconf_dependency_parse_str(pkgconf_client_t *client, pkgconf_list_t *deplist_h
parse_state_t state = OUTSIDE_MODULE;
pkgconf_pkg_comparator_t compare = PKGCONF_CMP_ANY;
char cmpname[PKGCONF_ITEM_SIZE];
char buf[PKGCONF_BUFSIZE];
size_t package_sz = 0, version_sz = 0;
char *start = buf;
char *ptr = buf;
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);
pkgconf_strlcpy(buf, depends, sizeof buf);
pkgconf_strlcat(buf, " ", sizeof buf);
buf_sz = (strlen(depends) * 2) + 1;
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)
{
@ -427,6 +443,8 @@ pkgconf_dependency_parse_str(pkgconf_client_t *client, pkgconf_list_t *deplist_h
ptr++;
}
free(buf);
}
/*
@ -471,6 +489,9 @@ pkgconf_dependency_copy(pkgconf_client_t *client, const pkgconf_dependency_t *de
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)

View File

@ -32,6 +32,31 @@ struct pkgconf_fragment_check {
size_t len;
};
static inline bool
pkgconf_fragment_is_greedy(const char *string)
{
static const struct pkgconf_fragment_check check_fragments[] = {
{"-F", 2},
{"-I", 2},
{"-L", 2},
{"-D", 2},
{"-l", 2},
};
if (*string != '-')
return false;
for (size_t i = 0; i < PKGCONF_ARRAY_SIZE(check_fragments); i++)
if (!strncmp(string, check_fragments[i].token, check_fragments[i].len))
{
/* if it is the bare flag, then we want the next token to be the data */
if (!*(string + check_fragments[i].len))
return true;
}
return false;
}
static inline bool
pkgconf_fragment_is_unmergeable(const char *string)
{
@ -81,6 +106,24 @@ pkgconf_fragment_should_munge(const char *string, const char *sysroot_dir)
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)
{
@ -199,16 +242,25 @@ pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const
pkgconf_fragment_is_unmergeable(parent->data) &&
!(parent->flags & PKGCONF_PKG_FRAGF_TERMINATED))
{
target = &parent->children;
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 = calloc(1, sizeof(pkgconf_fragment_t));
frag->type = *(string + 1);
frag->data = pkgconf_fragment_copy_munged(client, string + 2, flags);
@ -216,8 +268,6 @@ pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const
}
else
{
frag = calloc(1, sizeof(pkgconf_fragment_t));
frag->type = 0;
frag->data = pkgconf_fragment_copy_munged(client, string, flags);
@ -444,49 +494,27 @@ pkgconf_fragment_filter(const pkgconf_client_t *client, pkgconf_list_t *dest, pk
}
}
static inline char *
fragment_quote(const pkgconf_fragment_t *frag)
static void
fragment_quote(pkgconf_buffer_t *out, 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;
return;
out = dst = calloc(1, outlen);
const pkgconf_buffer_t *src = PKGCONF_BUFFER_FROM_STR(frag->data);
const pkgconf_span_t quote_spans[] = {
{ 0x00, 0x1f },
{ (unsigned char)' ', (unsigned char)'#' },
{ (unsigned char)'%', (unsigned char)'\'' },
{ (unsigned char)'*', (unsigned char)'*' },
{ (unsigned char)';', (unsigned char)'<' },
{ (unsigned char)'>', (unsigned char)'?' },
{ (unsigned char)'[', (unsigned char)']' },
{ (unsigned char)'`', (unsigned char)'`' },
{ (unsigned char)'{', (unsigned char)'}' },
{ 0x7f, 0xff },
};
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;
out = realloc(out, outlen);
dst = out + offset;
}
}
*dst = 0;
return out;
pkgconf_buffer_escape(out, src, quote_spans, PKGCONF_ARRAY_SIZE(quote_spans));
}
static inline size_t
@ -501,9 +529,10 @@ pkgconf_fragment_len(const pkgconf_fragment_t *frag)
{
pkgconf_node_t *iter;
char *quoted = fragment_quote(frag);
len += strlen(quoted);
free(quoted);
pkgconf_buffer_t quoted = PKGCONF_BUFFER_INITIALIZER;
fragment_quote(&quoted, frag);
len += pkgconf_buffer_len(&quoted);
pkgconf_buffer_finalize(&quoted);
PKGCONF_FOREACH_LIST_ENTRY(frag->children.head, iter)
{
@ -533,61 +562,44 @@ fragment_render_len(const pkgconf_list_t *list, bool escape)
}
static inline size_t
fragment_render_item(const pkgconf_fragment_t *frag, char *bptr, size_t bufremain)
fragment_render_item(const pkgconf_fragment_t *frag, pkgconf_buffer_t *buf, char delim)
{
char *base = bptr;
char *quoted = fragment_quote(frag);
const pkgconf_node_t *iter;
pkgconf_buffer_t quoted = PKGCONF_BUFFER_INITIALIZER;
if (strlen(quoted) > bufremain)
{
free(quoted);
return 0;
}
fragment_quote(&quoted, frag);
if (frag->type)
{
*bptr++ = '-';
*bptr++ = frag->type;
}
pkgconf_buffer_append_fmt(buf, "-%c", frag->type);
if (quoted != NULL)
{
bptr += pkgconf_strlcpy(bptr, quoted, bufremain - (bptr - base));
free(quoted);
}
pkgconf_buffer_append(buf, pkgconf_buffer_str_or_empty(&quoted));
pkgconf_buffer_finalize(&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));
pkgconf_buffer_push_byte(buf, delim);
fragment_render_item(child_frag, buf, delim);
}
return bptr - base;
return pkgconf_buffer_len(buf);
}
static void
fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape)
fragment_render_buf(const pkgconf_list_t *list, pkgconf_buffer_t *buf, bool escape, char delim)
{
(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;
fragment_render_item(frag, buf, delim);
if (node->next != NULL)
*bptr++ = ' ';
pkgconf_buffer_push_byte(buf, delim);
}
}
@ -621,7 +633,7 @@ pkgconf_fragment_render_len(const pkgconf_list_t *list, bool escape, const pkgco
/*
* !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)
* .. 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, char delim)
*
* Renders a `fragment list` into a buffer.
*
@ -630,41 +642,16 @@ pkgconf_fragment_render_len(const pkgconf_list_t *list, bool escape, const pkgco
* :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``.
* :param char delim: The delimiter to use between fragments.
* :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)
pkgconf_fragment_render_buf(const pkgconf_list_t *list, pkgconf_buffer_t *buf, bool escape, const pkgconf_fragment_render_ops_t *ops, char delim)
{
(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;
ops->render_buf(list, buf, true, delim);
}
/*
@ -745,8 +732,6 @@ pkgconf_fragment_parse(const pkgconf_client_t *client, pkgconf_list_t *list, pkg
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);
@ -755,7 +740,24 @@ pkgconf_fragment_parse(const pkgconf_client_t *client, pkgconf_list_t *list, pkg
return false;
}
pkgconf_fragment_add(client, list, argv[i], flags);
bool greedy = pkgconf_fragment_is_greedy(argv[i]);
PKGCONF_TRACE(client, "processing [%s] greedy=%d", argv[i], greedy);
if (greedy && i + 1 < argc)
{
pkgconf_buffer_t greedybuf = PKGCONF_BUFFER_INITIALIZER;
pkgconf_buffer_append(&greedybuf, argv[i]);
pkgconf_buffer_append(&greedybuf, argv[i + 1]);
pkgconf_fragment_add(client, list, pkgconf_buffer_str(&greedybuf), flags);
pkgconf_buffer_finalize(&greedybuf);
/* skip over next arg as we combined them */
i++;
}
else
pkgconf_fragment_add(client, list, argv[i], flags);
}
pkgconf_argv_free(argv);

View File

@ -22,6 +22,7 @@
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <libpkgconf/libpkgconf-api.h>
#include <libpkgconf/iter.h>
#include <libpkgconf/bsdstubs.h>
@ -30,23 +31,6 @@
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 {
@ -64,11 +48,14 @@ typedef enum {
typedef struct pkgconf_pkg_ pkgconf_pkg_t;
typedef struct pkgconf_dependency_ pkgconf_dependency_t;
typedef struct pkgconf_tuple_ pkgconf_tuple_t;
typedef struct pkgconf_buffer_ pkgconf_buffer_t;
typedef struct pkgconf_span_ pkgconf_span_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;
typedef struct pkgconf_output_ pkgconf_output_t;
#define PKGCONF_ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
@ -81,8 +68,8 @@ typedef struct pkgconf_queue_ pkgconf_queue_t;
#define PKGCONF_FOREACH_LIST_ENTRY_REVERSE(tail, value) \
for ((value) = (tail); (value) != NULL; (value) = (value)->prev)
#define LIBPKGCONF_VERSION 20402
#define LIBPKGCONF_VERSION_STR "2.4.2"
#define LIBPKGCONF_VERSION 20501
#define LIBPKGCONF_VERSION_STR "2.5.1"
struct pkgconf_queue_ {
pkgconf_node_t iter;
@ -116,6 +103,8 @@ struct pkgconf_dependency_ {
int refcount;
pkgconf_client_t *owner;
char *why;
};
struct pkgconf_tuple_ {
@ -127,6 +116,11 @@ struct pkgconf_tuple_ {
unsigned int flags;
};
struct pkgconf_buffer_ {
char *base;
char *end;
};
#define PKGCONF_PKG_TUPLEF_OVERRIDE 0x1
struct pkgconf_path_ {
@ -146,6 +140,7 @@ struct pkgconf_path_ {
#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;
@ -159,6 +154,8 @@ struct pkgconf_pkg_ {
char *license;
char *maintainer;
char *copyright;
char *source;
char *license_file;
char *why;
pkgconf_list_t libs;
@ -185,12 +182,16 @@ struct pkgconf_pkg_ {
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);
typedef const char *(*pkgconf_environ_lookup_handler_func_t)(const pkgconf_client_t *client, const char *variable);
struct pkgconf_client_ {
pkgconf_list_t dir_list;
@ -200,6 +201,7 @@ struct pkgconf_client_ {
pkgconf_list_t global_vars;
void *client_data;
void *error_handler_data;
void *warn_handler_data;
void *trace_handler_data;
@ -208,6 +210,8 @@ struct pkgconf_client_ {
pkgconf_error_handler_func_t warn_handler;
pkgconf_error_handler_func_t trace_handler;
pkgconf_environ_lookup_handler_func_t environ_lookup_handler;
FILE *auditf;
char *sysroot_dir;
@ -224,10 +228,18 @@ struct pkgconf_client_ {
pkgconf_pkg_t **cache_table;
size_t cache_count;
pkgconf_unveil_handler_func_t unveil_handler;
pkgconf_list_t preloaded_pkgs;
pkgconf_output_t *output;
const pkgconf_cross_personality_t *personality;
};
struct pkgconf_cross_personality_ {
const char *name;
char *name;
pkgconf_list_t dir_list;
@ -241,8 +253,8 @@ struct pkgconf_cross_personality_ {
};
/* 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_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality, void *client_data, pkgconf_environ_lookup_handler_func_t environ_lookup_handler);
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, void *client_data, pkgconf_environ_lookup_handler_func_t environ_lookup_handler);
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);
@ -259,7 +271,14 @@ PKGCONF_API pkgconf_error_handler_func_t pkgconf_client_get_error_handler(const
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_one(pkgconf_client_t *client, pkgconf_pkg_t *pkg);
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);
PKGCONF_API void pkgconf_client_set_output(pkgconf_client_t *client, pkgconf_output_t *output);
PKGCONF_API const char *pkgconf_client_getenv(const pkgconf_client_t *client, const char *key);
/* personality.c */
PKGCONF_API pkgconf_cross_personality_t *pkgconf_cross_personality_default(void);
@ -287,6 +306,7 @@ PKGCONF_API void pkgconf_cross_personality_deinit(pkgconf_cross_personality_t *p
#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_PKGF_REQUIRE_INTERNAL 0x20000
#define PKGCONF_PKG_DEPF_INTERNAL 0x1
#define PKGCONF_PKG_DEPF_PRIVATE 0x2
@ -298,21 +318,23 @@ PKGCONF_API void pkgconf_cross_personality_deinit(pkgconf_cross_personality_t *p
#define PKGCONF_PKG_ERRF_PACKAGE_CONFLICT 0x4
#define PKGCONF_PKG_ERRF_DEPGRAPH_BREAK 0x8
#if defined(__GNUC__) || defined(__INTEL_COMPILER)
#define PRINTFLIKE(fmtarg, firstvararg) \
__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
#define DEPRECATED \
__attribute__((deprecated))
#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)
#define DEPRECATED
#endif /* defined(__INTEL_COMPILER) || defined(__GNUC__) */
# define PRINTFLIKE(fmtarg, firstvararg)
#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);
#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
/* pkg.c */
PKGCONF_API bool pkgconf_error(const pkgconf_client_t *client, const char *format, ...) PRINTFLIKE(2, 3);
@ -339,19 +361,19 @@ 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_walk_conflicts_list(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *deplist);
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_file(pkgconf_client_t *client, const char *path, FILE *f, unsigned int flags);
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);
@ -369,7 +391,7 @@ 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);
void (*render_buf)(const pkgconf_list_t *list, pkgconf_buffer_t *buf, bool escape, char delim);
} pkgconf_fragment_render_ops_t;
typedef bool (*pkgconf_fragment_filter_func_t)(const pkgconf_client_t *client, const pkgconf_fragment_t *frag, void *data);
@ -382,8 +404,7 @@ PKGCONF_API void pkgconf_fragment_delete(pkgconf_list_t *list, pkgconf_fragment_
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 void pkgconf_fragment_render_buf(const pkgconf_list_t *list, pkgconf_buffer_t *buf, bool escape, const pkgconf_fragment_render_ops_t *ops, char delim);
PKGCONF_API bool pkgconf_fragment_has_system_dir(const pkgconf_client_t *client, const pkgconf_fragment_t *frag);
/* tuple.c */
@ -399,6 +420,7 @@ PKGCONF_API void pkgconf_tuple_define_global(pkgconf_client_t *client, const cha
/* queue.c */
PKGCONF_API void pkgconf_queue_push(pkgconf_list_t *list, const char *package);
PKGCONF_API void pkgconf_queue_push_dependency(pkgconf_list_t *list, const pkgconf_dependency_t *dep);
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);
@ -421,26 +443,52 @@ PKGCONF_API void pkgconf_audit_log_dependency(pkgconf_client_t *client, const pk
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);
PKGCONF_API size_t pkgconf_path_build_from_environ(const pkgconf_client_t *client, 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;
struct pkgconf_span_ {
unsigned char lo;
unsigned char hi; /* inclusive */
};
static inline bool pkgconf_span_contains(unsigned char c, const pkgconf_span_t *spans, size_t nspans) {
for (size_t i = 0; i < nspans; i++)
if (c >= spans[i].lo && c <= spans[i].hi)
return true;
return false;
}
PKGCONF_API void pkgconf_buffer_append(pkgconf_buffer_t *buffer, const char *text);
PKGCONF_API void pkgconf_buffer_append_fmt(pkgconf_buffer_t *buffer, const char *fmt, ...) PRINTFLIKE(2, 3);
PKGCONF_API void pkgconf_buffer_append_vfmt(pkgconf_buffer_t *buffer, const char *fmt, va_list va);
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);
PKGCONF_API void pkgconf_buffer_fputs(pkgconf_buffer_t *buffer, FILE *out);
PKGCONF_API void pkgconf_buffer_vjoin(pkgconf_buffer_t *buffer, char delim, va_list va);
PKGCONF_API void pkgconf_buffer_join(pkgconf_buffer_t *buffer, int delim, ...);
PKGCONF_API bool pkgconf_buffer_contains(const pkgconf_buffer_t *haystack, const pkgconf_buffer_t *needle);
PKGCONF_API bool pkgconf_buffer_contains_byte(const pkgconf_buffer_t *haystack, char needle);
PKGCONF_API bool pkgconf_buffer_match(const pkgconf_buffer_t *haystack, const pkgconf_buffer_t *needle);
PKGCONF_API void pkgconf_buffer_subst(pkgconf_buffer_t *dest, const pkgconf_buffer_t *src, const char *pattern, const char *value);
PKGCONF_API void pkgconf_buffer_escape(pkgconf_buffer_t *dest, const pkgconf_buffer_t *src, const pkgconf_span_t *spans, size_t nspans);
static inline const char *pkgconf_buffer_str(const pkgconf_buffer_t *buffer) {
return buffer->base;
}
static inline const char *pkgconf_buffer_str_or_empty(const pkgconf_buffer_t *buffer) {
return buffer->base != NULL ? buffer->base : "";
}
static inline size_t pkgconf_buffer_len(const pkgconf_buffer_t *buffer) {
return (size_t)(ptrdiff_t)(buffer->end - buffer->base);
}
@ -453,15 +501,56 @@ static inline char pkgconf_buffer_lastc(const pkgconf_buffer_t *buffer) {
}
#define PKGCONF_BUFFER_INITIALIZER { NULL, NULL }
#define PKGCONF_BUFFER_FROM_STR(str) &(const pkgconf_buffer_t){ .base = str, .end = str + strlen(str) }
static inline void pkgconf_buffer_reset(pkgconf_buffer_t *buffer) {
pkgconf_buffer_finalize(buffer);
buffer->base = buffer->end = NULL;
}
static inline char *pkgconf_buffer_freeze(pkgconf_buffer_t *buffer) {
if (buffer->base == NULL)
return NULL;
char *out = strdup(pkgconf_buffer_str(buffer));
pkgconf_buffer_reset(buffer);
return out;
}
static inline void pkgconf_buffer_copy(pkgconf_buffer_t *buffer, pkgconf_buffer_t *newptr)
{
pkgconf_buffer_reset(newptr);
pkgconf_buffer_append(newptr, pkgconf_buffer_str(buffer));
}
/* fileio.c */
PKGCONF_API bool pkgconf_fgetline(pkgconf_buffer_t *buffer, FILE *stream);
/* parser.c */
typedef void (*pkgconf_parser_operand_func_t)(void *data, const char *warnprefix, const char *key, const char *value);
typedef void (*pkgconf_parser_warn_func_t)(void *data, const char *fmt, ...);
PKGCONF_API void pkgconf_parser_parse_buffer(void *data, const pkgconf_parser_operand_func_t *ops, const pkgconf_parser_warn_func_t warnfunc, pkgconf_buffer_t *buffer, const char *warnprefix);
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);
/* output.c */
typedef enum {
PKGCONF_OUTPUT_STDOUT,
PKGCONF_OUTPUT_STDERR,
} pkgconf_output_stream_t;
struct pkgconf_output_ {
void *privdata;
bool (*write)(pkgconf_output_t *output, pkgconf_output_stream_t stream, const pkgconf_buffer_t *buffer);
};
PKGCONF_API bool pkgconf_output_putbuf(pkgconf_output_t *output, pkgconf_output_stream_t stream, const pkgconf_buffer_t *buffer, bool newline);
PKGCONF_API bool pkgconf_output_puts(pkgconf_output_t *output, pkgconf_output_stream_t stream, const char *str);
PKGCONF_API bool pkgconf_output_fmt(pkgconf_output_t *output, pkgconf_output_stream_t stream, const char *fmt, ...);
PKGCONF_API bool pkgconf_output_vfmt(pkgconf_output_t *output, pkgconf_output_stream_t stream, const char *fmt, va_list va);
PKGCONF_API pkgconf_output_t *pkgconf_output_default(void);
#ifdef __cplusplus
}
#endif

110
libpkgconf/output.c Normal file
View File

@ -0,0 +1,110 @@
/*
* output.c
* I/O abstraction layer
*
* 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.
*/
#include <libpkgconf/stdinc.h>
#include <libpkgconf/libpkgconf.h>
bool
pkgconf_output_putbuf(pkgconf_output_t *output, pkgconf_output_stream_t stream, const pkgconf_buffer_t *buffer, bool newline)
{
bool ret;
pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER;
if (pkgconf_buffer_len(buffer) != 0)
pkgconf_buffer_append(&buf, pkgconf_buffer_str(buffer));
if (newline)
pkgconf_buffer_push_byte(&buf, '\n');
ret = output->write(output, stream, &buf);
pkgconf_buffer_finalize(&buf);
return ret;
}
bool
pkgconf_output_puts(pkgconf_output_t *output, pkgconf_output_stream_t stream, const char *str)
{
bool ret;
pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER;
pkgconf_buffer_append(&buf, str);
pkgconf_buffer_push_byte(&buf, '\n');
ret = output->write(output, stream, &buf);
pkgconf_buffer_finalize(&buf);
return ret;
}
bool
pkgconf_output_fmt(pkgconf_output_t *output, pkgconf_output_stream_t stream, const char *fmt, ...)
{
bool ret;
va_list va;
va_start(va, fmt);
ret = pkgconf_output_vfmt(output, stream, fmt, va);
va_end(va);
return ret;
}
bool
pkgconf_output_vfmt(pkgconf_output_t *output, pkgconf_output_stream_t stream, const char *fmt, va_list src_va)
{
va_list va;
bool ret;
pkgconf_buffer_t buf = PKGCONF_BUFFER_INITIALIZER;
va_copy(va, src_va);
pkgconf_buffer_append_vfmt(&buf, fmt, va);
va_end(va);
ret = output->write(output, stream, &buf);
pkgconf_buffer_finalize(&buf);
return ret;
}
static bool
pkgconf_output_stdio_write(pkgconf_output_t *output, pkgconf_output_stream_t stream, const pkgconf_buffer_t *buffer)
{
(void) output;
FILE *target = stream == PKGCONF_OUTPUT_STDERR ? stderr : stdout;
if (buffer != NULL)
{
const char *str = pkgconf_buffer_str(buffer);
size_t size = pkgconf_buffer_len(buffer);
if (size > 0 && !fwrite(str, size, 1, target))
return false;
}
fflush(target);
return true;
}
static pkgconf_output_t pkgconf_default_output = {
.privdata = NULL,
.write = pkgconf_output_stdio_write,
};
pkgconf_output_t *
pkgconf_output_default(void)
{
return &pkgconf_default_output;
}

View File

@ -2,7 +2,7 @@
* parser.c
* rfc822 message parser
*
* Copyright (c) 2018 pkgconf authors (see AUTHORS).
* Copyright (c) 2018, 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
@ -17,19 +17,66 @@
#include <libpkgconf/stdinc.h>
#include <libpkgconf/libpkgconf.h>
/*
* !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_buffer(void *data, const pkgconf_parser_operand_func_t *ops, const pkgconf_parser_warn_func_t warnfunc, pkgconf_buffer_t *buffer, const char *warnprefix)
{
char op, *p, *key, *value;
p = buffer->base;
if (p == NULL)
return;
while (*p && isspace((unsigned char)*p))
p++;
if (*p && p != buffer->base)
{
warnfunc(data, "%s: warning: whitespace encountered while parsing key section\n",
warnprefix);
}
key = p;
while (*p && (isalpha((unsigned char)*p) || isdigit((unsigned char)*p) || *p == '_' || *p == '.'))
p++;
if (!isalpha((unsigned char)*key) &&
!isdigit((unsigned char)*p))
return;
while (*p && isspace((unsigned char)*p))
{
warnfunc(data, "%s: warning: whitespace encountered while parsing key section\n",
warnprefix);
/* 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 (op == '=')
{
warnfunc(data, "%s: warning: trailing whitespace encountered while parsing value section\n",
warnprefix);
}
*p = '\0';
p--;
}
if (ops[(unsigned char) op])
ops[(unsigned char) op](data, warnprefix, key, value);
}
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)
{
@ -39,78 +86,15 @@ pkgconf_parser_parse(FILE *f, void *data, const pkgconf_parser_operand_func_t *o
while (continue_reading)
{
char op, *p, *key, *value;
bool warned_key_whitespace = false, warned_value_whitespace = false;
char warnprefix[PKGCONF_ITEM_SIZE];
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);
snprintf(warnprefix, sizeof warnprefix, "%s:" SIZE_FMT_SPECIFIER, filename, lineno);
pkgconf_parser_parse_buffer(data, ops, warnfunc, &readbuf, warnprefix);
pkgconf_buffer_reset(&readbuf);
}
fclose(f);
pkgconf_buffer_finalize(&readbuf);
}

View File

@ -91,6 +91,9 @@ prepare_path_node(const char *text, pkgconf_list_t *dirlist, bool filter)
#endif
node = calloc(1, sizeof(pkgconf_path_t));
if (node == NULL)
return NULL;
node->path = strdup(path);
#ifdef PKGCONF_CACHE_INODES
@ -189,6 +192,7 @@ pkgconf_path_split(const char *text, pkgconf_list_t *dirlist, bool filter)
* 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 pkgconf_client_t* client: The client to use for environmental variable lookup (can be NULL).
* :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.
@ -197,11 +201,11 @@ pkgconf_path_split(const char *text, pkgconf_list_t *dirlist, bool filter)
* :rtype: size_t
*/
size_t
pkgconf_path_build_from_environ(const char *envvarname, const char *fallback, pkgconf_list_t *dirlist, bool filter)
pkgconf_path_build_from_environ(const pkgconf_client_t *client, const char *envvarname, const char *fallback, pkgconf_list_t *dirlist, bool filter)
{
const char *data;
data = getenv(envvarname);
data = pkgconf_client_getenv(client, envvarname);
if (data != NULL)
return pkgconf_path_split(data, dirlist, filter);
@ -267,6 +271,9 @@ pkgconf_path_copy_list(pkgconf_list_t *dst, const pkgconf_list_t *src)
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
@ -278,6 +285,41 @@ pkgconf_path_copy_list(pkgconf_list_t *dst, const pkgconf_list_t *src)
}
}
/*
* !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
*
@ -363,3 +405,53 @@ pkgconf_path_relocate(char *buf, size_t buflen)
return true;
}
#ifdef _WIN32
#define PKG_CONFIG_REG_KEY "Software\\pkgconfig\\PKG_CONFIG_PATH"
/*
* !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

View File

@ -24,10 +24,6 @@
* =========================
*/
#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
@ -37,15 +33,6 @@ static unsigned default_personality_init = 0;
static pkgconf_cross_personality_t default_personality = {
.name = "default",
#ifdef _WIN32
/* PE/COFF uses a different linking model than ELF and Mach-O, where
* all transitive dependency references must be processed by the linker
* when linking the final executable image, even if those dependencies
* are pulled in as DLLs.
* This translates to always using --static on Windows targets.
*/
.want_default_static = true,
#endif
};
static inline void
@ -92,7 +79,7 @@ build_default_search_path(pkgconf_list_t* dirlist)
paths = NULL;
}
#else
pkgconf_path_split(PKG_DEFAULT_PATH, dirlist, true);
pkgconf_path_split(PKG_DEFAULT_PATH, dirlist, false);
#endif
}
@ -130,7 +117,8 @@ pkgconf_cross_personality_default(void)
*
* .. c:function:: void pkgconf_cross_personality_deinit(pkgconf_cross_personality_t *)
*
* Decrements the count of default cross personality instances.
* Destroys a cross personality object and/or decreases the reference count on the
* default cross personality object.
*
* Not thread safe.
*
@ -139,11 +127,28 @@ pkgconf_cross_personality_default(void)
void
pkgconf_cross_personality_deinit(pkgconf_cross_personality_t *personality)
{
if (--default_personality_init == 0) {
pkgconf_path_free(&personality->dir_list);
pkgconf_path_free(&personality->filter_libdirs);
pkgconf_path_free(&personality->filter_includedirs);
}
/* 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
@ -159,7 +164,7 @@ valid_triplet(const char *triplet)
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 void (*personality_keyword_func_t)(pkgconf_cross_personality_t *p, const char *keyword, const char *warnprefix, const ptrdiff_t offset, char *value);
typedef struct {
const char *keyword;
const personality_keyword_func_t func;
@ -167,30 +172,30 @@ typedef struct {
} 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)
personality_bool_func(pkgconf_cross_personality_t *p, const char *keyword, const char *warnprefix, const ptrdiff_t offset, char *value)
{
(void) keyword;
(void) lineno;
(void) warnprefix;
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)
personality_copy_func(pkgconf_cross_personality_t *p, const char *keyword, const char *warnprefix, const ptrdiff_t offset, char *value)
{
(void) keyword;
(void) lineno;
(void) warnprefix;
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)
personality_fragment_func(pkgconf_cross_personality_t *p, const char *keyword, const char *warnprefix, const ptrdiff_t offset, char *value)
{
(void) keyword;
(void) lineno;
(void) warnprefix;
pkgconf_list_t *dest = (pkgconf_list_t *)((char *) p + offset);
pkgconf_path_split(value, dest, false);
@ -215,7 +220,7 @@ personality_keyword_pair_cmp(const void *key, const void *ptr)
}
static void
personality_keyword_set(pkgconf_cross_personality_t *p, const size_t lineno, const char *keyword, char *value)
personality_keyword_set(pkgconf_cross_personality_t *p, const char *warnprefix, const char *keyword, char *value)
{
const personality_keyword_pair_t *pair = bsearch(keyword,
personality_keyword_pairs, PKGCONF_ARRAY_SIZE(personality_keyword_pairs),
@ -224,7 +229,7 @@ personality_keyword_set(pkgconf_cross_personality_t *p, const size_t lineno, con
if (pair == NULL || pair->func == NULL)
return;
pair->func(p, keyword, lineno, pair->offset, value);
pair->func(p, keyword, warnprefix, pair->offset, value);
}
static const pkgconf_parser_operand_func_t personality_parser_ops[256] = {
@ -260,13 +265,19 @@ load_personality_with_path(const char *path, const char *triplet, bool datadir)
else
snprintf(pathbuf, sizeof pathbuf, "%s/%s.personality", path, triplet);
f = fopen(pathbuf, "r");
if (f == NULL)
p = calloc(1, sizeof(pkgconf_cross_personality_t));
if (p == NULL)
return NULL;
p = calloc(1, sizeof(pkgconf_cross_personality_t));
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;
@ -313,7 +324,7 @@ pkgconf_cross_personality_find(const char *triplet)
}
}
pkgconf_path_build_from_environ("XDG_DATA_DIRS", "/usr/local/share" PKG_CONFIG_PATH_SEP_S "/usr/share", &plist, true);
pkgconf_path_build_from_environ(NULL, "XDG_DATA_DIRS", "/usr/local/share" PKG_CONFIG_PATH_SEP_S "/usr/share", &plist, true);
PKGCONF_FOREACH_LIST_ENTRY(plist.head, n)
{

View File

@ -17,15 +17,6 @@
#include <libpkgconf/stdinc.h>
#include <libpkgconf/libpkgconf.h>
#ifndef _WIN32
#include <fcntl.h> // open
#include <libgen.h> // basename/dirname
#include <sys/stat.h> // lstat, S_ISLNK
#include <unistd.h> // close, readlinkat
#include <string.h>
#endif
/*
* !doc
*
@ -36,14 +27,6 @@
* routines.
*/
#ifdef _WIN32
# define PKG_CONFIG_REG_KEY "Software\\pkgconfig\\PKG_CONFIG_PATH"
# 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
@ -80,6 +63,8 @@ pkg_get_parent_dir(pkgconf_pkg_t *pkg)
struct stat path_stat;
while (!lstat(buf, &path_stat) && S_ISLNK(path_stat.st_mode))
{
char sourcebuf[PKGCONF_ITEM_SIZE];
/*
* Have to split the path into the dir + file components,
* in order to extract the directory file descriptor.
@ -92,19 +77,22 @@ pkg_get_parent_dir(pkgconf_pkg_t *pkg)
*/
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);
#ifdef HAVE_DECL_READLINKAT
const char* targetfilename = basename(basenamebuf);
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);
#else
ssize_t len = readlink(buf, sourcebuf, sizeof(sourcebuf) - 1);
#endif
if (len == -1)
break;
@ -140,7 +128,7 @@ pkg_get_parent_dir(pkgconf_pkg_t *pkg)
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 void (*pkgconf_pkg_parser_keyword_func_t)(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value);
typedef struct {
const char *keyword;
const pkgconf_pkg_parser_keyword_func_t func;
@ -154,20 +142,19 @@ static int pkgconf_pkg_parser_keyword_pair_cmp(const void *key, const void *ptr)
}
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)
pkgconf_pkg_parser_tuple_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value)
{
(void) keyword;
(void) lineno;
(void) warnprefix;
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)
pkgconf_pkg_parser_version_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value)
{
(void) keyword;
(void) lineno;
char *p, *i;
size_t len;
char **dest = (char **)((char *) pkg + offset);
@ -181,15 +168,15 @@ pkgconf_pkg_parser_version_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, co
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);
pkgconf_warn(client, "%s: warning: malformed version field with whitespace, trimming to [%s]\n",
warnprefix, 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_pkg_parser_fragment_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value)
{
pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset);
@ -204,40 +191,52 @@ pkgconf_pkg_parser_fragment_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, c
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);
pkgconf_warn(client, "%s: warning: unable to parse field '%s' into an argument vector, value [%s]\n",
warnprefix, 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)
pkgconf_pkg_parser_dependency_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value)
{
(void) keyword;
(void) lineno;
pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset);
if (dest->tail != NULL)
{
pkgconf_warn(client, "%s: warning: merging duplicate field '%s' (undefined behavior)\n",
warnprefix, keyword);
}
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)
pkgconf_pkg_parser_internal_dependency_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value)
{
(void) keyword;
(void) lineno;
pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset);
if (dest->tail != NULL)
{
pkgconf_warn(client, "%s: warning: merging duplicate field '%s' (undefined behavior)\n",
warnprefix, keyword);
}
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)
pkgconf_pkg_parser_private_dependency_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const char *warnprefix, const ptrdiff_t offset, const char *value)
{
(void) keyword;
(void) lineno;
pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset);
if (dest->tail != NULL)
{
pkgconf_warn(client, "%s: warning: merging duplicate field '%s' (undefined behavior)\n",
warnprefix, keyword);
}
pkgconf_dependency_parse(client, pkg, dest, value, PKGCONF_PKG_DEPF_PRIVATE);
}
@ -251,18 +250,20 @@ static const pkgconf_pkg_parser_keyword_pair_t pkgconf_pkg_parser_keyword_funcs[
{"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)},
{"License.file", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, license_file)},
{"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)},
{"Source", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, source)},
{"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_parser_keyword_set(void *opaque, const char *warnprefix, const char *keyword, const char *value)
{
pkgconf_pkg_t *pkg = opaque;
@ -273,7 +274,7 @@ pkgconf_pkg_parser_keyword_set(void *opaque, const size_t lineno, const char *ke
if (pair == NULL || pair->func == NULL)
return;
pair->func(pkg->owner, pkg, keyword, lineno, pair->offset, value);
pair->func(pkg->owner, pkg, keyword, warnprefix, pair->offset, value);
}
static const char *
@ -327,6 +328,9 @@ 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;
@ -381,13 +385,40 @@ is_path_prefix_equal(const char *path1, const char *path2, size_t path2_len)
#endif
}
static inline const char *
lookup_val_from_env(const pkgconf_client_t *client, const char *pkg_id, const char *keyword)
{
char env_var[PKGCONF_ITEM_SIZE];
char *c;
snprintf(env_var, sizeof env_var, "PKG_CONFIG_%s_%s", pkg_id, keyword);
for (c = env_var; *c; c++)
{
*c = toupper((unsigned char) *c);
if (!isalnum((unsigned char) *c))
*c = '_';
}
return pkgconf_client_getenv(client, env_var);
}
static void
pkgconf_pkg_parser_value_set(void *opaque, const size_t lineno, const char *keyword, const char *value)
pkgconf_pkg_parser_value_set(void *opaque, const char *warnprefix, const char *keyword, const char *value)
{
char canonicalized_value[PKGCONF_ITEM_SIZE];
pkgconf_pkg_t *pkg = opaque;
const char *env_content;
(void) lineno;
(void) warnprefix;
env_content = lookup_val_from_env(pkg->owner, pkg->id, keyword);
if (env_content != NULL)
{
PKGCONF_TRACE(pkg->owner, "overriding %s from environment", keyword);
value = env_content;
}
pkgconf_strlcpy(canonicalized_value, value, sizeof canonicalized_value);
canonicalize_path(canonicalized_value);
@ -475,10 +506,74 @@ pkgconf_pkg_validate(const pkgconf_client_t *client, const pkgconf_pkg_t *pkg)
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->license_file != NULL)
free(pkg->license_file);
if (pkg->maintainer != NULL)
free(pkg->maintainer);
if (pkg->copyright != NULL)
free(pkg->copyright);
if (pkg->why != NULL)
free(pkg->why);
if (pkg->source != NULL)
free(pkg->source);
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_file(const pkgconf_client_t *client, const char *filename, FILE *f, unsigned int flags)
* .. 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.
*
@ -490,17 +585,46 @@ pkgconf_pkg_validate(const pkgconf_client_t *client, const pkgconf_pkg_t *pkg)
* :rtype: pkgconf_pkg_t *
*/
pkgconf_pkg_t *
pkgconf_pkg_new_from_file(pkgconf_client_t *client, const char *filename, FILE *f, unsigned int flags)
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->filename = strdup(filename);
pkg->pc_filedir = pkg_get_parent_dir(pkg);
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);
@ -509,7 +633,8 @@ pkgconf_pkg_new_from_file(pkgconf_client_t *client, const char *filename, FILE *
* package.
* See https://github.com/pkgconf/pkgconf/issues/213
*/
if (client->sysroot_dir && strncmp(pkg->pc_filedir, client->sysroot_dir, strlen(client->sysroot_dir)))
if (client->sysroot_dir != NULL && strncmp(pkg->pc_filedir, client->sysroot_dir, strlen(client->sysroot_dir)) &&
!(client->flags & PKGCONF_PKG_PKGF_PKGCONF1_SYSROOT_RULES))
pkgconf_tuple_add(client, &pkg->vars, "pc_sysrootdir", "", false, pkg->flags);
/* make module id */
@ -529,6 +654,14 @@ pkgconf_pkg_new_from_file(pkgconf_client_t *client, const char *filename, FILE *
#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';
@ -541,6 +674,7 @@ pkgconf_pkg_new_from_file(pkgconf_client_t *client, const char *filename, FILE *
}
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))
{
@ -577,55 +711,12 @@ pkgconf_pkg_free(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
pkgconf_cache_remove(client, 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);
pkg_free_lists(pkg);
if (pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL)
return;
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);
pkg_free_object(pkg);
}
/*
@ -666,6 +757,11 @@ pkgconf_pkg_ref(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
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);
@ -680,7 +776,6 @@ 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;
FILE *f;
char locbuf[PKGCONF_ITEM_SIZE];
char uninst_locbuf[PKGCONF_ITEM_SIZE];
@ -689,16 +784,14 @@ pkgconf_pkg_try_specific_path(pkgconf_client_t *client, const char *path, const
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) && (f = fopen(uninst_locbuf, "r")) != NULL)
{
PKGCONF_TRACE(client, "found (uninstalled): %s", uninst_locbuf);
pkg = pkgconf_pkg_new_from_file(client, uninst_locbuf, f, PKGCONF_PKG_PROPF_UNINSTALLED);
}
else if ((f = fopen(locbuf, "r")) != NULL)
{
PKGCONF_TRACE(client, "found: %s", locbuf);
pkg = pkgconf_pkg_new_from_file(client, locbuf, f, 0);
}
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;
}
@ -720,7 +813,6 @@ pkgconf_pkg_scan_dir(pkgconf_client_t *client, const char *path, void *data, pkg
{
char filebuf[PKGCONF_ITEM_SIZE];
pkgconf_pkg_t *pkg;
FILE *f;
pkgconf_strlcpy(filebuf, path, sizeof filebuf);
pkgconf_strlcat(filebuf, "/", sizeof filebuf);
@ -731,11 +823,7 @@ pkgconf_pkg_scan_dir(pkgconf_client_t *client, const char *path, void *data, pkg
PKGCONF_TRACE(client, "trying file [%s]", filebuf);
f = fopen(filebuf, "r");
if (f == NULL)
continue;
pkg = pkgconf_pkg_new_from_file(client, filebuf, f, 0);
pkg = pkgconf_pkg_new_from_path(client, filebuf, 0);
if (pkg != NULL)
{
if (func(pkg, data))
@ -774,6 +862,22 @@ pkgconf_scan_all(pkgconf_client_t *client, void *data, pkgconf_pkg_iteration_fun
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;
@ -787,43 +891,24 @@ pkgconf_scan_all(pkgconf_client_t *client, void *data, pkgconf_pkg_iteration_fun
return NULL;
}
#ifdef _WIN32
static pkgconf_pkg_t *
pkgconf_pkg_find_in_registry_key(pkgconf_client_t *client, HKEY hkey, const char *name)
search_preload_list(pkgconf_client_t *client, const char *name)
{
pkgconf_pkg_t *pkg = NULL;
pkgconf_node_t *n;
HKEY key;
int i = 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 NULL;
while (RegEnumValue(key, i++, buf, &bufsize, NULL, NULL, NULL, NULL)
== ERROR_SUCCESS)
PKGCONF_FOREACH_LIST_ENTRY(client->preloaded_pkgs.head, n)
{
char pathbuf[PKGCONF_ITEM_SIZE];
DWORD type;
DWORD pathbuflen = sizeof pathbuf;
pkgconf_pkg_t *pkg = n->data;
if (RegQueryValueEx(key, buf, NULL, &type, (LPBYTE) pathbuf, &pathbuflen)
== ERROR_SUCCESS && type == REG_SZ)
if (!strcmp(pkg->id, name))
{
pkg = pkgconf_pkg_try_specific_path(client, pathbuf, name);
if (pkg != NULL)
break;
pkgconf_pkg_ref(client, pkg);
return pkg;
}
bufsize = sizeof buf;
}
RegCloseKey(key);
return pkg;
return NULL;
}
#endif
/*
* !doc
@ -842,31 +927,26 @@ pkgconf_pkg_find(pkgconf_client_t *client, const char *name)
{
pkgconf_pkg_t *pkg = NULL;
pkgconf_node_t *n;
FILE *f;
PKGCONF_TRACE(client, "looking for: %s", name);
/* name might actually be a filename. */
if (str_has_suffix(name, PKG_CONFIG_EXT))
{
if ((f = fopen(name, "r")) != NULL)
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);
pkg = pkgconf_pkg_new_from_file(client, name, f, 0);
if (pkg != NULL)
{
pkgconf_path_add(pkg->pc_filedir, &client->dir_list, true);
goto out;
}
}
}
if (client->unveil_handler != NULL)
client->unveil_handler(client, pkg->pc_filedir, "r");
/* check builtins */
if ((pkg = pkgconf_builtin_pkg_get(name)) != NULL)
{
PKGCONF_TRACE(client, "%s is a builtin", name);
return pkg;
pkgconf_path_add(pkg->pc_filedir, &client->dir_list, true);
goto out;
}
}
/* check cache */
@ -879,6 +959,13 @@ pkgconf_pkg_find(pkgconf_client_t *client, const char *name)
}
}
/* 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;
@ -888,13 +975,6 @@ pkgconf_pkg_find(pkgconf_client_t *client, const char *name)
goto out;
}
#ifdef _WIN32
/* support getting PKG_CONFIG_PATH from registry */
pkg = pkgconf_pkg_find_in_registry_key(client, HKEY_CURRENT_USER, name);
if (!pkg)
pkg = pkgconf_pkg_find_in_registry_key(client, HKEY_LOCAL_MACHINE, name);
#endif
out:
pkgconf_cache_add(client, pkg);
@ -1036,105 +1116,6 @@ pkgconf_compare_version(const char *a, const char *b)
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 {
@ -1272,7 +1253,7 @@ static const pkgconf_pkg_provides_vermatch_rule_t pkgconf_pkg_provides_vermatch_
},
.depcmp = {
[PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none,
},
},
},
[PKGCONF_CMP_LESS_THAN] = {
.rulecmp = {
@ -1346,7 +1327,7 @@ static const pkgconf_pkg_provides_vermatch_rule_t pkgconf_pkg_provides_vermatch_
},
.depcmp = {
[PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none,
},
},
},
[PKGCONF_CMP_NOT_EQUAL] = {
.rulecmp = {
@ -1360,7 +1341,7 @@ static const pkgconf_pkg_provides_vermatch_rule_t pkgconf_pkg_provides_vermatch_
},
.depcmp = {
[PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none,
},
},
},
};
@ -1522,7 +1503,7 @@ pkgconf_pkg_report_graph_error(pkgconf_client_t *client, pkgconf_pkg_t *parent,
{
if (eflags & PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND)
{
if (!(client->flags & PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS) & !client->already_sent_notice)
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);
@ -1553,6 +1534,18 @@ pkgconf_pkg_report_graph_error(pkgconf_client_t *client, pkgconf_pkg_t *parent,
return eflags;
}
static inline bool
missing_node_is_tolerable(const pkgconf_client_t *client, const pkgconf_dependency_t *dep)
{
if (!(dep->flags & PKGCONF_PKG_DEPF_INTERNAL))
return false;
if ((client->flags & PKGCONF_PKG_PKGF_REQUIRE_INTERNAL))
return false;
return true;
}
static inline unsigned int
pkgconf_pkg_walk_list(pkgconf_client_t *client,
pkgconf_pkg_t *parent,
@ -1577,15 +1570,17 @@ pkgconf_pkg_walk_list(pkgconf_client_t *client,
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))
if (eflags_local != PKGCONF_PKG_ERRF_OK)
{
pkgconf_pkg_report_graph_error(client, parent, pkgdep, depnode, eflags_local);
if (missing_node_is_tolerable(client, depnode))
continue;
if (!(client->flags & PKGCONF_PKG_PKGF_SKIP_ERRORS))
pkgconf_pkg_report_graph_error(client, parent, pkgdep, depnode, eflags_local);
eflags |= eflags_local;
continue;
}
if (pkgdep == NULL)
continue;
if((pkgdep->flags & PKGCONF_PKG_PROPF_ANCESTOR) != 0)
{
@ -1626,7 +1621,7 @@ next:
return eflags;
}
static inline unsigned int
unsigned int
pkgconf_pkg_walk_conflicts_list(pkgconf_client_t *client,
pkgconf_pkg_t *root, pkgconf_list_t *deplist)
{
@ -1652,7 +1647,7 @@ pkgconf_pkg_walk_conflicts_list(pkgconf_client_t *client,
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),
pkgdep->version, pkgdep->id, parentnode->why, parentnode->package, pkgconf_pkg_get_comparator(parentnode),
parentnode->version != NULL ? " " : "", parentnode->version != NULL ? parentnode->version : "");
if (!(client->flags & PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS))

View File

@ -29,6 +29,32 @@
* Using the `queue` module functions is the recommended way of working with dependency graphs.
*/
/*
* !doc
*
* .. c:function:: void pkgconf_queue_push_dependency(pkgconf_list_t *list, const pkgconf_dependency_t *dep)
*
* Pushes a requested dependency onto the dependency resolver's queue which is described by
* a pkgconf_dependency_t node.
*
* :param pkgconf_list_t* list: the dependency resolution queue to add the package request to.
* :param pkgconf_dependency_t* dep: the dependency requested
* :return: nothing
*/
void
pkgconf_queue_push_dependency(pkgconf_list_t *list, const pkgconf_dependency_t *dep)
{
pkgconf_buffer_t depbuf = PKGCONF_BUFFER_INITIALIZER;
pkgconf_queue_t *pkgq = calloc(1, sizeof(pkgconf_queue_t));
pkgconf_buffer_append(&depbuf, dep->package);
if (dep->version != NULL)
pkgconf_buffer_append_fmt(&depbuf, " %s %s", pkgconf_pkg_get_comparator(dep), dep->version);
pkgq->package = pkgconf_buffer_freeze(&depbuf);
pkgconf_node_insert_tail(&pkgq->iter, pkgq, list);
}
/*
* !doc
*
@ -220,6 +246,45 @@ pkgconf_queue_collect_dependencies(pkgconf_client_t *client,
return pkgconf_queue_collect_dependencies_main(client, root, data, maxdepth);
}
static inline unsigned int
pkgconf_queue_collect_conflicts(pkgconf_client_t *client,
pkgconf_pkg_t *root,
pkgconf_pkg_t *world,
int maxdepth)
{
unsigned int eflags = PKGCONF_PKG_ERRF_OK;
pkgconf_node_t *node;
PKGCONF_TRACE(client, "%s: collecting conflicts, level %d", root->id, maxdepth);
PKGCONF_FOREACH_LIST_ENTRY(root->required.head, node)
{
pkgconf_dependency_t *dep = node->data;
pkgconf_pkg_t *pkg = dep->match;
pkgconf_node_t *cnode;
if (*dep->package == '\0')
continue;
if (pkg == NULL)
{
PKGCONF_TRACE(client, "WTF: unmatched dependency %p <%s>", dep, dep->package);
continue;
}
PKGCONF_FOREACH_LIST_ENTRY(pkg->conflicts.head, cnode)
{
pkgconf_dependency_t *conflict = cnode->data;
pkgconf_dependency_t *flattened_conflict = pkgconf_dependency_copy(client, conflict);
flattened_conflict->why = strdup(pkg->id);
pkgconf_node_insert(&flattened_conflict->iter, flattened_conflict, &world->conflicts);
}
}
return eflags;
}
static inline unsigned int
pkgconf_queue_verify(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *list, int maxdepth)
{
@ -253,6 +318,13 @@ pkgconf_queue_verify(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_lis
return result;
}
result = pkgconf_queue_collect_conflicts(client, 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");
@ -267,6 +339,18 @@ pkgconf_queue_verify(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_lis
}
}
if (!(client->flags & PKGCONF_PKG_PKGF_SKIP_CONFLICTS))
{
PKGCONF_TRACE(client, "checking for conflicts");
result = pkgconf_pkg_walk_conflicts_list(client, world, &world->conflicts);
if (result != PKGCONF_PKG_ERRF_OK)
{
pkgconf_solution_free(client, &initial_world);
return result;
}
}
/* free the initial solution */
pkgconf_solution_free(client, &initial_world);
@ -293,6 +377,7 @@ pkgconf_solution_free(pkgconf_client_t *client, pkgconf_pkg_t *world)
{
pkgconf_dependency_free(&world->required);
pkgconf_dependency_free(&world->requires_private);
pkgconf_dependency_free(&world->conflicts);
}
}

View File

@ -25,14 +25,21 @@
#include <string.h>
#include <sys/types.h>
#include <stdint.h>
#include <errno.h>
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <malloc.h>
# include <io.h> /* for _setmode() */
# include <fcntl.h>
# define PATH_DEV_NULL "nul"
# ifdef _WIN64
# define SIZE_FMT_SPECIFIER "%I64u"
# ifndef __MINGW32__
# define SIZE_FMT_SPECIFIER "%I64u"
# else
# define SIZE_FMT_SPECIFIER "%llu"
# endif
# else
# define SIZE_FMT_SPECIFIER "%u"
# endif
@ -47,9 +54,14 @@
# ifndef __MINGW32__
# include "win-dirent.h"
# else
# include <dirent.h>
# include <dirent.h>
# endif
# define PKGCONF_ITEM_SIZE (_MAX_PATH + 1024)
# define PKG_CONFIG_PATH_SEP_S ";"
# define PKG_DIR_SEP_S '\\'
# define strcasecmp _stricmp
# define strncasecmp _strnicmp
# define realpath(N,R) _fullpath((R),(N),_MAX_PATH)
#else
# define PATH_DEV_NULL "/dev/null"
# define SIZE_FMT_SPECIFIER "%zu"
@ -60,11 +72,18 @@
# include <unistd.h>
# include <limits.h>
# include <strings.h>
# include <fcntl.h> // open
# include <libgen.h> // basename/dirname
# include <sys/stat.h> // lstat, S_ISLNK
# include <unistd.h> // close, readlinkat
# include <string.h>
# ifdef PATH_MAX
# define PKGCONF_ITEM_SIZE (PATH_MAX + 1024)
# else
# define PKGCONF_ITEM_SIZE (4096 + 1024)
# endif
# define PKG_CONFIG_PATH_SEP_S ":"
# define PKG_DIR_SEP_S '/'
#endif
#endif

View File

@ -199,7 +199,7 @@ should_rewrite_sysroot(const pkgconf_client_t *client, pkgconf_list_t *vars, con
return false;
sysroot_dir = find_sysroot(client, vars);
if (sysroot_dir == NULL)
if (sysroot_dir == NULL || !*sysroot_dir)
return false;
if (*buf != '/')
@ -238,8 +238,6 @@ pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *list, const ch
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);
@ -250,6 +248,8 @@ pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *list, const ch
PKGCONF_TRACE(client, "adding tuple to @%p: %s => %s (parsed? %d)", list, key, tuple->value, parse);
pkgconf_tuple_find_delete(list, key);
pkgconf_node_insert(&tuple->iter, tuple, list);
free(dequote_value);
@ -314,12 +314,13 @@ pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *vars, const
char buf[PKGCONF_BUFSIZE];
const char *ptr;
char *bptr = buf;
const char *sysroot_dir = find_sysroot(client, vars);
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);
if (*value == '/' && sysroot_dir != NULL && *sysroot_dir && strncmp(value, sysroot_dir, strlen(sysroot_dir)))
bptr += pkgconf_strlcpy(buf, sysroot_dir, sizeof buf);
}
for (ptr = value; *ptr != '\0' && bptr - buf < PKGCONF_BUFSIZE; ptr++)
@ -421,7 +422,6 @@ pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *vars, const
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);

100
man/bomtool.1 Normal file
View File

@ -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

View File

@ -116,15 +116,31 @@ not described as dependencies should be specified here.
.It License
The asserted SPDX license tag that should be applied to the given package.
(optional; literal; pkgconf extension)
.It License.file
License file location for whole license text.
(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 Source
The asserted SPDX downloadLocation tag that should be applied to the given package.
Source should be URI contain tarball with exact version or Repository that contains
tag with version same as mentioned in version tag. It can be also be URI with hash
that is exact release point of the source.
(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.internal
Required dependencies that must be met for the package to be usable for
static linking.
The main differences verses Requires.private are that CFLAGS will not be
included and the solver will not consider the dependency when solving for
CFLAGS only.
(optional; dependency list; pkgconf extension)
.It Requires.private
Required dependencies that must be met for the package to be usable for header
inclusion and static linking.

View File

@ -16,212 +16,745 @@
.Sh SYNOPSIS
.Nm
.Op Ar options
.Op Ar list of modules
.Ar module ...
.Sh DESCRIPTION
The
.Nm
is a program which helps 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 GENERAL OPTIONS
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 -license-file ,
.Fl -source ,
.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 ,
.Fl -newlines :
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 -version
Display the supported pkg-config version and exit.
.It Fl -atleast-pkgconfig-version Ns = Ns Ar VERSION
Exit with error if we do not support the requested pkg-config version.
.It Fl -errors-to-stdout
Print all errors on the main output stream instead of the error output stream.
.It Fl -silence-errors
Do not display any errors at all.
.It Fl -list-all
Walk all directories listed in the
.Va PKG_CONFIG_PATH
environmental variable and display information on packages which have registered
information there.
.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.
.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 -ignore-conflicts
Ignore
.Sq Conflicts
rules in modules.
.It Fl -env-only
Learn about pkgconf's configuration strictly from environmental variables.
.It Fl -validate Ar package ...
Validate specific
.Sq .pc
files for correctness.
.It Fl -maximum-traverse-depth Ns = Ns Ar DEPTH
Impose a limit on the allowed depth in the dependency graph.
For example, a depth of 2 will restrict the resolver from acting on child
dependencies of modules added to the resolver's solution.
.It Fl -static
Compute a deeper dependency graph and use compiler/linker flags intended for
static linking.
.It Fl -shared
Compute a simple dependency graph that is only suitable for shared linking.
.It Fl -pure
Treats the computed dependency graph as if it were pure.
This is mainly intended for use with the
.Fl -static
flag.
.It Fl -no-provides
Ignore
.Sq Provides
rules in modules when resolving dependencies.
.It Fl -with-path Ns = Ns Ar PATH
Adds a new module search path to pkgconf's dependency resolver.
Paths added in this way are given preference before other paths.
.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 -dont-define-prefix
Disables the
.Sq define-prefix
feature.
.It Fl -prefix-variable Ns = Ns Ar VARIABLE
Sets the
.Sq prefix
variable used by the
.Sq define-prefix
feature.
.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 -dont-relocate-paths
Disables the path relocation feature.
.El
.Sh MODULE-SPECIFIC OPTIONS
.Bl -tag -width indent
.It Fl -atleast-version Ns = Ns Ar VERSION
Exit with error if a module's version is less than the specified version.
.It Fl -exact-version Ns = Ns Ar VERSION
Exit with error if a module's version is not exactly the specified version.
.It Fl -max-version Ns = Ns Ar VERSION
Exit with error if a module's version is greater than the specified version.
.It Fl -exists
Exit with a non-zero result if the dependency resolver was unable to find all of
the requested modules.
.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 -no-uninstalled
Forbids the dependency resolver from considering 'uninstalled' modules as part
of a solution.
.El
.Sh QUERY-SPECIFIC OPTIONS
.Bl -tag -width indent
.It Fl -cflags , Fl -cflags-only-I , Fl -cflags-only-other
Display either all CFLAGS, only
.Fl I
CFLAGS or only CFLAGS that are not
.Fl I .
.It Fl -libs , Fl -libs-only-L , Fl -libs-only-l , Fl -libs-only-other
Display either all linker flags, only
.Fl L
linker flags, only
.Fl l
linker flags or only linker flags that are not
.Fl L
or
.Fl l .
.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 -define-variable Ns = Ns Ar VARNAME Ns = Ns Ar VALUE
.It Fl -define-variable Ns = Ns Ar varname Ns = Ns Ar value
Define
.Va VARNAME
.Ar varname
as
.Va VALUE .
.Ar value .
Variables are used in query output, and some modules' results may change based
on the presence of a variable definition.
.It Fl -print-variables
Print all seen variables for a module to the output channel.
.It Fl -print-provides
Print all relevant
.Sq Provides
entries for a module to the output channel.
.It Fl -variable Ns = Ns Ar VARNAME
Print the value of
.Va VARNAME .
.It Fl -print-requires , Fl -print-requires-private
Print the modules included in either the
.Va Requires
field or the
.Va Requires.private
field.
.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.
.It Fl -path
Display the filenames of the
.Sq .pc
files used by the dependency resolver for a given dependency set.
.It Fl -env Ns = Ns Ar VARNAME
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 -fragment-filter Ns = Ns Ar TYPES
Filter the fragment lists for the specified types.
.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
Print the version of the queried module.
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 -newlines
Use newlines to separate individual CFLAGS or linker flag fragments
instead of spaces.
.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 Va PKG_CONFIG_PATH
List of secondary directories where
.Sq .pc
files are looked up.
.It Va PKG_CONFIG_LIBDIR
List of primary directories where
.Sq .pc
files are looked up.
.It Va PKG_CONFIG_SYSROOT_DIR
.Sq sysroot
directory, will be prepended to every path defined in
.Va PKG_CONFIG_PATH .
Useful for cross compilation.
.It Va PKG_CONFIG_TOP_BUILD_DIR
Provides an alternative setting for the
.Sq pc_top_builddir
global variable.
.It Va PKG_CONFIG_PURE_DEPGRAPH
If set, enables the same behaviour as the
.Fl -pure
flag.
.It Va PKG_CONFIG_SYSTEM_INCLUDE_PATH
List of paths that are considered system include paths by the toolchain.
This is a pkgconf-specific extension.
.It Va PKG_CONFIG_SYSTEM_LIBRARY_PATH
List of paths that are considered system library paths by the toolchain.
This is a pkgconf-specific extension.
.It Va PKG_CONFIG_DISABLE_UNINSTALLED
.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 Va PKG_CONFIG_LOG
.Sq logfile
which is used for dumping audit information concerning installed module versions.
.It Va PKG_CONFIG_DEBUG_SPEW
If set, enables additional debug logging.
The format of the debug log messages is implementation-specific.
.It Va PKG_CONFIG_DONT_RELOCATE_PATHS
.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 Va PKG_CONFIG_MSVC_SYNTAX
If set, uses MSVC syntax for fragments.
.It Va PKG_CONFIG_FDO_SYSROOT_RULES
.It Ev PKG_CONFIG_FDO_SYSROOT_RULES
If set, follow the sysroot prefixing rules that freedesktop.org pkg-config uses.
.It Va DESTDIR
If set to PKG_CONFIG_SYSROOT_DIR, assume that PKG_CONFIG_FDO_SYSROOT_RULES is set.
.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

View File

@ -1,15 +1,17 @@
project('pkgconf', 'c',
version : '2.4.1',
version : '2.5.1',
license : 'ISC',
meson_version : '>=0.49',
default_options : ['c_std=c99'],
meson_version: '>=0.52',
)
cc = meson.get_compiler('c')
add_project_arguments(
'-D_BSD_SOURCE',
'-D_DARWIN_C_SOURCE',
'-D_DEFAULT_SOURCE',
'-D_POSIX_C_SOURCE=200809L',
cc.get_supported_arguments(
'-Wimplicit-function-declaration',
'-Wmisleading-indentation',
@ -27,11 +29,14 @@ check_functions = [
['strncasecmp', 'strings.h'],
['strcasecmp', 'strings.h'],
['reallocarray', 'stdlib.h'],
['pledge', 'unistd.h'],
['unveil', 'unistd.h'],
['readlinkat', '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#include <@0@>'.format(f[1])) and cc.has_header_symbol(f[1], f[0], prefix : '#define _BSD_SOURCE\n#define _DEFAULT_SOURCE')
if cc.has_function(f[0], prefix : '#define _BSD_SOURCE\n#define _DARWIN_C_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 _DARWIN_C_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
@ -89,6 +94,7 @@ libpkgconf = library('pkgconf',
'libpkgconf/dependency.c',
'libpkgconf/fileio.c',
'libpkgconf/fragment.c',
'libpkgconf/output.c',
'libpkgconf/parser.c',
'libpkgconf/path.c',
'libpkgconf/personality.c',
@ -97,8 +103,8 @@ libpkgconf = library('pkgconf',
'libpkgconf/tuple.c',
c_args: ['-DLIBPKGCONF_EXPORT', build_static],
install : true,
version : '6.0.0',
soversion : '6',
version : '7.0.0',
soversion : '7',
)
# For other projects using libpkgconfig as a subproject
@ -123,15 +129,64 @@ pkg.generate(libpkgconf,
extra_cflags : build_static
)
cli_include = include_directories('cli')
pkgconf_exe = executable('pkgconf',
'cli/main.c',
'cli/core.c',
'cli/getopt_long.c',
'cli/renderer-msvc.c',
link_with : libpkgconf,
c_args: build_static,
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)
spdxtool_exe = executable('spdxtool',
'cli/spdxtool/main.c',
'cli/spdxtool/core.c',
'cli/spdxtool/software.c',
'cli/spdxtool/serialize.c',
'cli/spdxtool/simplelicensing.c',
'cli/spdxtool/util.c',
'cli/getopt_long.c',
link_with : libpkgconf,
c_args : build_static,
include_directories : include_directories('cli', 'cli/spdxtool'),
install : true)
test_runner_exe = executable('test-runner',
'cli/core.c',
'cli/getopt_long.c',
'cli/renderer-msvc.c',
'tests/test-runner.c',
link_with : libpkgconf,
c_args : build_static,
include_directories : cli_include,
install : true)
fixtures_dir = '@0@/tests'.format(meson.current_source_dir())
test_suites = [
'basic',
'ordering',
'parser',
'solver',
'sbom',
'sysroot',
'tuple',
]
foreach t : test_suites
test(t, test_runner_exe, args : ['--test-fixtures', fixtures_dir, '@0@/t/@1@'.format(meson.current_source_dir(), t)])
endforeach
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)
@ -139,6 +194,7 @@ kyuafile = configure_file(input : 'Kyuafile.in', output : 'Kyuafile', configurat
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')
@ -146,3 +202,40 @@ 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

21
pkg.m4
View File

@ -1,5 +1,5 @@
# pkg.m4 - Macros to locate and use pkg-config. -*- Autoconf -*-
# serial 13 (pkgconf)
# serial 16 (pkgconf)
dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
@ -44,8 +44,8 @@ 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 PKG_PROG_PKG_CONFIG([MIN-VERSION])
dnl ----------------------------------
dnl Since: 0.16
dnl
dnl Search for the pkg-config tool and set the PKG_CONFIG variable to
@ -53,12 +53,6 @@ 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)))?$])
@ -79,9 +73,6 @@ if test -n "$PKG_CONFIG"; then
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
@ -182,13 +173,13 @@ _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
[A pkg-config implementation 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 <http://pkg-config.freedesktop.org/>.])[]dnl
To get a pkg-config implementation, see <http://github.com/pkgconf/pkgconf>.])[]dnl
])
else
$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
@ -309,7 +300,7 @@ AC_ARG_WITH(with_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)],
[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])])

64
pkgconf.wxs.in Normal file
View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?define Arch = "@WIXL_ARCH@"?>
<?if $(var.Arch) = "x64"?>
<?define GLIB_ARCH = "win64"?>
<?define ArchString = "64-bit"?>
<?define ArchProgramFilesFolder = "ProgramFiles64Folder"?>
<?define Win64 = "yes"?>
<?else?>
<?define GLIB_ARCH = "win32"?>
<?define ArchString = "32-bit"?>
<?define ArchProgramFilesFolder = "ProgramFilesFolder"?>
<?define Win64 = "no"?>
<?endif?>
<Product Id="*"
Name="pkgconf @VERSION@ ($(var.ArchString))"
Language="1033"
Version="@VERSION@"
Manufacturer="pkgconf"
UpgradeCode="4faedad2-3f9d-45cc-89a7-3732ad2db0f7">
<Package InstallerVersion="200"
Compressed="yes"
InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes" />
<Feature Id="ProductFeature" Title="pkgconf" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="$(var.ArchProgramFilesFolder)">
<Directory Id="INSTALLFOLDER" Name="pkgconf @VERSION@" />
</Directory>
</Directory>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="PkgconfExe" Guid="*" Win64="$(var.Win64)">
<File Id="PkgconfExeFile"
Source="@EXE@"
KeyPath="yes" />
<File Id="PkgconfigExeFile"
Name="pkg-config.exe"
Source="@EXE@"/>
<File Id="PkgconfDllFile"
Source="@DLL@"/>
<Environment Id="PATH"
Name="PATH"
Value="[INSTALLFOLDER]"
Permanent="no"
Part="last"
Action="set"
System="yes" />
</Component>
</ComponentGroup>
<UIRef Id="WixUI_Minimal" />
</Product>
</Wix>

View File

@ -0,0 +1,3 @@
Query: %TEST_FIXTURES_DIR%/lib1/foo.pc
WantedFlags: libs
ExpectedStdout: -L/test/lib -lfoo

View File

@ -0,0 +1,3 @@
Query: pkgconf
WantedFlags: exists
ExpectedExitCode: 0

View File

@ -0,0 +1,5 @@
PackageSearchPath: lib1
Query: pkg-config
WantVariable: pc_path
ExpectedExitCode: 0
ExpectedStdout: %TEST_FIXTURES_DIR%/lib1

View File

@ -0,0 +1,3 @@
Query: pkgconf
WantedFlags: exists
ExpectedExitCode: 0

View File

@ -0,0 +1,5 @@
PackageSearchPath: lib1
Query: pkgconf
WantVariable: pc_path
ExpectedExitCode: 0
ExpectedStdout: %TEST_FIXTURES_DIR%/lib1

View File

@ -0,0 +1,5 @@
Environment: PKG_CONFIG_PATH=%TEST_FIXTURES_DIR%/lib1/child-prefix/pkgconfig
Environment: PKG_CONFIG_RELOCATE_PATHS=1
WantedFlags: cflags libs
Query: child-prefix-1
ExpectedStdout: -I%TEST_FIXTURES_DIR%/lib1/include/child-prefix-1 -L%TEST_FIXTURES_DIR%/lib1/lib64 -lchild-prefix-1

View File

@ -0,0 +1,4 @@
PackageSearchPath: lib1/child-prefix/pkgconfig
WantedFlags: define-prefix cflags libs
Query: child-prefix-1
ExpectedStdout: -I%TEST_FIXTURES_DIR%/lib1/include/child-prefix-1 -L%TEST_FIXTURES_DIR%/lib1/lib64 -lchild-prefix-1

View File

@ -0,0 +1,4 @@
WantVariable: prefix
WantedFlags: define-prefix
Query: %TEST_FIXTURES_DIR%/lib-relocatable/lib/pkgconfig/foo.pc
ExpectedStdout: %TEST_FIXTURES_DIR%/lib-relocatable

View File

@ -0,0 +1,4 @@
Environment: PKG_CONFIG_PATH=%TEST_FIXTURES_DIR%/lib2
Query: %TEST_FIXTURES_DIR%/lib3/bar.pc
WantedFlags: cflags
ExpectedStdout: -fPIC -I/test/include/foo

5
t/basic/env-tuple.test Normal file
View File

@ -0,0 +1,5 @@
Environment: PKG_CONFIG_DUPLICATE_TUPLE_PREFIX=/bar
PackageSearchPath: lib1
WantVariable: prefix
Query: duplicate-tuple
ExpectedStdout: /bar

View File

@ -0,0 +1,8 @@
PackageSearchPath: lib1
Query: foo
WantedFlags: cflags exists-cflags
ExpectedExitCode: 0
ExpectedStdout: FOO_CFLAGS='-DHAVE_FOO'
MatchStdout: partial
WantEnvPrefix: FOO
FragmentFilter: D

View File

@ -0,0 +1,6 @@
PackageSearchPath: lib1
Query: foo
WantedFlags: cflags exists-cflags
ExpectedExitCode: 0
ExpectedStdout: -DHAVE_FOO
MatchStdout: partial

View File

@ -0,0 +1,4 @@
PackageSearchPath: lib1
Query: nonexistent
WantedFlags: exists
ExpectedExitCode: 1

View File

@ -0,0 +1,3 @@
PackageSearchPath: lib1
Query: tilde >= 1.0.0
ExpectedExitCode: 1

View File

@ -0,0 +1,4 @@
PackageSearchPath: lib1
Query: foo > 1.2.3
WantedFlags: exists
ExpectedExitCode: 1

View File

@ -0,0 +1,3 @@
PackageSearchPath: lib1
Query: tilde <= 1.0.0
ExpectedExitCode: 0

View File

@ -0,0 +1,4 @@
PackageSearchPath: lib1
Query: foo >=
WantedFlags: exists
ExpectedExitCode: 1

View File

@ -0,0 +1,4 @@
PackageSearchPath: lib1
Query: foo > 1.2
WantedFlags: exists
ExpectedExitCode: 0

View File

@ -0,0 +1,3 @@
PackageSearchPath: lib1
Query: tilde = 1.0.0~rc1
ExpectedExitCode: 0

View File

@ -0,0 +1,4 @@
PackageSearchPath: lib1
Query: incomplete
WantedFlags: cflags
ExpectedStdout:

View File

@ -0,0 +1,4 @@
PackageSearchPath: lib1
Query: incomplete
WantedFlags: libs
ExpectedStdout:

View File

@ -0,0 +1,4 @@
Environment: PKG_CONFIG_SYSTEM_LIBRARY_PATH=/test/local/lib
PackageSearchPath: lib1
WantedFlags: libs-only-ldpath keep-system-libs
Query: cflags-libs-only

View File

@ -0,0 +1,6 @@
Environment: PKG_CONFIG_SYSTEM_LIBRARY_PATH=/test/local/lib
PackageSearchPath: lib1
WantedFlags: libs-only-ldpath
ExpectedStdout:
MatchStdout: empty
Query: cflags-libs-only

View File

@ -0,0 +1,5 @@
PackageSearchPath: lib1
Query: foo > 1.2,bar >= 1.3
WantedFlags: libs cflags
ExpectedExitCode: 0
ExpectedStdout: -fPIC -I/test/include/foo -L/test/lib -lbar -lfoo

View File

@ -0,0 +1,5 @@
PackageSearchPath: lib1
Query: foo > 1.2 bar >= 1.3
WantedFlags: libs cflags
ExpectedExitCode: 0
ExpectedStdout: -fPIC -I/test/include/foo -L/test/lib -lbar -lfoo

View File

@ -0,0 +1,6 @@
PackageSearchPath: lib1
Query: foo != 1.2.3
WantedFlags: libs cflags
ExpectedExitCode: 1
MatchStderr: partial
ExpectedStderr: Package 'foo' has version '1.2.3', required version is '!= 1.2.3'

View File

@ -0,0 +1,5 @@
PackageSearchPath: lib1
Query: foo != 1.3.0
WantedFlags: libs cflags
ExpectedExitCode: 0
ExpectedStdout: -fPIC -I/test/include/foo -L/test/lib -lfoo

View File

@ -0,0 +1,5 @@
PackageSearchPath: lib1
Query: foo > 1.2
WantedFlags: libs cflags
ExpectedExitCode: 0
ExpectedStdout: -fPIC -I/test/include/foo -L/test/lib -lfoo

5
t/basic/libs-cflags.test Normal file
View File

@ -0,0 +1,5 @@
PackageSearchPath: lib1
Query: foo
WantedFlags: libs cflags
ExpectedExitCode: 0
ExpectedStdout: -fPIC -I/test/include/foo -L/test/lib -lfoo

6
t/basic/libs-env.test Normal file
View File

@ -0,0 +1,6 @@
PackageSearchPath: lib1
Query: foo
WantedFlags: libs
ExpectedExitCode: 0
ExpectedStdout: FOO_LIBS='-L/test/lib -lfoo'
WantEnvPrefix: FOO

4
t/basic/libs-only.test Normal file
View File

@ -0,0 +1,4 @@
PackageSearchPath: lib1
Query: cflags-libs-only
WantedFlags: libs-only-ldpath libs-only-libname
ExpectedStdout: -L/test/local/lib -lfoo

5
t/basic/libs.test Normal file
View File

@ -0,0 +1,5 @@
PackageSearchPath: lib1
Query: foo
WantedFlags: libs
ExpectedExitCode: 0
ExpectedStdout: -L/test/lib -lfoo

View File

@ -0,0 +1,3 @@
PackageSearchPath: lib1
Query: , foo
ExpectedExitCode: 0

View File

@ -0,0 +1,3 @@
WantedFlags: modversion
Query: %TEST_FIXTURES_DIR%/lib1/bar.pc
ExpectedStdout: 1.3

View File

@ -0,0 +1,4 @@
PackageSearchPath: lib1
WantedFlags: modversion
Query: bar
ExpectedStdout: 1.3

View File

@ -0,0 +1,4 @@
PackageSearchPath: lib1
WantedFlags: modversion
Query: omg
ExpectedStdout: 1.2.3

View File

@ -0,0 +1,7 @@
PackageSearchPath: lib1
Query: foo, foobar
WantedFlags: modversion
VerbosityLevel: 1
ExpectedStdout: foo: 1.2.3
ExpectedStdout: foobar: 3.2.1
MatchStdout: partial

3
t/basic/noargs.test Normal file
View File

@ -0,0 +1,3 @@
Query:
ExpectedExitCode: 1
ExpectedStderr: Please specify at least one package name on the command line.

5
t/basic/nocflags.test Normal file
View File

@ -0,0 +1,5 @@
PackageSearchPath: lib1
Query: nocflag
WantedFlags: cflags
ExpectedStdout:
ExpectedExitCode: 0

5
t/basic/nolibs.test Normal file
View File

@ -0,0 +1,5 @@
PackageSearchPath: lib1
Query: nolib
WantedFlags: libs
ExpectedStdout:
ExpectedExitCode: 0

View File

@ -0,0 +1,4 @@
Environment: PKG_CONFIG_PATH=%TEST_FIXTURES_DIR%/lib1%DIR_SEP%%TEST_FIXTURES_DIR%/lib2
WantedFlags: libs
Query: bar
ExpectedStdout: -L/test/lib -lbar -lfoo

View File

@ -0,0 +1,12 @@
PackageSearchPath: lib1
Query: foo
WantedFlags: print-variables cflags libs
WantEnvPrefix: FOO
ExpectedStdout: FOO_CFLAGS='-fPIC -I/test/include/foo'
ExpectedStdout: FOO_LIBS='-L/test/lib -lfoo'
ExpectedStdout: FOO_INCLUDEDIR='/test/include'
ExpectedStdout: FOO_LIBDIR='/test/lib'
ExpectedStdout: FOO_EXEC_PREFIX='/test'
ExpectedStdout: FOO_PREFIX='/test'
ExpectedStdout: FOO_PCFILEDIR='%TEST_FIXTURES_DIR%/lib1'
MatchStdout: partial

View File

@ -0,0 +1,5 @@
Environment: PKG_CONFIG_MAXIMUM_TRAVERSE_DEPTH=1
PackageSearchPath: lib3
WantedFlags: print-requires
Query: bar
ExpectedStdout: foo

View File

@ -0,0 +1,4 @@
PackageSearchPath: lib1
WantedFlags: libs
Query: static-archive-libs
ExpectedStdout: /libfoo.a -pthread

View File

@ -0,0 +1,4 @@
PackageSearchPath: lib1
Query: foo
WantedFlags: uninstalled
ExpectedExitCode: 1

4
t/basic/uninstalled.test Normal file
View File

@ -0,0 +1,4 @@
PackageSearchPath: lib1
Query: omg
WantedFlags: uninstalled
ExpectedExitCode: 0

View File

@ -0,0 +1,5 @@
PackageSearchPath: lib1
Query: foo
WantVariable: includedir
WantEnvPrefix: FOO
ExpectedStdout: FOO_INCLUDEDIR='/test/include'

View File

@ -0,0 +1,4 @@
PackageSearchPath: lib1
Query: bar
WantVariable: includedir
ExpectedStdout: /test/include

4
t/basic/variable.test Normal file
View File

@ -0,0 +1,4 @@
PackageSearchPath: lib1
Query: foo
WantVariable: includedir
ExpectedStdout: /test/include

Some files were not shown because too many files have changed in this diff Show More