jbsrc/lib/src/core/jb-tests.c (14366B) - raw
1 /* 2 * JB, the Jean-Yves Lefort's Build System 3 * Copyright (C) 2008 Jean-Yves Lefort <jylefort@brutele.be> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 */ 19 20 #include <stdio.h> 21 #include <string.h> 22 #include <unistd.h> 23 #include <glib.h> 24 #include "jb-util.h" 25 #include "jb-variable.h" 26 #include "jb-compile-options.h" 27 #include "jb-tests.h" 28 #include "jb-feature.h" 29 30 void 31 jb_check_host_system (void) 32 { 33 char *output; 34 gboolean status = FALSE; 35 36 if (jb_variable_get_variable("host-cpu") != NULL) 37 return; /* already checked */ 38 39 jb_message_checking("the host system type"); 40 41 if (jb_exec(&output, NULL, "jbsrc/tools/config.sub `jbsrc/tools/config.guess`")) 42 { 43 char **fields; 44 int len; 45 46 fields = g_strsplit(output, "-", 0); 47 len = g_strv_length(fields); 48 49 if (len == 3) 50 { 51 jb_variable_set_string("host-cpu", fields[0]); 52 jb_variable_set_string("host-manufacturer", fields[1]); 53 jb_variable_set_string("host-kernel", NULL); 54 jb_variable_set_string("host-os", fields[2]); 55 status = TRUE; 56 } 57 else if (len == 4) 58 { 59 jb_variable_set_string("host-cpu", fields[0]); 60 jb_variable_set_string("host-manufacturer", fields[1]); 61 jb_variable_set_string("host-kernel", fields[2]); 62 jb_variable_set_string("host-os", fields[3]); 63 status = TRUE; 64 } 65 66 g_strfreev(fields); 67 } 68 69 if (status) 70 jb_message_result_string(output); 71 else 72 jb_message_result_string("unknown"); 73 74 g_free(output); 75 76 if (! status) 77 jb_error("unable to determine host system type"); 78 } 79 80 void 81 jb_register_program (const char *name, JBVariableFlags flags) 82 { 83 char *description; 84 char *program; 85 86 g_return_if_fail(name != NULL); 87 88 description = g_strdup_printf("%s program", name); 89 90 program = g_find_program_in_path(name); 91 92 jb_variable_add_string(name, 93 description, 94 jb_variable_group_external_programs, 95 flags, 96 program); 97 98 g_free(description); 99 g_free(program); 100 } 101 102 /* the program must have been registered with jb_register_program() */ 103 gboolean 104 jb_check_program (const char *name) 105 { 106 static GHashTable *checked_programs = NULL; 107 JBVariable *variable; 108 const char *program; 109 gboolean result = FALSE; 110 111 g_return_val_if_fail(name != NULL, FALSE); 112 113 /* do not check for the same program twice */ 114 if (checked_programs != NULL) 115 { 116 gpointer checked_result; 117 118 if (g_hash_table_lookup_extended(checked_programs, name, NULL, &checked_result)) 119 return GPOINTER_TO_INT(checked_result); 120 } 121 else 122 checked_programs = g_hash_table_new(g_str_hash, g_str_equal); 123 124 jb_message_checking("for %s", name); 125 126 variable = jb_variable_get_variable_or_error(name); 127 128 program = g_value_get_string(&variable->value); 129 130 if (variable->user_set) 131 { 132 char *absolute_program; 133 134 absolute_program = g_find_program_in_path(program); 135 if (absolute_program != NULL) 136 { 137 jb_message_result_string(absolute_program); 138 g_value_take_string(&variable->value, absolute_program); 139 result = TRUE; 140 } 141 else 142 { 143 jb_message_result_string_format("not found (\"%s\" was specified but was not found, is a directory or is not executable)", program); 144 g_value_set_string(&variable->value, NULL); 145 } 146 } 147 else 148 { 149 if (program != NULL) 150 { 151 jb_message_result_string(program); 152 result = TRUE; 153 } 154 else 155 jb_message_result_string("not found"); 156 } 157 158 g_hash_table_insert(checked_programs, g_strdup(name), GINT_TO_POINTER(result)); 159 160 return result; 161 } 162 163 void 164 jb_require_program (const char *name) 165 { 166 g_return_if_fail(name != NULL); 167 168 if (! jb_check_program(name)) 169 jb_error("required program \"%s\" not found", name); 170 } 171 172 static void 173 log_c_test (const char *filename, const char *action) 174 { 175 char *source; 176 char **lines; 177 int i; 178 179 source = jb_read_file_or_exit(filename); 180 lines = g_strsplit(source, "\n", 0); 181 g_free(source); 182 183 jb_log("attempting to %s program %s:", action, filename); 184 185 for (i = 0; lines[i] != NULL; i++) 186 { 187 const char *line = lines[i]; 188 189 /* do not output a spurious empty last line */ 190 if (*line == '\0' && lines[i + 1] == NULL) 191 break; 192 193 jb_log("%4i %s", i + 1, lines[i]); 194 } 195 196 jb_log(JB_SEPARATOR); 197 198 g_strfreev(lines); 199 } 200 201 gboolean 202 jb_test_compile (const char *filename, 203 const char *cflags, 204 const char *cppflags) 205 { 206 g_return_val_if_fail(filename != NULL, FALSE); 207 208 log_c_test(filename, "compile"); 209 210 return jb_exec_expand(NULL, NULL, 211 "$cc -c -o build/test.o" 212 " $cflags $extra-cflags" 213 " $cppflags $extra-cppflags" 214 " $filename", 215 "extra-cflags", cflags, 216 "extra-cppflags", cppflags, 217 "filename", filename, 218 NULL); 219 } 220 221 gboolean 222 jb_test_compile_string (const char *str, 223 const char *cflags, 224 const char *cppflags) 225 { 226 g_return_val_if_fail(str != NULL, FALSE); 227 228 jb_write_file_or_exit("build/test.c", str); 229 230 return jb_test_compile("build/test.c", cflags, cppflags); 231 } 232 233 gboolean 234 jb_test_link (const char *filename, 235 const char *cflags, 236 const char *cppflags, 237 const char *ldflags, 238 const char *libs) 239 { 240 g_return_val_if_fail(filename != NULL, FALSE); 241 242 log_c_test(filename, "link"); 243 244 return jb_exec_expand(NULL, NULL, 245 "$cc -o build/test" 246 " $cflags $extra-cflags" 247 " $cppflags $extra-cppflags" 248 " $ldflags $extra-ldflags" 249 " $filename" 250 " $libs $extra-libs", 251 "filename", filename, 252 "extra-cflags", cflags, 253 "extra-cppflags", cppflags, 254 "extra-ldflags", ldflags, 255 "extra-libs", libs, 256 NULL); 257 } 258 259 gboolean 260 jb_test_link_string (const char *str, 261 const char *cflags, 262 const char *cppflags, 263 const char *ldflags, 264 const char *libs) 265 { 266 g_return_val_if_fail(str != NULL, FALSE); 267 268 jb_write_file_or_exit("build/test.c", str); 269 270 return jb_test_link("build/test.c", cflags, cppflags, ldflags, libs); 271 } 272 273 gboolean 274 jb_test_run (const char *filename, 275 const char *cflags, 276 const char *cppflags, 277 const char *ldflags, 278 const char *libs) 279 { 280 g_return_val_if_fail(filename != NULL, FALSE); 281 282 if (! jb_test_link(filename, cflags, cppflags, ldflags, libs)) 283 return FALSE; 284 285 return jb_exec(NULL, NULL, "build/test"); 286 } 287 288 gboolean 289 jb_test_run_string (const char *str, 290 const char *cflags, 291 const char *cppflags, 292 const char *ldflags, 293 const char *libs) 294 { 295 g_return_val_if_fail(str != NULL, FALSE); 296 297 jb_write_file_or_exit("build/test.c", str); 298 299 return jb_test_run("build/test.c", cflags, cppflags, ldflags, libs); 300 } 301 302 gboolean 303 jb_check_functions (const char *functions, const char *libname) 304 { 305 char **functions_array; 306 int i; 307 GString *checking_message; 308 GString *program; 309 char *libs = NULL; 310 gboolean result; 311 312 g_return_val_if_fail(functions != NULL, FALSE); 313 314 functions_array = g_strsplit(functions, " ", 0); 315 316 checking_message = g_string_new("for "); 317 318 for (i = 0; functions_array[i] != NULL; i++) 319 { 320 if (i != 0) 321 g_string_append_printf(checking_message, ", "); 322 323 g_string_append_printf(checking_message, "%s()", functions_array[i]); 324 } 325 326 if (libname) 327 g_string_append_printf(checking_message, " in -l%s", libname); 328 else 329 g_string_append(checking_message, " in libc"); 330 331 jb_message_checking("%s", checking_message->str); 332 g_string_free(checking_message, TRUE); 333 334 program = g_string_new(NULL); 335 336 /* 337 * Quoting c.m4 in autoconf: "Override any GCC internal prototype to 338 * avoid an error. Use char because int might match the return type 339 * of a GCC builtin and then its argument prototype would still 340 * apply." 341 */ 342 for (i = 0; functions_array[i] != NULL; i++) 343 g_string_append_printf(program, "char %s ();\n", functions_array[i]); 344 345 g_string_append(program, "int main () {"); 346 347 for (i = 0; functions_array[i] != NULL; i++) 348 g_string_append_printf(program, " %s();", functions_array[i]); 349 350 g_string_append(program, " }\n"); 351 352 if (libname) 353 libs = g_strdup_printf("-l%s", libname); 354 355 result = jb_test_link_string(program->str, NULL, NULL, NULL, libs); 356 357 g_string_free(program, TRUE); 358 g_free(libs); 359 360 jb_message_result_bool(result); 361 362 return result; 363 } 364 365 gboolean 366 jb_check_packages (const char *group_name, 367 const char *varprefix, 368 const char *packages) 369 { 370 char *quoted_packages; 371 char *cflags = NULL; 372 char *libs = NULL; 373 char *error = NULL; 374 gboolean status; 375 376 g_return_val_if_fail(group_name != NULL, FALSE); 377 g_return_val_if_fail(varprefix != NULL, FALSE); 378 g_return_val_if_fail(packages != NULL, FALSE); 379 g_return_val_if_fail(jb_feature_is_enabled(&jb_pkg_config_feature), FALSE); 380 381 jb_require_program("pkg-config"); 382 383 jb_message_checking("for %s", group_name); 384 385 quoted_packages = g_shell_quote(packages); 386 387 status = jb_exec_expand(&cflags, NULL, "$pkg-config --cflags $packages", 388 "packages", quoted_packages, 389 NULL) 390 && jb_exec_expand(&libs, NULL, "$pkg-config --libs $packages", 391 "packages", quoted_packages, 392 NULL); 393 394 if (status) 395 jb_variable_set_package_flags(varprefix, cflags, NULL, NULL, libs); 396 else 397 jb_exec_expand(NULL, &error, "$pkg-config --print-errors $packages", 398 "packages", quoted_packages, 399 NULL); 400 401 g_free(quoted_packages); 402 g_free(cflags); 403 g_free(libs); 404 405 jb_message_result_bool(status); 406 407 if (error != NULL) 408 { 409 jb_warning("%s", error); 410 g_free(error); 411 } 412 413 return status; 414 } 415 416 /* 417 * If one or more options (bool variables) in the provided 418 * NULL-terminated list are enabled, checks for the specified 419 * packages. If the packages are not found, disable the provided 420 * options. 421 */ 422 void 423 jb_check_packages_for_options (const char *group_name, 424 const char *varprefix, 425 const char *packages, 426 ...) 427 { 428 GSList *options = NULL; 429 va_list args; 430 const char *option; 431 gboolean needs_packages = FALSE; 432 433 g_return_if_fail(group_name != NULL); 434 g_return_if_fail(varprefix != NULL); 435 g_return_if_fail(packages != NULL); 436 g_return_if_fail(jb_feature_is_enabled(&jb_pkg_config_feature)); 437 438 va_start(args, packages); 439 while ((option = va_arg(args, const char *)) != NULL) 440 { 441 options = g_slist_append(options, (gpointer) option); 442 443 if (jb_variable_get_bool(option)) 444 needs_packages = TRUE; 445 } 446 va_end(args); 447 448 if (needs_packages && ! jb_check_packages(group_name, varprefix, packages)) 449 { 450 GSList *l; 451 452 JB_LIST_FOREACH(l, options) 453 { 454 option = l->data; 455 456 if (jb_variable_get_bool(option)) 457 { 458 jb_warning("disabling option \"%s\" since %s was not found", option, group_name); 459 jb_variable_set_bool(option, FALSE); 460 } 461 } 462 } 463 464 g_slist_free(options); 465 } 466 467 void 468 jb_require_packages (const char *group_name, 469 const char *varprefix, 470 const char *packages) 471 { 472 g_return_if_fail(group_name != NULL); 473 g_return_if_fail(varprefix != NULL); 474 g_return_if_fail(packages != NULL); 475 g_return_if_fail(jb_feature_is_enabled(&jb_pkg_config_feature)); 476 477 if (! jb_check_packages(group_name, varprefix, packages)) 478 jb_error("unable to find %s", group_name); 479 } 480 481 char * 482 jb_get_package_variable (const char *package, const char *variable) 483 { 484 char *value; 485 486 g_return_val_if_fail(package != NULL, NULL); 487 g_return_val_if_fail(variable != NULL, NULL); 488 g_return_val_if_fail(jb_feature_is_enabled(&jb_pkg_config_feature), FALSE); 489 490 jb_require_program("pkg-config"); 491 492 if (! jb_exec_expand(&value, NULL, "$pkg-config --variable=$variable $package", 493 "variable", variable, 494 "package", package, 495 NULL)) 496 { 497 g_free(value); 498 value = NULL; 499 } 500 501 return value; 502 } 503 504 void 505 jb_check_cc_dependency_style (void) 506 { 507 if (! jb_variable_get_bool("cc-dependency-tracking")) 508 return; /* we don't need to know since we will not use it */ 509 510 jb_message_checking("the C compiler dependency style"); 511 512 /* make sure they do not exist beforehand */ 513 unlink("build/test.o"); 514 unlink("build/test.o.deps"); 515 516 if (jb_test_compile_string("#include <stdio.h>\n" 517 "int main () {}\n", 518 "-MT build/test.o -MD -MP -MF build/test.o.deps", 519 NULL) 520 && g_file_test("build/test.o", G_FILE_TEST_IS_REGULAR) 521 && g_file_test("build/test.o.deps", G_FILE_TEST_IS_REGULAR)) 522 jb_message_result_string("GCC"); 523 else 524 { 525 jb_message_result_string("unknown, disabling dependency tracking"); 526 jb_variable_set_bool("cc-dependency-tracking", FALSE); 527 } 528 } 529 530 void 531 jb_check_glibc (void) 532 { 533 gboolean result; 534 535 jb_message_checking("for the GNU C library"); 536 result = jb_test_compile_string("#include <features.h>\n" 537 "int main() {\n" 538 "#ifndef __GLIBC__\n" 539 "#error \"glibc not found\"\n" 540 "#endif\n" 541 "}\n", 542 NULL, 543 NULL); 544 jb_message_result_bool(result); 545 546 jb_variable_set_bool("glibc", result); 547 } 548 549 static gboolean 550 check_reentrant_resolver_real (gboolean *result) 551 { 552 const char *os; 553 int freebsd_major; 554 int freebsd_minor; 555 556 if (! strcmp(jb_variable_get_string("host-kernel"), "linux")) 557 { 558 *result = TRUE; 559 return TRUE; 560 } 561 562 os = jb_variable_get_string("host-os"); 563 564 if (sscanf(os, "freebsd%d.%d", &freebsd_major, &freebsd_minor) == 2) 565 { 566 /* FreeBSD >= 5.3 */ 567 *result = (freebsd_major == 5 && freebsd_minor >= 3) || freebsd_major >= 6; 568 return TRUE; 569 } 570 571 if (g_str_has_prefix(os, "netbsd") || g_str_has_prefix(os, "openbsd")) 572 { 573 *result = FALSE; 574 return TRUE; 575 } 576 577 return FALSE; /* unknown */ 578 } 579 580 void 581 jb_check_reentrant_dns_resolver (void) 582 { 583 gboolean result; 584 585 jb_check_host_system(); 586 587 jb_message_checking("if the DNS resolver is reentrant"); 588 589 if (check_reentrant_resolver_real(&result)) 590 { 591 jb_message_result_bool(result); 592 if (result) 593 jb_compile_options_add_cppflags(jb_compile_options, "-DHAVE_REENTRANT_RESOLVER"); 594 } 595 else 596 jb_message_result_string("unknown, assuming it is not"); 597 }