install_fips: Create the OPENSSLDIR as it might not exist
[openssl.git] / Configurations / unix-Makefile.tmpl
index 62ca26dde589c6173fb0f99f5105d9af431fc599..bf53cbcec5dbba329e87f6f3a73418d842051a11 100644 (file)
@@ -3,7 +3,10 @@
 ##
 ## {- join("\n## ", @autowarntext) -}
 {-
-     our $makedepprog = platform->makedepprog();
+     use OpenSSL::Util;
+
+     our $makedep_scheme = $config{makedep_scheme};
+     our $makedepcmd = platform->makedepcmd();
 
      sub windowsdll { $config{target} =~ /^(?:Cygwin|mingw)/ }
 
@@ -61,8 +64,10 @@ OPTIONS={- $config{options} -}
 CONFIGURE_ARGS=({- join(", ",quotify_l(@{$config{perlargv}})) -})
 SRCDIR={- $config{sourcedir} -}
 BLDDIR={- $config{builddir} -}
+FIPSKEY={- $config{FIPSKEY} -}
 
 VERSION={- "$config{full_version}" -}
+VERSION_NUMBER={- "$config{version}" -}
 MAJOR={- $config{major} -}
 MINOR={- $config{minor} -}
 SHLIB_VERSION_NUMBER={- $config{shlib_version} -}
@@ -79,13 +84,30 @@ SHLIBS={- join(" \\\n" . ' ' x 7,
 SHLIB_INFO={- join(" \\\n" . ' ' x 11,
                    fill_lines(" ", $COLUMNS - 11,
                           map { my $x = platform->sharedlib($_);
-                                my $y = platform->sharedlib_simple($_);
-                                $x ? "\"$x;$y\"" : () }
+                                my $y = platform->sharedlib_simple($_) // '';
+                                my $z = platform->sharedlib_import($_) // '';
+                                $x ? "\"$x;$y;$z\"" : () }
                           @{$unified_info{libraries}})) -}
 MODULES={- join(" \\\n" . ' ' x 8,
                 fill_lines(" ", $COLUMNS - 8,
                            map { platform->dso($_) }
+                           # Drop all modules that are dependencies, they will
+                           # be processed through their dependents
+                           grep { my $x = $_;
+                                  !grep { grep { $_ eq $x } @$_ }
+                                        values %{$unified_info{depends}} }
                            @{$unified_info{modules}})) -}
+FIPSMODULE={- # We do some extra checking here, as there should be only one
+              use File::Basename;
+              our @fipsmodules =
+                  grep { !$unified_info{attributes}->{modules}->{$_}->{noinst}
+                         && $unified_info{attributes}->{modules}->{$_}->{fips} }
+                  @{$unified_info{modules}};
+              die "More that one FIPS module" if scalar @fipsmodules > 1;
+              join(" ", map { platform->dso($_) } @fipsmodules) -}
+FIPSMODULENAME={- die "More that one FIPS module" if scalar @fipsmodules > 1;
+                  join(" ", map { basename(platform->dso($_)) } @fipsmodules) -}
+
 PROGRAMS={- join(" \\\n" . ' ' x 9,
                  fill_lines(" ", $COLUMNS - 9,
                             map { platform->bin($_) }
@@ -102,6 +124,19 @@ DEPS={- join(" \\\n" . ' ' x 5,
 GENERATED_MANDATORY={- join(" \\\n" . ' ' x 20,
                             fill_lines(" ", $COLUMNS - 20,
                                        @{$unified_info{depends}->{""}})) -}
+GENERATED_PODS={- # common0.tmpl provides @generated
+                  join(" \\\n" . ' ' x 15,
+                       fill_lines(" ", $COLUMNS - 15,
+                                  map { my $x = $_;
+                                        (
+                                          grep { 
+                                                 $unified_info{attributes}->{depends}
+                                                 ->{$x}->{$_}->{pod} // 0
+                                               }
+                                              keys %{$unified_info{attributes}->{depends}->{$x}}
+                                        ) ? $x : ();
+                                      }
+                                      @generated)) -}
 GENERATED={- # common0.tmpl provides @generated
              join(" \\\n" . ' ' x 5,
                   fill_lines(" ", $COLUMNS - 5,
@@ -125,8 +160,9 @@ INSTALL_SHLIB_INFO={-
         join(" \\\n" . ' ' x 19,
              fill_lines(" ", $COLUMNS - 19,
                         map { my $x = platform->sharedlib($_);
-                              my $y = platform->sharedlib_simple($_);
-                              $x ? "\"$x;$y\"" : () }
+                              my $y = platform->sharedlib_simple($_) // '';
+                              my $z = platform->sharedlib_import($_) // '';
+                              $x ? "\"$x;$y;$z\"" : () }
                         grep { !$unified_info{attributes}->{libraries}->{$_}->{noinst} }
                         @{$unified_info{libraries}}))
 -}
@@ -138,6 +174,24 @@ INSTALL_ENGINES={-
                                && $unified_info{attributes}->{modules}->{$_}->{engine} }
                         @{$unified_info{modules}}))
 -}
+INSTALL_MODULES={-
+        join(" \\\n" . ' ' x 16,
+             fill_lines(" ", $COLUMNS - 16,
+                        map { platform->dso($_) }
+                        grep { !$unified_info{attributes}->{modules}->{$_}->{noinst}
+                               && !$unified_info{attributes}->{modules}->{$_}->{engine}
+                               && !$unified_info{attributes}->{modules}->{$_}->{fips} }
+                        @{$unified_info{modules}}))
+-}
+INSTALL_FIPSMODULE={-
+        join(" \\\n" . ' ' x 16,
+             fill_lines(" ", $COLUMNS - 16,
+                        map { platform->dso($_) }
+                        grep { !$unified_info{attributes}->{modules}->{$_}->{noinst}
+                               && $unified_info{attributes}->{modules}->{$_}->{fips} }
+                        @{$unified_info{modules}}))
+-}
+INSTALL_FIPSMODULECONF=providers/fipsmodule.cnf
 INSTALL_PROGRAMS={-
         join(" \\\n" . ' ' x 16,
              fill_lines(" ", $COLUMNS - 16, map { platform->bin($_) }
@@ -162,41 +216,57 @@ MISC_SCRIPTS={-
                                && $unified_info{attributes}->{scripts}->{$_}->{misc} }
                         @{$unified_info{scripts}}))
 -}
+IMAGEDOCS1={-
+        join(" \\\n" . ' ' x 10,
+             fill_lines(" ", $COLUMNS - 10,
+                        @{$unified_info{imagedocs}->{man1}})) -}
+IMAGEDOCS3={-
+        join(" \\\n" . ' ' x 10,
+             fill_lines(" ", $COLUMNS - 10,
+                        @{$unified_info{imagedocs}->{man3}})) -}
+IMAGEDOCS5={-
+        join(" \\\n" . ' ' x 10,
+             fill_lines(" ", $COLUMNS - 10,
+                        @{$unified_info{imagedocs}->{man5}})) -}
+IMAGEDOCS7={-
+        join(" \\\n" . ' ' x 10,
+             fill_lines(" ", $COLUMNS - 10,
+                        @{$unified_info{imagedocs}->{man7}})) -}
 HTMLDOCS1={-
         join(" \\\n" . ' ' x 10,
-             fill_lines(" ", $COLUMNS - 10, map { platform->bin($_) }
+             fill_lines(" ", $COLUMNS - 10,
                         @{$unified_info{htmldocs}->{man1}})) -}
 HTMLDOCS3={-
         join(" \\\n" . ' ' x 10,
-             fill_lines(" ", $COLUMNS - 10, map { platform->bin($_) }
+             fill_lines(" ", $COLUMNS - 10,
                         @{$unified_info{htmldocs}->{man3}})) -}
 HTMLDOCS5={-
         join(" \\\n" . ' ' x 10,
-             fill_lines(" ", $COLUMNS - 10, map { platform->bin($_) }
+             fill_lines(" ", $COLUMNS - 10,
                         @{$unified_info{htmldocs}->{man5}})) -}
 HTMLDOCS7={-
         join(" \\\n" . ' ' x 10,
-             fill_lines(" ", $COLUMNS - 10, map { platform->bin($_) }
+             fill_lines(" ", $COLUMNS - 10,
                         @{$unified_info{htmldocs}->{man7}})) -}
 MANDOCS1={-
         join(" \\\n" . ' ' x 9,
-             fill_lines(" ", $COLUMNS - 9, map { platform->bin($_) }
+             fill_lines(" ", $COLUMNS - 9,
                         @{$unified_info{mandocs}->{man1}})) -}
 MANDOCS3={-
         join(" \\\n" . ' ' x 9,
-             fill_lines(" ", $COLUMNS - 9, map { platform->bin($_) }
+             fill_lines(" ", $COLUMNS - 9,
                         @{$unified_info{mandocs}->{man3}})) -}
 MANDOCS5={-
         join(" \\\n" . ' ' x 9,
-             fill_lines(" ", $COLUMNS - 9, map { platform->bin($_) }
+             fill_lines(" ", $COLUMNS - 9,
                         @{$unified_info{mandocs}->{man5}})) -}
 MANDOCS7={-
         join(" \\\n" . ' ' x 9,
-             fill_lines(" ", $COLUMNS - 9, map { platform->bin($_) }
+             fill_lines(" ", $COLUMNS - 9,
                         @{$unified_info{mandocs}->{man7}})) -}
 
-APPS_OPENSSL={- use File::Spec::Functions;
-                catfile("apps","openssl") -}
+APPS_OPENSSL="{- use File::Spec::Functions;
+                 catfile("apps","openssl") -}"
 
 # DESTDIR is for package builders so that they can configure for, say,
 # /usr/ and yet have everything installed to /tmp/somedir/usr/.
@@ -228,14 +298,7 @@ OPENSSLDIR={- #
               $openssldir -}
 LIBDIR={- our $libdir = $config{libdir};
           unless ($libdir) {
-              #
-              # if $prefix/lib$target{multilib} is not an existing
-              # directory, then assume that it's not searched by linker
-              # automatically, in which case adding $target{multilib} suffix
-              # causes more grief than we're ready to tolerate, so don't...
-              our $multilib =
-                  -d "$prefix/lib$target{multilib}" ? $target{multilib} : "";
-              $libdir = "lib$multilib";
+              $libdir = "lib$target{multilib}";
           }
           file_name_is_absolute($libdir) ? "" : $libdir -}
 # $(libdir) is chosen to be compatible with the GNU coding standards
@@ -255,7 +318,7 @@ HTMLDIR=$(DOCDIR)/html
 # MANSUFFIX is for the benefit of anyone who may want to have a suffix
 # appended after the manpage file section number.  "ssl" is popular,
 # resulting in files such as config.5ssl rather than config.5.
-MANSUFFIX=
+MANSUFFIX=ossl
 HTMLSUFFIX=html
 
 # For "optional" echo messages, to get "real" silence
@@ -282,7 +345,7 @@ CXXFLAGS={- join(' ', @{$config{CXXFLAGS}}) -}
 LDFLAGS= {- join(' ', @{$config{LDFLAGS}}) -}
 EX_LIBS= {- join(' ', @{$config{LDLIBS}}) -}
 
-MAKEDEPEND={- $config{makedepprog} -}
+MAKEDEPEND={- $config{makedepcmd} -}
 
 PERL={- $config{PERL} -}
 
@@ -438,6 +501,7 @@ LANG=C
 {- dependmagic('build_modules'); -}: build_modules_nodep
 {- dependmagic('build_programs'); -}: build_programs_nodep
 
+build_generated_pods: $(GENERATED_PODS)
 build_docs: build_man_docs build_html_docs
 build_man_docs: $(MANDOCS1) $(MANDOCS3) $(MANDOCS5) $(MANDOCS7)
 build_html_docs: $(HTMLDOCS1) $(HTMLDOCS3) $(HTMLDOCS5) $(HTMLDOCS7)
@@ -462,65 +526,72 @@ build_all_generated: $(GENERATED_MANDATORY) $(GENERATED) build_docs
 all: build_sw build_docs
 
 test: tests
-{- dependmagic('tests'); -}: build_programs_nodep build_modules_nodep link-utils
+{- dependmagic('tests'); -}: build_programs_nodep build_modules_nodep link-utils run_tests
+run_tests:
        @ : {- output_off() if $disabled{tests}; "" -}
-       ( cd test; \
-         mkdir -p test-runs; \
-         SRCTOP=../$(SRCDIR) \
-         BLDTOP=../$(BLDDIR) \
-         RESULT_D=test-runs \
+       ( SRCTOP=$(SRCDIR) \
+         BLDTOP=$(BLDDIR) \
          PERL="$(PERL)" \
+         FIPSKEY="$(FIPSKEY)" \
          EXE_EXT={- platform->binext() -} \
-         OPENSSL_ENGINES=`cd ../$(BLDDIR)/engines 2>/dev/null && pwd` \
-         OPENSSL_MODULES=`cd ../$(BLDDIR)/providers 2>/dev/null && pwd` \
-         $(PERL) ../$(SRCDIR)/test/run_tests.pl $(TESTS) )
+         $(PERL) $(SRCDIR)/test/run_tests.pl $(TESTS) )
        @ : {- if ($disabled{tests}) { output_on(); } else { output_off(); } "" -}
        @echo "Tests are not supported with your chosen Configure options"
        @ : {- output_on() if !$disabled{tests}; "" -}
 
 list-tests:
        @ : {- output_off() if $disabled{tests}; "" -}
-       @SRCTOP="$(SRCDIR)" \
-        $(PERL) $(SRCDIR)/test/run_tests.pl list
+       $(MAKE) run_tests TESTS=list
        @ : {- if ($disabled{tests}) { output_on(); } else { output_off(); } "" -}
        @echo "Tests are not supported with your chosen Configure options"
        @ : {- output_on() if !$disabled{tests}; "" -}
 
-install: install_sw install_ssldirs install_docs
+install: install_sw install_ssldirs install_docs {- $disabled{fips} ? "" : "install_fips" -}
 
-uninstall: uninstall_docs uninstall_sw
+uninstall: uninstall_docs uninstall_sw {- $disabled{fips} ? "" : "uninstall_fips" -}
 
 libclean:
        @set -e; for s in $(SHLIB_INFO); do \
                if [ "$$s" = ";" ]; then continue; fi; \
                s1=`echo "$$s" | cut -f1 -d";"`; \
                s2=`echo "$$s" | cut -f2 -d";"`; \
+               s3=`echo "$$s" | cut -f3 -d";"`; \
                $(ECHO) $(RM) $$s1; {- output_off() unless windowsdll(); "" -}\
                $(RM) apps/$$s1; \
                $(RM) test/$$s1; \
                $(RM) fuzz/$$s1; {- output_on() unless windowsdll(); "" -}\
                $(RM) $$s1; \
-               if [ "$$s1" != "$$s2" ]; then \
+               if [ "$$s2" != "" ]; then \
                        $(ECHO) $(RM) $$s2; \
                        $(RM) $$s2; \
                fi; \
+               if [ "$$s3" != "" ]; then \
+                       $(ECHO) $(RM) $$s3; \
+                       $(RM) $$s3; \
+               fi; \
        done
        $(RM) $(LIBS)
        $(RM) *{- platform->defext() -}
 
 clean: libclean
-       $(RM) $(HTMLDOCS1) $(HTMLDOCS3) $(HTMLDOCS5) $(HTMLDOCS7)
-       $(RM) $(MANDOCS1) $(MANDOCS3) $(MANDOCS5) $(MANDOCS7)
+       $(RM) $(HTMLDOCS1)
+       $(RM) $(HTMLDOCS3)
+       $(RM) $(HTMLDOCS5)
+       $(RM) $(HTMLDOCS7)
+       $(RM) $(MANDOCS1)
+       $(RM) $(MANDOCS3)
+       $(RM) $(MANDOCS5)
+       $(RM) $(MANDOCS7)
        $(RM) $(PROGRAMS) $(TESTPROGS) $(MODULES) $(SCRIPTS)
        $(RM) $(GENERATED_MANDATORY) $(GENERATED)
-       -$(RM) `find . -name '*{- platform->depext() -}' \! -name '.*' \! -type d -print`
-       -$(RM) `find . -name '*{- platform->objext() -}' \! -name '.*' \! -type d -print`
+       -find . -name '*{- platform->depext() -}' \! -name '.*' \! -type d -exec $(RM) {} \;
+       -find . -name '*{- platform->objext() -}' \! -name '.*' \! -type d -exec $(RM) {} \;
        $(RM) core
-       $(RM) tags TAGS doc-nits
+       $(RM) tags TAGS doc-nits md-nits
        $(RM) -r test/test-runs
+       $(RM) providers/fips*.new
        $(RM) openssl.pc libcrypto.pc libssl.pc
-       -$(RM) `find . -type l \! -name '.*' -print`
-       $(RM) $(TARFILE)
+       -find . -type l \! -name '.*' -exec $(RM) {} \;
 
 distclean: clean
        $(RM) configdata.pm
@@ -530,22 +601,49 @@ distclean: clean
 # concatenate only if that is true.
 depend:
        @: {- output_off() if $disabled{makedepend}; "" -}
-       @$(PERL) $(SRCDIR)/util/add-depends.pl {-
-                defined $makedepprog  && $makedepprog =~ /\/makedepend/
-                 ? 'makedepend' : 'gcc' -}
+       @$(PERL) $(SRCDIR)/util/add-depends.pl "{- $makedep_scheme -}"
        @: {- output_on() if $disabled{makedepend}; "" -}
 
 # Install helper targets #############################################
 
-install_sw: install_dev install_engines install_runtime
+install_sw: install_dev install_engines install_modules install_runtime
 
-uninstall_sw: uninstall_runtime uninstall_engines uninstall_dev
+uninstall_sw: uninstall_runtime uninstall_modules uninstall_engines uninstall_dev
 
 install_docs: install_man_docs install_html_docs
 
 uninstall_docs: uninstall_man_docs uninstall_html_docs
        $(RM) -r $(DESTDIR)$(DOCDIR)
 
+{- output_off() if $disabled{fips}; "" -}
+install_fips: build_sw $(INSTALL_FIPSMODULECONF)
+       @[ -n "$(INSTALLTOP)" ] || (echo INSTALLTOP should not be empty; exit 1)
+       @$(PERL) $(SRCDIR)/util/mkdir-p.pl $(DESTDIR)$(MODULESDIR)
+       @$(PERL) $(SRCDIR)/util/mkdir-p.pl $(DESTDIR)$(OPENSSLDIR)
+       @$(ECHO) "*** Installing FIPS module"
+       @$(ECHO) "install $(INSTALL_FIPSMODULE) -> $(DESTDIR)$(MODULESDIR)/$(FIPSMODULENAME)"
+       @cp "$(INSTALL_FIPSMODULE)" $(DESTDIR)$(MODULESDIR)/$(FIPSMODULENAME).new
+       @chmod 755 $(DESTDIR)$(MODULESDIR)/$(FIPSMODULENAME).new
+       @mv -f $(DESTDIR)$(MODULESDIR)/$(FIPSMODULENAME).new \
+              $(DESTDIR)$(MODULESDIR)/$(FIPSMODULENAME)
+       @$(ECHO) "*** Installing FIPS module configuration"
+       @$(ECHO) "install $(INSTALL_FIPSMODULECONF) -> $(DESTDIR)$(OPENSSLDIR)/fipsmodule.cnf"
+       @cp $(INSTALL_FIPSMODULECONF) $(DESTDIR)$(OPENSSLDIR)/fipsmodule.cnf
+
+uninstall_fips:
+       @$(ECHO) "*** Uninstalling FIPS module configuration"
+       $(RM) $(DESTDIR)$(OPENSSLDIR)/fipsmodule.cnf
+       @$(ECHO) "*** Uninstalling FIPS module"
+       $(RM) $(DESTDIR)$(MODULESDIR)/$(FIPSMODULENAME)
+{- if ($disabled{fips}) { output_on(); } else { output_off(); } "" -}
+install_fips:
+       @$(ECHO) "The 'install_fips' target requires the 'enable-fips' option"
+
+uninstall_fips:
+       @$(ECHO) "The 'uninstall_fips' target requires the 'enable-fips' option"
+{- output_on() if !$disabled{fips}; "" -}
+
+
 install_ssldirs:
        @$(PERL) $(SRCDIR)/util/mkdir-p.pl $(DESTDIR)$(OPENSSLDIR)/certs
        @$(PERL) $(SRCDIR)/util/mkdir-p.pl $(DESTDIR)$(OPENSSLDIR)/private
@@ -621,19 +719,23 @@ install_dev: install_runtime_libs
        @set -e; for s in $(INSTALL_SHLIB_INFO); do \
                s1=`echo "$$s" | cut -f1 -d";"`; \
                s2=`echo "$$s" | cut -f2 -d";"`; \
-               fn1=`basename $$s1`; \
-               fn2=`basename $$s2`; \
+               s3=`echo "$$s" | cut -f3 -d";"`; \
+               fn1=`basename "$$s1"`; \
+               fn2=`basename "$$s2"`; \
+               fn3=`basename "$$s3"`; \
                : {- output_off(); output_on() unless windowsdll() or sharedaix(); "" -}; \
-               if [ "$$fn1" != "$$fn2" ]; then \
+               if [ "$$fn2" != "" ]; then \
                        $(ECHO) "link $(DESTDIR)$(libdir)/$$fn2 -> $(DESTDIR)$(libdir)/$$fn1"; \
                        ln -sf $$fn1 $(DESTDIR)$(libdir)/$$fn2; \
                fi; \
                : {- output_off() unless windowsdll() or sharedaix(); output_on() if windowsdll(); "" -}; \
-               $(ECHO) "install $$s2 -> $(DESTDIR)$(libdir)/$$fn2"; \
-               cp $$s2 $(DESTDIR)$(libdir)/$$fn2.new; \
-               chmod 755 $(DESTDIR)$(libdir)/$$fn2.new; \
-               mv -f $(DESTDIR)$(libdir)/$$fn2.new \
-                     $(DESTDIR)$(libdir)/$$fn2; \
+               if [ "$$fn3" != "" ]; then \
+                       $(ECHO) "install $$s3 -> $(DESTDIR)$(libdir)/$$fn3"; \
+                       cp $$s3 $(DESTDIR)$(libdir)/$$fn3.new; \
+                       chmod 755 $(DESTDIR)$(libdir)/$$fn3.new; \
+                       mv -f $(DESTDIR)$(libdir)/$$fn3.new \
+                             $(DESTDIR)$(libdir)/$$fn3; \
+               fi; \
                : {- output_off() if windowsdll(); output_on() if sharedaix(); "" -}; \
                a=$(DESTDIR)$(libdir)/$$fn2; \
                $(ECHO) "install $$s1 -> $$a"; \
@@ -686,18 +788,22 @@ uninstall_dev: uninstall_runtime_libs
        @set -e; for s in $(INSTALL_SHLIB_INFO); do \
                s1=`echo "$$s" | cut -f1 -d";"`; \
                s2=`echo "$$s" | cut -f2 -d";"`; \
-               fn1=`basename $$s1`; \
-               fn2=`basename $$s2`; \
+               s3=`echo "$$s" | cut -f3 -d";"`; \
+               fn1=`basename "$$s1"`; \
+               fn2=`basename "$$s2"`; \
+               fn3=`basename "$$s3"`; \
                : {- output_off() if windowsdll(); "" -}; \
-               $(ECHO) "$(RM) $(DESTDIR)$(libdir)/$$fn2"; \
-               $(RM) $(DESTDIR)$(libdir)/$$fn2; \
-               if [ "$$fn1" != "$$fn2" -a -f "$(DESTDIR)$(libdir)/$$fn1" ]; then \
-                       $(ECHO) "$(RM) $(DESTDIR)$(libdir)/$$fn1"; \
-                       $(RM) $(DESTDIR)$(libdir)/$$fn1; \
+               $(ECHO) "$(RM) $(DESTDIR)$(libdir)/$$fn1"; \
+               $(RM) $(DESTDIR)$(libdir)/$$fn1; \
+               if [ -n "$$fn2" ]; then \
+                       $(ECHO) "$(RM) $(DESTDIR)$(libdir)/$$fn2"; \
+                       $(RM) $(DESTDIR)$(libdir)/$$fn2; \
                fi; \
                : {- output_on() if windowsdll(); "" -}{- output_off() unless windowsdll(); "" -}; \
-               $(ECHO) "$(RM) $(DESTDIR)$(libdir)/$$fn2"; \
-               $(RM) $(DESTDIR)$(libdir)/$$fn2; \
+               if [ -n "$$fn3" ]; then \
+                       $(ECHO) "$(RM) $(DESTDIR)$(libdir)/$$fn3"; \
+                       $(RM) $(DESTDIR)$(libdir)/$$fn3; \
+               fi; \
                : {- output_on() unless windowsdll(); "" -}; \
        done
        @ : {- output_on() if $disabled{shared}; "" -}
@@ -707,10 +813,12 @@ uninstall_dev: uninstall_runtime_libs
        -$(RMDIR) $(DESTDIR)$(libdir)/pkgconfig
        -$(RMDIR) $(DESTDIR)$(libdir)
 
-install_engines: install_runtime_libs build_modules
+_install_modules_deps: install_runtime_libs build_modules
+
+install_engines: _install_modules_deps
        @[ -n "$(INSTALLTOP)" ] || (echo INSTALLTOP should not be empty; exit 1)
        @$(PERL) $(SRCDIR)/util/mkdir-p.pl $(DESTDIR)$(ENGINESDIR)/
-       @$(ECHO) "*** Installing ENGINE modules"
+       @$(ECHO) "*** Installing engines"
        @set -e; for e in dummy $(INSTALL_ENGINES); do \
                if [ "$$e" = "dummy" ]; then continue; fi; \
                fn=`basename $$e`; \
@@ -722,18 +830,39 @@ install_engines: install_runtime_libs build_modules
        done
 
 uninstall_engines:
-       @$(ECHO) "*** Uninstalling ENGINE modules"
+       @$(ECHO) "*** Uninstalling engines"
        @set -e; for e in dummy $(INSTALL_ENGINES); do \
                if [ "$$e" = "dummy" ]; then continue; fi; \
                fn=`basename $$e`; \
-               if [ "$$fn" = '{- platform->dso("ossltest") -}' ]; then \
-                       continue; \
-               fi; \
                $(ECHO) "$(RM) $(DESTDIR)$(ENGINESDIR)/$$fn"; \
                $(RM) $(DESTDIR)$(ENGINESDIR)/$$fn; \
        done
        -$(RMDIR) $(DESTDIR)$(ENGINESDIR)
 
+install_modules: _install_modules_deps
+       @[ -n "$(INSTALLTOP)" ] || (echo INSTALLTOP should not be empty; exit 1)
+       @$(PERL) $(SRCDIR)/util/mkdir-p.pl $(DESTDIR)$(MODULESDIR)/
+       @$(ECHO) "*** Installing modules"
+       @set -e; for e in dummy $(INSTALL_MODULES); do \
+               if [ "$$e" = "dummy" ]; then continue; fi; \
+               fn=`basename $$e`; \
+               $(ECHO) "install $$e -> $(DESTDIR)$(MODULESDIR)/$$fn"; \
+               cp $$e $(DESTDIR)$(MODULESDIR)/$$fn.new; \
+               chmod 755 $(DESTDIR)$(MODULESDIR)/$$fn.new; \
+               mv -f $(DESTDIR)$(MODULESDIR)/$$fn.new \
+                     $(DESTDIR)$(MODULESDIR)/$$fn; \
+       done
+
+uninstall_modules:
+       @$(ECHO) "*** Uninstalling modules"
+       @set -e; for e in dummy $(INSTALL_MODULES); do \
+               if [ "$$e" = "dummy" ]; then continue; fi; \
+               fn=`basename $$e`; \
+               $(ECHO) "$(RM) $(DESTDIR)$(MODULESDIR)/$$fn"; \
+               $(RM) $(DESTDIR)$(MODULESDIR)/$$fn; \
+       done
+       -$(RMDIR) $(DESTDIR)$(MODULESDIR)
+
 install_runtime: install_programs
 
 install_runtime_libs: build_libs
@@ -827,60 +956,68 @@ install_man_docs: build_man_docs
        @set -e; for x in dummy $(MANDOCS1); do \
                if [ "$$x" = "dummy" ]; then continue; fi; \
                fn=`basename $$x`; \
-               $(ECHO) "install $$x -> $(DESTDIR)$(MANDIR)/man1/$$fn"; \
-               cp $$x $(DESTDIR)$(MANDIR)/man1/$$fn$(MANSUFFIX); \
-               chmod 755 $(DESTDIR)$(MANDIR)/man1/$$fn$(MANSUFFIX); \
+               $(ECHO) "install $$x -> $(DESTDIR)$(MANDIR)/man1/$${fn}$(MANSUFFIX)"; \
+               cp $$x $(DESTDIR)$(MANDIR)/man1/$${fn}$(MANSUFFIX); \
+               chmod 644 $(DESTDIR)$(MANDIR)/man1/$${fn}$(MANSUFFIX); \
+               $(PERL) $(SRCDIR)/util/write-man-symlinks install $(SRCDIR)/doc/man1 $(BLDDIR)/doc/man1 $${fn}$(MANSUFFIX) $(DESTDIR)$(MANDIR)/man1; \
        done
        @set -e; for x in dummy $(MANDOCS3); do \
                if [ "$$x" = "dummy" ]; then continue; fi; \
                fn=`basename $$x`; \
-               $(ECHO) "install $$x -> $(DESTDIR)$(MANDIR)/man3/$$fn"; \
-               cp $$x $(DESTDIR)$(MANDIR)/man3/$$fn$(MANSUFFIX); \
-               chmod 755 $(DESTDIR)$(MANDIR)/man3/$$fn$(MANSUFFIX); \
+               $(ECHO) "install $$x -> $(DESTDIR)$(MANDIR)/man3/$${fn}$(MANSUFFIX)"; \
+               cp $$x $(DESTDIR)$(MANDIR)/man3/$${fn}$(MANSUFFIX); \
+               chmod 644 $(DESTDIR)$(MANDIR)/man3/$${fn}$(MANSUFFIX); \
+               $(PERL) $(SRCDIR)/util/write-man-symlinks install $(SRCDIR)/doc/man3 $(BLDDIR)/doc/man3 $${fn}$(MANSUFFIX) $(DESTDIR)$(MANDIR)/man3; \
        done
        @set -e; for x in dummy $(MANDOCS5); do \
                if [ "$$x" = "dummy" ]; then continue; fi; \
                fn=`basename $$x`; \
-               $(ECHO) "install $$x -> $(DESTDIR)$(MANDIR)/man5/$$fn"; \
-               cp $$x $(DESTDIR)$(MANDIR)/man5/$$fn$(MANSUFFIX); \
-               chmod 755 $(DESTDIR)$(MANDIR)/man5/$$fn$(MANSUFFIX); \
+               $(ECHO) "install $$x -> $(DESTDIR)$(MANDIR)/man5/$${fn}$(MANSUFFIX)"; \
+               cp $$x $(DESTDIR)$(MANDIR)/man5/$${fn}$(MANSUFFIX); \
+               chmod 644 $(DESTDIR)$(MANDIR)/man5/$${fn}$(MANSUFFIX); \
+               $(PERL) $(SRCDIR)/util/write-man-symlinks install $(SRCDIR)/doc/man5 $(BLDDIR)/doc/man5 $${fn}$(MANSUFFIX) $(DESTDIR)$(MANDIR)/man5; \
        done
        @set -e; for x in dummy $(MANDOCS7); do \
                if [ "$$x" = "dummy" ]; then continue; fi; \
                fn=`basename $$x`; \
-               $(ECHO) "install $$x -> $(DESTDIR)$(MANDIR)/man7/$$fn"; \
-               cp $$x $(DESTDIR)$(MANDIR)/man7/$$fn$(MANSUFFIX); \
-               chmod 755 $(DESTDIR)$(MANDIR)/man7/$$fn$(MANSUFFIX); \
+               $(ECHO) "install $$x -> $(DESTDIR)$(MANDIR)/man7/$${fn}$(MANSUFFIX)"; \
+               cp $$x $(DESTDIR)$(MANDIR)/man7/$${fn}$(MANSUFFIX); \
+               chmod 644 $(DESTDIR)$(MANDIR)/man7/$${fn}$(MANSUFFIX); \
+               $(PERL) $(SRCDIR)/util/write-man-symlinks install $(SRCDIR)/doc/man7 $(BLDDIR)/doc/man7 $${fn}$(MANSUFFIX) $(DESTDIR)$(MANDIR)/man7; \
        done
 
-uninstall_man_docs:
+uninstall_man_docs: build_man_docs
        @$(ECHO) "*** Uninstalling manpages"
        @set -e; for x in dummy $(MANDOCS1); do \
                if [ "$$x" = "dummy" ]; then continue; fi; \
                fn=`basename $$x`; \
-               $(ECHO) "$(RM) $(DESTDIR)$(MANDIR)/man1/$$fn"; \
-               $(RM) $(DESTDIR)$(MANDIR)/man1/$$fn$(MANSUFFIX); \
+               $(ECHO) "$(RM) $(DESTDIR)$(MANDIR)/man1/$${fn}$(MANSUFFIX)"; \
+               $(RM) $(DESTDIR)$(MANDIR)/man1/$${fn}$(MANSUFFIX); \
+               $(PERL) $(SRCDIR)/util/write-man-symlinks uninstall $(SRCDIR)/doc/man1 $(BLDDIR)/doc/man1 $${fn}$(MANSUFFIX) $(DESTDIR)$(MANDIR)/man1; \
        done
        @set -e; for x in dummy $(MANDOCS3); do \
                if [ "$$x" = "dummy" ]; then continue; fi; \
                fn=`basename $$x`; \
-               $(ECHO) "$(RM) $(DESTDIR)$(MANDIR)/man3/$$fn"; \
-               $(RM) $(DESTDIR)$(MANDIR)/man3/$$fn$(MANSUFFIX); \
+               $(ECHO) "$(RM) $(DESTDIR)$(MANDIR)/man3/$${fn}$(MANSUFFIX)"; \
+               $(RM) $(DESTDIR)$(MANDIR)/man3/$${fn}$(MANSUFFIX); \
+               $(PERL) $(SRCDIR)/util/write-man-symlinks uninstall $(SRCDIR)/doc/man3 $(BLDDIR)/doc/man3 $${fn}$(MANSUFFIX) $(DESTDIR)$(MANDIR)/man3; \
        done
        @set -e; for x in dummy $(MANDOCS5); do \
                if [ "$$x" = "dummy" ]; then continue; fi; \
                fn=`basename $$x`; \
-               $(ECHO) "$(RM) $(DESTDIR)$(MANDIR)/man5/$$fn"; \
-               $(RM) $(DESTDIR)$(MANDIR)/man5/$$fn$(MANSUFFIX); \
+               $(ECHO) "$(RM) $(DESTDIR)$(MANDIR)/man5/$${fn}$(MANSUFFIX)"; \
+               $(RM) $(DESTDIR)$(MANDIR)/man5/$${fn}$(MANSUFFIX); \
+               $(PERL) $(SRCDIR)/util/write-man-symlinks uninstall $(SRCDIR)/doc/man5 $(BLDDIR)/doc/man5 $${fn}$(MANSUFFIX) $(DESTDIR)$(MANDIR)/man5; \
        done
        @set -e; for x in dummy $(MANDOCS7); do \
                if [ "$$x" = "dummy" ]; then continue; fi; \
                fn=`basename $$x`; \
-               $(ECHO) "$(RM) $(DESTDIR)$(MANDIR)/man7/$$fn"; \
-               $(RM) $(DESTDIR)$(MANDIR)/man7/$$fn$(MANSUFFIX); \
+               $(ECHO) "$(RM) $(DESTDIR)$(MANDIR)/man7/$${fn}$(MANSUFFIX)"; \
+               $(RM) $(DESTDIR)$(MANDIR)/man7/$${fn}$(MANSUFFIX); \
+               $(PERL) $(SRCDIR)/util/write-man-symlinks uninstall $(SRCDIR)/doc/man7 $(BLDDIR)/doc/man7 $${fn}$(MANSUFFIX) $(DESTDIR)$(MANDIR)/man7; \
        done
 
-install_html_docs: build_html_docs
+install_html_docs: install_image_docs build_html_docs
        @[ -n "$(INSTALLTOP)" ] || (echo INSTALLTOP should not be empty; exit 1)
        @$(PERL) $(SRCDIR)/util/mkdir-p.pl $(DESTDIR)$(HTMLDIR)/man1
        @$(PERL) $(SRCDIR)/util/mkdir-p.pl $(DESTDIR)$(HTMLDIR)/man3
@@ -892,31 +1029,31 @@ install_html_docs: build_html_docs
                fn=`basename $$x`; \
                $(ECHO) "install $$x -> $(DESTDIR)$(HTMLDIR)/man1/$$fn"; \
                cp $$x $(DESTDIR)$(HTMLDIR)/man1/$$fn; \
-               chmod 755 $(DESTDIR)$(HTMLDIR)/man1/$$fn; \
+               chmod 644 $(DESTDIR)$(HTMLDIR)/man1/$$fn; \
        done
        @set -e; for x in dummy $(HTMLDOCS3); do \
                if [ "$$x" = "dummy" ]; then continue; fi; \
                fn=`basename $$x`; \
                $(ECHO) "install $$x -> $(DESTDIR)$(HTMLDIR)/man3/$$fn"; \
                cp $$x $(DESTDIR)$(HTMLDIR)/man3/$$fn; \
-               chmod 755 $(DESTDIR)$(HTMLDIR)/man3/$$fn; \
+               chmod 644 $(DESTDIR)$(HTMLDIR)/man3/$$fn; \
        done
        @set -e; for x in dummy $(HTMLDOCS5); do \
                if [ "$$x" = "dummy" ]; then continue; fi; \
                fn=`basename $$x`; \
                $(ECHO) "install $$x -> $(DESTDIR)$(HTMLDIR)/man5/$$fn"; \
                cp $$x $(DESTDIR)$(HTMLDIR)/man5/$$fn; \
-               chmod 755 $(DESTDIR)$(HTMLDIR)/man5/$$fn; \
+               chmod 644 $(DESTDIR)$(HTMLDIR)/man5/$$fn; \
        done
        @set -e; for x in dummy $(HTMLDOCS7); do \
                if [ "$$x" = "dummy" ]; then continue; fi; \
                fn=`basename $$x`; \
                $(ECHO) "install $$x -> $(DESTDIR)$(HTMLDIR)/man7/$$fn"; \
                cp $$x $(DESTDIR)$(HTMLDIR)/man7/$$fn; \
-               chmod 755 $(DESTDIR)$(HTMLDIR)/man7/$$fn; \
+               chmod 644 $(DESTDIR)$(HTMLDIR)/man7/$$fn; \
        done
 
-uninstall_html_docs:
+uninstall_html_docs: uninstall_image_docs
        @$(ECHO) "*** Uninstalling HTML manpages"
        @set -e; for x in dummy $(HTMLDOCS1); do \
                if [ "$$x" = "dummy" ]; then continue; fi; \
@@ -943,16 +1080,47 @@ uninstall_html_docs:
                $(RM) $(DESTDIR)$(HTMLDIR)/man7/$$fn; \
        done
 
+install_image_docs:
+       @$(PERL) $(SRCDIR)/util/mkdir-p.pl $(DESTDIR)$(HTMLDIR)/man7/img
+       @set -e; for x in dummy $(IMAGEDOCS7); do \
+               if [ "$$x" = "dummy" ]; then continue; fi; \
+               fn=`basename $$x`; \
+               $(ECHO) "install $$x -> $(DESTDIR)$(HTMLDIR)/man7/img/$$fn"; \
+               cp $(SRCDIR)/$$x $(DESTDIR)$(HTMLDIR)/man7/img/$$fn; \
+               chmod 644 $(DESTDIR)$(HTMLDIR)/man7/img/$$fn; \
+       done
+
+uninstall_image_docs:
+       @set -e; for x in dummy $(IMAGEDOCS7); do \
+               if [ "$$x" = "dummy" ]; then continue; fi; \
+               fn=`basename $$x`; \
+               $(ECHO) "$(RM) $(DESTDIR)$(HTMLDIR)/man7/img/$$fn"; \
+               $(RM) $(DESTDIR)$(HTMLDIR)/man7/img/$$fn; \
+       done
+
 # Developer targets (note: these are only available on Unix) #########
 
-update: generate errors ordinals
+# It's important that generate_buildinfo comes after ordinals, as ordinals
+# is sensitive to build.info changes.
+update: generate errors ordinals generate_buildinfo
 
 generate: generate_apps generate_crypto_bn generate_crypto_objects \
           generate_crypto_conf generate_crypto_asn1 generate_fuzz_oids
 
-.PHONY: doc-nits
-doc-nits: build_generated
-       (cd $(SRCDIR); $(PERL) util/find-doc-nits -n -l -e )
+generate_buildinfo: generate_doc_buildinfo
+
+.PHONY: doc-nits md-nits
+doc-nits: build_generated_pods
+       $(PERL) $(SRCDIR)/util/find-doc-nits -c -n -l -e
+
+# This uses "mdl", the markdownlint application, which is written in ruby.
+# The source is at https://github.com/markdownlint/markdownlint
+# If you have ruby installed, "gem install mdl" should work.
+# Another option is at https://snapcraft.io/install/mdl/debian
+# Finally, there's a Node.js version, which we haven't tried, that
+# can be found at https://github.com/DavidAnson/markdownlint
+md-nits:
+       mdl -s util/markdownlint.rb .
 
 # Test coverage is a good idea for the future
 #coverage: $(PROGRAMS) $(TESTPROGRAMS)
@@ -964,12 +1132,6 @@ lint:
 generate_apps:
        ( cd $(SRCDIR); $(PERL) VMS/VMSify-conf.pl \
                                < apps/openssl.cnf > apps/openssl-vms.cnf )
-       @ : {- output_off() if $disabled{apps}; "" -}
-       ( b=`pwd`; cd $(SRCDIR); \
-         $(PERL) -I$$b apps/progs.pl -H $(APPS_OPENSSL) > apps/progs.h )
-       ( b=`pwd`; cd $(SRCDIR); \
-         $(PERL) -I$$b apps/progs.pl -C $(APPS_OPENSSL) > apps/progs.c )
-       @ : {- output_on() if $disabled{apps}; "" -}
 
 generate_crypto_bn:
        ( cd $(SRCDIR); $(PERL) crypto/bn/bn_prime.pl > crypto/bn/bn_prime.h )
@@ -991,6 +1153,7 @@ generate_crypto_objects:
                                crypto/objects/obj_mac.num \
                                crypto/objects/obj_xref.txt \
                                > crypto/objects/obj_xref.h )
+       ( cd $(SRCDIR); cat crypto/objects/obj_compat.h >> include/openssl/obj_mac.h )
 
 generate_crypto_conf:
        ( cd $(SRCDIR); $(PERL) crypto/conf/keysets.pl \
@@ -1005,6 +1168,70 @@ generate_fuzz_oids:
                                crypto/objects/obj_dat.h \
                                > fuzz/oids.txt )
 
+generate_doc_buildinfo:
+       ( $(PERL) -I$(BLDDIR) -Mconfigdata \
+                $(SRCDIR)/util/dofile.pl -o Makefile \
+                        $(SRCDIR)/doc/build.info.in \
+                        > $(SRCDIR)/doc/build.info.new; \
+          if ( test -e $(SRCDIR)/doc/build.info \
+               && cmp $(SRCDIR)/doc/build.info.new $(SRCDIR)/doc/build.info \
+                  > /dev/null ); \
+          then \
+                rm $(SRCDIR)/doc/build.info.new; \
+          else \
+                mv $(SRCDIR)/doc/build.info.new $(SRCDIR)/doc/build.info; \
+          fi )
+
+generate_fips_sources: providers/fips.module.sources.new
+providers/fips.module.sources.new: configdata.pm
+       rm -rf sources-tmp
+       mkdir sources-tmp
+       ( \
+         srcdir=`cd $(SRCDIR); pwd`; \
+         cd sources-tmp \
+         && $$srcdir/Configure --banner=Configured enable-fips -O0 \
+         && ./configdata.pm --query 'get_sources("providers/fips")' > sources1 \
+         && $(MAKE) -sj 4 build_generated providers/fips.so \
+         && find . -name '*.d' | xargs cat > dep1 \
+          && $(MAKE) distclean \
+         && $$srcdir/Configure --banner=Configured enable-fips no-asm -O0 \
+         && ./configdata.pm --query 'get_sources("providers/fips")' > sources2 \
+         && $(MAKE) -sj 4 build_generated providers/fips.so \
+         && find . -name '*.d' | xargs cat > dep2 \
+         && cat sources1 sources2 \
+            | grep -v ' : \\$$' | grep -v util/providers.num \
+            | sed -e 's/^ *//' -e 's/ *\\$$//' \
+            | sort | uniq > sources \
+         && cat dep1 dep2 \
+            | $(PERL) -p -e 's/\\\n//' \
+            | sed -e 's/^.*: *//' -e 's/  */ /g' \
+            | fgrep -f sources \
+            | tr ' ' '\n' \
+            | sort | uniq > deps.raw \
+         && cat deps.raw \
+            | xargs ./configdata.pm --query 'get_sources(@ARGV)' \
+            | $(PERL) -p -e 's/\\\n//' \
+            | sed -e 's/\./\\\./g' -e 's/ : */:/' -e 's/^/s:/' -e 's/$$/:/' \
+            > deps.sed \
+         && cat deps.raw | sed -f deps.sed > deps \
+       )
+       ( \
+         cat sources-tmp/sources sources-tmp/deps \
+            | $(PERL) -p -e 's:^ *\Q../\E:: ;' \
+                         -e 's:^\Q$(SRCDIR)/\E:: if "$(SRCDIR)" ne "." ;' \
+                         -e 'my $$x; do { $$x = $$_; s:(^|/)((?!\Q../\E)[^/]*/)\Q..\E($$|/):$$1: } while ($$x ne $$_) ;' ; \
+         cd $(SRCDIR); \
+         for x in crypto/bn/asm/*.pl crypto/bn/asm/*.S \
+                  crypto/aes/asm/*.pl crypto/aes/asm/*.S \
+                  crypto/ec/asm/*.pl \
+                  crypto/modes/asm/*.pl \
+                  crypto/sha/asm/*.pl \
+                  crypto/x86_64cpuid.pl; do \
+           echo "$$x"; \
+         done \
+       ) | sort | uniq > providers/fips.module.sources.new
+       rm -rf sources-tmp
+
 # Set to -force to force a rebuild
 ERROR_REBUILD=
 errors:
@@ -1021,58 +1248,120 @@ errors:
 
 {- use File::Basename;
 
-   our @sslheaders =
+   my @sslheaders_tmpl =
        qw( include/openssl/ssl.h
            include/openssl/ssl2.h
            include/openssl/ssl3.h
            include/openssl/sslerr.h
            include/openssl/tls1.h
            include/openssl/dtls1.h
-           include/openssl/srtp.h );
-   our @cryptoheaders =
+           include/openssl/srtp.h
+           include/openssl/sslerr_legacy.h );
+   my @cryptoheaders_tmpl =
        qw( include/internal/dso.h
            include/internal/o_dir.h
            include/internal/err.h
+           include/internal/evp.h
+           include/internal/pem.h
+           include/internal/asn1.h
            include/internal/sslconf.h );
-   our @cryptoskipheaders = ( @sslheaders,
+   my @cryptoskipheaders = ( @sslheaders_tmpl,
        qw( include/openssl/conf_api.h
            include/openssl/ebcdic.h
            include/openssl/opensslconf.h
            include/openssl/symhacks.h ) );
-   foreach my $f ( glob(catfile($config{sourcedir},
-                                'include','openssl','*.h')) ) {
-       my $fn = "include/openssl/" . basename($f);
-       push @cryptoheaders, $fn unless grep { $_ eq $fn } @cryptoskipheaders;
+   our %cryptoheaders = ();
+   our %sslheaders = ();
+   foreach my $d ( qw( include/openssl include/internal ) ) {
+       my @header_patterns =
+           map { catfile($config{sourcedir}, $d, $_) } ( '*.h', '*.h.in' );
+       foreach my $f ( map { glob($_) } @header_patterns ) {
+           my $base = basename($f);
+           my $base_in = basename($f, '.in');
+           my $dir = catfile($config{sourcedir}, $d);
+           if ($base ne $base_in) {
+               # We have a .h.in file, which means the header file is in the
+               # build tree.
+               $base = $base_in;
+               $dir = catfile($config{builddir}, $d);
+           }
+           my $new_f = catfile($dir, $base);
+           my $fn = "$d/$base";
+           # The logic to add files to @cryptoheaders is a bit complex.  The
+           # file to be added must be either in the public header directory
+           # or one of the pre-declared internal headers, and must under no
+           # circumstances be one of those that must be skipped.
+           $cryptoheaders{$new_f} = 1
+               if (($d eq 'include/openssl'
+                    || ( grep { $_ eq $fn } @cryptoheaders_tmpl ))
+                   && !( grep { $_ eq $fn } @cryptoskipheaders ));
+           # The logic to add files to @sslheaders is much simpler...
+           $sslheaders{$new_f} = 1 if grep { $_ eq $fn } @sslheaders_tmpl;
+       }
    }
    "";
 -}
 CRYPTOHEADERS={- join(" \\\n" . ' ' x 14,
-                      fill_lines(" ", $COLUMNS - 14, sort @cryptoheaders)) -}
+                      fill_lines(" ", $COLUMNS - 14, sort keys %cryptoheaders)) -}
 SSLHEADERS={- join(" \\\n" . ' ' x 11,
-                   fill_lines(" ", $COLUMNS - 11, sort @sslheaders)) -}
-ordinals:
-       ( cd $(SRCDIR); \
-          $(PERL) util/mknum.pl --version $(VERSION) --no-warnings \
-                  --ordinals util/libcrypto.num \
-                  --symhacks include/openssl/symhacks.h \
-                  $(CRYPTOHEADERS) )
-       ( cd $(SRCDIR); \
-          $(PERL) util/mknum.pl --version $(VERSION) --no-warnings \
-                  --ordinals util/libssl.num \
-                  --symhacks include/openssl/symhacks.h \
-                  $(SSLHEADERS))
+                   fill_lines(" ", $COLUMNS - 11, sort keys %sslheaders)) -}
+
+renumber: build_generated
+       $(PERL) $(SRCDIR)/util/mknum.pl --version $(VERSION_NUMBER) --no-warnings \
+                --ordinals $(SRCDIR)/util/libcrypto.num \
+                --symhacks $(SRCDIR)/include/openssl/symhacks.h \
+                --renumber \
+                $(CRYPTOHEADERS)
+       $(PERL) $(SRCDIR)/util/mknum.pl --version $(VERSION_NUMBER) --no-warnings \
+                --ordinals $(SRCDIR)/util/libssl.num \
+                --symhacks $(SRCDIR)/include/openssl/symhacks.h \
+                --renumber \
+                $(SSLHEADERS)
+
+ordinals: build_generated
+       $(PERL) $(SRCDIR)/util/mknum.pl --version $(VERSION_NUMBER) --no-warnings \
+                --ordinals $(SRCDIR)/util/libcrypto.num \
+                --symhacks $(SRCDIR)/include/openssl/symhacks.h \
+                $(CRYPTOHEADERS)
+       $(PERL) $(SRCDIR)/util/mknum.pl --version $(VERSION_NUMBER) --no-warnings \
+                --ordinals $(SRCDIR)/util/libssl.num \
+                --symhacks $(SRCDIR)/include/openssl/symhacks.h \
+                $(SSLHEADERS)
 
 test_ordinals:
-       ( cd test; \
-         SRCTOP=../$(SRCDIR) \
-         BLDTOP=../$(BLDDIR) \
-           $(PERL) ../$(SRCDIR)/test/run_tests.pl test_ordinals )
+       $(MAKE) run_tests TESTS=test_ordinals
 
 tags TAGS: FORCE
        rm -f TAGS tags
        -ctags -R .
        -etags `find . -name '*.[ch]' -o -name '*.pm'`
 
+providers/fips.checksum.new: providers/fips.module.sources.new
+       @which unifdef > /dev/null || \
+       ( echo >&2 "ERROR: unifdef not in your \$$PATH, FIPS checksums not calculated"; \
+         false )
+       ( sources=`pwd`/providers/fips.module.sources.new; \
+         cd $(SRCDIR) \
+         && cat $$sources \
+                | xargs ./util/fips-checksums.sh ) \
+                > providers/fips-sources.checksums.new \
+       && sha256sum providers/fips-sources.checksums.new \
+            | sed -e 's|\.new||' > providers/fips.checksum.new
+
+fips-checksums: providers/fips.checksum.new
+
+$(SRCDIR)/providers/fips.checksum: providers/fips.checksum.new
+       cp -p providers/fips.module.sources.new $(SRCDIR)/providers/fips.module.sources
+       cp -p providers/fips-sources.checksums.new $(SRCDIR)/providers/fips-sources.checksums
+       cp -p providers/fips.checksum.new $(SRCDIR)/providers/fips.checksum
+
+update-fips-checksums: $(SRCDIR)/providers/fips.checksum
+
+diff-fips-checksums: fips-checksums
+       diff -u $(SRCDIR)/providers/fips.module.sources providers/fips.module.sources.new
+       diff -u $(SRCDIR)/providers/fips-sources.checksums providers/fips-sources.checksums.new
+       diff -u $(SRCDIR)/providers/fips.checksum providers/fips.checksum.new
+
 # Release targets (note: only available on Unix) #####################
 
 tar:
@@ -1080,19 +1369,25 @@ tar:
 
 # Helper targets #####################################################
 
-link-utils: $(BLDDIR)/util/opensslwrap.sh
+link-utils: $(BLDDIR)/util/opensslwrap.sh $(BLDDIR)/apps/openssl.cnf
 
 $(BLDDIR)/util/opensslwrap.sh: configdata.pm
        @if [ "$(SRCDIR)" != "$(BLDDIR)" ]; then \
            mkdir -p "$(BLDDIR)/util"; \
-           ln -sf "../$(SRCDIR)/util/opensslwrap.sh" "$(BLDDIR)/util"; \
+           ln -sf "../$(SRCDIR)/util/`basename "$@"`" "$(BLDDIR)/util"; \
+       fi
+
+$(BLDDIR)/apps/openssl.cnf: configdata.pm
+       @if [ "$(SRCDIR)" != "$(BLDDIR)" ]; then \
+           mkdir -p "$(BLDDIR)/apps"; \
+           ln -sf "../$(SRCDIR)/apps/`basename "$@"`" "$(BLDDIR)/apps"; \
        fi
 
 FORCE:
 
 # Building targets ###################################################
 
-libcrypto.pc libssl.pc openssl.pc: configdata.pm $(LIBS) {- join(" ",map { platform->sharedlib_simple($_) // () } @{$unified_info{libraries}}) -}
+libcrypto.pc libssl.pc openssl.pc: configdata.pm $(LIBS) {- join(" ",map { platform->sharedlib_simple($_) // platform->sharedlib_import($_) // platform->sharedlib($_) // () } @{$unified_info{libraries}}) -}
 libcrypto.pc:
        @ ( echo 'prefix=$(INSTALLTOP)'; \
            echo 'exec_prefix=$${prefix}'; \
@@ -1165,73 +1460,87 @@ reconfigure reconf:
   use File::Basename;
   use File::Spec::Functions qw/:DEFAULT abs2rel rel2abs/;
 
+  # Helper function to convert dependencies in platform agnostic form to
+  # dependencies in platform form.
+  sub compute_platform_depends {
+      map { my $x = $_;
+
+            grep { $x eq $_ } @{$unified_info{programs}} and platform->bin($x)
+            or grep { $x eq $_ } @{$unified_info{modules}} and platform->dso($x)
+            or grep { $x eq $_ } @{$unified_info{libraries}} and platform->lib($x)
+            or platform->convertext($x); } @_;
+  }
+
   # Helper function to figure out dependencies on libraries
   # It takes a list of library names and outputs a list of dependencies
   sub compute_lib_depends {
       # Depending on shared libraries:
       # On Windows POSIX layers, we depend on {libname}.dll.a
       # On Unix platforms, we depend on {shlibname}.so
-      return map { platform->sharedlib_simple($_) // platform->staticlib($_) } @_;
+      return map { platform->sharedlib_simple($_)
+                   // platform->sharedlib_import($_)
+                   // platform->sharedlib($_)
+                   // platform->staticlib($_)
+                 } @_;
+  }
+
+  sub generatetarget {
+      my %args = @_;
+      my $deps = join(" ", compute_platform_depends(@{$args{deps}}));
+      return <<"EOF";
+$args{target}: $deps
+EOF
   }
 
   sub generatesrc {
       my %args = @_;
-      my $generator = join(" ", @{$args{generator}});
-      my $generator_incs = join("", map { " -I".$_ } @{$args{generator_incs}});
+      my $gen0 = $args{generator}->[0];
+      my $gen_args = join('', map { " $_" }
+                              @{$args{generator}}[1..$#{$args{generator}}]);
+      my $gen_incs = join("", map { " -I".$_ } @{$args{generator_incs}});
       my $incs = join("", map { " -I".$_ } @{$args{incs}});
       my $defs = join("", map { " -D".$_ } @{$args{defs}});
-      my $deps = join(" ", @{$args{generator_deps}}, @{$args{deps}});
+      my $deps = join(" ", compute_platform_depends(@{$args{generator_deps}},
+                                                    @{$args{deps}}));
 
       if ($args{src} =~ /\.html$/) {
-         my $title = basename($args{src}, ".html");
-         my $pod = $args{generator}->[0];
-         return <<"EOF";
+          #
+          # HTML generator
+          #
+          my $title = basename($args{src}, ".html");
+          my $pod = $gen0;
+          return <<"EOF";
 $args{src}: $pod
-       pod2html "--podroot=\$(SRCDIR)/doc" --htmldir=.. \\
-                --podpath=man1:man3:man5:man7 --infile=$pod "--title=$title" \\
-       | \$(PERL) -pe 's|href="http://man\\.he\\.net/(man\\d/[^"]+)(?:\\.html)?"|href="../\$1.html|g;' \\
-       > \$\@
+       \$(PERL) \$(SRCDIR)/util/mkpod2html.pl -i "$pod" -o \$\@ -t "$title" -r "\$(SRCDIR)/doc"
 EOF
       } elsif ($args{src} =~ /\.(\d)$/) {
-         my $section = $1;
-         my $name = uc basename($args{src}, ".$section");
-         my $pod = $args{generator}->[0];
-         return <<"EOF";
+          #
+          # Man-page generator
+          #
+          my $section = $1;
+          my $name = uc basename($args{src}, ".$section");
+          my $pod = $gen0;
+          return <<"EOF";
 $args{src}: $pod
-       pod2man --name=$name --section=$section --center=OpenSSL \\
-               --release=\$(VERSION) $pod \\
-       > \$\@
+       pod2man --name=$name --section=$section\$(MANSUFFIX) --center=OpenSSL \\
+               --release=\$(VERSION) $pod >\$\@
 EOF
       } elsif (platform->isdef($args{src})) {
+          #
+          # Linker script-ish generator
+          #
           my $target = platform->def($args{src});
           (my $mkdef_os = $target{shared_target}) =~ s|-shared$||;
-          my $ord_ver = $args{intent} eq 'lib' ? ' --version $(VERSION)' : '';
+          my $ord_ver = $args{intent} eq 'lib' ? ' --version $(VERSION_NUMBER)' : '';
           my $ord_name = $args{generator}->[1] || $args{product};
           return <<"EOF";
-$target: $args{generator}->[0] $deps \$(SRCDIR)/util/mkdef.pl
-       \$(PERL) \$(SRCDIR)/util/mkdef.pl$ord_ver --ordinals $args{generator}->[0]  --name $ord_name --OS $mkdef_os > $target
-EOF
-      } elsif (!platform->isasm($args{src})) {
-          if ($args{generator}->[0] =~ m|^.*\.in$|) {
-              my $dofile = abs2rel(rel2abs(catfile($config{sourcedir},
-                                                   "util", "dofile.pl")),
-                                   rel2abs($config{builddir}));
-              my @modules = ( 'configdata.pm',
-                              grep { $_ =~ m|\.pm$| } @{$args{deps}} );
-              my %moduleincs = map { '"-I'.dirname($_).'"' => 1 } @modules;
-              @modules = map { "-M".basename($_, '.pm') } @modules;
-              my $modules = join(' ', '', sort keys %moduleincs, @modules);
-              return <<"EOF";
-$args{src}: $args{generator}->[0] $deps \$(BLDDIR)/configdata.pm
-       \$(PERL)$modules "$dofile" "-o$target{build_file}" $generator > \$@
-EOF
-         } else {
-              return <<"EOF";
-$args{src}: $args{generator}->[0] $deps
-       \$(PERL)$generator_incs $generator > \$@
+$target: $gen0 $deps \$(SRCDIR)/util/mkdef.pl
+       \$(PERL) \$(SRCDIR)/util/mkdef.pl$ord_ver --ordinals $gen0  --name $ord_name --OS $mkdef_os > $target
 EOF
-         }
-      } else {
+      } elsif (platform->isasm($args{src})) {
+          #
+          # Assembler generator
+          #
           my $cppflags = {
               shlib => '$(LIB_CFLAGS) $(LIB_CPPFLAGS)',
               lib => '$(LIB_CFLAGS) $(LIB_CPPFLAGS)',
@@ -1239,27 +1548,66 @@ EOF
               bin => '$(BIN_CFLAGS) $(BIN_CPPFLAGS)'
           } -> {$args{intent}};
 
-          if ($args{generator}->[0] =~ /\.pl$/) {
-              $generator = 'CC="$(CC)" $(PERL)'.$generator_incs.' '.$generator
-                  .' "$(PERLASM_SCHEME)"'.$incs.' '.$cppflags.$defs.' $(PROCESSSOR)';
-          } elsif ($args{generator}->[0] =~ /\.m4$/) {
-              $generator = 'm4 -B 8192'.$generator_incs.' '.$generator.' >'
-          } elsif ($args{generator}->[0] =~ /\.S$/) {
+          my $generator;
+          if ($gen0 =~ /\.pl$/) {
+              $generator = 'CC="$(CC)" $(PERL)'.$gen_incs.' '.$gen0.$gen_args
+                  .' "$(PERLASM_SCHEME)"'.$incs.' '.$cppflags.$defs.' $(PROCESSOR)';
+          } elsif ($gen0 =~ /\.m4$/) {
+              $generator = 'm4 -B 8192'.$gen_incs.' '.$gen0.$gen_args.' >'
+          } elsif ($gen0 =~ /\.S$/) {
               $generator = undef;
           } else {
-              die "Generator type for $args{src} unknown: $generator\n";
+              die "Generator type for $args{src} unknown: $gen0\n";
           }
 
           if (defined($generator)) {
               return <<"EOF";
-$args{src}: $args{generator}->[0] $deps
+$args{src}: $gen0 $deps
        $generator \$@
 EOF
           }
           return <<"EOF";
-$args{src}: $args{generator}->[0] $deps
-       \$(CC) $incs $cppflags $defs -E $args{generator}->[0] | \\
+$args{src}: $gen0 $deps
+       \$(CC) $incs $cppflags $defs -E $gen0 | \\
        \$(PERL) -ne '/^#(line)?\\s*[0-9]+/ or print' > \$@
+EOF
+      } elsif ($gen0 =~ m|^.*\.in$|) {
+          #
+          # "dofile" generator (file.in -> file)
+          #
+          my $dofile = abs2rel(rel2abs(catfile($config{sourcedir},
+                                               "util", "dofile.pl")),
+                               rel2abs($config{builddir}));
+          my @perlmodules = ( 'configdata.pm',
+                              grep { $_ =~ m|\.pm$| } @{$args{deps}} );
+          my %perlmoduleincs = map { '"-I'.dirname($_).'"' => 1 } @perlmodules;
+          $deps = join(' ', $deps, compute_platform_depends(@perlmodules));
+          @perlmodules = map { "-M".basename($_, '.pm') } @perlmodules;
+          my $perlmodules = join(' ', '', sort keys %perlmoduleincs, @perlmodules);
+          return <<"EOF";
+$args{src}: $gen0 $deps
+       \$(PERL)$perlmodules "$dofile" "-o$target{build_file}" $gen0$gen_args > \$@
+EOF
+      } elsif (grep { $_ eq $gen0 } @{$unified_info{programs}}) {
+          #
+          # Generic generator using OpenSSL programs
+          #
+
+          # Redo $gen0, to ensure that we have the proper extension where
+          # necessary.
+          $gen0 = platform->bin($gen0);
+          # Use $(PERL) to execute wrap.pl directly to avoid calling env
+          return <<"EOF";
+$args{src}: $gen0 $deps \$(BLDDIR)/util/wrap.pl
+       \$(PERL) \$(BLDDIR)/util/wrap.pl $gen0$gen_args > \$@
+EOF
+      } else {
+          #
+          # Generic generator using Perl
+          #
+          return <<"EOF";
+$args{src}: $gen0 $deps
+       \$(PERL)$gen_incs $gen0$gen_args > \$@
 EOF
       }
   }
@@ -1310,7 +1658,7 @@ $obj: $deps
        $cmd $cmdflags -c -o \$\@ $srcs
 EOF
       } elsif (grep /\.S$/, @srcs) {
-          # Originally there was mutli-step rule with $(CC) -E file.S
+          # Originally there was multi-step rule with $(CC) -E file.S
           # followed by $(CC) -c file.s. It compensated for one of
           # legacy platform compiler's inability to handle .S files.
           # The platform is long discontinued by vendor so there is
@@ -1319,8 +1667,7 @@ EOF
 $obj: $deps
        $cmd $incs $defs $cmdflags -c -o \$\@ $srcs
 EOF
-      } elsif (defined $makedepprog && $makedepprog !~ /\/makedepend/
-               && !grep /\.rc$/, @srcs) {
+      } elsif ($makedep_scheme eq 'gcc' && !grep /\.rc$/, @srcs) {
           $recipe .= <<"EOF";
 $obj: $deps
        $cmd $incs $defs $cmdflags -MMD -MF $dep.tmp -MT \$\@ -c -o \$\@ $srcs
@@ -1336,7 +1683,7 @@ EOF
 $obj: $deps
        $cmd $incs $defs $cmdflags $cmdcompile -o \$\@ $srcs
 EOF
-          if (defined $makedepprog  && $makedepprog =~ /\/makedepend/) {
+          if ($makedep_scheme eq 'makedepend') {
               $recipe .= <<"EOF";
        \$(MAKEDEPEND) -f- -Y -- $incs $cmdflags -- $srcs 2>/dev/null \\
            > $dep
@@ -1373,52 +1720,101 @@ EOF
       my @deps = compute_lib_depends(@{$args{deps}});
       die "More than one exported symbol map" if scalar @defs > 1;
 
-      my $simple = platform->sharedlib_simple($args{lib});
       my $full = platform->sharedlib($args{lib});
+      # $import is for Windows and subsystems thereof, where static import
+      # libraries for DLLs are a thing.  On platforms that have this mechanism,
+      # $import has the name of this import library.  On platforms that don't
+      # have this mechanism, $import will be |undef|.
+      my $import = platform->sharedlib_import($args{lib});
+      # $simple is for platforms where full shared library names include the
+      # shared library version, and there's a simpler name that doesn't include
+      # that version.  On such platforms, $simple has the simpler name.  On
+      # other platforms, it will be |undef|.
+      my $simple = platform->sharedlib_simple($args{lib});
+
+      my $argfile = defined $target{shared_argfileflag} ? $full.".args" : undef;
       my $shared_soname = "";
       $shared_soname .= ' '.$target{shared_sonameflag}.basename($full)
           if defined $target{shared_sonameflag};
       my $shared_imp = "";
-      $shared_imp .= ' '.$target{shared_impflag}.basename($simple)
-          if defined $target{shared_impflag};
+      $shared_imp .= ' '.$target{shared_impflag}.basename($import)
+          if defined $target{shared_impflag} && defined $import;
       my $shared_def = join("", map { ' '.$target{shared_defflag}.$_ } @defs);
 
-      my $objs = join(" \\\n\t\t", fill_lines(' ', $COLUMNS - 16, @objs));
-      my $deps = join(" \\\n" . ' ' x (length($full) + 2),
-                      fill_lines(' ', $COLUMNS - length($full) - 2,
-                                 @objs, @defs, @deps));
-
-      my $recipe = <<"EOF";
+      # There is at least one platform where the compiler-as-linker needs to
+      # have one object file directly on the command line.  That won't hurt
+      # any other platform, so we do that for everyone when there's an argfile
+      # to be had.  This depends heavily on splice, which removes elements from
+      # the given array, and returns them so they can be captured.
+      my @argfileobjs = $argfile
+          ? splice(@objs, 1)
+          : ();
+      my $argfilecmds = $argfile
+          ? join("\n\t", map { "echo $_ >> $argfile" } @argfileobjs)
+          : undef;
+      my $argfiledeps = $argfile
+          ? join(" \\\n" . ' ' x (length($argfile) + 2),
+                 fill_lines(' ', $COLUMNS - length($full) - 2, @argfileobjs))
+          : undef;
+      my @fulldeps = (@objs, ($argfile ? $argfile : ()), @defs, @deps);
+      my @fullobjs = (
+          @objs,
+          ($argfile ? $target{shared_argfileflag}.$argfile : ())
+      );
+      my $fulldeps =
+          join(" \\\n" . ' ' x (length($full) + 2),
+               fill_lines(' ', $COLUMNS - length($full) - 2, @fulldeps));
+      my $fullobjs =
+          join(" \\\n\t\t", fill_lines(' ', $COLUMNS - 16, @fullobjs));
+
+      my $recipe = '';
+
+      if (defined $simple && $simple ne $full) {
+          if (sharedaix()) {
+              $recipe .= <<"EOF";
 $simple: $full
-EOF
-      if (sharedaix()) {
-          $recipe .= <<"EOF";
        rm -f $simple && \\
        \$(AR) r $simple $full
 EOF
-      } elsif ($simple ne $full) {
-          $recipe .= <<"EOF";
+          } else {
+              $recipe .= <<"EOF";
+$simple: $full
        rm -f $simple && \\
        ln -s $full $simple
+EOF
+          }
+      }
+      if (defined $import) {
+      $recipe .= <<"EOF";
+$import: $full
 EOF
       }
       $recipe .= <<"EOF";
-$full: $deps
+$full: $fulldeps
        \$(CC) \$(LIB_CFLAGS) $linkflags\$(LIB_LDFLAGS)$shared_soname$shared_imp \\
                -o $full$shared_def \\
-               $objs \\
+               $fullobjs \\
                $linklibs \$(LIB_EX_LIBS)
 EOF
       if (windowsdll()) {
           $recipe .= <<"EOF";
        rm -f apps/$full
-       rm -f test/$full
        rm -f fuzz/$full
        cp -p $full apps/
-       cp -p $full test/
        cp -p $full fuzz/
 EOF
+          if (!$disabled{tests}) {
+            $recipe .= <<"EOF";
+       rm -f test/$full
+       cp -p $full test/
+EOF
+          }
       }
+      $recipe .= <<"EOF" if defined $argfile;
+$argfile: $argfiledeps
+       \$(RM) $argfile
+       $argfilecmds
+EOF
       return $recipe;
   }
   sub obj2dso {
@@ -1449,7 +1845,8 @@ EOF
                  @{$args{objs}};
       my @deps = compute_lib_depends(@{$args{deps}});
       my $shared_def = join("", map { ' '.$target{shared_defflag}.$_ } @defs);
-
+      # Next line needs to become "less magic" (see PR #11950)
+      $shared_def .= ' '.$target{shared_fipsflag} if (defined $target{shared_fipsflag} && $shared_def =~ m/providers\/fips/);
       my $objs = join(" \\\n\t\t", fill_lines(' ', $COLUMNS - 16, @objs));
       my $deps = join(" \\\n" . ' ' x (length($dso) + 2),
                       fill_lines(' ', $COLUMNS - length($dso) - 2,
@@ -1467,11 +1864,17 @@ EOF
       my %args = @_;
       my $lib = platform->staticlib($args{lib});
       my @objs = map { platform->obj($_) } @{$args{objs}};
-      my $objs = join(" \\\n" . ' ' x (length($lib) + 2),
+      my $deps = join(" \\\n" . ' ' x (length($lib) + 2),
                       fill_lines(' ', $COLUMNS - length($lib) - 2, @objs));
+      my $max_per_call = 500;
+      my @objs_grouped;
+      push @objs_grouped, join(" ", splice @objs, 0, $max_per_call) while @objs;
+      my $fill_lib =
+          join("\n\t", (map { "\$(AR) \$(ARFLAGS) $lib $_" } @objs_grouped));
       return <<"EOF";
-$lib: $objs
-       \$(AR) \$(ARFLAGS) \$\@ \$\?
+$lib: $deps
+       \$(RM) $lib
+       $fill_lib
        \$(RANLIB) \$\@ || echo Never mind.
 EOF
   }
@@ -1528,7 +1931,8 @@ EOF
                                            "util", "dofile.pl")),
                            rel2abs($config{builddir}));
       return <<"EOF";
-$script: $sources
+$script: $sources configdata.pm
+       \$(RM) "$script"
        \$(PERL) "-I\$(BLDDIR)" -Mconfigdata "$dofile" \\
            "-o$target{build_file}" $sources > "$script"
        chmod a+x $script
@@ -1537,11 +1941,8 @@ EOF
   sub generatedir {
       my %args = @_;
       my $dir = $args{dir};
-      my @deps = map { platform->convertext($_) } @{$args{deps}};
+      my @deps = compute_platform_depends(@{$args{deps}});
       my @comments = ();
-      my %extinfo = ( dso => platform->dsoext(),
-                      lib => platform->libext(),
-                      bin => platform->binext() );
 
       # We already have a 'test' target, and the top directory is just plain
       # silly
@@ -1556,7 +1957,7 @@ EOF
           if ($type ne "lib") {
               foreach my $prod (@{$unified_info{dirinfo}->{$dir}->{products}->{$type}}) {
                   if (dirname($prod) eq $dir) {
-                      push @deps, $prod.$extinfo{$type};
+                      push @deps, compute_platform_depends($prod);
                   } else {
                       push @comments, "# No support to produce $type ".join(", ", @{$unified_info{dirinfo}->{$dir}->{products}->{$type}});
                   }