summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Smith <psmith@gnu.org>2023-01-03 01:57:35 -0500
committerPaul Smith <psmith@gnu.org>2023-01-03 01:57:35 -0500
commit1ceeb8c64bf2a95bdfc5e5bd221886c6df74f560 (patch)
tree134ec02d7f337d65d5d880c6738fdff097068699
parent8791d2b38ec916ea4206d5cae3b0036bb440c918 (diff)
downloadmake-git-1ceeb8c64bf2a95bdfc5e5bd221886c6df74f560.tar.gz
[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.
-rw-r--r--NEWS7
-rw-r--r--doc/make.texi41
-rw-r--r--src/remake.c18
-rw-r--r--tests/scripts/features/archives36
4 files changed, 86 insertions, 16 deletions
diff --git a/NEWS b/NEWS
index 6e9482b8..e8fce031 100644
--- a/NEWS
+++ b/NEWS
@@ -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
diff --git a/doc/make.texi b/doc/make.texi
index 29c852a9..b8cbb52c 100644
--- a/doc/make.texi
+++ b/doc/make.texi
@@ -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
diff --git a/src/remake.c b/src/remake.c
index a1f1d1d9..8e2547eb 100644
--- a/src/remake.c
+++ b/src/remake.c
@@ -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
diff --git a/tests/scripts/features/archives b/tests/scripts/features/archives
index b0d479b2..2ad34d92 100644
--- a/tests/scripts/features/archives
+++ b/tests/scripts/features/archives
@@ -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;