Configure: rework build.info grammar and attributes
authorRichard Levitte <levitte@openssl.org>
Thu, 3 Oct 2019 21:30:58 +0000 (23:30 +0200)
committerRichard Levitte <levitte@openssl.org>
Thu, 10 Oct 2019 12:12:15 +0000 (14:12 +0200)
The build.info grammar's regular expressions were a horrible read.
By assigning certain sub-expressions to variables, we hope to make
it a little more readable.

Also, the handling of build.info attributes is reworked to use a
common function instead of having copies of the same code.

Finally, the attributes are reorganized to specify if they belong with
programs, libraries, modules or scripts.  This will enable more
intricate attribute assignment in changes to come.

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/10088)

Configurations/common.tmpl
Configurations/descrip.mms.tmpl
Configurations/unix-Makefile.tmpl
Configurations/windows-makefile.tmpl
Configure

index 62b1102c79876d129a6f6147f03cbdda07b07476..d28df743fe3bb7ffef0f4c3bc46be0faca212ad6 100644 (file)
      unless ($disabled{shared} || $lib =~ /\.a$/) {
          my $obj2shlib = defined &obj2shlib ? \&obj2shlib : \&libobj2shlib;
          $OUT .= $obj2shlib->(lib => $lib,
-                              attrs => $unified_info{attributes}->{$lib},
+                              attrs => $unified_info{attributes}->{libraries}->{$lib},
                               objs => $unified_info{shared_sources}->{$lib},
                               deps => [ reducedepends(resolvedepends($lib)) ]);
          foreach ((@{$unified_info{shared_sources}->{$lib}},
              # Otherwise, it might simply be generated
              if (defined $unified_info{sources}->{$_}) {
                  doobj($_, $lib, intent => "shlib",
-                       attrs => $unified_info{attributes}->{$lib});
+                       attrs => $unified_info{attributes}->{libraries}->{$lib});
              } else {
                  dogenerate($_, undef, undef, intent => "lib");
              }
          }
      }
      $OUT .= obj2lib(lib => $lib,
-                     attrs => $unified_info{attributes}->{$lib},
+                     attrs => $unified_info{attributes}->{libraries}->{$lib},
                      objs => [ @{$unified_info{sources}->{$lib}} ]);
      foreach (@{$unified_info{sources}->{$lib}}) {
          doobj($_, $lib, intent => "lib",
-               attrs => $unified_info{attributes}->{$lib});
+               attrs => $unified_info{attributes}->{libraries}->{$lib});
      }
      $cache{$lib} = 1;
  }
  # obj2dso, and also makes sure all object files for the library
  # are built.
  sub domodule {
-     my $lib = shift;
-     return "" if $cache{$lib};
-     $OUT .= obj2dso(lib => $lib,
-                     attrs => $unified_info{attributes}->{$lib},
-                     objs => $unified_info{sources}->{$lib},
-                     deps => [ resolvedepends($lib) ]);
-     foreach (@{$unified_info{sources}->{$lib}}) {
+     my $module = shift;
+     return "" if $cache{$module};
+     $OUT .= obj2dso(module => $module,
+                     attrs => $unified_info{attributes}->{modules}->{$module},
+                     objs => $unified_info{sources}->{$module},
+                     deps => [ resolvedepends($module) ]);
+     foreach (@{$unified_info{sources}->{$module}}) {
          # If this is somehow a compiled object, take care of it that way
          # Otherwise, it might simply be generated
          if (defined $unified_info{sources}->{$_}) {
-             doobj($_, $lib, intent => "dso",
-                   attrs => $unified_info{attributes}->{$lib});
+             doobj($_, $module, intent => "dso",
+                   attrs => $unified_info{attributes}->{modules}->{$module});
          } else {
-             dogenerate($_, undef, $lib, intent => "dso");
+             dogenerate($_, undef, $module, intent => "dso");
          }
      }
-     $cache{$lib} = 1;
+     $cache{$module} = 1;
  }
 
  # dobin is responsible for building programs.  It will call obj2bin,
      return "" if $cache{$bin};
      my $deps = [ reducedepends(resolvedepends($bin)) ];
      $OUT .= obj2bin(bin => $bin,
-                     attrs => $unified_info{attributes}->{$bin},
+                     attrs => $unified_info{attributes}->{programs}->{$bin},
                      objs => [ @{$unified_info{sources}->{$bin}} ],
                      deps => $deps);
      foreach (@{$unified_info{sources}->{$bin}}) {
index 892102dd91b5c659cdd3dc214eb08917d5b75a44..e177f2202ee12063d828f2ba6bd6c829eaed1cbd 100644 (file)
       @{$unified_info{libraries}};
   our @install_libs =
       map { platform->staticname($_) }
-      grep { !$unified_info{attributes}->{$_}->{noinst} }
+      grep { !$unified_info{attributes}->{libraries}->{$_}->{noinst} }
       @{$unified_info{libraries}};
   our @install_shlibs =
       map { platform->sharedname($_) // () }
-      grep { !$unified_info{attributes}->{$_}->{noinst} }
+      grep { !$unified_info{attributes}->{libraries}->{$_}->{noinst} }
       @{$unified_info{libraries}};
   our @install_engines =
-      grep { !$unified_info{attributes}->{$_}->{noinst}
-             && $unified_info{attributes}->{$_}->{engine} }
+      grep { !$unified_info{attributes}->{modules}->{$_}->{noinst}
+             && $unified_info{attributes}->{modules}->{$_}->{engine} }
       @{$unified_info{modules}};
   our @install_programs =
-      grep { !$unified_info{attributes}->{$_}->{noinst} }
+      grep { !$unified_info{attributes}->{programs}->{$_}->{noinst} }
       @{$unified_info{programs}};
   our @install_bin_scripts =
-      grep { !$unified_info{attributes}->{$_}->{noinst}
-             && !$unified_info{attributes}->{$_}->{misc} }
+      grep { !$unified_info{attributes}->{scripts}->{$_}->{noinst}
+             && !$unified_info{attributes}->{scripts}->{$_}->{misc} }
       @{$unified_info{scripts}};
   our @install_misc_scripts =
-      grep { !$unified_info{attributes}->{$_}->{noinst}
-             && $unified_info{attributes}->{$_}->{misc} }
+      grep { !$unified_info{attributes}->{scripts}->{$_}->{noinst}
+             && $unified_info{attributes}->{scripts}->{$_}->{misc} }
       @{$unified_info{scripts}};
 
   # This is a horrible hack, but is needed because recursive inclusion of files
@@ -704,7 +704,7 @@ reconfigure reconf :
       # On Unix platforms, we depend on {shlibname}.so
       return map {
           { lib   => platform->sharedlib($_) // platform->staticlib($_),
-            attrs => $unified_info{attributes}->{$_} }
+            attrs => $unified_info{attributes}->{libraries}->{$_} }
       } @_;
   }
 
@@ -1014,8 +1014,8 @@ EOF
   }
   sub obj2dso {
       my %args = @_;
-      my $dsoname = platform->dsoname($args{lib});
-      my $dso = platform->dso($args{lib});
+      my $dsoname = platform->dsoname($args{module});
+      my $dso = platform->dso($args{module});
       my @objs = map { platform->convertext($_) }
                  grep { platform->isobj($_) }
                  @{$args{objs}};
index 74b9079219f9391441511d5dd3a157be1ecae6fb..6de2acc9e7933445bba3bc027fea18287afa282f 100644 (file)
@@ -54,44 +54,44 @@ GENERATED={- # common0.tmpl provides @generated
 
 INSTALL_LIBS={-
         join(" ", map { platform->staticlib($_) // () }
-                  grep { !$unified_info{attributes}->{$_}->{noinst} }
+                  grep { !$unified_info{attributes}->{libraries}->{$_}->{noinst} }
                   @{$unified_info{libraries}})
 -}
 INSTALL_SHLIBS={-
         join(" ", map { platform->sharedlib($_) // () }
-                  grep { !$unified_info{attributes}->{$_}->{noinst} }
+                  grep { !$unified_info{attributes}->{libraries}->{$_}->{noinst} }
                   @{$unified_info{libraries}})
 -}
 INSTALL_SHLIB_INFO={-
         join(" ", map { my $x = platform->sharedlib($_);
                         my $y = platform->sharedlib_simple($_);
                         $x ? "\"$x;$y\"" : () }
-                  grep { !$unified_info{attributes}->{$_}->{noinst} }
+                  grep { !$unified_info{attributes}->{libraries}->{$_}->{noinst} }
                   @{$unified_info{libraries}})
 -}
 INSTALL_ENGINES={-
         join(" ", map { platform->dso($_) }
-                  grep { !$unified_info{attributes}->{$_}->{noinst}
-                         && $unified_info{attributes}->{$_}->{engine} }
+                  grep { !$unified_info{attributes}->{modules}->{$_}->{noinst}
+                         && $unified_info{attributes}->{modules}->{$_}->{engine} }
                   @{$unified_info{modules}})
 -}
 INSTALL_PROGRAMS={-
         join(" ", map { platform->bin($_) }
-                  grep { !$unified_info{attributes}->{$_}->{noinst} }
+                  grep { !$unified_info{attributes}->{programs}->{$_}->{noinst} }
                   @{$unified_info{programs}})
 -}
 BIN_SCRIPTS={-
-        join(" ", map { my $x = $unified_info{attributes}->{$_}->{linkname};
+        join(" ", map { my $x = $unified_info{attributes}->{scripts}->{$_}->{linkname};
                         $x ? "$_:$x" : $_ }
-                  grep { !$unified_info{attributes}->{$_}->{noinst}
-                         && !$unified_info{attributes}->{$_}->{misc} }
+                  grep { !$unified_info{attributes}->{scripts}->{$_}->{noinst}
+                         && !$unified_info{attributes}->{scripts}->{$_}->{misc} }
                   @{$unified_info{scripts}})
 -}
 MISC_SCRIPTS={-
-        join(" ", map { my $x = $unified_info{attributes}->{$_}->{linkname};
+        join(" ", map { my $x = $unified_info{attributes}->{scripts}->{$_}->{linkname};
                         $x ? "$_:$x" : $_ }
-                  grep { !$unified_info{attributes}->{$_}->{noinst}
-                         && $unified_info{attributes}->{$_}->{misc} }
+                  grep { !$unified_info{attributes}->{scripts}->{$_}->{noinst}
+                         && $unified_info{attributes}->{scripts}->{$_}->{misc} }
                   @{$unified_info{scripts}})
 -}
 
@@ -1156,7 +1156,7 @@ EOF
   }
   sub obj2dso {
       my %args = @_;
-      my $dso = platform->dso($args{lib});
+      my $dso = platform->dso($args{module});
       my @linkdirs = ();
       foreach (@{args{deps}}) {
           my $d = dirname($_);
index bfe88f6cc99fd7e75ee7c3864709d27a933a7b9e..4a83d0cee6aaf788a8288927e100416fbfe1e108 100644 (file)
@@ -62,53 +62,53 @@ GENERATED={- # common0.tmpl provides @generated
 INSTALL_LIBS={-
         join(" ", map { quotify1(platform->sharedlib_import($_)
                                  // platform->staticlib($_)) }
-                  grep { !$unified_info{attributes}->{$_}->{noinst} }
+                  grep { !$unified_info{attributes}->{libraries}->{$_}->{noinst} }
                   @{$unified_info{libraries}})
 -}
 INSTALL_SHLIBS={-
         join(" ", map { my $x = platform->sharedlib($_);
                         $x ? quotify_l($x) : () }
-                  grep { !$unified_info{attributes}->{$_}->{noinst} }
+                  grep { !$unified_info{attributes}->{libraries}->{$_}->{noinst} }
                   @{$unified_info{libraries}})
 -}
 INSTALL_SHLIBPDBS={-
         join(" ", map { my $x = platform->sharedlibpdb($_);
                         $x ? quotify_l($x) : () }
-                  grep { !$unified_info{attributes}->{$_}->{noinst} }
+                  grep { !$unified_info{attributes}->{libraries}->{$_}->{noinst} }
                   @{$unified_info{libraries}})
 -}
 INSTALL_ENGINES={-
         join(" ", map { quotify1(platform->dso($_)) }
-                  grep { !$unified_info{attributes}->{$_}->{noinst}
-                         && $unified_info{attributes}->{$_}->{engine} }
+                  grep { !$unified_info{attributes}->{modules}->{$_}->{noinst}
+                         && $unified_info{attributes}->{modules}->{$_}->{engine} }
                   @{$unified_info{modules}})
 -}
 INSTALL_ENGINEPDBS={-
         join(" ", map { quotify1(platform->dsopdb($_)) }
-                  grep { !$unified_info{attributes}->{$_}->{noinst}
-                         && $unified_info{attributes}->{$_}->{engine} }
+                  grep { !$unified_info{attributes}->{modules}->{$_}->{noinst}
+                         && $unified_info{attributes}->{modules}->{$_}->{engine} }
                   @{$unified_info{modules}})
 -}
 INSTALL_PROGRAMS={-
         join(" ", map { quotify1(platform->bin($_)) }
-                  grep { !$unified_info{attributes}->{$_}->{noinst} }
+                  grep { !$unified_info{attributes}->{programs}->{$_}->{noinst} }
                   @{$unified_info{programs}})
 -}
 INSTALL_PROGRAMPDBS={-
         join(" ", map { quotify1(platform->binpdb($_)) }
-                  grep { !$unified_info{attributes}->{$_}->{noinst} }
+                  grep { !$unified_info{attributes}->{programs}->{$_}->{noinst} }
                   @{$unified_info{programs}})
 -}
 BIN_SCRIPTS={-
         join(" ", map { quotify1($_) }
-                  grep { !$unified_info{attributes}->{$_}->{noinst}
-                         && !$unified_info{attributes}->{$_}->{misc} }
+                  grep { !$unified_info{attributes}->{scripts}->{$_}->{noinst}
+                         && !$unified_info{attributes}->{scripts}->{$_}->{misc} }
                   @{$unified_info{scripts}})
 -}
 MISC_SCRIPTS={-
         join(" ", map { quotify1($_) }
-                  grep { !$unified_info{attributes}->{$_}->{noinst}
-                         && $unified_info{attributes}->{$_}->{misc} }
+                  grep { !$unified_info{attributes}->{scripts}->{$_}->{noinst}
+                         && $unified_info{attributes}->{scripts}->{$_}->{misc} }
                   @{$unified_info{scripts}})
 -}
 
@@ -714,8 +714,8 @@ EOF
  }
  sub obj2dso {
      my %args = @_;
-     my $dso = platform->dso($args{lib});
-     my $dso_n = platform->dsoname($args{lib});
+     my $dso = platform->dso($args{module});
+     my $dso_n = platform->dsoname($args{module});
      my @objs = map { platform->convertext($_) }
                 grep { platform->isobj($_) || platform->isres($_) }
                 @{$args{objs}};
index 3a29d903298c8853674546a81d17493361c71664..9185f5dccc22c1f18bc51d2a021ca6102539f927 100755 (executable)
--- a/Configure
+++ b/Configure
@@ -1724,7 +1724,6 @@ if ($builder eq "unified") {
         my @modules = ();
         my @scripts = ();
 
-        my %attributes = ();
         my %sources = ();
         my %shared_sources = ();
         my %includes = ();
@@ -1737,7 +1736,7 @@ if ($builder eq "unified") {
         # contains a dollar sign, it had better be escaped, or it will be
         # taken for a variable name prefix.
         my %variables = ();
-        my $variable_re = qr/\$([[:alpha:]][[:alnum:]_]*)/;
+        my $variable_re = qr/\$(?P<VARIABLE>[[:alpha:]][[:alnum:]_]*)/;
         my $expand_variables = sub {
             my $value = '';
             my $value_rest = shift;
@@ -1748,7 +1747,7 @@ if ($builder eq "unified") {
             }
             while ($value_rest =~ /(?<!\\)${variable_re}/) {
                 $value .= $`;
-                $value .= $variables{$1};
+                $value .= $variables{$+{VARIABLE}};
                 $value_rest = $';
             }
             if ($ENV{CONFIGURE_DEBUG_VARIABLE_EXPAND}) {
@@ -1758,6 +1757,35 @@ if ($builder eq "unified") {
             return $value . $value_rest;
         };
 
+        # Support for attributes in build.info files
+        my %attributes = ();
+        my $handle_attributes = sub {
+            my $attr_str = shift;
+            my $ref = shift;
+            my @goals = @_;
+
+            return unless defined $attr_str;
+
+            my @a = tokenize($attr_str, qr|\s*,\s*|);
+            foreach my $a (@a) {
+                my $ac = 1;
+                my $ak = $a;
+                my $av = 1;
+                if ($a =~ m|^(!)?(.*?)\s* = \s*(.*?)$|) {
+                    $ac = ! $1;
+                    $ak = $1;
+                    $av = $2;
+                }
+                foreach my $g (@goals) {
+                    if ($ac) {
+                        $$ref->{$g}->{$ak} = $av;
+                    } else {
+                        delete $$ref->{$g}->{$ak};
+                    }
+                }
+            }
+        };
+
         # We want to detect configdata.pm in the source tree, so we
         # don't use it if the build tree is different.
         my $src_configdata = cleanfile($srcdir, "configdata.pm", $blddir);
@@ -1787,153 +1815,122 @@ if ($builder eq "unified") {
         # 1 last was positive (don't skip lines until next ELSE, ELSIF or ENDIF)
         # 2 positive ELSE (following ELSIF should fail)
         my @skip = ();
+
+        # A few useful generic regexps
+        my $index_re = qr/\[\s*(?P<INDEX>(?:\\.|.)*?)\s*\]/;
+        my $cond_re = qr/\[\s*(?P<COND>(?:\\.|.)*?)\s*\]/;
+        my $attribs_re = qr/(?:\{\s*(?P<ATTRIBS>(?:\\.|.)*?)\s*\})?/;
+        my $value_re = qr/\s*(?P<VALUE>.*?)\s*/;
         collect_information(
             collect_from_array([ @text ],
                                qr/\\$/ => sub { my $l1 = shift; my $l2 = shift;
                                                 $l1 =~ s/\\$//; $l1.$l2 }),
             # Info we're looking for
-            qr/^\s*IF\[((?:\\.|[^\\\]])*)\]\s*$/
+            qr/^\s* IF ${cond_re} \s*$/x
             => sub {
                 if (! @skip || $skip[$#skip] > 0) {
-                    push @skip, !! $expand_variables->($1);
+                    push @skip, !! $expand_variables->($+{COND});
                 } else {
                     push @skip, -1;
                 }
             },
-            qr/^\s*ELSIF\[((?:\\.|[^\\\]])*)\]\s*$/
+            qr/^\s* ELSIF ${cond_re} \s*$/x
             => sub { die "ELSIF out of scope" if ! @skip;
                      die "ELSIF following ELSE" if abs($skip[$#skip]) == 2;
                      $skip[$#skip] = -1 if $skip[$#skip] != 0;
-                     $skip[$#skip] = !! $expand_variables->($1)
+                     $skip[$#skip] = !! $expand_variables->($+{COND})
                          if $skip[$#skip] == 0; },
-            qr/^\s*ELSE\s*$/
+            qr/^\s* ELSE \s*$/x
             => sub { die "ELSE out of scope" if ! @skip;
                      $skip[$#skip] = -2 if $skip[$#skip] != 0;
                      $skip[$#skip] = 2 if $skip[$#skip] == 0; },
-            qr/^\s*ENDIF\s*$/
+            qr/^\s* ENDIF \s*$/x
             => sub { die "ENDIF out of scope" if ! @skip;
                      pop @skip; },
-            qr/^\s*${variable_re}\s*=\s*(.*?)\s*$/
+            qr/^\s* ${variable_re} \s* = ${value_re} $/x
             => sub {
                 if (!@skip || $skip[$#skip] > 0) {
-                    my $n = $1;
-                    my $v = $2;
-                    $variables{$n} = $expand_variables->($v);
+                    $variables{$+{VARIABLE}} = $expand_variables->($+{VALUE});
                 }
             },
-            qr/^\s*SUBDIRS\s*=\s*(.*)\s*$/
+            qr/^\s* SUBDIRS \s* = ${value_re} $/x
             => sub {
                 if (!@skip || $skip[$#skip] > 0) {
-                    foreach (tokenize($expand_variables->($1))) {
+                    foreach (tokenize($expand_variables->($+{VALUE}))) {
                         push @build_dirs, [ @curd, splitdir($_, 1) ];
                     }
                 }
             },
-            qr/^\s*PROGRAMS(?:{([\w=]+(?:\s*,\s*[\w=]+)*)})?\s*=\s*(.*)\s*$/
+            qr/^\s* PROGRAMS ${attribs_re} \s* =  ${value_re} $/x
             => sub {
                 if (!@skip || $skip[$#skip] > 0) {
-                    my @a = tokenize($1, qr|\s*,\s*|);
-                    my @p = tokenize($expand_variables->($2));
+                    my @p = tokenize($expand_variables->($+{VALUE}));
                     push @programs, @p;
-                    foreach my $a (@a) {
-                        my $ak = $a;
-                        my $av = 1;
-                        if ($a =~ m|^(.*?)\s*=\s*(.*?)$|) {
-                            $ak = $1;
-                            $av = $2;
-                        }
-                        foreach my $p (@p) {
-                            $attributes{$p}->{$ak} = $av;
-                        }
-                    }
+                    $handle_attributes->($+{ATTRIBS},
+                                         \$attributes{programs},
+                                         @p);
                 }
             },
-            qr/^\s*LIBS(?:{([\w=]+(?:\s*,\s*[\w=]+)*)})?\s*=\s*(.*)\s*$/
+            qr/^\s* LIBS ${attribs_re} \s* =  ${value_re} $/x
             => sub {
                 if (!@skip || $skip[$#skip] > 0) {
-                    my @a = tokenize($1, qr|\s*,\s*|);
-                    my @l = tokenize($expand_variables->($2));
+                    my @l = tokenize($expand_variables->($+{VALUE}));
                     push @libraries, @l;
-                    foreach my $a (@a) {
-                        my $ak = $a;
-                        my $av = 1;
-                        if ($a =~ m|^(.*?)\s*=\s*(.*?)$|) {
-                            $ak = $1;
-                            $av = $2;
-                        }
-                        foreach my $l (@l) {
-                            $attributes{$l}->{$ak} = $av;
-                        }
-                    }
+                    $handle_attributes->($+{ATTRIBS},
+                                         \$attributes{libraries},
+                                         @l);
                 }
             },
-            qr/^\s*MODULES(?:{([\w=]+(?:\s*,\s*[\w=]+)*)})?\s*=\s*(.*)\s*$/
+            qr/^\s* MODULES ${attribs_re} \s* =  ${value_re} $/x
             => sub {
                 if (!@skip || $skip[$#skip] > 0) {
-                    my @a = tokenize($1, qr|\s*,\s*|);
-                    my @m = tokenize($expand_variables->($2));
+                    my @m = tokenize($expand_variables->($+{VALUE}));
                     push @modules, @m;
-                    foreach my $a (@a) {
-                        my $ak = $a;
-                        my $av = 1;
-                        if ($a =~ m|^(.*?)\s*=\s*(.*?)$|) {
-                            $ak = $1;
-                            $av = $2;
-                        }
-                        foreach my $m (@m) {
-                            $attributes{$m}->{$ak} = $av;
-                        }
-                    }
+                    $handle_attributes->($+{ATTRIBS},
+                                         \$attributes{modules},
+                                         @m);
                 }
             },
-            qr/^\s*SCRIPTS(?:{([\w=]+(?:\s*,\s*[\w=]+)*)})?\s*=\s*(.*)\s*$/
+            qr/^\s* SCRIPTS ${attribs_re} \s* =  ${value_re} $/x
             => sub {
                 if (!@skip || $skip[$#skip] > 0) {
-                    my @a = tokenize($1, qr|\s*,\s*|);
-                    my @s = tokenize($expand_variables->($2));
+                    my @s = tokenize($expand_variables->($+{VALUE}));
                     push @scripts, @s;
-                    foreach my $a (@a) {
-                        my $ak = $a;
-                        my $av = 1;
-                        if ($a =~ m|^(.*?)\s*=\s*(.*?)$|) {
-                            $ak = $1;
-                            $av = $2;
-                        }
-                        foreach my $s (@s) {
-                            $attributes{$s}->{$ak} = $av;
-                        }
-                    }
+                    $handle_attributes->($+{ATTRIBS},
+                                         \$attributes{scripts},
+                                         @s);
                 }
             },
 
-            qr/^\s*ORDINALS\[((?:\\.|[^\\\]])+)\]\s*=\s*(.*)\s*$/,
-            => sub { push @{$ordinals{$expand_variables->($1)}},
-                         tokenize($expand_variables->($2))
+            qr/^\s* ORDINALS ${index_re} = ${value_re} $/x
+            => sub { push @{$ordinals{$expand_variables->($+{INDEX})}},
+                         tokenize($expand_variables->($+{VALUE}))
                          if !@skip || $skip[$#skip] > 0 },
-            qr/^\s*SOURCE\[((?:\\.|[^\\\]])+)\]\s*=\s*(.*)\s*$/
-            => sub { push @{$sources{$expand_variables->($1)}},
-                         tokenize($expand_variables->($2))
+            qr/^\s* SOURCE ${index_re} = ${value_re} $/x
+            => sub { push @{$sources{$expand_variables->($+{INDEX})}},
+                         tokenize($expand_variables->($+{VALUE}))
                          if !@skip || $skip[$#skip] > 0 },
-            qr/^\s*SHARED_SOURCE\[((?:\\.|[^\\\]])+)\]\s*=\s*(.*)\s*$/
-            => sub { push @{$shared_sources{$expand_variables->($1)}},
-                         tokenize($expand_variables->($2))
+            qr/^\s* SHARED_SOURCE ${index_re} = ${value_re} $/x
+            => sub { push @{$shared_sources{$expand_variables->($+{INDEX})}},
+                         tokenize($expand_variables->($+{VALUE}))
                          if !@skip || $skip[$#skip] > 0 },
-            qr/^\s*INCLUDE\[((?:\\.|[^\\\]])+)\]\s*=\s*(.*)\s*$/
-            => sub { push @{$includes{$expand_variables->($1)}},
-                         tokenize($expand_variables->($2))
+            qr/^\s* INCLUDE ${index_re} = ${value_re} $/x
+            => sub { push @{$includes{$expand_variables->($+{INDEX})}},
+                         tokenize($expand_variables->($+{VALUE}))
                          if !@skip || $skip[$#skip] > 0 },
-            qr/^\s*DEFINE\[((?:\\.|[^\\\]])+)\]\s*=\s*(.*)\s*$/
-            => sub { push @{$defines{$expand_variables->($1)}},
-                         tokenize($expand_variables->($2))
+            qr/^\s* DEFINE ${index_re} = ${value_re} $/x
+            => sub { push @{$defines{$expand_variables->($+{INDEX})}},
+                         tokenize($expand_variables->($+{VALUE}))
                          if !@skip || $skip[$#skip] > 0 },
-            qr/^\s*DEPEND\[((?:\\.|[^\\\]])*)\]\s*=\s*(.*)\s*$/
-            => sub { push @{$depends{$expand_variables->($1)}},
-                         tokenize($expand_variables->($2))
+            qr/^\s* DEPEND ${index_re} = ${value_re} $/x
+            => sub { push @{$depends{$expand_variables->($+{INDEX})}},
+                         tokenize($expand_variables->($+{VALUE}))
                          if !@skip || $skip[$#skip] > 0 },
-            qr/^\s*GENERATE\[((?:\\.|[^\\\]])+)\]\s*=\s*(.*)\s*$/
-            => sub { push @{$generate{$expand_variables->($1)}}, $2
+            qr/^\s* GENERATE ${index_re} = ${value_re} $/x
+            => sub { push @{$generate{$expand_variables->($+{INDEX})}},
+                         $+{VALUE}
                          if !@skip || $skip[$#skip] > 0 },
-            qr/^\s*(?:#.*)?$/ => sub { },
+            qr/^\s* (?:\#.*)? $/x => sub { },
             "OTHERWISE" => sub { die "Something wrong with this line:\n$_\nat $sourced/$f" },
             "BEFORE" => sub {
                 if ($buildinfo_debug) {
@@ -1949,7 +1946,7 @@ if ($builder eq "unified") {
             );
         die "runaway IF?" if (@skip);
 
-        if (grep { defined $attributes{$_}->{engine} } keys %attributes
+        if (grep { defined $attributes{modules}->{$_}->{engine} } keys %attributes
                 and !$config{dynamic_engines}) {
             die <<"EOF"
 ENGINES can only be used if configured with 'dynamic-engine'.
@@ -1957,15 +1954,6 @@ This is usually a fault in a build.info file.
 EOF
         }
 
-        foreach (keys %attributes) {
-            my $dest = $_;
-            my $ddest = cleanfile($buildd, $_, $blddir);
-            foreach (keys %{$attributes{$dest} // {}}) {
-                $unified_info{attributes}->{$ddest}->{$_} =
-                    $attributes{$dest}->{$_};
-            }
-        }
-
         {
             my %infos = ( programs  => [ @programs  ],
                           libraries => [ @libraries ],
@@ -1975,6 +1963,11 @@ EOF
                 foreach (@{$infos{$k}}) {
                     my $item = cleanfile($buildd, $_, $blddir);
                     $unified_info{$k}->{$item} = 1;
+
+                    # Fix up associated attributes
+                    $unified_info{attributes}->{$k}->{$item} =
+                        $attributes{$k}->{$_}
+                        if defined $attributes{$k}->{$_};
                 }
             }
         }