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 +* 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;