jbsrc/lib/src/core/jb-action.c (19679B) - 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 <string.h> 21 #include <sys/types.h> 22 #include <sys/stat.h> 23 #include <fcntl.h> 24 #include <unistd.h> 25 #include <errno.h> 26 #include "jb-group.h" 27 #include "jb-resource.h" 28 #include "jb-tests.h" 29 #include "jb-feature.h" 30 #include "jb-config.h" 31 #include "jb-action.h" 32 33 static const char *standard_distfiles[] = { 34 "jb", 35 "jbsrc/jb.c", 36 "jbsrc/lib/COPYING", 37 "jbsrc/lib/README", 38 "jbsrc/tools/config.guess", 39 "jbsrc/tools/config.sub", 40 JB_PACKAGE_SOURCES, 41 JB_SOURCES 42 }; 43 44 static const char *optional_distfiles[] = { 45 "AUTHORS", 46 "COPYING", 47 "COPYING-DOCS", 48 "INSTALL", 49 "NEWS", 50 "README", 51 "TODO", 52 "TRANSLATING", 53 "po/POTFILES.in", 54 "po/POTFILES.skip" 55 }; 56 57 static const char *distcleanfiles[] = { 58 JB_CONFIG_FILE, 59 "build/jb", 60 "build/jb.c", 61 "build/jbsrc/jb", 62 "build/jbsrc/jb.c" 63 }; 64 65 static const char *maintainercleanfiles[] = { 66 "jb", 67 "jbsrc/lib", 68 "jbsrc/tools/config.guess", 69 "jbsrc/tools/config.sub" 70 }; 71 72 static GSList * 73 get_groups (GSList *group_names) 74 { 75 GSList *l; 76 GSList *groups = NULL; 77 78 if (group_names == NULL) 79 groups = g_slist_copy(jb_groups); 80 else 81 JB_LIST_FOREACH(l, group_names) 82 { 83 const char *name = l->data; 84 JBGroup *group; 85 86 group = jb_group_get(name); 87 if (group == NULL) 88 jb_error("unknown group \"%s\"", name); 89 90 groups = g_slist_append(groups, group); 91 } 92 93 return groups; 94 } 95 96 static void 97 perform_action (GSList *groups, void (*perform) (JBResource *res)) 98 { 99 GSList *la; 100 101 JB_LIST_FOREACH(la, groups) 102 { 103 JBGroup *group = la->data; 104 GSList *lb; 105 106 JB_LIST_FOREACH(lb, group->resources) 107 { 108 JBResource *res = lb->data; 109 110 perform(res); 111 } 112 } 113 } 114 115 static void 116 core_configure (void) 117 { 118 jb_check_cc_dependency_style(); 119 } 120 121 static void 122 report_variable_group (const JBVariableGroup *group, int varname_len) 123 { 124 GSList *l; 125 126 jb_message(""); 127 jb_message(" %s:", group->name); 128 jb_message(""); 129 130 JB_LIST_FOREACH(l, jb_variables) 131 { 132 JBVariable *variable = l->data; 133 134 if (variable->group == group && (variable->flags & JB_VARIABLE_NO_REPORT) == 0) 135 { 136 char *varname; 137 char *value; 138 139 varname = g_strdup_printf("%s:", variable->name); 140 value = jb_variable_to_string(variable); 141 142 jb_message(" %-*s %s", varname_len, varname, value); 143 144 g_free(varname); 145 g_free(value); 146 } 147 } 148 } 149 150 static int 151 get_longest_displayable_variable_name_len (void) 152 { 153 GSList *l; 154 int longest = 0; 155 156 JB_LIST_FOREACH(l, jb_variables) 157 { 158 JBVariable *variable = l->data; 159 int len; 160 161 if (variable->group == NULL) 162 continue; 163 164 len = strlen(variable->name) + 1; /* +1 for the semicolon */ 165 if (len > longest) 166 longest = len; 167 } 168 169 return longest; 170 } 171 172 static void 173 print_configure_report (void) 174 { 175 int varname_len; 176 GSList *l; 177 178 jb_message(""); 179 jb_message_expand("$human-package $version was configured successfully.", NULL); 180 jb_message("The following variables are in effect:"); 181 182 varname_len = get_longest_displayable_variable_name_len(); 183 184 JB_LIST_FOREACH(l, jb_variable_groups) 185 report_variable_group(l->data, varname_len); 186 187 jb_message(""); 188 jb_message_expand("Type \"./jb build\" to build $human-package $version.", NULL); 189 } 190 191 static void 192 show_variable_group_help (const JBVariableGroup *group) 193 { 194 GSList *l; 195 196 jb_message(""); 197 jb_message(" %s:", group->name); 198 199 JB_LIST_FOREACH(l, jb_variables) 200 { 201 JBVariable *variable = l->data; 202 char *value; 203 204 if (variable->group != group) 205 continue; 206 207 jb_message(""); 208 jb_message(" variable: %s (%s)", variable->name, jb_variable_get_type_name(variable)); 209 jb_message(" description: %s", variable->description); 210 211 value = jb_variable_to_string(variable); 212 jb_message(" default value: %s", value); 213 g_free(value); 214 } 215 } 216 217 void 218 jb_action_help (void) 219 { 220 GSList *l; 221 222 jb_message("Usage:"); 223 jb_message(""); 224 jb_message(" ./jb help"); 225 jb_message(" ./jb configure [VARIABLE=VALUE...]"); 226 jb_message(" ./jb build [GROUP...]"); 227 jb_message(" ./jb install [GROUP...]"); 228 jb_message(" ./jb makedist"); 229 jb_message(" ./jb clean [GROUP...]"); 230 jb_message(" ./jb distclean [GROUP...]"); 231 jb_message(" ./jb maintainerclean [GROUP...]"); 232 jb_message(""); 233 jb_message("Variables:"); 234 235 JB_LIST_FOREACH(l, jb_variable_groups) 236 show_variable_group_help(l->data); 237 } 238 239 static void 240 configure_real (void) 241 { 242 jb_set_log_file("build/configure.log"); 243 244 jb_feature_configure(); 245 core_configure(); 246 jb_package_configure(); 247 248 /* remove our temporary test files */ 249 unlink("build/test"); 250 unlink("build/test.c"); 251 unlink("build/test.o"); 252 unlink("build/test.o.deps"); 253 254 jb_config_save(); 255 } 256 257 void 258 jb_action_configure (void) 259 { 260 configure_real(); 261 262 print_configure_report(); 263 } 264 265 static void 266 ensure_configure (void) 267 { 268 if (g_file_test(JB_CONFIG_FILE, G_FILE_TEST_EXISTS)) 269 jb_config_load(); 270 else 271 { 272 jb_message_expand("configuring $human-package $version with default values since \"./jb configure\" was not run...", NULL); 273 configure_real(); 274 } 275 } 276 277 static void 278 build_real (GSList *groups) 279 { 280 jb_set_log_file("build/build.log"); 281 282 perform_action(groups, jb_resource_pre_build); 283 perform_action(groups, jb_resource_build); 284 } 285 286 void 287 jb_action_build (GSList *group_names) 288 { 289 GSList *groups; 290 291 ensure_configure(); 292 jb_package_add_resources(); 293 294 groups = get_groups(group_names); 295 296 build_real(groups); 297 298 jb_message(""); 299 jb_message_expand("$human-package $version was built successfully.", NULL); 300 jb_message_expand("Type \"sudo ./jb install\" to install $human-package $version.", NULL); 301 302 g_slist_free(groups); 303 } 304 305 static void 306 install_real (GSList *groups) 307 { 308 jb_set_log_file("build/install.log"); 309 310 perform_action(groups, jb_resource_install); 311 } 312 313 void 314 jb_action_install (GSList *group_names) 315 { 316 GSList *groups; 317 318 ensure_configure(); 319 jb_package_add_resources(); 320 321 groups = get_groups(group_names); 322 323 build_real(groups); 324 install_real(groups); 325 326 jb_message(""); 327 jb_message_expand("$human-package $version was installed successfully.", NULL); 328 329 g_slist_free(groups); 330 } 331 332 static void 333 add_standard_distfiles_to_dist (void) 334 { 335 int i; 336 337 for (i = 0; i < G_N_ELEMENTS(standard_distfiles); i++) 338 jb_action_add_to_dist_string_list(standard_distfiles[i]); 339 } 340 341 static void 342 add_optional_distfiles_to_dist (void) 343 { 344 int i; 345 346 for (i = 0; i < G_N_ELEMENTS(optional_distfiles); i++) 347 { 348 const char *file = optional_distfiles[i]; 349 350 if (g_file_test(file, G_FILE_TEST_EXISTS)) 351 jb_action_add_to_dist(file); 352 } 353 } 354 355 static void 356 makedist_real (GSList *groups) 357 { 358 jb_set_log_file("build/makedist.log"); 359 360 /* just in case */ 361 jb_rmtree(jb_action_get_distdir()); 362 363 add_standard_distfiles_to_dist(); 364 add_optional_distfiles_to_dist(); 365 366 perform_action(groups, jb_resource_makedist); 367 } 368 369 void 370 jb_action_makedist (void) 371 { 372 GSList *groups; 373 char *tarball; 374 375 ensure_configure(); 376 jb_package_add_resources(); 377 378 groups = get_groups(NULL); 379 380 build_real(groups); 381 makedist_real(groups); 382 383 tarball = jb_variable_expand("${package}-$version.tar.bz2", NULL); 384 385 jb_message("creating build/%s", tarball); 386 387 jb_action_exec("cd build && tar -chof - ${package}-$version | bzip2 -9 -c >$tarball", 388 "tarball", tarball, 389 NULL); 390 391 jb_rmtree(jb_action_get_distdir()); 392 393 jb_message(""); 394 jb_message("build/%s was created successfully.", tarball); 395 396 g_slist_free(groups); 397 g_free(tarball); 398 } 399 400 static void 401 clean_real (GSList *groups) 402 { 403 jb_set_log_file("build/clean.log"); 404 405 perform_action(groups, jb_resource_clean); 406 } 407 408 void 409 jb_action_clean (GSList *group_names) 410 { 411 GSList *groups; 412 413 ensure_configure(); 414 jb_package_add_resources(); 415 416 groups = get_groups(group_names); 417 418 clean_real(groups); 419 420 jb_message(""); 421 jb_message_expand("$human-package $version was cleaned successfully.", NULL); 422 423 g_slist_free(groups); 424 } 425 426 static void 427 distclean_real (GSList *groups) 428 { 429 jb_set_log_file("build/distclean.log"); 430 431 perform_action(groups, jb_resource_distclean); 432 433 jb_action_rm_array((char **) distcleanfiles, G_N_ELEMENTS(distcleanfiles)); 434 } 435 436 void 437 jb_action_distclean (GSList *group_names) 438 { 439 GSList *groups; 440 441 ensure_configure(); 442 jb_package_add_resources(); 443 444 groups = get_groups(group_names); 445 446 clean_real(groups); 447 distclean_real(groups); 448 449 jb_message(""); 450 jb_message_expand("$human-package $version was dist-cleaned successfully.", NULL); 451 452 g_slist_free(groups); 453 } 454 455 static void 456 maintainerclean_real (GSList *groups) 457 { 458 jb_set_log_file("build/maintainerclean.log"); 459 460 perform_action(groups, jb_resource_maintainerclean); 461 462 jb_action_rm_array((char **) maintainercleanfiles, G_N_ELEMENTS(maintainercleanfiles)); 463 } 464 465 void 466 jb_action_maintainerclean (GSList *group_names) 467 { 468 GSList *groups; 469 470 ensure_configure(); 471 jb_package_add_resources(); 472 473 groups = get_groups(group_names); 474 475 clean_real(groups); 476 distclean_real(groups); 477 maintainerclean_real(groups); 478 479 jb_message(""); 480 jb_message_expand("$human-package $version was maintainer-cleaned successfully.", NULL); 481 482 g_slist_free(groups); 483 } 484 485 void 486 jb_action_exec (const char *str, ...) 487 { 488 va_list args; 489 char *command; 490 gboolean can_fail = FALSE; 491 char *standard_output; 492 char *standard_error; 493 494 g_return_if_fail(str != NULL); 495 496 va_start(args, str); 497 command = jb_variable_expandv(str, args); 498 va_end(args); 499 500 if (command[0] == '-') 501 { 502 char *tmp; 503 504 can_fail = TRUE; 505 506 tmp = g_strdup(command + 1); 507 g_free(command); 508 command = tmp; 509 } 510 511 if (! jb_exec(&standard_output, &standard_error, "%s", command) && ! can_fail) 512 { 513 g_printerr("%s\n", command); 514 515 if (*standard_output != '\0' && *standard_error != '\0') 516 { 517 g_printerr("standard output:\n"); 518 g_printerr("%s\n", standard_output); 519 520 g_printerr("standard error:\n"); 521 g_printerr("%s\n", standard_error); 522 } 523 else if (*standard_output != '\0') 524 g_printerr("%s\n", standard_output); 525 else if (*standard_error != '\0') 526 g_printerr("%s\n", standard_error); 527 528 jb_error("command failed"); 529 } 530 531 g_free(command); 532 } 533 534 /* 535 * Doing this internally is much faster than using an external program 536 * (install or cp), and also more portable. 537 */ 538 static gboolean 539 install_file_real (const char *srcfile, 540 const char *dstfile, 541 const char *owner, 542 const char *group, 543 mode_t mode, 544 GError **err) 545 { 546 int in; 547 int out; 548 549 in = open(srcfile, O_RDONLY); 550 if (in < 0) 551 { 552 g_set_error(err, 0, 0, "cannot open %s for reading: %s", srcfile, g_strerror(errno)); 553 return FALSE; 554 } 555 556 out = open(dstfile, O_CREAT | O_WRONLY | O_TRUNC, mode); 557 if (out < 0) 558 { 559 /* 560 * Unlink the file and try again, in case the file could not be 561 * opened because it already existed and was not writable. Do 562 * this unconditionally without testing EPERM, since it might 563 * not be portable. 564 */ 565 unlink(dstfile); 566 567 out = open(dstfile, O_CREAT | O_WRONLY | O_TRUNC, mode); 568 if (out < 0) 569 { 570 g_set_error(err, 0, 0, "cannot open %s for writing: %s", dstfile, g_strerror(errno)); 571 goto error; 572 } 573 } 574 575 while (TRUE) 576 { 577 char buf[4096]; 578 ssize_t bytes_read; 579 ssize_t bytes_written; 580 581 bytes_read = read(in, buf, sizeof(buf)); 582 if (bytes_read < 0) 583 { 584 g_set_error(err, 0, 0, "cannot read from %s: %s", srcfile, g_strerror(errno)); 585 goto error; 586 } 587 if (bytes_read == 0) 588 break; 589 590 bytes_written = write(out, buf, bytes_read); 591 if (bytes_written < 0) 592 { 593 g_set_error(err, 0, 0, "cannot write to %s: %s", dstfile, g_strerror(errno)); 594 goto error; 595 } 596 if (bytes_written != bytes_read) 597 { 598 g_set_error(err, 0, 0, "cannot write to %s", dstfile); 599 goto error; 600 } 601 } 602 603 /* 604 * The Linux manpage of fchmod() mentions that "as a security 605 * measure, depending on the file system, the set-user-ID and 606 * set-group-ID execution bits may be turned off if a file is 607 * written", so set the ownership and permissions after writing the 608 * file. 609 */ 610 611 if (owner != NULL || group != NULL) 612 { 613 GError *tmp_err = NULL; 614 615 if (! jb_fchown_by_name(out, owner, group, &tmp_err)) 616 { 617 if (owner != NULL && group != NULL) 618 g_set_error(err, 0, 0, "cannot chown %s to %s:%s: %s", dstfile, owner, group, tmp_err->message); 619 else if (owner != NULL) 620 g_set_error(err, 0, 0, "cannot chown %s to owner %s: %s", dstfile, owner, tmp_err->message); 621 else 622 g_set_error(err, 0, 0, "cannot chown %s to group %s: %s", dstfile, group, tmp_err->message); 623 624 g_error_free(tmp_err); 625 goto error; 626 } 627 } 628 629 /* 630 * Set the permissions after chowning the file, since the chown can 631 * clear the setuid/setgid bits. 632 */ 633 634 if (fchmod(out, mode) < 0) 635 { 636 g_set_error(err, 0, 0, "cannot chmod %s to " JB_MODE_FORMAT ": %s", dstfile, (unsigned int) mode, g_strerror(errno)); 637 goto error; 638 } 639 640 if (close(out) < 0) 641 { 642 g_set_error(err, 0, 0, "cannot close %s: %s", dstfile, g_strerror(errno)); 643 goto error; 644 } 645 646 close(in); 647 648 return TRUE; 649 650 error: 651 close(in); 652 close(out); 653 654 return FALSE; 655 } 656 657 void 658 jb_action_install_file (const char *srcfile, 659 const char *dstdir, 660 const char *owner, 661 const char *group, 662 mode_t mode) 663 { 664 char *srcfile_basename; 665 char *dstfile; 666 667 g_return_if_fail(srcfile != NULL); 668 g_return_if_fail(dstdir != NULL); 669 670 srcfile_basename = g_path_get_basename(srcfile); 671 dstfile = g_strdup_printf("%s/%s", dstdir, srcfile_basename); 672 g_free(srcfile_basename); 673 674 jb_action_install_to_file(srcfile, dstfile, owner, group, mode); 675 g_free(dstfile); 676 } 677 678 void 679 jb_action_install_to_file (const char *srcfile, 680 const char *dstfile, 681 const char *owner, 682 const char *group, 683 mode_t mode) 684 { 685 char *real_dstfile; 686 GError *err = NULL; 687 688 g_return_if_fail(srcfile != NULL); 689 g_return_if_fail(dstfile != NULL); 690 691 real_dstfile = jb_variable_expand("$destdir$dstfile", 692 "dstfile", dstfile, 693 NULL); 694 695 jb_message("installing %s", real_dstfile); 696 697 jb_mkdir_of_file(real_dstfile); 698 699 if (! install_file_real(srcfile, real_dstfile, owner, group, mode, &err)) 700 jb_error("%s", err->message); 701 702 g_free(real_dstfile); 703 } 704 705 void 706 jb_action_install_data (const char *srcfile, const char *dstdir) 707 { 708 g_return_if_fail(srcfile != NULL); 709 g_return_if_fail(dstdir != NULL); 710 711 jb_action_install_file(srcfile, 712 dstdir, 713 jb_variable_get_string_or_null("data-owner"), 714 jb_variable_get_string_or_null("data-group"), 715 jb_variable_get_mode("data-mode")); 716 } 717 718 void 719 jb_action_install_data_to_file (const char *srcfile, const char *dstfile) 720 { 721 g_return_if_fail(srcfile != NULL); 722 g_return_if_fail(dstfile != NULL); 723 724 jb_action_install_to_file(srcfile, 725 dstfile, 726 jb_variable_get_string_or_null("data-owner"), 727 jb_variable_get_string_or_null("data-group"), 728 jb_variable_get_mode("data-mode")); 729 } 730 731 void 732 jb_action_install_data_list (GSList *srcfiles, const char *dstdir) 733 { 734 GSList *l; 735 736 g_return_if_fail(dstdir != NULL); 737 738 JB_LIST_FOREACH(l, srcfiles) 739 { 740 const char *srcfile = l->data; 741 742 jb_action_install_data(srcfile, dstdir); 743 } 744 } 745 746 void 747 jb_action_install_program (const char *srcfile, const char *dstdir) 748 { 749 g_return_if_fail(srcfile != NULL); 750 g_return_if_fail(dstdir != NULL); 751 752 jb_action_install_file(srcfile, 753 dstdir, 754 jb_variable_get_string_or_null("program-owner"), 755 jb_variable_get_string_or_null("program-group"), 756 jb_variable_get_mode("program-mode")); 757 } 758 759 void 760 jb_action_install_library (const char *srcfile, const char *dstdir) 761 { 762 g_return_if_fail(srcfile != NULL); 763 g_return_if_fail(dstdir != NULL); 764 765 jb_action_install_file(srcfile, 766 dstdir, 767 jb_variable_get_string_or_null("library-owner"), 768 jb_variable_get_string_or_null("library-group"), 769 jb_variable_get_mode("library-mode")); 770 } 771 772 void 773 jb_action_rm (const char *file) 774 { 775 g_return_if_fail(file != NULL); 776 777 if (g_file_test(file, G_FILE_TEST_EXISTS)) 778 { 779 jb_message("removing %s", file); 780 unlink(file); 781 } 782 783 /* if the parent directories were created by JB, also remove them */ 784 785 if (g_str_has_prefix(file, "jbsrc/tools/")) 786 jb_action_rmdir("jbsrc/tools"); 787 else if (g_str_has_prefix(file, "build/")) 788 { 789 GSList *parentdirs = NULL; 790 char *dir; 791 792 dir = g_path_get_dirname(file); 793 794 while (TRUE) 795 { 796 parentdirs = g_slist_append(parentdirs, dir); 797 798 if (! strcmp(dir, "build")) 799 break; 800 801 dir = g_path_get_dirname(dir); 802 g_assert(strcmp(dir, "/")); 803 } 804 805 jb_action_rmdir_list(parentdirs); 806 807 jb_g_slist_free_deep(parentdirs); 808 } 809 } 810 811 void 812 jb_action_rm_array (char **files, int len) 813 { 814 int i; 815 816 for (i = 0; i < len; i++) 817 jb_action_rm(files[i]); 818 } 819 820 void 821 jb_action_rm_list (GSList *files) 822 { 823 GSList *l; 824 825 JB_LIST_FOREACH(l, files) 826 jb_action_rm(l->data); 827 } 828 829 static gboolean 830 dir_is_empty (const char *dir) 831 { 832 GDir *gdir; 833 gboolean is_empty; 834 835 gdir = g_dir_open(dir, 0, NULL); 836 if (gdir == NULL) 837 return TRUE; 838 839 is_empty = g_dir_read_name(gdir) == NULL; 840 841 g_dir_close(gdir); 842 843 return is_empty; 844 } 845 846 void 847 jb_action_rmdir (const char *dir) 848 { 849 g_return_if_fail(dir != NULL); 850 851 if (g_file_test(dir, G_FILE_TEST_EXISTS) && dir_is_empty(dir)) 852 { 853 jb_message("removing directory %s", dir); 854 rmdir(dir); 855 } 856 } 857 858 void 859 jb_action_rmdir_list (GSList *dirs) 860 { 861 GSList *l; 862 863 JB_LIST_FOREACH(l, dirs) 864 jb_action_rmdir(l->data); 865 } 866 867 static gboolean 868 add_to_hash_set (JBStringHashSet **hash_set, const char *value) 869 { 870 if (*hash_set == NULL) 871 *hash_set = jb_string_hash_set_new(); 872 873 if (! jb_string_hash_set_add(*hash_set, value)) 874 return FALSE; 875 876 return TRUE; 877 } 878 879 void 880 jb_action_add_to_dist (const char *file) 881 { 882 static JBStringHashSet *distfiles = NULL; 883 char *dir; 884 char *distdir; 885 886 g_return_if_fail(file != NULL); 887 g_return_if_fail(*file != '\0'); 888 889 if (! add_to_hash_set(&distfiles, file)) 890 return; 891 892 jb_message("adding %s to dist", file); 893 894 dir = g_path_get_dirname(file); 895 distdir = g_strdup_printf("%s/%s", jb_action_get_distdir(), dir); 896 g_free(dir); 897 898 jb_mkdir(distdir); 899 900 jb_action_exec("cp -p $file $distdir", 901 "file", file, 902 "distdir", distdir, 903 NULL); 904 905 g_free(distdir); 906 } 907 908 void 909 jb_action_add_to_dist_list (GSList *files) 910 { 911 GSList *l; 912 913 JB_LIST_FOREACH(l, files) 914 { 915 const char *file = l->data; 916 jb_action_add_to_dist(file); 917 } 918 } 919 920 void 921 jb_action_add_to_dist_string_list (const char *files) 922 { 923 int i; 924 char **array; 925 926 g_return_if_fail(files != NULL); 927 928 array = g_strsplit(files, " ", 0); 929 930 for (i = 0; array[i] != NULL; i++) 931 { 932 const char *file = array[i]; 933 934 /* the files array can contain extra spaces */ 935 if (*file != '\0') 936 jb_action_add_to_dist(array[i]); 937 } 938 939 g_strfreev(array); 940 } 941 942 const char * 943 jb_action_get_distdir (void) 944 { 945 static char *distdir = NULL; 946 947 if (distdir == NULL) 948 distdir = jb_variable_expand("build/${package}-$version", NULL); 949 950 return distdir; 951 }