]> git.ipfire.org Git - thirdparty/make.git/commitdiff
[SV 14927] Allow parallel builds for archives
authorPaul Smith <psmith@gnu.org>
Tue, 3 Jan 2023 06:57:35 +0000 (01:57 -0500)
committerPaul Smith <psmith@gnu.org>
Tue, 3 Jan 2023 06:57:35 +0000 (01:57 -0500)
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.

NEWS
doc/make.texi
src/remake.c
tests/scripts/features/archives

diff --git a/NEWS b/NEWS
index 6e9482b8146094e574788b31315cf80ef22175aa..e8fce031c18bd923b140c24095db3bb409280178 100644 (file)
--- 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
index 29c852a98aca49e9fa0c2453b8afcedc7f46c6c4..b8cbb52ca32377f2b8d69d041d2292e783ca0fac 100644 (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
index a1f1d1d97dc9f3f4a9b4c1b4a58ae377632a5fea..8e2547eb7607dbbc92e01399bd774aa285cd773d 100644 (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
index b0d479b2f968d5e5b47b1f61ac0999a1c0caef3e..2ad34d922f25150ab854997e5c77a2f7e56b9bc3 100644 (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;