1 /* build.c -- main file of the Build.
3 Copyright (C) 2022 Sergey Sushilin <sergeysushilin@protonmail.com>
5 This file is part of Build.
7 Build is free software: you can redistribute it and/or
8 modify it under the terms of either the GNU General Public License
9 as published by the Free Software Foundation;
10 either version 2 of the License, or version 3 of the License,
11 or both in parallel, as here.
13 Build is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received copies of the GNU General Public License
19 version 2 and 3 along with this program.
20 If not, see http://www.gnu.org/licenses/. */
29 #if defined (__FreeBSD__) || defined (__OpenBSD__) || defined (__NetBSD__)
30 # define __BSD_VISIBLE 1
31 # include <sys/types.h>
32 # include <sys/sysctl.h>
40 //#include "depfile-scan.h"
41 #include "diagnostic.h"
43 #include "job-server.h"
54 variable_create (Hash
*variables
, uniqstr name
, struct uniqstr_array
*strings
)
56 struct variable
*variable
;
58 if (UNLIKELY (STRLITEQ (name
, "in")
59 || STRLITEQ (name
, "out")
60 || STRLITEQ (name
, "target")
61 || STRLITEQ (name
, "dependencies")
62 || STRLITEQ (name
, "files")))
64 error (_("%s: this variable name is reserved"), name
);
68 variable
= xmalloc (sizeof (*variable
));
69 variable
->name
= name
;
70 variable
->value
= *strings
;
71 HashInsert (variables
, name
, uniqstr_len (name
), variable
);
76 variable_lookup (const Hash
*variables
, uniqstr name
,
77 const struct variable
**v
)
79 const struct variable
*variable
= HashFind (variables
, name
, uniqstr_len (name
));
81 if (LIKELY (variable
!= NULL
))
87 error (_("variable %s is not defined"), name
);
92 variable_destroy (void *p
)
94 struct variable
*variable
= p
;
96 free (variable
->value
.array
);
100 static inline void NONNULL (1, 2)
101 variables_duplicate (Hash
*dst
, const Hash
*src
)
105 for (HashElem
*p
= HashFirst (src
); p
!= NULL
; p
= HashNext (p
))
107 const struct variable
*v
= p
->data
;
108 struct variable
*variable
= xmalloc (sizeof (*variable
));
110 variable
->name
= v
->name
;
111 variable
->value
.count
= v
->value
.count
;
112 variable
->value
.array
= xmallocarray (variable
->value
.count
,
113 sizeof (*variable
->value
.array
));
115 for (size_t i
= 0; i
< variable
->value
.count
; i
++)
116 variable
->value
.array
[i
] = v
->value
.array
[i
];
118 HashInsert (dst
, HashKeyPtr (p
), HashKeySize (p
), variable
);
123 target_create (struct target
**t
,
126 struct target_skeleton
*skeleton
)
128 if (UNLIKELY (HashFind (targets
, name
, uniqstr_len (name
)) != NULL
))
130 error (_("redefinition of target %s"), name
);
134 if (skeleton
->files
.count
!= 0 && skeleton
->apply_rules
.count
== 0)
136 error (_("target %s depends on files, but no rules provided"), name
);
140 struct target
*target
= xmalloc (sizeof (*target
));
143 target
->skeleton
= skeleton
;
145 HashInsert (targets
, name
, uniqstr_len (name
), target
);
154 target_skeleton_destroy (void *p
)
157 struct target_skeleton
*skeleton
= p
;
159 free (skeleton
->dependencies
.array
);
160 free (skeleton
->files
.array
);
161 free (skeleton
->apply_rules
.array
);
165 static void rule_destroy (void *p
);
168 rule_create (Hash
*rules
, uniqstr name
, uniqstr description
,
169 struct uniqstr_array
*commands
, const Hash
*variables
)
171 if (UNLIKELY (HashFind (rules
, name
, uniqstr_len (name
)) != NULL
))
173 error (_("redefinition of rule %s"), name
);
177 struct rule
*rule
= xmalloc (sizeof (*rule
));
180 rule
->description
= description
;
181 rule
->commands
= *commands
;
182 variables_duplicate (&rule
->variables
, variables
);
183 HashInsert (rules
, name
, uniqstr_len (name
), rule
);
188 rule_destroy (void *p
)
190 struct rule
*rule
= p
;
192 free (rule
->commands
.array
);
193 HashClearWithDestructor (&rule
->variables
, variable_destroy
);
198 is_name_character (char c
)
200 #if '@' == 0x40 /* ASCII character set. */
202 ((uint64_t) 1 << (c % BITSIZE))
204 enum { BITSIZE
= 8 * sizeof (uint64_t) };
205 const uint64_t name_characters
[256 / BITSIZE
] =
278 const unsigned char uc
= *(unsigned char *) &c
;
280 return name_characters
[uc
/ BITSIZE
] & ((uint64_t) 1 << (uc
% BITSIZE
));
282 return (c
>= 'a' && c
<= 'z')
283 || (c
>= 'A' && c
<= 'Z')
284 || (c
>= '0' && c
<= '9')
285 || (c
== '-' || c
== '_');
290 static inline NODISCARD
bool
291 argv_append (unsigned int *restrict arga
,
292 unsigned int *restrict argc
,
296 unsigned int a
= *arga
;
297 unsigned int c
= *argc
;
300 if (UNLIKELY (c
>= a
- 1))
302 v
= xreallocarray (v
, 2 * a
, sizeof (*v
));
314 argv_free (uniqstr
*argv
)
320 ascii_isspace (char c
)
322 /* Make sure that characters have appropriate ASCII numbers. */
342 const uint64_t spaces
[256 / sizeof (uint64_t)] =
344 ((uint64_t) 1 << '\t')
345 | ((uint64_t) 1 << '\f')
346 | ((uint64_t) 1 << '\n')
347 | ((uint64_t) 1 << '\r')
348 | ((uint64_t) 1 << '\v')
349 | ((uint64_t) 1 << ' '),
351 const unsigned char uc
= *(unsigned char *) &c
;
352 enum { BITSIZE
= 8 * sizeof (uint64_t) };
354 return spaces
[uc
/ BITSIZE
] & ((uint64_t) 1 << (uc
% BITSIZE
));
358 static inline NODISCARD
bool
359 parse_command (uniqstr command
,
360 const Hash
*variables
,
367 unsigned int argc
= 0;
368 unsigned int arga
= 8;
372 argv
= xmalloc (arga
* sizeof (*argv
));
373 buffer
= xmalloc (uniqstr_len (command
) + 1);
375 /* Is a do-while to always execute the loop once. Always return an
376 argv, even for null strings. */
379 while (ascii_isspace (command
[i
]))
382 /* Begin scanning argument. */
385 while (command
[i
] != '\0')
387 if (UNLIKELY (ascii_isspace (command
[i
]) && !dquote
))
390 dquote
^= (command
[i
] == '"');
392 if (command
[i
] == '@')
394 bool begins_by_curly_bracked
= (command
[i
+ 1] == '{');
396 if (!is_name_character (command
[i
+ 1])
397 && !begins_by_curly_bracked
)
399 error (_("expected variable name"));
405 const struct variable
*variable
;
409 while (command
[i
+ len
] != '\0')
411 if ((begins_by_curly_bracked
&& command
[i
+ len
] == '}')
412 || ascii_isspace (command
[i
+ len
]))
415 if (!is_name_character (command
[i
+ len
]))
417 if (begins_by_curly_bracked
)
418 error (_("expected '}', but got '%c'"),
429 name
= uniqstr_new_from_buffer (command
+ i
+ begins_by_curly_bracked
, len
- begins_by_curly_bracked
);
431 if (UNLIKELY (!variable_lookup (variables
, name
, &variable
)))
434 if (begins_by_curly_bracked
&& variable
->value
.count
!= 1)
436 error (_("can not use curly brackets with multi-string variable"));
440 for (size_t j
= 0; j
< variable
->value
.count
; j
++)
441 if (UNLIKELY (!argv_append (&arga
, &argc
, &argv
,
442 variable
->value
.array
[j
])))
445 i
+= len
+ begins_by_curly_bracked
;
456 if (UNLIKELY (!argv_append (&arga
, &argc
, &argv
, uniqstr_new (buffer
))))
460 while (ascii_isspace (command
[i
]))
463 while (command
[i
] != '\0');
477 static inline NODISCARD
bool
478 parse_description (const uniqstr description
,
479 const Hash
*variables
,
482 size_t slen
= uniqstr_len (description
);
483 uniqstr d
= description
;
485 char *s
= xmalloc (slen
+ 1);
489 if (d
[0] == '@' && is_name_character (d
[1]))
493 const struct variable
*variable
;
495 while (is_name_character (d
[len
]))
498 name
= uniqstr_new_from_buffer (d
+ 1, len
- 1);
500 if (UNLIKELY (!variable_lookup (variables
, name
, &variable
)))
503 for (size_t i
= 0; i
< variable
->value
.count
; i
++)
504 slen
+= uniqstr_len (variable
->value
.array
[i
]) + 1;
506 s
= xrealloc (s
, slen
+ 1);
508 for (size_t i
= 0; i
< variable
->value
.count
; i
++)
510 memcpy (s
+ written
, variable
->value
.array
[i
],
511 uniqstr_len (variable
->value
.array
[i
]));
512 written
+= uniqstr_len (variable
->value
.array
[i
]);
534 static char *substitude_variables (uniqstr command
, const Hash
*variables
,
535 struct quoting_slots
*qs
)
539 size_t size
= uniqstr_len (command
) + 1;
540 char *result
= xmalloc (size
);
541 size_t rest_length
= uniqstr_len (command
);
542 const char *cur
= command
;
543 const char *prv
= command
;
544 const char *const end
= command
+ rest_length
;
546 while ((cur
= memchr (cur
, '@', rest_length
)) != NULL
)
550 memcpy (result
+ length
, prv
, (size_t) (cur
- prv
));
551 length
+= (size_t) (cur
- prv
);
556 size_t variable_name_length
= 0;
558 for (size_t i
= 0; is_name_character (cur
[i
]); i
++)
559 variable_name_length
++;
561 uniqstr name
= uniqstr_new_from_buffer (cur
, variable_name_length
);
562 struct variable
*variable
= HashFind (variables
, name
, uniqstr_len (name
));
564 if (UNLIKELY (variable
== NULL
))
566 error (_("variable is not declared: %s"),
571 struct uniqstr_array
*value
= &variable
->value
;
574 for (size_t i
= 0; i
< value
->count
; i
++)
575 add_size
+= (uniqstr_len (value
->array
[i
]) + ((i
!= value
->count
- 1) ? 1 : 0));
578 size
-= variable_name_length
+ 1;
579 result
= xrealloc (result
, size
);
581 for (size_t i
= 0; i
< value
->count
; i
++)
583 memcpy (result
+ length
, value
->array
[i
],
584 uniqstr_len (value
->array
[i
]));
585 length
+= uniqstr_len (value
->array
[i
]);
587 if (i
!= value
->count
- 1)
588 result
[length
++] = ' ';
591 cur
+=variable_name_length
;
592 rest_length
-= (size_t) (cur
- prv
);
598 memcpy (result
+ length
, prv
, (size_t) (end
- prv
));
599 length
+= (size_t) (end
- prv
);
607 static inline NODISCARD
bool NONNULL (2, 3, 4)
608 command_execute (uniqstr description
,
610 const Hash
*variables
,
611 struct quoting_slots
*qs
,
615 //TODOunsigned int argc;
618 if (description
== NULL
)
621 for (unsigned int j = 0; j < argc; j++)
623 fputs (argv[j], stdout);
629 fputc ('\n', stdout);*/
636 if (LIKELY (parse_description (description
, variables
, &desc
)))
642 warning (_("%s: failed to parse rule description"), quote (qs
, description
));
646 if (UNLIKELY (!parse_command (command
, variables
, &argc
, &argv
)))
649 if (!dryrun
&& spawn_process (argv
, envp
) != SPAWN_SUCCESS
)
651 error (_("%s: failed to execute"), quotef (qs
, argv
[0]));
658 command_t
*root
= NULL
;
660 if (!bsh_parse_line (/* substitude_variables (command, variables, qs) */command
, &root
, variables
))
663 if (!dryrun
&& root
!= NULL
&& bsh_parse_command (root
, NULL
) != 0)
670 static inline NODISCARD
bool NONNULL (1, 2, 3)
671 rule_apply (const struct apply_rule
*apply_rule
,
673 struct quoting_slots
*qs
,
678 struct variable in
= { 0 };
679 struct variable out
= { 0 };
680 struct rule
*r
= HashFind (rules
, apply_rule
->name
, uniqstr_len (apply_rule
->name
));
681 uniqstr out_array
[1] = { 0 };
683 if (UNLIKELY (r
== NULL
))
685 error (_("can not find rule %s"), quote (qs
, apply_rule
->name
));
689 variables_duplicate (&variables
, &r
->variables
);
691 if (LIKELY (apply_rule
->in
.count
!= 0))
693 in
.name
= uniqstr_new_from_literal_string ("in");
694 in
.value
.count
= apply_rule
->in
.count
;
695 in
.value
.array
= apply_rule
->in
.array
;
696 HashInsert (&variables
, in
.name
, uniqstr_len (in
.name
), &in
);
699 if (LIKELY (apply_rule
->out
!= NULL
))
701 out_array
[0] = (uniqstr
) apply_rule
->out
;
702 out
.name
= uniqstr_new_from_literal_string ("out");
704 out
.value
.array
= out_array
;
705 HashInsert (&variables
, out
.name
, uniqstr_len (out
.name
), &out
);
710 for (size_t i
= 0; i
< r
->commands
.count
; i
++)
711 if (UNLIKELY (!command_execute (r
->description
, r
->commands
.array
[i
],
712 &variables
, qs
, envp
, dryrun
)))
718 if (LIKELY (apply_rule
->in
.count
!= 0))
719 HashRemove (&variables
, in
.name
, uniqstr_len (in
.name
));
721 if (LIKELY (apply_rule
->out
!= NULL
))
722 HashRemove (&variables
, out
.name
, uniqstr_len (out
.name
));
724 HashClearWithDestructor (&variables
, variable_destroy
);
729 static inline NODISCARD
bool
730 pattern_target_match (uniqstr target_name
, uniqstr name
, uniqstr
*percent
)
732 const char *p
= uniqstr_chr (target_name
, '%');
733 const size_t start_len
= (size_t) (p
- target_name
);
734 const size_t finish_len
= (size_t) (target_name
+ uniqstr_len (target_name
) - p
- 1);
736 /* Always non-null. */
737 if (uniqstr_len (name
) < (start_len
+ finish_len
+ 1))
740 if (start_len
!= 0 && memcmp (name
, target_name
, start_len
) != 0)
743 if (finish_len
!= 0 && memcmp (name
+ uniqstr_len (name
) - finish_len
, target_name
+ uniqstr_len (target_name
) - finish_len
, finish_len
) != 0)
746 *percent
= uniqstr_new_from_buffer (name
+ start_len
, uniqstr_len (name
) - (start_len
+ finish_len
));
750 static inline NODISCARD
size_t PURE
751 percent_count (uniqstr s
)
754 size_t n
= uniqstr_len (s
);
756 for (const char *p
= s
; (s
= memchr (s
, '%', n
)) != NULL
; p
= s
)
759 n
-= (size_t) (s
- p
);
766 static inline NODISCARD uniqstr
767 replace_percents (uniqstr src
, uniqstr percent
)
770 size_t count
= percent_count (src
);
775 char *const base
= uniqstr_prepare (count
* uniqstr_len (percent
) + uniqstr_len (src
) - 1);
777 const char *prv
= src
;
778 const char *cur
= src
;
779 size_t len
= uniqstr_len (src
);
780 const char *const end
= src
+ len
;
782 while ((cur
= memchr (cur
, '%', len
)) != NULL
)
786 memcpy (r
, prv
, (size_t) (cur
- prv
));
790 memcpy (r
, percent
, uniqstr_len (percent
));
791 r
+= uniqstr_len (percent
);
793 len
-= (size_t) (cur
- prv
);
799 memcpy (r
, prv
, (size_t) (end
- prv
));
804 s
= uniqstr_finish_preparation (base
);
809 struct target_skeleton
*
810 target_skeleton_create (Hash
*skeletons
,
811 struct uniqstr_array
*dependencies
,
812 struct uniqstr_array
*files
,
813 struct uniqstr_array
*outfiles
,
814 struct apply_rule_array
*apply_rules
)
816 struct target_skeleton
*skeleton
= xmalloc (sizeof (*skeleton
));
818 skeleton
->dependencies
= *dependencies
;
819 skeleton
->files
= *files
;
820 skeleton
->outfiles
= *outfiles
;
821 skeleton
->apply_rules
= *apply_rules
;
822 atomic_init (&skeleton
->need_rebuild
, false);
823 HashInsert (skeletons
, skeleton
, sizeof (*skeleton
), skeleton
);
828 static inline NODISCARD struct target * NONNULL (1, 2)
829 target_create_from_pattern_target (Hash *targets,
830 const struct target *pattern_target,
833 #define ARRAY_DUPLICATE(a0, a1) \
836 if (LIKELY ((a1).count != 0)) \
838 (a0).count = (a1).count; \
839 (a0).array = xmallocarray ((a0).count, sizeof (*(a0).array)); \
841 for (size_t __i = 0; __i < (a0).count; __i++) \
842 (a0).array[__i] = replace_percents ((a1).array[__i], \
850 struct uniqstr_array dependencies = { 0 };
851 struct uniqstr_array files = { 0 };
852 struct uniqstr_array outfiles = { 0 };
853 struct apply_rule_array apply_rules = { 0 };
855 name = replace_percents (pattern_target->name, percent);
856 ARRAY_DUPLICATE (dependencies, pattern_target->skeleton->dependencies);
857 ARRAY_DUPLICATE (files, pattern_target->skeleton->files);
858 ARRAY_DUPLICATE (outfiles, pattern_target->skeleton->outfiles);
860 if (LIKELY (pattern_target->skeleton->apply_rules.count != 0))
862 apply_rules.count = pattern_target->skeleton->apply_rules.count;
863 apply_rules.array = xmallocarray (apply_rules.count, sizeof (*apply_rules.array));
865 for (size_t i = 0; i < pattern_target->skeleton->apply_rules.count; i++)
867 apply_rules.array[i].name = pattern_target->skeleton->apply_rules.array[i].name;
869 if (LIKELY (pattern_target->skeleton->apply_rules.array[i].in.count != 0))
870 ARRAY_DUPLICATE (apply_rules.array[i].in, pattern_target->skeleton->apply_rules.array[i].in);
872 if (LIKELY (pattern_target->skeleton->apply_rules.array[i].out != NULL))
873 apply_rules.array[i].out = replace_percents (pattern_target->skeleton->apply_rules.array[i].out, percent);
875 apply_rules.array[i].out = NULL;
879 struct target_skeleton *skeleton = target_skeleton_create (&dependencies,
884 if (LIKELY (target_create (&t, targets, name, skeleton)))
888 #undef ARRAY_DUPLICATE
892 static NODISCARD
struct target
* NONNULL (1, 2)
893 target_find (const Hash
*targets
, uniqstr name
)
895 return HashFind (targets
, name
, uniqstr_len (name
));
898 static NODISCARD
bool NONNULL (1, 2, 3, 4, 6, 7, 8, 9)
899 build_tree_create (struct build_tree
*root
,
900 struct build_tree_branch
**pbranch
,
901 const Hash
*restrict targets
,
902 Hash
*restrict branches
,
906 struct quoting_slots
*qs
,
907 struct Hash
*restrict table_for_loop_detection
,
908 struct Hash
*restrict tracked_files
)
910 struct target
*target
= target_find (targets
, target_name
);
912 if (UNLIKELY (target
== NULL
))
914 error (_("there is no %s target"), quote (qs
, target_name
));
918 if (UNLIKELY (HashFind (table_for_loop_detection
,
920 uniqstr_len (target
->name
)) != NULL
))
922 error (_("dependence loop detected: %s depends on itself"),
923 quote (qs
, target_name
));
927 HashInsert (table_for_loop_detection
, target
->name
,
928 uniqstr_len (target
->name
), target
);
930 struct build_tree_branch
*branch
= HashFind (branches
, target
->name
,
931 uniqstr_len (target
->name
));
935 branch
= xmalloc (sizeof (*branch
));
936 branch
->target
= target
;
938 if (LIKELY (target
->skeleton
->dependencies
.count
!= 0))
940 branch
->branches
.count
= target
->skeleton
->dependencies
.count
;
941 branch
->branches
.array
= xmallocarray (branch
->branches
.count
, sizeof (*branch
->branches
.array
));
943 for (size_t i
= 0; i
< target
->skeleton
->dependencies
.count
; i
++)
945 if (UNLIKELY (!build_tree_create (root
,
946 &branch
->branches
.array
[i
],
950 target
->skeleton
->dependencies
.array
[i
],
951 wd
, qs
, table_for_loop_detection
,
955 target
->skeleton
->need_rebuild
|= branch
->branches
.array
[i
]->target
->skeleton
->need_rebuild
;
960 branch
->branches
.count
= 0;
961 branch
->branches
.array
= NULL
;
964 for (size_t i
= 0; i
< target
->skeleton
->files
.count
; i
++)
966 if (target_find (targets
, target
->skeleton
->files
.array
[i
]) != NULL
)
968 branch
->branches
.array
= xreallocarray (branch
->branches
.array
, (branch
->branches
.count
+ 1),
969 sizeof (*branch
->branches
.array
));
971 if (UNLIKELY (!build_tree_create (root
,
972 &branch
->branches
.array
[branch
->branches
.count
],
976 target
->skeleton
->files
.array
[i
],
977 wd
, qs
, table_for_loop_detection
,
981 target
->skeleton
->need_rebuild
|= branch
->branches
.array
[branch
->branches
.count
]->target
->skeleton
->need_rebuild
;
982 branch
->branches
.count
++;
986 target
->skeleton
->need_rebuild
|= rebuild
;
988 for (size_t i
= 0; i
< target
->skeleton
->files
.count
; i
++)
989 target
->skeleton
->need_rebuild
|= watchdog_track_file (wd
, tracked_files
, target
->skeleton
->files
.array
[i
], qs
);
991 for (size_t i
= 0; !target
->skeleton
->need_rebuild
&& i
< target
->skeleton
->outfiles
.count
; i
++)
992 target
->skeleton
->need_rebuild
|= (access (target
->skeleton
->outfiles
.array
[i
], F_OK
) != 0);
994 root
->count_of_targets
++;
995 HashInsert (branches
, target
->name
, uniqstr_len (target
->name
), branch
);
998 HashRemove (table_for_loop_detection
, target
->name
, uniqstr_len (target
->name
));
1003 static NODISCARD
bool NONNULL (1, 2, 3)
1004 build (const struct build_tree_branch
*branch
,
1006 struct quoting_slots
*qs
,
1010 for (size_t i
= 0; i
< branch
->target
->skeleton
->apply_rules
.count
; i
++)
1011 if (UNLIKELY (!rule_apply (&branch
->target
->skeleton
->apply_rules
.array
[i
], rules
, qs
, envp
, dryrun
)))
1014 for (size_t i
= 0; i
< branch
->target
->skeleton
->outfiles
.count
; i
++)
1015 if (access (branch
->target
->skeleton
->outfiles
.array
[i
], F_OK
) != 0)
1017 error (_("target %s must have created file %s"),
1018 quote_n (qs
, 0, branch
->target
->name
),
1019 quote_n (qs
, 1, branch
->target
->skeleton
->outfiles
.array
[i
]));
1023 branch
->target
->skeleton
->need_rebuild
= false;
1028 build_target (const struct build_tree_branch
*branch
,
1029 const Hash
*restrict rules
,
1030 struct quoting_slots
*qs
,
1032 const Hash
*restrict tracked_files
,
1036 bool need_rebuild
= branch
->target
->skeleton
->need_rebuild
;
1038 for (size_t i
= 0; i
< branch
->target
->skeleton
->files
.count
; i
++)
1039 need_rebuild
|= watchdog_file_updated (tracked_files
, branch
->target
->skeleton
->files
.array
[i
], qs
);
1043 info (_("nothing to be done for %s"), quote (qs
, branch
->target
->name
));
1048 if (LIKELY (build (branch
, rules
, qs
, envp
, dryrun
)))
1050 /* Do not inform about targets with no rules. */
1051 if (branch
->target
->skeleton
->apply_rules
.count
!= 0)
1052 info (_("%s: was successfully built"),
1053 quote (qs
, branch
->target
->name
));
1059 error (_("%s: building failed"), quote (qs
, branch
->target
->name
));
1067 static NODISCARD
bool NONNULL (1, 2, 3)
1068 build_tree_traverse (struct build_tree
*tree
,
1069 const Hash
*restrict rules
,
1071 const Hash
*restrict tracked_files
,
1075 struct job_server job_server
;
1078 if (tree
->count_of_targets
== 0)
1081 if (!job_server_initialize (&job_server
, build_target
, n_cores
* 2,
1082 rules
, envp
, tracked_files
, dryrun
))
1085 if (!job_list_create_from_build_tree (&job_server
.job_list
, tree
))
1088 status
&= job_server_start (&job_server
);
1089 status
&= job_server_finalize (&job_server
);
1094 build_tree_branch_destroy (struct build_tree_branch
*branch
)
1096 for (size_t i
= 0; i
< branch
->branches
.count
; i
++)
1097 build_tree_branch_destroy (branch
->branches
.array
[i
]);
1099 if (branch
->branches
.count
!= 0)
1101 free (branch
->branches
.array
);
1102 branch
->branches
.count
= 0;
1105 //TODO free (branch);
1109 build_tree_destroy (struct build_tree
*tree
)
1111 build_tree_branch_destroy (tree
->root
);
1112 //TODO free (tree->root);
1115 static inline NODISCARD
size_t
1116 get_count_of_processor_cores (void)
1118 #if defined (__linux__)
1119 long n_cores
= sysconf (_SC_NPROCESSORS_ONLN
);
1121 return (n_cores
< 1) ? 1 : (size_t) n_cores
;
1122 #elif defined (__FreeBSD__) || defined (__OpenBSD__) || defined (__NetBSD__)
1125 size_t size
= sizeof (n_cores
);
1127 /* Set the mib for hw.ncpu. */
1131 /* Get the number of CPUs from the system. */
1132 sysctl (mib
, 2, &n_cores
, &size
, NULL
, 0);
1134 return (n_cores
< 1) ? 1 : (size_t) n_cores
;
1135 #elif defined (_WIN32)
1136 SYSTEM_INFO sysinfo
;
1139 GetSystemInfo (&sysinfo
);
1140 n_cores
= sysinfo
.dwNumberOfProcessors
;
1142 return (size_t) n_cores
;
1144 # warning "can not determine count of processor's cores."
1149 struct build_options
1159 bool list_targets
:1;
1161 static const char getopt_options
[] = "f:j:t:d:Dgrvl";
1163 static const options_t getopt_options =
1165 ['f'] = { .is_option = true, .takes_argument = true },
1166 ['j'] = { .is_option = true, .takes_argument = true },
1167 ['t'] = { .is_option = true, .takes_argument = true },
1168 ['d'] = { .is_option = true, .takes_argument = true },
1170 ['D'] = { .is_option = true, .takes_argument = false },
1171 ['g'] = { .is_option = true, .takes_argument = false },
1172 ['r'] = { .is_option = true, .takes_argument = false },
1173 ['v'] = { .is_option = true, .takes_argument = false },
1174 ['l'] = { .is_option = true, .takes_argument = false },
1178 build_options_initialize (struct build_options
*o
)
1180 o
->file
= uniqstr_new_from_literal_string ("default.build");
1181 o
->target
= uniqstr_new_from_literal_string ("default");
1182 o
->directory
= NULL
;
1183 o
->n_jobs
= 2 * get_count_of_processor_cores ();
1188 o
->list_targets
= false;
1191 static inline bool ACCESS (write_only
, 1) ACCESS (read_only
, 2)
1192 string_to_number (size_t *number
, const char *string
)
1195 const char *s
= string
;
1197 while (((unsigned int) *s
- '0') <= 9)
1199 if (x
> SIZE_MAX
/ 10 || (x
* 10 > SIZE_MAX
- (size_t) (*s
- '0')))
1200 fatal (_("number is too big: %s"), string
);
1202 x
= x
* 10 + (size_t) (*s
++ - '0');
1207 error ("unexpected character '%c' in number", *s
);
1215 static inline NODISCARD
bool
1216 parse_options (struct build_options
*o
, int argc
, char **argv
)
1220 while ((optc
= getopt (argc
, argv
, getopt_options
)) >= 0)
1225 o
->file
= uniqstr_new (optarg
);
1228 o
->target
= uniqstr_new (optarg
);
1231 o
->directory
= uniqstr_new (optarg
);
1234 if (!string_to_number (&o
->n_jobs
, optarg
))
1251 o
->list_targets
= true;
1262 parse_build_file (uniqstr file
,
1263 struct build_context
*context
,
1264 struct quoting_slots
*qs
)
1272 if (UNLIKELY (yylex_init (&yyscanner
) != 0))
1274 error (_("failed to initialize flex scanner"));
1278 fp
= fopen (file
, "r");
1280 if (UNLIKELY (fp
== NULL
))
1282 error (_("%s: can not open file: %s"),
1283 quotef (qs
, file
), strerror (errno
));
1287 extra
.yylocation
= &yylocation
;
1289 yylocation_initialize (&yylocation
, uniqstr_new (quotef (qs
, file
)));
1290 yyset_extra (&extra
, yyscanner
);
1291 yyset_in (fp
, yyscanner
);
1292 yyset_out (stdout
, yyscanner
);
1293 status
= yyparse (yyscanner
, context
);
1294 yylex_destroy (yyscanner
);
1297 if (UNLIKELY (status
!= 0))
1299 error (_("%s: parsing failed"), quotef (qs
, file
));
1307 uniqstr_comparator (uniqstr
*u1
, uniqstr
*u2
)
1309 return uniqstr_cmp (*u1
, *u2
);
1313 target_list (const Hash
*targets
)
1315 struct uniqstr_array array
=
1317 .count
= targets
->count
,
1318 .array
= xmallocarray (targets
->count
, sizeof (uniqstr
))
1322 HashElem
*p
= HashFirst (targets
);
1324 for (size_t i
= 0; i
< array
.count
; i
++)
1326 array
.array
[i
] = ((struct target
*) HashData (p
))->name
;
1331 qsort (array
.array
, array
.count
, sizeof (uniqstr
),
1332 (int (*) (const void *, const void *)) uniqstr_comparator
);
1334 for (size_t i
= 0; i
< array
.count
; i
++)
1335 puts (array
.array
[i
]);
1339 main (int argc
, char **argv
, char **envp
)
1341 struct build_context context
;
1342 struct build_tree tree
;
1343 struct quoting_slots quoting_slots
;
1344 struct build_options options
;
1345 struct watchdog watchdog
;
1346 Hash table_for_loop_detection
;
1350 setlocale (LC_ALL
, "");
1351 diagnostic_initialize (argv
[0]);
1353 if (UNLIKELY (bindtextdomain (PACKAGE
, LOCALEDIR
) == NULL
))
1354 warning (_("failed to set base directory for text domain"));
1356 if (UNLIKELY (textdomain (PACKAGE
) == NULL
))
1357 warning (_("failed to set current message domain"));
1359 quoting_slots_initialize ("ing_slots
);
1360 uniqstrs_table_create ();
1361 build_options_initialize (&options
);
1362 context
.default_target
= NULL
;
1363 HashInit (&context
.skeletons
);
1364 HashInit (&context
.variables
);
1365 HashInit (&context
.rules
);
1366 HashInit (&context
.targets
);
1367 // HashInit (&context.depfiles);
1368 HashInit (&tracked_files
);
1370 if (UNLIKELY (!parse_options (&options
, argc
, argv
)))
1373 if (options
.directory
!= NULL
)
1374 if (chdir (options
.directory
) != 0)
1375 fatal (_("failed to change directory to %s: %s"),
1376 quotef ("ing_slots
, options
.directory
), strerror (errno
));
1378 if (options
.verbose
)
1379 diagnostic_enable_verbose ();
1383 if (options
.rebuild
)
1384 warning (_("-r option is useless with -g option"));
1387 warning (_("-d option is useless with -g option"));
1390 if (UNLIKELY (!parse_build_file (options
.file
, &context
, "ing_slots
)))
1393 if (options
.list_targets
)
1395 target_list (&context
.targets
);
1399 /*TODO struct depfile_scanner depfile_scanner;
1401 if (!depfile_scanner_initialize (&depfile_scanner, "ing_slots))
1403 error (_("failed to initialize dependency file scanner"));
1408 if (UNLIKELY (!watchdog_initialize (&watchdog
)))
1412 for (HashElem *p = HashFirst (&context.depfiles); p != NULL; p = HashNext (p))
1413 depfile_scan (&depfile_scanner, &watchdog, &tracked_files, HashData (p), &context.targets);
1415 depfile_scanner_finalize (&depfile_scanner);
1417 options
.rebuild
|= watchdog_track_file (&watchdog
, &tracked_files
, options
.file
, "ing_slots
);
1418 HashInit (&branches
);
1419 HashInit (&table_for_loop_detection
);
1420 tree
.count_of_targets
= 0;
1422 uniqstr target
= (options
.target
== NULL
1423 ? (context
.default_target
== NULL
1424 ? uniqstr_new_from_literal_string ("default")
1425 : context
.default_target
)
1428 if (UNLIKELY (!build_tree_create (&tree
,
1436 &table_for_loop_detection
,
1439 error (_("failed to create build-tree"));
1443 watchdog_finalize (&watchdog
);
1444 //TODOtree.count_of_targets += (tree.root->target->skeleton->need_rebuild ? 1 : 0);
1445 HashClear (&table_for_loop_detection
);
1449 else if (UNLIKELY (!build_tree_traverse (&tree
,
1456 error (_("failed to build"));
1460 build_tree_destroy (&tree
);
1461 HashClearWithDestructor (&tracked_files
, free
);
1462 HashClearWithDestructor (&branches
, free
);
1463 HashClearWithDestructor (&context
.targets
, free
);
1464 HashClearWithDestructor (&context
.rules
, rule_destroy
);
1465 HashClearWithDestructor (&context
.variables
, variable_destroy
);
1466 HashClearWithDestructor (&context
.skeletons
, target_skeleton_destroy
);
1467 uniqstrs_table_destroy ();
1468 quoting_slots_finalize ("ing_slots
);