-
Notifications
You must be signed in to change notification settings - Fork 3
/
remake.cpp
3155 lines (2865 loc) · 81.3 KB
/
remake.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* -*- mode: C++; indent-tabs-mode: t; c-basic-offset: 8; -*- */
/**
@mainpage Remake, a build system that bridges the gap between make and redo.
As with <b>make</b>, <b>remake</b> uses a centralized rule file, which is
named <b>Remakefile</b>. It contains rules with a <em>make</em>-like
syntax:
@verbatim
target1 target2 ... : prerequisite1 prerequisite2 ...
shell script
that builds
the targets
@endverbatim
A target is known to be up-to-date if all its prerequisites are. If it
has no known prerequisites yet the file already exits, it is assumed to
be up-to-date. Obsolete targets are rebuilt thanks to the shell script
provided by the rule.
As with <b>redo</b>, <b>remake</b> supports dynamic dependencies in
addition to these static dependencies. Whenever a script executes
`remake prerequisite4 prerequisite5 ...`, these prerequisites are
rebuilt if they are obsolete. (So <b>remake</b> acts like
<b>redo-ifchange</b>.) Moreover, all the dependencies are stored in file
<b>.remake</b> so that they are remembered in subsequent runs. Note that
dynamic dependencies from previous runs are only used to decide whether a
target is obsolete; they are not automatically rebuilt when they are
obsolete yet a target depends on them. They will only be rebuilt once the
dynamic call to <b>remake</b> is executed.
In other words, the following two rules have almost the same behavior.
@verbatim
target1 target2 ... : prerequisite1 prerequisite2 ...
shell script
target1 target2 ... :
remake prerequisite1 prerequisite2 ...
shell script
@endverbatim
(There is a difference if the targets already exist, have never been
built before, and the prerequisites are either younger or obsolete, since
the targets will not be rebuilt in the second case.)
The above usage of dynamic dependencies is hardly useful. Their strength
lies in the fact that they can be computed on the fly:
@verbatim
%.o : %.c
gcc -MMD -MF [email protected] -o $@ -c $<
remake -r < [email protected]
%.cmo : %.ml
ocamldep $< | remake -r $@
ocamlc -c $<
after.xml: before.xml rules.xsl
xsltproc --load-trace -o after.xml rules.xsl before.xml 2> deps
remake `sed -n -e "\\,//,! s,^.*URL=\"\\([^\"]*\\).*\$,\\1,p" deps`
rm deps
@endverbatim
Note that the first rule fails if any of the header files included by
a C source file has to be automatically generated. In that case, one
should perform a first call to <b>remake</b> them before calling the
compiler. (Dependencies from several calls to <b>remake</b> are
cumulative, so they will all be remembered the next time.)
\section sec-usage Usage
Usage: <tt>remake <i>options</i> <i>targets</i></tt>
Options:
- `-B`, `--always-make`: Unconditionally make all targets.
- `-d`: Echo script commands.
- `-f FILE`: Read `FILE` as <b>Remakefile</b>.
- `-j[N]`, `--jobs=[N]`: Allow `N` jobs at once;
infinite jobs with no argument.
- `-k`, `--keep-going`: Keep going when some targets cannot be made.
- `-r`: Look up targets from the dependencies on standard input.
- `-s`, `--silent`, `--quiet`: Do not echo targets.
\section sec-syntax Syntax
Lines starting with a space character or a tabulation are assumed to be rule
scripts. They are only allowed after a rule header.
Lines starting with `#` are considered to be comments and are ignored.
They do interrupt rule scripts though.
Any other line is either a variable definition or a rule header. If such a
line ends with a backslash, the following line break is ignored and the line
extends to the next one.
Variable definitions are a single name followed by equal followed by a list
of names, possibly empty.
Rule headers are a nonempty list of names, followed by a colon, followed by
another list of names, possibly empty. Basically, the syntax of a rule is as
follows:
@verbatim
targets : prerequisites
shell script
@endverbatim
List of names are space-separated sequences of names. If a name contains
a space character, it should be put into double quotes. Names cannot be
any of the following special characters `:$(),="`. Again, quotation
should be used. Quotation marks can be escaped by a backslash inside
quoted names.
\subsection sec-variables Variables
Variables can be used to factor lists of targets or prerequisites. They are
expanded as they are encountered during <b>Remakefile</b> parsing.
@verbatim
VAR2 = a
VAR1 = c d
VAR2 += $(VAR1) b
$(VAR2) e :
@endverbatim
Variable assignments can appear instead of prerequisites inside non-generic
rules with no script. They are then expanded inside the corresponding
generic rule.
@verbatim
foo.o: CFLAGS += -DBAR
%.o : %.c
gcc $(CFLAGS) -MMD -MF [email protected] -o $@ -c $<
remake -r < [email protected]
@endverbatim
Note: contrarily to <b>make</b>, variable names have to be enclosed in
parentheses. For instance, `$y` is not a shorthand for <tt>\$(y)</tt> and
is left unexpanded.
\subsection sec-autovars Automatic variables
The following special symbols can appear inside scripts:
- `$<` expands to the first static prerequisite of the rule.
- `$^` expands to all the static prerequisites of the rule, including
duplicates if any.
- `$@` expands to the first target of the rule.
- `$*` expands to the string that matched `%` in a generic rule.
- `$$` expands to a single dollar symbol.
Note: contrarily to <b>make</b>, there are no corresponding variables.
For instance, `$^` is not a shorthand for `$(^)`. Another difference is
that `$@` is always the first target, not the one that triggered the
rule.
\subsection sec-functions Built-in functions
<b>remake</b> also supports a few built-in functions inspired from <b>make</b>.
- <tt>$(addprefix <i>prefix</i>, <i>list</i>)</tt> returns the list obtained
by prepending its first argument to each element of its second argument.
- <tt>$(addsuffix <i>suffix</i>, <i>list</i>)</tt> returns the list obtained
by appending its first argument to each element of its second argument.
\subsection sec-order Order-only prerequisites
If the static prerequisites of a rule contain a pipe symbol, prerequisites
on its right do not cause the targets to become obsolete if they are newer
(unless they are also dynamically registered as dependencies). They are
meant to be used when the targets do not directly depend on them, but the
computation of their dynamic dependencies does.
@verbatim
%.o : %.c | parser.h
gcc -MMD -MF [email protected] -o $@ -c $<
remake -r < [email protected]
parser.c parser.h: parser.y
yacc -d -o parser.c parser.y
@endverbatim
\subsection sec-static-pattern Static pattern rules
A rule with the following structure is expanded into several rules, one
per target.
@verbatim
targets: pattern1 pattern2 ...: prerequisites
@endverbatim
Every target is matched against one of the patterns containing the `%`
character. A rule is then created using the patterns as targets, after
having substituted `%` in the patterns and prerequisites. The automatic
variable `$*` can be used in the script of the rule.
\subsection sec-special-tgt Special targets
Target `.PHONY` marks its prerequisites as being always obsolete.
\subsection sec-special-var Special variables
Variable `.OPTIONS` is handled specially. Its content enables some
features of <b>remake</b> that are not enabled by default.
- `variable-propagation`: When a variable is set in the prerequisite
part of a rule, it is propagated to the rules of all the targets this rule
depends on. This option also enables variables to be set on the command
line. Note that, as in <b>make</b>, this features introduces non-determinism:
the content of some variables will depend on the build order.
\section sec-semantics Semantics
\subsection src-obsolete When are targets obsolete?
A target is obsolete:
- if there is no file corresponding to the target, or to one of its siblings
in a multi-target rule,
- if any of its dynamic prerequisites from a previous run or any of its static
prerequisites is obsolete,
- if the latest file corresponding to its siblings or itself is older than any
of its dynamic prerequisites or static prerequisites.
In all the other cases, it is assumed to be up-to-date (and so are all its
siblings). Note that the last rule above says "latest" and not "earliest". While
it might cause some obsolete targets to go unnoticed in corner cases, it allows
for the following kind of rules:
@verbatim
config.h stamp-config_h: config.h.in config.status
./config.status config.h
touch stamp-config_h
@endverbatim
A `config.status` file generally does not update header files (here
`config.h`) if they would not change. As a consequence, if not for the
`stamp-config_h` file above, a header would always be considered obsolete
once one of its prerequisites is modified. Note that touching `config.h`
rather than `stamp-config_h` would defeat the point of not updating it in
the first place, since the program files would need to be rebuilt.
Once all the static prerequisites of a target have been rebuilt, <b>remake</b>
checks whether the target still needs to be built. If it was obsolete only
because its prerequisites needed to be rebuilt and none of them changed, the
target is assumed to be up-to-date.
\subsection sec-rules How are targets (re)built?
There are two kinds of rules. If any of the targets or prerequisites contains
a `%` character, the rule is said to be <em>generic</em>. All the
targets of the rule shall then contain a single `%` character. All the
other rules are said to be <em>specific</em>.
A rule is said to <em>match</em> a given target:
- if it is specific and the target appears inside its target list,
- if it is generic and there is a way to replace the `%` character
from one of its targets so that it matches the given target.
When <b>remake</b> tries to build a given target, it looks for a specific rule
that matches it. If there is one and its script is nonempty, it uses it to
rebuild the target.
Otherwise, it looks for a generic rule that matches the target. If there are
several matching rules, it chooses the one with the shortest pattern (and if
there are several ones, the earliest one). It then looks for specific rules
that match each target of the generic rule. All the prerequisites of these
specific rules are added to those of the generic rule. The script of the
generic rule is used to build the target.
Example:
@verbatim
t%1 t2%: p1 p%2
commands building t%1 and t2%
t2z: p4
commands building t2z
ty1: p3
# t2x is built by the first rule (which also builds tx1) and its prerequisites are p1, px2
# t2y is built by the first rule (which also builds ty1) and its prerequisites are p1, py2, p3
# t2z is built by the second rule and its prerequisite is p4
@endverbatim
The set of rules from <b>Remakefile</b> is ill-formed:
- if any specific rule matching a target of the generic rule has a nonempty script,
- if any target of the generic rule is matched by a generic rule with a shorter pattern.
\section sec-compilation Compilation
- On Linux, MacOSX, and BSD: `g++ -o remake remake.cpp`
- On Windows: `g++ -o remake.exe remake.cpp -lws2_32`
Installing <b>remake</b> is needed only if <b>Remakefile</b> does not
specify the path to the executable for its recursive calls. Thanks to its
single source file, <b>remake</b> can be shipped inside other packages and
built at configuration time.
\section sec-differences Differences with other build systems
Differences with <b>make</b>:
- Dynamic dependencies are supported.
- For rules with multiple targets, the shell script is executed only once
and is assumed to build all the targets. There is no need for
convoluted rules that are robust enough for parallel builds. For generic
rules, this is similar to the behavior of pattern rules from <b>gmake</b>.
- As with <b>redo</b>, only one shell is run when executing a script,
rather than one per script line. Note that the shells are run with
option `-e`, thus causing them to exit as soon as an error is
encountered.
- The prerequisites of generic rules (known as implicit rules in <b>make</b>
lingo) are not used to decide between several of them, which means that
<b>remake</b> does not select one for which it could satisfy the dependencies.
- Variables and built-in functions are expanded as they are encountered
during <b>Remakefile</b> parsing.
- Target-specific variables are not propagated, unless specifically enabled,
since this causes non-deterministic builds. This is the same for variables
set on the command line.
Differences with <b>redo</b>:
- As with <b>make</b>, it is possible to write the following kind of rules
in <b>remake</b>.
@verbatim
Remakefile: Remakefile.in ./config.status
./config.status Remakefile
@endverbatim
- If a target is already built the first time <b>remake</b> runs, it still
uses the static prerequisites of rules mentioning it to check whether it
needs to be rebuilt. It does not assume it to be up-to-date. As with
<b>redo</b> though, if its obsolete status would be due to a dynamic
prerequisite, it will go unnoticed; it should be removed beforehand.
- Multiple targets are supported.
- <b>remake</b> has almost no features: no checksum-based dependencies, no
compatibility with job servers, etc.
\section sec-limitations Limitations
- If a rule script calls <b>remake</b>, the current working directory should
be the directory containing <b>Remakefile</b> (or the working directory
from the original <b>remake</b> if it was called with option `-f`).
- As with <b>make</b>, variables passed on the command line should keep
the same values, to ensure deterministic builds.
- Some cases of ill-formed rules are not caught by <b>remake</b> and can
thus lead to unpredictable behaviors.
\section sec-links Links
@see http://cr.yp.to/redo.html for the philosophy of <b>redo</b> and
https://github.com/apenwarr/redo for an implementation and some comprehensive documentation.
\section sec-licensing Licensing
@author Guillaume Melquiond
@version 0.15
@date 2012-2020
@copyright
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
\n
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
\section sec-internals Internals
The parent <b>remake</b> process acts as a server. The other ones have a
REMAKE_SOCKET environment variable that tells them how to contact the
server. They send the content of the REMAKE_JOB_ID environment variable,
so that the server can associate the child targets to the jobs that
spawned them. They then wait for completion and exit with the status
returned by the server. This is handled by #client_mode.
The server calls #load_dependencies and #save_dependencies to serialize
dynamic dependencies from <b>.remake</b>. It loads <b>Remakefile</b> with
#load_rules. It then runs #server_mode, which calls #server_loop.
When building a target, the following sequence of events happens:
- #start calls #find_rule (and #find_generic_rule) to get the rule.
- It then creates a pseudo-client if the rule has static dependencies, or
calls #run_script otherwise. In both cases, a new job is created; the
rule and the variables are stored into #jobs.
- #run_script creates a shell process and stores it in #job_pids. It
increases #running_jobs.
- The child process possibly calls <b>remake</b> with a list of targets.
- #accept_client receives a build request from a child process and adds
it to #clients. It also records the new dependencies of the job into
#dependencies. It increases #waiting_jobs.
- #handle_clients uses #get_status to look up the obsoleteness of the
targets.
- Once the targets of a request have been built or one of them has failed,
#handle_clients calls #complete_request and removes the request from
#clients.
- If the build targets come from a pseudo-client, #complete_request calls
#run_script. Otherwise it sends the reply to the corresponding child
process and decreases #waiting_jobs.
- When a child process ends, #server_loop calls #finalize_job, which
removes the process from #job_pids, decreases #running_jobs, and calls
#complete_job.
- #complete_job removes the job from #jobs and calls #update_status
to change the status of the targets. It also removes the target files in
case of failure.
*/
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define WINDOWS
#endif
#include <fstream>
#include <iostream>
#include <list>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifdef __APPLE__
#define MACOSX
#endif
#ifdef __linux__
#define LINUX
#endif
#ifdef WINDOWS
#include <windows.h>
#include <winbase.h>
#include <winsock2.h>
#define pid_t HANDLE
typedef SOCKET socket_t;
#else
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
typedef int socket_t;
enum { INVALID_SOCKET = -1 };
extern char **environ;
#endif
#if (defined(WINDOWS) || defined(MACOSX)) && !defined(MSG_NOSIGNAL)
enum { MSG_NOSIGNAL = 0 };
#endif
typedef std::list<std::string> string_list;
typedef std::set<std::string> string_set;
/**
* Reference-counted shared object.
* @note The default constructor delays the creation of the object until it
* is first dereferenced.
*/
template<class T>
struct ref_ptr
{
struct content
{
size_t cnt;
T val;
content(): cnt(1) {}
content(T const &t): cnt(1), val(t) {}
};
mutable content *ptr;
ref_ptr(): ptr(NULL) {}
ref_ptr(T const &t): ptr(new content(t)) {}
ref_ptr(ref_ptr const &p): ptr(p.ptr) { if (ptr) ++ptr->cnt; }
~ref_ptr() { if (ptr && --ptr->cnt == 0) delete ptr; }
ref_ptr &operator=(ref_ptr const &p)
{
if (ptr == p.ptr) return *this;
if (ptr && --ptr->cnt == 0) delete ptr;
ptr = p.ptr;
if (ptr) ++ptr->cnt;
return *this;
}
T &operator*() const
{
if (!ptr) ptr = new content;
return ptr->val;
}
T *operator->() const { return &**this; }
};
struct dependency_t
{
string_list targets;
string_set deps;
};
typedef std::map<std::string, ref_ptr<dependency_t> > dependency_map;
typedef std::map<std::string, string_list> variable_map;
/**
* Build status of a target.
*/
enum status_e
{
Uptodate, ///< Target is up-to-date.
Todo, ///< Target is missing or obsolete.
Recheck, ///< Target has an obsolete dependency.
Running, ///< Target is being rebuilt.
RunningRecheck, ///< Static prerequisites are being rebuilt.
Remade, ///< Target was successfully rebuilt.
Failed ///< Build failed for target.
};
/**
* Build status of a target.
*/
struct status_t
{
status_e status; ///< Actual status.
time_t last; ///< Last-modified date.
};
typedef std::map<std::string, status_t> status_map;
/**
* Delayed assignment to a variable.
*/
struct assign_t
{
bool append;
string_list value;
};
typedef std::map<std::string, assign_t> assign_map;
/**
* A rule loaded from Remakefile.
*/
struct rule_t
{
string_list targets; ///< Files produced by this rule.
string_list deps; ///< Dependencies used for an implicit call to remake at the start of the script.
string_list wdeps; ///< Like #deps, except that they are not registered as dependencies.
assign_map assigns; ///< Assignment of variables.
std::string stem; ///< Stem used to instantiate the rule, if any.
std::string script; ///< Shell script for building the targets.
};
typedef std::list<rule_t> rule_list;
typedef std::map<std::string, ref_ptr<rule_t> > rule_map;
/**
* A job created from a set of rules.
*/
struct job_t
{
rule_t rule; ///< Original rule.
variable_map vars; ///< Values of local variables.
};
typedef std::map<int, job_t> job_map;
typedef std::map<pid_t, int> pid_job_map;
/**
* Client waiting for a request to complete.
*
* There are two kinds of clients:
* - real clients, which are instances of remake created by built scripts,
* - pseudo clients, which are created by the server to build specific targets.
*
* Among pseudo clients, there are two categories:
* - original clients, which are created for the targets passed on the
* command line by the user or for the initial regeneration of the rule file,
* - dependency clients, which are created to handle rules that have
* explicit dependencies and thus to emulate a call to remake.
*/
struct client_t
{
socket_t socket; ///< Socket used to reply to the client (invalid for pseudo clients).
int job_id; ///< Job for which the built script called remake and spawned the client (negative for original clients).
bool failed; ///< Whether some targets failed in mode -k.
string_list pending; ///< Targets not yet started.
string_set running; ///< Targets being built.
variable_map vars; ///< Variables set on request.
bool delayed; ///< Whether it is a dependency client and a script has to be started on request completion.
client_t(): socket(INVALID_SOCKET), job_id(-1), failed(false), delayed(false) {}
};
typedef std::list<client_t> client_list;
/**
* Map from variable names to their content.
* Initialized with the values passed on the command line.
*/
static variable_map variables;
/**
* Map from targets to their known dependencies.
*/
static dependency_map dependencies;
/**
* Map from targets to their build status.
*/
static status_map status;
/**
* Set of generic rules loaded from Remakefile.
*/
static rule_list generic_rules;
/**
* Map from targets to specific rules loaded from Remakefile.
*/
static rule_map specific_rules;
/**
* Map of jobs being built.
*/
static job_map jobs;
/**
* Map from jobs to shell pids.
*/
static pid_job_map job_pids;
/**
* List of clients waiting for a request to complete.
* New clients are put to front, so that the build process is depth-first.
*/
static client_list clients;
/**
* Maximum number of parallel jobs (non-positive if unbounded).
* Can be modified by the -j option.
*/
static int max_active_jobs = 1;
/**
* Whether to keep building targets in case of failure.
* Can be modified by the -k option.
*/
static bool keep_going = false;
/**
* Number of jobs currently running:
* - it increases when a process is created in #run_script,
* - it decreases when a completion message is received in #finalize_job.
*
* @note There might be some jobs running while #clients is empty.
* Indeed, if a client requested two targets to be rebuilt, if they
* are running concurrently, if one of them fails, the client will
* get a failure notice and might terminate before the other target
* finishes.
*/
static int running_jobs = 0;
/**
* Number of jobs currently waiting for a build request to finish:
* - it increases when a build request is received in #accept_client
* (since the client is presumably waiting for the reply),
* - it decreases when a reply is sent in #complete_request.
*/
static int waiting_jobs = 0;
/**
* Global counter used to produce increasing job numbers.
* @see jobs
*/
static int job_counter = 0;
/**
* Socket on which the server listens for client request.
*/
static socket_t socket_fd;
/**
* Whether the request of an original client failed.
*/
static bool build_failure;
#ifndef WINDOWS
/**
* Name of the server socket in the file system.
*/
static char *socket_name;
#endif
/**
* Name of the first target of the first specific rule, used for default run.
*/
static std::string first_target;
/**
* Whether a short message should be displayed for each target.
*/
static bool show_targets = true;
/**
* Whether script commands are echoed.
*/
static bool echo_scripts = false;
/**
* Time at the start of the program.
*/
static time_t now = time(NULL);
/**
* Directory with respect to which command-line names are relative.
*/
static std::string working_dir;
/**
* Directory with respect to which targets are relative.
*/
static std::string prefix_dir;
/**
* Whether the prefix directory is different from #working_dir.
*/
static bool changed_prefix_dir;
/**
* Whether target-specific variables are propagated to prerequisites.
*/
static bool propagate_vars = false;
/**
* Whether targets are unconditionally obsolete.
*/
static bool obsolete_targets = false;
#ifndef WINDOWS
static sigset_t old_sigmask;
static volatile sig_atomic_t got_SIGCHLD = 0;
static void sigchld_handler(int)
{
got_SIGCHLD = 1;
}
static void sigint_handler(int)
{
// Child processes will receive the signal too, so just prevent
// new jobs from starting and wait for the running jobs to fail.
keep_going = false;
}
#endif
struct log
{
bool active, open;
int depth;
log(): active(false), open(false), depth(0)
{
}
std::ostream &operator()()
{
if (open) std::cerr << std::endl;
assert(depth >= 0);
std::cerr << std::string(depth * 2, ' ');
open = false;
return std::cerr;
}
std::ostream &operator()(bool o)
{
if (o && open) std::cerr << std::endl;
if (!o) --depth;
assert(depth >= 0);
if (o || !open) std::cerr << std::string(depth * 2, ' ');
if (o) ++depth;
open = o;
return std::cerr;
}
};
static struct log debug;
struct log_auto_close
{
bool still_open;
log_auto_close(): still_open(true)
{
}
~log_auto_close()
{
if (debug.active && still_open) debug(false) << "done\n";
}
};
#define DEBUG if (debug.active) debug()
#define DEBUG_open log_auto_close auto_close; if (debug.active) debug(true)
#define DEBUG_close if ((auto_close.still_open = false), debug.active) debug(false)
/**
* Strong typedef for strings that need escaping.
* @note The string is stored as a reference, so the constructed object is
* meant to be immediately consumed.
*/
struct escape_string
{
std::string const &input;
escape_string(std::string const &s): input(s) {}
};
/**
* Write the string in @a se to @a out if it does not contain any special
* characters, a quoted and escaped string otherwise.
*/
static std::ostream &operator<<(std::ostream &out, escape_string const &se)
{
std::string const &s = se.input;
char const *quoted_char = ",: '";
char const *escaped_char = "\"\\$!";
bool need_quotes = false;
char *buf = NULL;
size_t len = s.length(), last = 0, j = 0;
for (size_t i = 0; i < len; ++i)
{
if (strchr(escaped_char, s[i]))
{
need_quotes = true;
if (!buf) buf = new char[len * 2];
memcpy(&buf[j], &s[last], i - last);
j += i - last;
buf[j++] = '\\';
buf[j++] = s[i];
last = i + 1;
}
if (!need_quotes && strchr(quoted_char, s[i]))
need_quotes = true;
}
if (!need_quotes) return out << s;
out << '"';
if (!buf) return out << s << '"';
out.write(buf, j);
out.write(&s[last], len - last);
delete[] buf;
return out << '"';
}
/**
* @defgroup paths Path helpers
*
* @{
*/
/**
* Initialize #working_dir.
*/
static void init_working_dir()
{
char buf[1024];
char *res = getcwd(buf, sizeof(buf));
if (!res)
{
perror("Failed to get working directory");
exit(EXIT_FAILURE);
}
working_dir = buf;
#ifdef WINDOWS
for (size_t i = 0, l = working_dir.size(); i != l; ++i)
{
if (working_dir[i] == '\\') working_dir[i] = '/';
}
#endif
prefix_dir = working_dir;
}
/**
* Initialize #prefix_dir and switch to it.
*/
static void init_prefix_dir()
{
for (;;)
{
struct stat s;
if (stat((prefix_dir + "/Remakefile").c_str(), &s) == 0)
{
if (!changed_prefix_dir) return;
if (chdir(prefix_dir.c_str()))
{
perror("Failed to change working directory");
exit(EXIT_FAILURE);
}
if (show_targets)
{
std::cout << "remake: Entering directory `" << prefix_dir << '\'' << std::endl;
}
return;
}
size_t pos = prefix_dir.find_last_of('/');
if (pos == std::string::npos)
{
std::cerr << "Failed to locate Remakefile in the current directory or one of its parents" << std::endl;
exit(EXIT_FAILURE);
}
prefix_dir.erase(pos);
changed_prefix_dir = true;
}
}
/**
* Normalize an absolute path with respect to @a p.
* Paths outside the subtree are left unchanged.
*/
static std::string normalize_abs(std::string const &s, std::string const &p)
{
size_t l = p.length();
if (s.compare(0, l, p)) return s;
size_t ll = s.length();
if (ll == l) return ".";
if (s[l] != '/')
{
size_t pos = s.rfind('/', l);
assert(pos != std::string::npos);
return s.substr(pos + 1);
}
if (ll == l + 1) return ".";
return s.substr(l + 1);
}
/**
* Normalize path @a s (possibly relative to @a w) with respect to @a p.
*
* - If both @a p and @a w are empty, the function just removes ".", "..", "//".
* - If only @a p is empty, the function returns an absolute path.
*/
static std::string normalize(std::string const &s, std::string const &w, std::string const &p)
{
#ifdef WINDOWS
char const *delim = "/\\";
#else
char delim = '/';
#endif
size_t pos = s.find_first_of(delim);
if (pos == std::string::npos && w == p) return s;
bool absolute = pos == 0;
if (!absolute && w != p && !w.empty())
return normalize(w + '/' + s, w, p);
size_t prev = 0, len = s.length();
string_list l;
for (;;)
{
if (pos != prev)
{
std::string n = s.substr(prev, pos - prev);
if (n == "..")
{
if (!l.empty()) l.pop_back();
else if (!absolute && !w.empty())
return normalize(w + '/' + s, w, p);
}
else if (n != ".")
l.push_back(n);
}
++pos;
if (pos >= len) break;
prev = pos;
pos = s.find_first_of(delim, prev);
if (pos == std::string::npos) pos = len;
}
string_list::const_iterator i = l.begin(), i_end = l.end();
if (i == i_end) return absolute ? "/" : ".";
std::string n;
if (absolute) n.push_back('/');
n.append(*i);
for (++i; i != i_end; ++i)
{
n.push_back('/');
n.append(*i);
}
if (absolute && !p.empty()) return normalize_abs(n, p);
return n;