r22030: Simplify include parsing for config.mk files, making sure we get
[gd/samba/.git] / source4 / build / smb_build / config_mk.pm
index 11ef8cf7b15f13ec366ac81c9889335752563825..6c095c5a5ba04a3348cbe941206294a4aab4dfb2 100644 (file)
-###########################################################
-### SMB Build System                                   ###
-### - config.mk parsing functions                      ###
-###                                                    ###
-###  Copyright (C) Stefan (metze) Metzmacher 2004      ###
-###  Released under the GNU GPL                                ###
-###########################################################
-
-package config_mk;
-use input;
-
-use strict;
-
-###########################################################
-# The parsing function which parses the file
-#
-# $result = _parse_config_mk($filename)
-#
-# $filename -  the path of the config.mk file
-#              which should be parsed
-#
-# $result -    the resulting structure
+# Samba Build System
+# - config.mk parsing functions
 #
-# $result->{ERROR_CODE} -      the error_code, '0' means success
-# $result->{ERROR_STR} -       the error string
+#  Copyright (C) Stefan (metze) Metzmacher 2004
+#  Copyright (C) Jelmer Vernooij 2005
+#  Released under the GNU GPL
 #
-# $result->{$key}{KEY} -       the key == the variable which was parsed
-# $result->{$key}{VAL} -       the value of the variable
-sub _parse_config_mk($)
-{
-       my $filename = shift;
-       my $result;
-       my $linenum = -1;
-       my $waiting = 0;
-       my $section = "GLOBAL";
-       my $key;
 
-       $result->{ERROR_CODE} = -1;
+package smb_build::config_mk;
+use smb_build::input;
+use File::Basename;
 
-       open(CONFIG_MK, "< $filename") || die ("Can't open $filename\n");
+use strict;
 
-       while (<CONFIG_MK>) {
-               my $line = $_;
-               my $val;
+my $section_types = {
+       "EXT_LIB" => {
+               "LIBS"                  => "list",
+               "CFLAGS"                => "list",
+               "CPPFLAGS"              => "list",
+               "LDFLAGS"               => "list",
+               },
+       "SUBSYSTEM" => {
+               "OBJ_FILES"             => "list",
 
-               $linenum++;
+               "PRIVATE_DEPENDENCIES"  => "list",
+               "PUBLIC_DEPENDENCIES"   => "list",
 
-               #
-               # lines beginnig with '#' are ignored
-               # 
-               if ($line =~ /^\#.*$/) {
-                       next;
-               }
+               "ENABLE"                => "bool",
 
-               #
-               #
-               #
-               if (($waiting == 0) && ($line =~ /^\[([a-zA-Z0-9_:]+)\][\t ]*$/)) {
-                       $section = $1;
-                       next;
-               }
-               
-               #
-               # 1.)   lines with an alphanumeric character indicate
-               #       a new variable, 
-               # 2.)   followed by zero or more whitespaces or tabs
-               # 3.)   then one '=' character
-               # 4.)   followed by the value of the variable
-               # 5.)   a newline ('\n') can be escaped by a '\' before the newline
-               #       and the next line needs to start with a tab ('\t')
-               #
-               if (($waiting == 0) && ($line =~ /^([a-zA-Z0-9_]+)[\t ]*=(.*)$/)) {
-                       $key = $1;
-                       $val = $2;
-
-                       #
-                       # when we have a '\' before the newline 
-                       # then skip it and wait for the next line.
-                       #
-                       if ($val =~ /(.*)(\\)$/) {
-                               $val = $1;
-                               $waiting = 1;           
-                       } else {
-                               $waiting = 0;
-                       }
-
-                       $result->{$section}{$key}{KEY} = $key;
-                       $result->{$section}{$key}{VAL} = $val;
-                       next;
-               }
-
-               #
-               # when we are waiting for a value to continue then
-               # check if it has a leading tab.
-               #
-               if (($waiting == 1) && ($line =~ /^\t(.*)$/)) {
-                       $val = $1;
-
-                       #
-                       # when we have a '\' before the newline 
-                       # then skip it and wait for the next line.
-                       #
-                       if ($val =~ /(.*)( \\)$/) {
-                               $val = $1;
-                               $waiting = 1;           
-                       } else {
-                               $waiting = 0;
-                       }
+               "MANPAGE"               => "string",
 
-                       $result->{$section}{$key}{VAL} .= " ";
-                       $result->{$section}{$key}{VAL} .= $val;
-                       next;
-               }
+               "PUBLIC_PROTO_HEADER"   => "string",
+               "PRIVATE_PROTO_HEADER"  => "string",
 
-               #
-               # catch empty lines they're ignored
-               # and we're no longer waiting for the value to continue
-               #
-               if ($line =~ /^$/) {
-                       $waiting = 0;
-                       next;
-               }
+               "PUBLIC_HEADERS"        => "list",
 
-               close(CONFIG_MK);
+               "CFLAGS"                => "list",
+               "LDFLAGS"               => "list",
+               "STANDARD_VISIBILITY"   => "string"
+               },
+       "MODULE" => {
+               "SUBSYSTEM"             => "string",
 
-               $result->{ERROR_STR} = "Bad line while parsing $filename\n$filename:$linenum: $line";
+               "INIT_FUNCTION"         => "string",
+               "OBJ_FILES"             => "list",
 
-               return $result;
-       }
+               "PUBLIC_DEPENDENCIES"   => "list",
+               "PRIVATE_DEPENDENCIES"  => "list",
 
-       close(CONFIG_MK);
+               "ALIASES" => "list",
 
-       $result->{ERROR_CODE} = 0;
+               "ENABLE"                => "bool",
 
-       return $result;
-}
+               "OUTPUT_TYPE"           => "list",
 
-###########################################################
-# A caching function to avoid to parse
-# a file twice or more
-#
-# $result = _get_parse_results($filename)
-#
-# $filename -  the path of the config.mk file
-#              which should be parsed
-#
-# $result -    the resulting structure
-#
-# $result->{ERROR_CODE} -      the error_code, '0' means success
-# $result->{ERROR_STR} -       the error string
-#
-# $result->{$key}{KEY} -       the key == the variable which was parsed
-# $result->{$key}{VAL} -       the value of the variable
-my $_get_parse_results_cache;
-sub _get_parse_results($)
-{
-       my $filename = shift;
+               "MANPAGE"               => "string",
+               "PRIVATE_PROTO_HEADER"  => "string",
+               "PUBLIC_PROTO_HEADER"   => "string",
 
-       if ((!defined($_get_parse_results_cache->{$filename}{ERROR_CODE}))
-               ||($_get_parse_results_cache->{$filename}{ERROR_CODE} != 0)) {
-               $_get_parse_results_cache->{$filename} = _parse_config_mk($filename);
-       }
 
-       return $_get_parse_results_cache->{$filename};
-}
+               "PUBLIC_HEADERS"        => "list",
 
-###########################################################
-# The fetching function to fetch the value of a variable 
-# out of the file
-#
-# $value = _fetch_var_from_config_mk($filename,$section,$variable)
-#
-# $filename -  the path of the config.mk file
-#              which should be parsed
-#
-# $section  -  the section name of the variable
-#
-# $variable -  the variable name of which we want the value
-#
-# $value -     the value of the variable
-sub _fetch_var_from_config_mk($$$)
-{
-       my $filename = shift;
-       my $section = shift;
-       my $key = shift;
-       my $val = "";
-       my $result;
+               "CFLAGS"                => "list"
+               },
+       "BINARY" => {
+               "OBJ_FILES"             => "list",
 
-       $result = _get_parse_results($filename);
+               "PRIVATE_DEPENDENCIES"  => "list",
 
-       if ($result->{ERROR_CODE} != 0) {
-               die ($result->{ERROR_STR});
-       }
+               "ENABLE"                => "bool",
 
-       if (defined($result->{$section}{$key})) {
-               $val = input::strtrim($result->{$section}{$key}{VAL});
-       } elsif (defined($result->{DEFAULT}{$key})) {
-               $val = input::strtrim($result->{DEFAULT}{$key}{VAL});
-       }
+               "MANPAGE"               => "string",
+               "INSTALLDIR"            => "string",
+               "PRIVATE_PROTO_HEADER"  => "string",
+               "PUBLIC_PROTO_HEADER"   => "string",
+               "PUBLIC_HEADERS"        => "list", 
 
-       return $val;
-}
+               "CFLAGS"                => "list",
+               "LDFLAGS"               => "list",
+               "STANDARD_VISIBILITY"   => "string",
 
-###########################################################
-# The fetching function to fetch the array of values of a variable 
-# out of the file
-#
-# $array = _fetch_array_from_config_mk($filename,$section,$variable)
-#
-# $filename -  the path of the config.mk file
-#              which should be parsed
-#
-# $section  -  the section name of the variable
-#
-# $variable -  the variable name of which we want the value
-#
-# $array -     the array of values of the variable
-sub _fetch_array_from_config_mk($$$)
-{
-       my $filename = shift;
-       my $section = shift;
-       my $key = shift;
-       my $result;
-       my $val = "";
+               "USE_HOSTCC"            => "bool"
+               },
+       "LIBRARY" => {
+               "VERSION"               => "string",
+               "SO_VERSION"            => "string",
+               "LIBRARY_REALNAME" => "string",
+               
+               "INIT_FUNCTION_TYPE"    => "string",
 
-       $result = _get_parse_results($filename);
+               "OBJ_FILES"             => "list",
 
-       if ($result->{ERROR_CODE} != 0) {
-               die ($result->{ERROR_STR});
-       }
+               "DESCRIPTION"           => "string",
 
-       if (defined($result->{$section}{$key})) {
-               $val = $result->{$section}{$key}{VAL};
-       } elsif (defined($result->{DEFAULT}{$key})) {
-               $val = $result->{DEFAULT}{$key}{VAL};
-       } 
+               "PRIVATE_DEPENDENCIES"  => "list",
+               "PUBLIC_DEPENDENCIES"   => "list",
 
-       return input::str2array($val);
-}
+               "ENABLE"                => "bool",
 
-###########################################################
-# A function for fetching MODULE_<module>_<parameter>
-# variables out of a config.mk file
-#
-# $value = module_get_var($filename,$module,$parameter)
-#
-# $filename -  the path of the config.mk file
-#              which should be parsed
-#
-# $module -    the middle part of the variable name of which we want the value
-#
-# $parameter - the last part of the variable name of which we want the value
-#
-# $value -     the value of the variable
-sub module_get_var($$$)
-{
-       my $filename = shift;
-       my $module = shift;
-       my $var = shift;
+               "MANPAGE"               => "string",
 
-       my $section = "MODULE::".$module;
+               "PUBLIC_HEADERS"        => "list",
 
-       return _fetch_var_from_config_mk($filename,$section,$var);
-}
+               "PUBLIC_PROTO_HEADER"   => "string",
+               "PRIVATE_PROTO_HEADER"  => "string",
 
-###########################################################
-# A function for fetching MODULE_<module>_<parameter>
-# variables out of a config.mk file
-#
-# $array = module_get_array($filename,$module,$parameter)
-#
-# $filename -  the path of the config.mk file
-#              which should be parsed
-#
-# $module -    the middle part of the variable name of which we want the value
-#
-# $parameter - the last part of the variable name of which we want the value
-#
-# $array -     the array of values of the variable
-sub module_get_array($$$)
-{
-       my $filename = shift;
-       my $module = shift;
-       my $var = shift;
+               "CFLAGS"                => "list",
+               "LDFLAGS"               => "list",
+               "STANDARD_VISIBILITY"   => "string"
+               }
+};
 
-       my $section = "MODULE::".$module;
+use vars qw(@parsed_files);
 
-       return _fetch_array_from_config_mk($filename,$section,$var);
-}
+@parsed_files = ();
 
-###########################################################
-# A function for fetching SUBSYSTEM_<subsystem>_<parameter>
-# variables out of a config.mk file
-#
-# $value = subsystem_get_var($filename,$subsystem,$parameter)
-#
-# $filename -  the path of the config.mk file
-#              which should be parsed
-#
-# $subsystem - the middle part of the variable name of which we want the value
-#
-# $parameter - the last part of the variable name of which we want the value
-#
-# $value -     the value of the variable
-sub subsystem_get_var($$$)
+sub _read_config_file
 {
+       use File::Basename;
+       use Cwd;
+
+       my $srcdir = shift;
+       my $builddir = shift;
        my $filename = shift;
-       my $subsystem = shift;
-       my $var = shift;
+       my @dirlist;
+
+       # We need to change our working directory because config.mk files can
+       # give shell commands as the argument to "include". These shell
+       # commands can take arguments that are relative paths and we don't have
+       # a way of sensibly rewriting these.
+       my $cwd = getcwd;
+       chomp $cwd;
+
+       if ($srcdir ne $builddir) {
+               # Push the builddir path on the front, so we prefer builddir
+               # to srcdir when the file exists in both.
+               @dirlist = ($builddir, $srcdir);
+       } else {
+               @dirlist = ($srcdir);
+       }
 
-       my $section = "SUBSYSTEM::".$subsystem;
+       foreach my $d (@dirlist) {
+               my @lines;
+               my $basedir;
+
+               chdir $cwd;
+               chdir $d;
+
+               # We need to catch the exception from open in the case where
+               # the filename is actually a shell pipeline. Why is this
+               # different to opening a regular file? Because this is perl!
+               eval {
+                       open(CONFIG_MK, "./$filename");
+                       @lines = <CONFIG_MK>;
+                       close(CONFIG_MK);
+               };
+
+               chdir $cwd;
+               next unless (@lines);
+
+               # I blame abartlett for this crazy hack -- jpeach
+               if ($filename =~ /\|$/) {
+                       $basedir = $builddir;
+               } else {
+                       $basedir = dirname($filename);
+               }
+               $basedir =~ s!^($builddir|$srcdir)[/]!!;
+               return ($filename, $basedir, @lines);
+       }
 
-       return _fetch_var_from_config_mk($filename,$section,$var);
+       chdir $cwd;
+       return;
 }
 
 ###########################################################
-# A function for fetching SUBSYSTEM_<subsystem>_<parameter>
-# variables out of a config.mk file
+# The parsing function which parses the file
 #
-# $array = subsystem_get_array($filename,$subsystem,$parameter)
+# $result = _parse_config_mk($input, $srcdir, $builddir, $filename)
 #
 # $filename -  the path of the config.mk file
 #              which should be parsed
-#
-# $subsystem - the middle part of the variable name of which we want the value
-#
-# $parameter - the last part of the variable name of which we want the value
-#
-# $array -     the array of values of the variable
-sub subsystem_get_array($$$)
+sub run_config_mk($$$$)
 {
-       my $filename = shift;
-       my $subsystem = shift;
-       my $var = shift;
+       sub run_config_mk($$$$);
+       my ($input, $srcdir, $builddir, $filename) = @_;
+       my $result;
+       my $linenum = -1;
+       my $infragment = 0;
+       my $section = "GLOBAL";
+       my $makefile = "";
 
-       my $section = "SUBSYSTEM::".$subsystem;
+       my $basedir;
 
-       return _fetch_array_from_config_mk($filename,$section,$var);
-}
+       my $parsing_file;
+       my @lines;
 
-###########################################################
-# A function for fetching LIBRARY_<library>_<parameter>
-# variables out of a config.mk file
-#
-# $value = library_get_var($filename,$library,$parameter)
-#
-# $filename -  the path of the config.mk file
-#              which should be parsed
-#
-# $library -   the middle part of the variable name of which we want the value
-#
-# $parameter - the last part of the variable name of which we want the value
-#
-# $value -     the value of the variable
-sub library_get_var($$$)
-{
-       my $filename = shift;
-       my $library = shift;
-       my $var = shift;
+       $ENV{builddir} = $builddir;
+       $ENV{srcdir} = $srcdir;
 
-       my $section = "LIBRARY::".$library;
+       ($parsing_file, $basedir, @lines) =
+           _read_config_file($srcdir, $builddir, $filename);
 
-       return _fetch_var_from_config_mk($filename,$section,$var);
-}
-
-###########################################################
-# A function for fetching LIBRARY_<library>_<parameter>
-# variables out of a config.mk file
-#
-# $array = library_get_array($filename,$library,$parameter)
-#
-# $filename -  the path of the config.mk file
-#              which should be parsed
-#
-# $library -   the middle part of the variable name of which we want the value
-#
-# $parameter - the last part of the variable name of which we want the value
-#
-# $array -     the array of values of the variable
-sub library_get_array($$$)
-{
-       my $filename = shift;
-       my $library = shift;
-       my $var = shift;
+       die ("$0: can't open '$filename'")
+               unless ($parsing_file and $basedir and @lines);
 
-       my $section = "LIBRARY::".$library;
+       my $line = "";
+       my $prev = "";
 
-       return _fetch_array_from_config_mk($filename,$section,$var);
-}
+       # Emit a line that lets us match up final makefile output with the
+       # corresponding input files. The curlies are so you can match the
+       # BEGIN/END pairs in a text editor.
+       $makefile .= "# BEGIN{ $parsing_file\n";
 
-###########################################################
-# A function for fetching BINARY_<binary>_<parameter>
-# variables out of a config.mk file
-#
-# $value = binary_get_var($filename,$binary,$parameter)
-#
-# $filename -  the path of the config.mk file
-#              which should be parsed
-#
-# $binary -    the middle part of the variable name of which we want the value
-#
-# $parameter - the last part of the variable name of which we want the value
-#
-# $value -     the value of the variable
-sub binary_get_var($$$)
-{
-       my $filename = shift;
-       my $binary = shift;
-       my $var = shift;
+       foreach (@lines) {
+               $linenum++;
 
-       my $section = "BINARY::".$binary;
+               # lines beginning with '#' are ignored
+               next if (/^\#.*$/);
+               
+               if (/^(.*)\\$/) {
+                       $prev .= $1;
+                       next;
+               } else {
+                       $line = "$prev$_";
+                       $prev = "";
+               }
 
-       return _fetch_var_from_config_mk($filename,$section,$var);
-}
+               if ($line =~ /^\[([-a-zA-Z0-9_:]+)\][\t ]*$/) 
+               {
+                       $section = $1;
+                       $infragment = 0;
+                       next;
+               }
 
-###########################################################
-# A function for fetching BINARY_<binary>_<parameter>
-# variables out of a config.mk file
-#
-# $array = binary_get_array($filename,$binary,$parameter)
-#
-# $filename -  the path of the config.mk file
-#              which should be parsed
-#
-# $binary -    the middle part of the variable name of which we want the value
-#
-# $parameter - the last part of the variable name of which we want the value
-#
-# $array -     the array of values of the variable
-sub binary_get_array($$$)
-{
-       my $filename = shift;
-       my $binary = shift;
-       my $var = shift;
+               # include
+               if ($line =~ /^include (.*)$/) {
+                       my $subfile= $1;
+                       my $subdir = dirname($filename);
+                       $subdir =~ s/^\.$//g;
+                       $subdir =~ s/^\.\///g;
+                       $subdir .= "/" if ($subdir ne "");
+                       $makefile .= run_config_mk($input, $srcdir, $builddir, $subdir.$subfile);
+                       next;
+               }
 
-       my $section = "BINARY::".$binary;
+               # empty line
+               if ($line =~ /^[ \t]*$/) {
+                       $section = "GLOBAL";
+                       if ($infragment) { $makefile.="\n"; }
+                       next;
+               }
 
-       return _fetch_array_from_config_mk($filename,$section,$var);
-}
+               # global stuff is considered part of the makefile
+               if ($section eq "GLOBAL") {
+                       if (!$infragment) { $makefile.="\n"; }
+                       $makefile .= $line;
+                       $infragment = 1;
+                       next;
+               }
 
-sub import_file($$)
-{
-       my $input = shift;
-       my $filename = shift;
+               
+               # Assignment
+               if ($line =~ /^([a-zA-Z0-9_]+)[\t ]*=(.*)$/) {
+                       $result->{$section}{$1}{VAL} = $2;
+                       $result->{$section}{$1}{KEY} = $1;
+               
+                       next;
+               }
 
-       my $result = _parse_config_mk($filename);
+               die("$parsing_file:$linenum: Bad line while parsing $parsing_file");
+       }
 
-       die ($result->{ERROR_STR}) unless $result->{ERROR_CODE} == 0;
+       $makefile .= "# }END $parsing_file\n";
 
        foreach my $section (keys %{$result}) {
-               next if ($section eq "ERROR_CODE");
                my ($type, $name) = split(/::/, $section, 2);
-               
+
+               my $sectype = $section_types->{$type};
+               if (not defined($sectype)) {
+                       die($parsing_file.":[".$section."] unknown section type \"".$type."\"!");
+               }
+
                $input->{$name}{NAME} = $name;
                $input->{$name}{TYPE} = $type;
+               $input->{$name}{MK_FILE} = $parsing_file;
+               $input->{$name}{BASEDIR} = $basedir;
 
                foreach my $key (values %{$result->{$section}}) {
-                       $input->{$name}{$key->{KEY}} = [input::str2array($key->{VAL})];
+                       $key->{VAL} = smb_build::input::strtrim($key->{VAL});
+                       my $vartype = $sectype->{$key->{KEY}};
+                       if (not defined($vartype)) {
+                               die($parsing_file.":[".$section."]: unknown attribute type \"$key->{KEY}\"!");
+                       }
+                       if ($vartype eq "string") {
+                               $input->{$name}{$key->{KEY}} = $key->{VAL};
+                       } elsif ($vartype eq "list") {
+                               $input->{$name}{$key->{KEY}} = [smb_build::input::str2array($key->{VAL})];
+                       } elsif ($vartype eq "bool") {
+                               if (($key->{VAL} ne "YES") and ($key->{VAL} ne "NO")) {
+                                       die("Invalid value for bool attribute $key->{KEY}: $key->{VAL} in section $section");
+                               }
+                               $input->{$name}{$key->{KEY}} = $key->{VAL};
+                       }
                }
        }
+
+       return $makefile;
 }
+
 1;