1 /* 2 * main.c 3 * main() routine 4 * 5 * SPDX-License-Identifier: pkgconf 6 * 7 * Copyright (c) 2011-2025 pkgconf authors (see AUTHORS). 8 * 9 * Permission to use, copy, modify, and/or distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * This software is provided 'as is' and without any warranty, express or 14 * implied. In no event shall the authors be liable for any damages arising 15 * from the use of this software. 16 */ 17 18 #include "libpkgconf/config.h" 19 #include <libpkgconf/stdinc.h> 20 #include <libpkgconf/libpkgconf.h> 21 #include "getopt_long.h" 22 #include "core.h" 23 24 static const char * 25 environ_lookup_handler(const pkgconf_client_t *client, const char *key) 26 { 27 (void) client; 28 29 return getenv(key); 30 } 31 32 static bool 33 error_handler(const char *msg, const pkgconf_client_t *client, void *data) 34 { 35 (void) data; 36 pkgconf_cli_state_t *state = client->client_data; 37 38 if (state->error_msgout == NULL) 39 return true; 40 41 pkgconf_output_file_fmt(state->error_msgout, "%s", msg); 42 return true; 43 } 44 45 static void 46 unveil_handler(const pkgconf_client_t *client, const char *path, const char *permissions) 47 { 48 (void) client; 49 50 if (pkgconf_unveil(path, permissions) == -1 && errno != ENOENT) 51 { 52 pkgconf_output_file_fmt(stderr, "pkgconf: unveil failed: %s\n", strerror(errno)); 53 exit(EXIT_FAILURE); 54 } 55 } 56 57 #ifndef PKGCONF_LITE 58 static pkgconf_cross_personality_t * 59 deduce_personality(char *argv[]) 60 { 61 const char *argv0 = argv[0]; 62 char *i, *prefix; 63 pkgconf_cross_personality_t *out; 64 65 i = strrchr(argv0, '/'); 66 if (i != NULL) 67 argv0 = i + 1; 68 69 #if defined(_WIN32) || defined(_WIN64) 70 i = strrchr(argv0, '\\'); 71 if (i != NULL) 72 argv0 = i + 1; 73 #endif 74 75 i = strstr(argv0, "-pkg"); 76 if (i == NULL) 77 return pkgconf_cross_personality_default(); 78 79 prefix = pkgconf_strndup(argv0, i - argv0); 80 out = pkgconf_cross_personality_find(prefix); 81 free(prefix); 82 if (out == NULL) 83 return pkgconf_cross_personality_default(); 84 85 return out; 86 } 87 #endif 88 89 #ifdef _WIN32 90 static UINT original_console_cp; 91 static UINT original_console_out_cp; 92 93 static void 94 restore_console_code_page(void) 95 { 96 SetConsoleCP(original_console_cp); 97 SetConsoleOutputCP(original_console_out_cp); 98 } 99 #endif 100 101 static void 102 version(void) 103 { 104 printf("%s\n", PACKAGE_VERSION); 105 } 106 107 static void 108 about(void) 109 { 110 printf("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); 111 printf("Copyright (c) 2011-2026 pkgconf authors (see AUTHORS in documentation directory)\n\n"); 112 printf("Permission to use, copy, modify, and/or distribute this software for any\n"); 113 printf("purpose with or without fee is hereby granted, provided that the above\n"); 114 printf("copyright notice and this permission notice appear in all copies.\n\n"); 115 printf("This software is provided 'as is' and without any warranty, express or\n"); 116 printf("implied. In no event shall the authors be liable for any damages arising\n"); 117 printf("from the use of this software.\n\n"); 118 printf("Report bugs at <%s>.\n", PACKAGE_BUGREPORT); 119 } 120 121 static void 122 usage(void) 123 { 124 printf("usage: %s [OPTIONS] [LIBRARIES]\n", PACKAGE_NAME); 125 126 printf("\nbasic options:\n\n"); 127 128 printf(" --help this message\n"); 129 printf(" --about print pkgconf version and license to stdout\n"); 130 printf(" --version print supported pkg-config version to stdout\n"); 131 printf(" --verbose print additional information\n"); 132 printf(" --atleast-pkgconfig-version check whether or not pkgconf is compatible\n"); 133 printf(" with a specified pkg-config version\n"); 134 printf(" --errors-to-stdout print all errors on stdout instead of stderr\n"); 135 printf(" --print-errors ensure all errors are printed\n"); 136 printf(" --short-errors be less verbose about some errors\n"); 137 printf(" --silence-errors explicitly be silent about errors\n"); 138 printf(" --list-all list all known packages\n"); 139 printf(" --list-package-names list all known package names\n"); 140 #ifndef PKGCONF_LITE 141 printf(" --simulate simulate walking the calculated dependency graph\n"); 142 #endif 143 printf(" --no-cache do not cache already seen packages when\n"); 144 printf(" walking the dependency graph\n"); 145 printf(" --log-file=filename write an audit log to a specified file\n"); 146 printf(" --with-path=path adds a directory to the search path\n"); 147 printf(" --define-prefix override the prefix variable with one that is guessed based on\n"); 148 printf(" the location of the .pc file\n"); 149 printf(" --dont-define-prefix do not override the prefix variable under any circumstances\n"); 150 printf(" --prefix-variable=varname sets the name of the variable that pkgconf considers\n"); 151 printf(" to be the package prefix\n"); 152 printf(" --dont-relocate-paths disables path relocation support\n"); 153 154 #ifndef PKGCONF_LITE 155 printf("\ncross-compilation personality support:\n\n"); 156 printf(" --personality=triplet|filename sets the personality to 'triplet' or a file named 'filename'\n"); 157 printf(" --dump-personality dumps details concerning selected personality\n"); 158 #endif 159 160 printf("\nchecking specific pkg-config database entries:\n\n"); 161 162 printf(" --atleast-version require a specific version of a module\n"); 163 printf(" --exact-version require an exact version of a module\n"); 164 printf(" --max-version require a maximum version of a module\n"); 165 printf(" --exists check whether or not a module exists\n"); 166 printf(" --uninstalled check whether or not an uninstalled module will be used\n"); 167 printf(" --no-uninstalled never use uninstalled modules when satisfying dependencies\n"); 168 printf(" --no-provides do not use 'provides' rules to resolve dependencies\n"); 169 printf(" --maximum-traverse-depth maximum allowed depth for dependency graph\n"); 170 printf(" --static be more aggressive when computing dependency graph\n"); 171 printf(" (for static linking)\n"); 172 printf(" --shared use a simplified dependency graph (usually default)\n"); 173 printf(" --pure optimize a static dependency graph as if it were a normal\n"); 174 printf(" dependency graph\n"); 175 printf(" --env-only look only for package entries in PKG_CONFIG_PATH\n"); 176 printf(" --ignore-conflicts ignore 'conflicts' rules in modules\n"); 177 printf(" --validate validate specific .pc files for correctness\n"); 178 179 printf("\nquerying specific pkg-config database fields:\n\n"); 180 181 printf(" --define-variable=varname=value define variable 'varname' as 'value'\n"); 182 printf(" --variable=varname print specified variable entry to stdout\n"); 183 printf(" --cflags print required CFLAGS to stdout\n"); 184 printf(" --cflags-only-I print required include-dir CFLAGS to stdout\n"); 185 printf(" --cflags-only-other print required non-include-dir CFLAGS to stdout\n"); 186 printf(" --libs print required linker flags to stdout\n"); 187 printf(" --libs-only-L print required LDPATH linker flags to stdout\n"); 188 printf(" --libs-only-l print required LIBNAME linker flags to stdout\n"); 189 printf(" --libs-only-other print required other linker flags to stdout\n"); 190 printf(" --print-requires print required dependency frameworks to stdout\n"); 191 printf(" --print-requires-private print required dependency frameworks for static\n"); 192 printf(" linking to stdout\n"); 193 printf(" --print-provides print provided dependencies to stdout\n"); 194 printf(" --print-variables print all known variables in module to stdout\n"); 195 #ifndef PKGCONF_LITE 196 printf(" --digraph print entire dependency graph in graphviz 'dot' format\n"); 197 printf(" --print-digraph-query-nodes also print query nodes in 'dot' format\n"); 198 printf(" --solution print dependency graph solution in a simple format\n"); 199 #endif 200 printf(" --keep-system-cflags keep -I%s entries in cflags output\n", SYSTEM_INCLUDEDIR); 201 printf(" --keep-system-libs keep -L%s entries in libs output\n", SYSTEM_LIBDIR); 202 printf(" --path show the exact filenames for any matching .pc files\n"); 203 printf(" --modversion print the specified module's version to stdout\n"); 204 printf(" --internal-cflags do not filter 'internal' cflags from output\n"); 205 printf(" --license print the specified module's license to stdout if known\n"); 206 printf(" --link-abi print the link ABIs (e.g. c, c++) the module must be linked against\n"); 207 printf(" --source print the specified module's source code location to stdout if known\n"); 208 printf(" --exists-cflags add -DHAVE_FOO fragments to cflags for each found module\n"); 209 210 printf("\nfiltering output:\n\n"); 211 #ifndef PKGCONF_LITE 212 printf(" --msvc-syntax print translatable fragments in MSVC syntax\n"); 213 #endif 214 printf(" --fragment-filter=types filter output fragments to the specified types\n"); 215 printf(" --env=prefix print output as shell-compatible environmental variables\n"); 216 printf(" --fragment-tree visualize printed CFLAGS/LIBS fragments as a tree\n"); 217 printf(" --newlines use newlines for whitespace between fragments\n"); 218 219 printf("\nreport bugs to <%s>.\n", PACKAGE_BUGREPORT); 220 } 221 222 int 223 main(int argc, char *argv[]) 224 { 225 #ifdef _WIN32 226 // When activeCodePage is set to UTF-8 in the application manifest (requires Windows 1903+), 227 // GetACP() returns CP_UTF8 but the console code pages are not automatically updated to match. 228 // Detect this condition via GetACP() == CP_UTF8 and align the console code pages accordingly. 229 // Restoring on exit is safe: if ACP is already CP_UTF8, resetting to the saved values is a no-op. 230 if (GetACP() == CP_UTF8) 231 { 232 original_console_cp = GetConsoleCP(); 233 original_console_out_cp = GetConsoleOutputCP(); 234 SetConsoleCP(CP_UTF8); 235 SetConsoleOutputCP(CP_UTF8); 236 atexit(restore_console_code_page); 237 } 238 #endif 239 240 int ret; 241 pkgconf_cli_state_t state = { 242 .want_flags = 0, 243 }; 244 pkgconf_list_t dir_list = PKGCONF_LIST_INITIALIZER; 245 char *env_traverse_depth; 246 char *logfile_arg = NULL; 247 pkgconf_cross_personality_t *personality = NULL; 248 249 if (pkgconf_pledge("stdio rpath wpath cpath unveil", NULL) == -1) 250 { 251 pkgconf_output_file_fmt(stderr, "pkgconf: pledge failed: %s\n", strerror(errno)); 252 return EXIT_FAILURE; 253 } 254 255 #ifdef _WIN32 256 /* When running regression tests in cygwin, and building native 257 * executable, tests fail unless native executable outputs unix 258 * line endings. Come to think of it, this will probably help 259 * real people who use cygwin build environments but native pkgconf, too. 260 */ 261 _setmode(fileno(stdout), O_BINARY); 262 _setmode(fileno(stderr), O_BINARY); 263 #endif 264 265 struct pkg_option options[] = { 266 { "version", no_argument, &state.want_flags, PKG_VERSION|PKG_PRINT_ERRORS, }, 267 { "about", no_argument, &state.want_flags, PKG_ABOUT|PKG_PRINT_ERRORS, }, 268 { "atleast-version", required_argument, NULL, 2, }, 269 { "atleast-pkgconfig-version", required_argument, NULL, 3, }, 270 { "libs", no_argument, &state.want_flags, PKG_LIBS|PKG_PRINT_ERRORS, }, 271 { "cflags", no_argument, &state.want_flags, PKG_CFLAGS|PKG_PRINT_ERRORS, }, 272 { "modversion", no_argument, &state.want_flags, PKG_MODVERSION|PKG_PRINT_ERRORS, }, 273 { "variable", required_argument, NULL, 7, }, 274 { "exists", no_argument, &state.want_flags, PKG_EXISTS, }, 275 { "print-errors", no_argument, &state.want_flags, PKG_PRINT_ERRORS, }, 276 { "short-errors", no_argument, &state.want_flags, PKG_SHORT_ERRORS, }, 277 { "maximum-traverse-depth", required_argument, NULL, 11, }, 278 { "static", no_argument, &state.want_flags, PKG_STATIC, }, 279 { "shared", no_argument, &state.want_flags, PKG_SHARED, }, 280 { "pure", no_argument, &state.want_flags, PKG_PURE, }, 281 { "print-requires", no_argument, &state.want_flags, PKG_REQUIRES, }, 282 { "print-variables", no_argument, &state.want_flags, PKG_VARIABLES|PKG_PRINT_ERRORS, }, 283 #ifndef PKGCONF_LITE 284 { "digraph", no_argument, &state.want_flags, PKG_DIGRAPH, }, 285 { "solution", no_argument, &state.want_flags, PKG_SOLUTION, }, 286 #endif 287 { "help", no_argument, &state.want_flags, PKG_HELP, }, 288 { "env-only", no_argument, &state.want_flags, PKG_ENV_ONLY, }, 289 { "print-requires-private", no_argument, &state.want_flags, PKG_REQUIRES_PRIVATE, }, 290 { "cflags-only-I", no_argument, &state.want_flags, PKG_CFLAGS_ONLY_I|PKG_PRINT_ERRORS, }, 291 { "cflags-only-other", no_argument, &state.want_flags, PKG_CFLAGS_ONLY_OTHER|PKG_PRINT_ERRORS, }, 292 { "libs-only-L", no_argument, &state.want_flags, PKG_LIBS_ONLY_LDPATH|PKG_PRINT_ERRORS, }, 293 { "libs-only-l", no_argument, &state.want_flags, PKG_LIBS_ONLY_LIBNAME|PKG_PRINT_ERRORS, }, 294 { "libs-only-other", no_argument, &state.want_flags, PKG_LIBS_ONLY_OTHER|PKG_PRINT_ERRORS, }, 295 { "uninstalled", no_argument, &state.want_flags, PKG_UNINSTALLED, }, 296 { "no-uninstalled", no_argument, &state.want_flags, PKG_NO_UNINSTALLED, }, 297 { "keep-system-cflags", no_argument, &state.want_flags, PKG_KEEP_SYSTEM_CFLAGS, }, 298 { "keep-system-libs", no_argument, &state.want_flags, PKG_KEEP_SYSTEM_LIBS, }, 299 { "define-variable", required_argument, NULL, 27, }, 300 { "exact-version", required_argument, NULL, 28, }, 301 { "max-version", required_argument, NULL, 29, }, 302 { "ignore-conflicts", no_argument, &state.want_flags, PKG_IGNORE_CONFLICTS, }, 303 { "errors-to-stdout", no_argument, &state.want_flags, PKG_ERRORS_ON_STDOUT, }, 304 { "silence-errors", no_argument, &state.want_flags, PKG_SILENCE_ERRORS, }, 305 { "list-all", no_argument, &state.want_flags, PKG_LIST|PKG_PRINT_ERRORS, }, 306 { "list-package-names", no_argument, &state.want_flags, PKG_LIST_PACKAGE_NAMES|PKG_PRINT_ERRORS, }, 307 #ifndef PKGCONF_LITE 308 { "simulate", no_argument, &state.want_flags, PKG_SIMULATE, }, 309 #endif 310 { "no-cache", no_argument, &state.want_flags, PKG_NO_CACHE, }, 311 { "print-provides", no_argument, &state.want_flags, PKG_PROVIDES, }, 312 { "no-provides", no_argument, &state.want_flags, PKG_NO_PROVIDES, }, 313 { "debug", no_argument, &state.want_flags, PKG_DEBUG|PKG_PRINT_ERRORS, }, 314 { "validate", no_argument, &state.want_flags, PKG_VALIDATE|PKG_PRINT_ERRORS|PKG_ERRORS_ON_STDOUT }, 315 { "log-file", required_argument, NULL, 40 }, 316 { "path", no_argument, &state.want_flags, PKG_PATH }, 317 { "with-path", required_argument, NULL, 42 }, 318 { "prefix-variable", required_argument, NULL, 43 }, 319 { "define-prefix", no_argument, &state.want_flags, PKG_DEFINE_PREFIX }, 320 { "dont-define-prefix", no_argument, &state.want_flags, PKG_DONT_DEFINE_PREFIX }, 321 { "dont-relocate-paths", no_argument, &state.want_flags, PKG_DONT_RELOCATE_PATHS }, 322 { "env", required_argument, NULL, 48 }, 323 #ifndef PKGCONF_LITE 324 { "msvc-syntax", no_argument, &state.want_flags, PKG_MSVC_SYNTAX }, 325 #endif 326 { "fragment-filter", required_argument, NULL, 50 }, 327 { "internal-cflags", no_argument, &state.want_flags, PKG_INTERNAL_CFLAGS }, 328 #ifndef PKGCONF_LITE 329 { "dump-personality", no_argument, &state.want_flags, PKG_DUMP_PERSONALITY }, 330 { "personality", required_argument, NULL, 53 }, 331 #endif 332 { "license", no_argument, &state.want_flags, PKG_DUMP_LICENSE }, 333 { "license-file", no_argument, &state.want_flags, PKG_DUMP_LICENSE_FILE }, 334 { "link-abi", no_argument, &state.want_flags, PKG_LINK_ABI }, 335 { "verbose", no_argument, NULL, 55 }, 336 { "exists-cflags", no_argument, &state.want_flags, PKG_EXISTS_CFLAGS }, 337 { "fragment-tree", no_argument, &state.want_flags, PKG_FRAGMENT_TREE }, 338 { "source", no_argument, &state.want_flags, PKG_DUMP_SOURCE }, 339 { "newlines", no_argument, &state.want_flags, PKG_NEWLINES }, 340 #ifndef PKGCONF_LITE 341 { "print-digraph-query-nodes", no_argument, &state.want_flags, PKG_PRINT_DIGRAPH_QUERY_NODES }, 342 #endif 343 { NULL, 0, NULL, 0 } 344 }; 345 346 #ifndef PKGCONF_LITE 347 if (getenv("PKG_CONFIG_EARLY_TRACE")) 348 { 349 state.error_msgout = stderr; 350 pkgconf_client_set_trace_handler(&state.pkg_client, error_handler, NULL); 351 } 352 #endif 353 354 while ((ret = pkg_getopt_long_only(argc, argv, "", options, NULL)) != -1) 355 { 356 switch (ret) 357 { 358 case 2: 359 state.required_module_version = pkg_optarg; 360 break; 361 case 3: 362 state.required_pkgconfig_version = pkg_optarg; 363 break; 364 case 7: 365 state.want_variable = pkg_optarg; 366 break; 367 case 11: 368 state.maximum_traverse_depth = atoi(pkg_optarg); 369 break; 370 case 27: 371 pkgconf_tuple_define_global(&state.pkg_client, pkg_optarg); 372 break; 373 case 28: 374 state.required_exact_module_version = pkg_optarg; 375 break; 376 case 29: 377 state.required_max_module_version = pkg_optarg; 378 break; 379 case 40: 380 logfile_arg = pkg_optarg; 381 break; 382 case 42: 383 pkgconf_path_prepend(pkg_optarg, &dir_list, true); 384 break; 385 case 43: 386 pkgconf_client_set_prefix_varname(&state.pkg_client, pkg_optarg); 387 break; 388 case 48: 389 state.want_env_prefix = pkg_optarg; 390 break; 391 case 50: 392 state.want_fragment_filter = pkg_optarg; 393 break; 394 #ifndef PKGCONF_LITE 395 case 53: 396 personality = pkgconf_cross_personality_find(pkg_optarg); 397 break; 398 #endif 399 case 55: 400 state.verbosity++; 401 break; 402 case '?': 403 case ':': 404 ret = EXIT_FAILURE; 405 goto out; 406 default: 407 break; 408 } 409 } 410 411 if (personality == NULL) { 412 #ifndef PKGCONF_LITE 413 personality = deduce_personality(argv); 414 #else 415 personality = pkgconf_cross_personality_default(); 416 #endif 417 } 418 419 /* now, bring up the client. settings are preserved since the client is prealloced */ 420 pkgconf_client_init(&state.pkg_client, error_handler, &state, personality, &state, environ_lookup_handler); 421 pkgconf_client_set_unveil_handler(&state.pkg_client, unveil_handler); 422 423 #ifndef PKGCONF_LITE 424 if (getenv("PKG_CONFIG_MSVC_SYNTAX") != NULL) 425 state.want_flags |= PKG_MSVC_SYNTAX; 426 #endif 427 428 if ((env_traverse_depth = getenv("PKG_CONFIG_MAXIMUM_TRAVERSE_DEPTH")) != NULL) 429 state.maximum_traverse_depth = atoi(env_traverse_depth); 430 431 if ((state.want_flags & PKG_PRINT_ERRORS) != PKG_PRINT_ERRORS) 432 state.want_flags |= (PKG_SILENCE_ERRORS); 433 434 if ((state.want_flags & PKG_SILENCE_ERRORS) == PKG_SILENCE_ERRORS && !getenv("PKG_CONFIG_DEBUG_SPEW")) 435 state.want_flags |= (PKG_SILENCE_ERRORS); 436 else 437 state.want_flags &= ~(PKG_SILENCE_ERRORS); 438 439 if (getenv("PKG_CONFIG_DONT_RELOCATE_PATHS")) 440 state.want_flags |= (PKG_DONT_RELOCATE_PATHS); 441 442 if ((state.want_flags & PKG_VALIDATE) == PKG_VALIDATE || (state.want_flags & PKG_DEBUG) == PKG_DEBUG) 443 pkgconf_client_set_warn_handler(&state.pkg_client, error_handler, NULL); 444 445 #ifndef PKGCONF_LITE 446 if ((state.want_flags & PKG_DEBUG) == PKG_DEBUG) 447 pkgconf_client_set_trace_handler(&state.pkg_client, error_handler, NULL); 448 #endif 449 450 pkgconf_path_prepend_list(&state.pkg_client.dir_list, &dir_list); 451 pkgconf_path_free(&dir_list); 452 453 if ((state.want_flags & PKG_ABOUT) == PKG_ABOUT) 454 { 455 about(); 456 457 ret = EXIT_SUCCESS; 458 goto out; 459 } 460 461 if ((state.want_flags & PKG_VERSION) == PKG_VERSION) 462 { 463 version(); 464 465 ret = EXIT_SUCCESS; 466 goto out; 467 } 468 469 if ((state.want_flags & PKG_HELP) == PKG_HELP) 470 { 471 usage(); 472 473 ret = EXIT_SUCCESS; 474 goto out; 475 } 476 477 if (logfile_arg == NULL) 478 logfile_arg = getenv("PKG_CONFIG_LOG"); 479 480 if (logfile_arg != NULL) 481 { 482 if (pkgconf_unveil(logfile_arg, "rwc") == -1) 483 { 484 pkgconf_output_file_fmt(stderr, "pkgconf: unveil failed: %s\n", strerror(errno)); 485 return EXIT_FAILURE; 486 } 487 488 state.logfile_out = fopen(logfile_arg, "a"); 489 pkgconf_audit_set_log(&state.pkg_client, state.logfile_out); 490 } 491 492 if (getenv("PKG_CONFIG_ALLOW_SYSTEM_CFLAGS") != NULL) 493 state.want_flags |= PKG_KEEP_SYSTEM_CFLAGS; 494 495 if (getenv("PKG_CONFIG_ALLOW_SYSTEM_LIBS") != NULL) 496 state.want_flags |= PKG_KEEP_SYSTEM_LIBS; 497 498 return pkgconf_cli_run(&state, argc, argv, pkg_optind); 499 500 out: 501 pkgconf_cli_state_reset(&state); 502 return ret; 503 } 504