[SV 14927] Allow parallel builds for archives

Compare the timestamp of the object file (if it exists) with the
archived object and if the object file is newer, ensure it's updated
in the archive.

* NEWS: Announce the new capability.
* doc/make.texi (Dangers When Using Archives): Explain how to enable
parallel builds with archives.
* src/remake.c (f_mtime): For archive element files check the mod
time of the object file (if it exists) against the archive object
(if it exists).
* tests/scripts/features/archives: Add tests for this capability.
This commit is contained in:
Paul Smith 2023-01-03 01:57:35 -05:00
parent 8791d2b38e
commit 1ceeb8c64b
4 changed files with 86 additions and 16 deletions

7
NEWS
View File

@ -24,6 +24,13 @@ https://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=110&se
makefiles. makefiles.
Implementation provided by Dmitry Goncharov <dgoncharov@users.sf.net> Implementation provided by Dmitry Goncharov <dgoncharov@users.sf.net>
* New feature: Parallel builds of archives
Previously it was not possible to use parallel builds with archives. It is
still not possible using the built-in rules, however you can now override
the built-in rules with a slightly different set of rules and use parallel
builds with archive creation. See the "Dangers When Using Archives" section
of the GNU Make manual, and https://savannah.gnu.org/bugs/index.php?14927
* Previously target-specific variables would inherit their "export" capability * Previously target-specific variables would inherit their "export" capability
from parent target-specific variables even if they were marked private. Now from parent target-specific variables even if they were marked private. Now
private parent target-specific variables have no affect. For more details private parent target-specific variables have no affect. For more details

View File

@ -11497,7 +11497,7 @@ and make all the members of the archive file prerequisites of that rule.
For example, For example,
@example @example
libfoo.a: libfoo.a(x.o) libfoo.a(y.o) @dots{} libfoo.a: libfoo.a(x.o y.o @dots{})
ranlib libfoo.a ranlib libfoo.a
@end example @end example
@ -11518,15 +11518,38 @@ updates the @file{__.SYMDEF} member automatically.
@cindex archive, and @code{-j} @cindex archive, and @code{-j}
@cindex @code{-j}, and archive update @cindex @code{-j}, and archive update
It is important to be careful when using parallel execution (the The built-in rules for updating archives are incompatible with parallel
@code{-j} switch; @pxref{Parallel, ,Parallel Execution}) and archives. builds. These rules (required by the POSIX standard) add each object file
If multiple @code{ar} commands run at the same time on the same archive into the archive as it's compiled. When parallel builds are enabled this
file, they will not know about each other and can corrupt the file. allows multiple @code{ar} commands to be updating the same archive
simultaneously, which is not supported.
Possibly a future version of @code{make} will provide a mechanism to If you want to use parallel builds with archives you can override the default
circumvent this problem by serializing all recipes that operate on the rules by adding these lines to your makefile:
same archive file. But for the time being, you must either write your
makefiles to avoid this problem in some other way, or not use @code{-j}. @example
(%) : % ;
%.a : ; $(AR) $(ARFLAGS) $@ $?
@end example
The first line changes the rule that updates an individual object in the
archive to do nothing, and the second line changes the default rule for
building an archive to update all the outdated objects (@code{$?}) in one
command.
Of course you will still need to declare the prerequisites of your library
using the archive syntax:
@example
libfoo.a: libfoo.a(x.o y.o @dots{})
@end example
If you prefer to write an explicit rule you can use:
@example
libfoo.a: libfoo.a(x.o y.o @dots{})
$(AR) $(ARFLAGS) $@ $?
@end example
@node Archive Suffix Rules, , Archive Pitfalls, Archives @node Archive Suffix Rules, , Archive Pitfalls, Archives
@section Suffix Rules for Archive Files @section Suffix Rules for Archive Files

View File

@ -1341,7 +1341,7 @@ f_mtime (struct file *file, int search)
if (ar_name (file->name)) if (ar_name (file->name))
{ {
/* This file is an archive-member reference. */ /* This file is an archive-member reference. */
FILE_TIMESTAMP memmtime;
char *arname, *memname; char *arname, *memname;
struct file *arfile; struct file *arfile;
time_t member_date; time_t member_date;
@ -1349,6 +1349,9 @@ f_mtime (struct file *file, int search)
/* Find the archive's name. */ /* Find the archive's name. */
ar_parse_name (file->name, &arname, &memname); ar_parse_name (file->name, &arname, &memname);
/* Find the mtime of the member file (it might not exist). */
memmtime = name_mtime (memname);
/* Find the modification time of the archive itself. /* Find the modification time of the archive itself.
Also allow for its name to be changed via VPATH search. */ Also allow for its name to be changed via VPATH search. */
arfile = lookup_file (arname); arfile = lookup_file (arname);
@ -1392,9 +1395,16 @@ f_mtime (struct file *file, int search)
return NONEXISTENT_MTIME; return NONEXISTENT_MTIME;
member_date = ar_member_date (file->hname); member_date = ar_member_date (file->hname);
mtime = (member_date == (time_t) -1
? NONEXISTENT_MTIME if (member_date == (time_t) -1
: file_timestamp_cons (file->hname, member_date, 0)); || (memmtime != NONEXISTENT_MTIME
&& (time_t) FILE_TIMESTAMP_S (memmtime) > member_date))
/* If the member file exists and is newer than the member in the
archive, pretend it's nonexistent. This means the member file was
updated but not added to the archive yet. */
mtime = NONEXISTENT_MTIME;
else
mtime = file_timestamp_cons (file->hname, member_date, 0);
} }
else else
#endif #endif

View File

@ -20,9 +20,7 @@ if ($osname eq 'VMS') {
# objects when the test tampers with the timestamp. # objects when the test tampers with the timestamp.
1 while unlink "$afile.c1"; 1 while unlink "$afile.c1";
1 while unlink "$afile.o"; 1 while unlink "$afile.o";
open (MYFILE, ">$afile.c1"); create_file("$afile.c1", "int $afile(void) {return 1;}\n");
print MYFILE "int $afile(void) {return 1;}\n";
close MYFILE;
system("cc $afile.c1 /object=$afile.o"); system("cc $afile.c1 /object=$afile.o");
} }
} else { } else {
@ -238,5 +236,37 @@ $pre%: ; touch \$\@
unlink($lib); unlink($lib);
} }
# SV 61436 : Allow redefining archive rules to propagate timestamps
# Find the output when creating an archive from multiple files
utouch(-10, 'a.o', 'b.o');
my $create2 = `$ar $arflags mylib.a a.o b.o $redir`;
touch('b.o');
my $add2 = `$ar $arflags mylib.a b.o $redir`;
unlink('a.o', 'b.o', 'mylib.a');
utouch(-20, 'a.c', 'b.c');
run_make_test(q!
mylib.a: mylib.a(a.o b.o)
(%): % ;
%.a: ; $(AR) $(ARFLAGS) $@ $?
%.o : %.c ; @echo Compile $<; $(COMPILE.c) -o $@ $<
!, $arvar, "Compile a.c\nCompile b.c\n$ar $arflags mylib.a a.o b.o\n${create2}rm b.o a.o");
run_make_test(undef, $arvar, "#MAKE#: 'mylib.a' is up to date.");
# Now update one of the source files and it should be compiled and archived
sleep(2);
touch('b.c');
run_make_test(undef, $arvar, "Compile b.c\n$ar $arflags mylib.a b.o\n${add2}rm b.o");
run_make_test(undef, $arvar, "#MAKE#: 'mylib.a' is up to date.");
unlink('a.c', 'b.c', 'a.o', 'b.o', 'mylib.a');
# This tells the test driver that the perl test script executed properly. # This tells the test driver that the perl test script executed properly.
1; 1;