[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.
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
from parent target-specific variables even if they were marked private. Now
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,
@example
libfoo.a: libfoo.a(x.o) libfoo.a(y.o) @dots{}
libfoo.a: libfoo.a(x.o y.o @dots{})
ranlib libfoo.a
@end example
@ -11518,15 +11518,38 @@ updates the @file{__.SYMDEF} member automatically.
@cindex archive, and @code{-j}
@cindex @code{-j}, and archive update
It is important to be careful when using parallel execution (the
@code{-j} switch; @pxref{Parallel, ,Parallel Execution}) and archives.
If multiple @code{ar} commands run at the same time on the same archive
file, they will not know about each other and can corrupt the file.
The built-in rules for updating archives are incompatible with parallel
builds. These rules (required by the POSIX standard) add each object file
into the archive as it's compiled. When parallel builds are enabled this
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
circumvent this problem by serializing all recipes that operate on the
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}.
If you want to use parallel builds with archives you can override the default
rules by adding these lines to your makefile:
@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
@section Suffix Rules for Archive Files

View File

@ -1341,7 +1341,7 @@ f_mtime (struct file *file, int search)
if (ar_name (file->name))
{
/* This file is an archive-member reference. */
FILE_TIMESTAMP memmtime;
char *arname, *memname;
struct file *arfile;
time_t member_date;
@ -1349,6 +1349,9 @@ f_mtime (struct file *file, int search)
/* Find the archive's name. */
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.
Also allow for its name to be changed via VPATH search. */
arfile = lookup_file (arname);
@ -1392,9 +1395,16 @@ f_mtime (struct file *file, int search)
return NONEXISTENT_MTIME;
member_date = ar_member_date (file->hname);
mtime = (member_date == (time_t) -1
? NONEXISTENT_MTIME
: file_timestamp_cons (file->hname, member_date, 0));
if (member_date == (time_t) -1
|| (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
#endif

View File

@ -20,9 +20,7 @@ if ($osname eq 'VMS') {
# objects when the test tampers with the timestamp.
1 while unlink "$afile.c1";
1 while unlink "$afile.o";
open (MYFILE, ">$afile.c1");
print MYFILE "int $afile(void) {return 1;}\n";
close MYFILE;
create_file("$afile.c1", "int $afile(void) {return 1;}\n");
system("cc $afile.c1 /object=$afile.o");
}
} else {
@ -238,5 +236,37 @@ $pre%: ; touch \$\@
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.
1;