From 9eb54e405af28fcb06ff3387bca44eac4d577f60 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Sat, 4 Aug 2012 08:47:28 +0200 Subject: [PATCH] Bug 774032 part 6 - Replace autoconf handling of config files and headers with our own. r=ted --- aclocal.m4 | 3 +- allmakefiles.sh | 1 + build/ConfigStatus.py | 313 +++++++++++++++++++++++++ build/Makefile.in | 4 + build/autoconf/config.status.m4 | 167 +++++++++++++ build/tests/unit-ConfigStatus.py | 282 ++++++++++++++++++++++ client.mk | 3 +- config/rules.mk | 11 +- configure.in | 123 ---------- gfx/cairo/cairo/src/Makefile.in | 2 +- js/src/aclocal.m4 | 1 + js/src/build/ConfigStatus.py | 313 +++++++++++++++++++++++++ js/src/build/autoconf/config.status.m4 | 167 +++++++++++++ js/src/config/rules.mk | 11 +- js/src/configure.in | 101 +------- js/src/js-confdefs.h.in | 17 ++ mozilla-config.h.in | 27 +++ 17 files changed, 1307 insertions(+), 239 deletions(-) create mode 100644 build/ConfigStatus.py create mode 100644 build/autoconf/config.status.m4 create mode 100644 build/tests/unit-ConfigStatus.py create mode 100644 js/src/build/ConfigStatus.py create mode 100644 js/src/build/autoconf/config.status.m4 create mode 100644 js/src/js-confdefs.h.in create mode 100644 mozilla-config.h.in diff --git a/aclocal.m4 b/aclocal.m4 index c1e6c07bd7fa9..e4c0760e9bd23 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1,8 +1,9 @@ dnl dnl Local autoconf macros used with mozilla dnl The contents of this file are under the Public Domain. -dnl +dnl +builtin(include, build/autoconf/config.status.m4)dnl builtin(include, build/autoconf/toolchain.m4)dnl builtin(include, build/autoconf/ccache.m4)dnl builtin(include, build/autoconf/nspr.m4)dnl diff --git a/allmakefiles.sh b/allmakefiles.sh index 5e21b10d7ed70..f485000e4e5ed 100755 --- a/allmakefiles.sh +++ b/allmakefiles.sh @@ -25,6 +25,7 @@ fi # Common makefiles used by everyone add_makefiles " +mozilla-config.h Makefile build/Makefile build/pgo/Makefile diff --git a/build/ConfigStatus.py b/build/ConfigStatus.py new file mode 100644 index 0000000000000..c0e873d6f7d84 --- /dev/null +++ b/build/ConfigStatus.py @@ -0,0 +1,313 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Combined with build/autoconf/config.status.m4, ConfigStatus is an almost +# drop-in replacement for autoconf 2.13's config.status, with features +# borrowed from autoconf > 2.5, and additional features. + +from __future__ import with_statement +from optparse import OptionParser +import sys, re, os, posixpath +from StringIO import StringIO +# Standalone js doesn't have virtualenv. +sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'config')) +from Preprocessor import Preprocessor + +# Basic logging facility +verbose = False +def log(string): + if verbose: + print >>sys.stderr, string + +# We need relpath, but it is introduced in python 2.6 +# http://docs.python.org/library/os.path.html +def my_relpath(path, start): + """ + Return a relative version of a path + from /usr/lib/python2.6/posixpath.py + """ + + if not path: + raise ValueError("no path specified") + + start_list = os.path.abspath(start).split(os.path.sep) + path_list = os.path.abspath(path).split(os.path.sep) + + # Work out how much of the filepath is shared by start and path. + i = len(os.path.commonprefix([start_list, path_list])) + + rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:] + if not rel_list: + return os.curdir + return os.path.join(*rel_list) + +relpath = getattr(os.path, "relpath", my_relpath) + +def ensureParentDir(file): + '''Ensures the directory parent to the given file exists''' + dir = os.path.dirname(file) + if dir and not os.path.exists(dir): + try: + os.makedirs(dir) + except OSError, error: + if error.errno != errno.EEXIST: + raise + +class FileAvoidWrite(StringIO): + '''file-like object that buffers its output and only writes it to disk + if the new contents are different from what the file may already contain. + ''' + def __init__(self, filename): + self.filename = filename + StringIO.__init__(self) + + def close(self): + buf = self.getvalue() + StringIO.close(self) + try: + file = open(self.filename, 'rU') + except IOError: + pass + else: + try: + if file.read() == buf: + log("%s is unchanged" % relpath(self.filename, os.curdir)) + return + except IOError: + pass + finally: + file.close() + + log("creating %s" % relpath(self.filename, os.curdir)) + ensureParentDir(self.filename) + with open(self.filename, 'w') as file: + file.write(buf) + + def __enter__(self): + return self + def __exit__(self, type, value, traceback): + self.close() + +def shell_escape(s): + '''Escape some characters with a backslash, and double dollar signs. + ''' + return re.sub('''([ \t`#$^&*(){}\\|;'"<>?\[\]])''', r'\\\1', str(s)).replace('$', '$$') + +class ConfigEnvironment(object): + '''A ConfigEnvironment is defined by a source directory and a build + directory. It preprocesses files from the source directory and stores + the result in the object directory. + + There are two types of files: config files and config headers, + each treated through a different member function. + + Creating a ConfigEnvironment requires a few arguments: + - topsrcdir and topobjdir are, respectively, the top source and + the top object directory. + - defines is a list of (name, value) tuples. In autoconf, these are + set with AC_DEFINE and AC_DEFINE_UNQUOTED + - non_global_defines are a list of names appearing in defines above + that are not meant to be exported in ACDEFINES and ALLDEFINES (see + below) + - substs is a list of (name, value) tuples. In autoconf, these are + set with AC_SUBST. + + ConfigEnvironment automatically defines two additional substs variables + from all the defines not appearing in non_global_defines: + - ACDEFINES contains the defines in the form -DNAME=VALUE, for use on + preprocessor command lines. The order in which defines were given + when creating the ConfigEnvironment is preserved. + - ALLDEFINES contains the defines in the form #define NAME VALUE, in + sorted order, for use in config files, for an automatic listing of + defines. + + ConfigEnvironment expects a "top_srcdir" subst to be set with the top + source directory, in msys format on windows. It is used to derive a + "srcdir" subst when treating config files. + ''' + + def __init__(self, topobjdir = '.', topsrcdir = '.', + defines = [], non_global_defines = [], substs = []): + self.defines = dict(defines) + self.substs = dict(substs) + self.topsrcdir = topsrcdir + self.topobjdir = topobjdir + global_defines = [name for name, value in defines if not name in non_global_defines] + self.substs['ACDEFINES'] = ' '.join(["-D%s=%s" % (name, shell_escape(self.defines[name])) for name in global_defines]) + self.substs['ALLDEFINES'] = '\n'.join(sorted(["#define %s %s" % (name, self.defines[name]) for name in global_defines])) + + def get_relative_srcdir(self, file): + '''Returns the relative source directory for the given file, always + using / as a path separator. + ''' + assert(isinstance(file, basestring)) + dir = posixpath.dirname(relpath(file, self.topobjdir).replace(os.sep, '/')) + if dir: + return dir + return '.' + + def get_file_srcdir(self, file): + '''Returns the srcdir for the given file, where srcdir is in msys + format on windows, thus derived from top_srcdir. + ''' + dir = self.get_relative_srcdir(file) + return posixpath.normpath(posixpath.join(self.substs['top_srcdir'], dir)) + + def get_depth(self, file): + '''Returns the DEPTH for the given file, that is, the path to the + object directory relative to the directory containing the given file. + Always uses / as a path separator. + ''' + return relpath(self.topobjdir, os.path.dirname(file)).replace(os.sep, '/') + + def get_input(self, file): + '''Returns the input file path in the source tree that can be used + to create the given config file or header. + ''' + assert(isinstance(file, basestring)) + return os.path.normpath(os.path.join(self.topsrcdir, "%s.in" % relpath(file, self.topobjdir))) + + def create_config_file(self, path): + '''Creates the given config file. A config file is generated by + taking the corresponding source file and replacing occurences of + "@VAR@" by the value corresponding to "VAR" in the substs dict. + + Additional substs are defined according to the file being treated: + "srcdir" for its the path to its source directory + "relativesrcdir" for its source directory relative to the top + "DEPTH" for the path to the top object directory + ''' + input = self.get_input(path) + pp = Preprocessor() + pp.context.update(self.substs) + pp.context.update(srcdir = self.get_file_srcdir(path)) + pp.context.update(relativesrcdir = self.get_relative_srcdir(path)) + pp.context.update(DEPTH = self.get_depth(path)) + pp.do_filter('attemptSubstitution') + pp.setMarker(None) + with FileAvoidWrite(path) as pp.out: + pp.do_include(input) + + def create_config_header(self, path): + '''Creates the given config header. A config header is generated by + taking the corresponding source file and replacing some #define/#undef + occurences: + "#undef NAME" is turned into "#define NAME VALUE" + "#define NAME" is unchanged + "#define NAME ORIGINAL_VALUE" is turned into "#define NAME VALUE" + "#undef UNKNOWN_NAME" is turned into "/* #undef UNKNOWN_NAME */" + Whitespaces are preserved. + ''' + with open(self.get_input(path), 'rU') as input: + ensureParentDir(path) + output = FileAvoidWrite(path) + r = re.compile('^\s*#\s*(?P[a-z]+)(?:\s+(?P\S+)(?:\s+(?P\S+))?)?', re.U) + for l in input: + m = r.match(l) + if m: + cmd = m.group('cmd') + name = m.group('name') + value = m.group('value') + if name: + if name in self.defines: + if cmd == 'define' and value: + l = l[:m.start('value')] + str(self.defines[name]) + l[m.end('value'):] + elif cmd == 'undef': + l = l[:m.start('cmd')] + 'define' + l[m.end('cmd'):m.end('name')] + ' ' + str(self.defines[name]) + l[m.end('name'):] + elif cmd == 'undef': + l = '/* ' + l[:m.end('name')] + ' */' + l[m.end('name'):] + + output.write(l) + output.close() + +def config_status(topobjdir = '.', topsrcdir = '.', + defines = [], non_global_defines = [], substs = [], + files = [], headers = []): + '''Main function, providing config.status functionality. + + Contrary to config.status, it doesn't use CONFIG_FILES or CONFIG_HEADERS + variables, but like config.status from autoconf 2.6, single files may be + generated with the --file and --header options. Several such options can + be given to generate several files at the same time. + + Without the -n option, this program acts as config.status and considers + the current directory as the top object directory, even when config.status + is in a different directory. It will, however, treat the directory + containing config.status as the top object directory with the -n option, + while files given to the --file and --header arguments are considered + relative to the current directory. + + The --recheck option, like with the original config.status, runs configure + again, with the options given in the "ac_configure_args" subst. + + The options to this function are passed when creating the + ConfigEnvironment, except for files and headers, which contain the list + of files and headers to be generated by default. These lists, as well as + the actual wrapper script around this function, are meant to be generated + by configure. See build/autoconf/config.status.m4. + + Unlike config.status behaviour with CONFIG_FILES and CONFIG_HEADERS, + but like config.status behaviour with --file and --header, providing + files or headers on the command line inhibits the default generation of + files when given headers and headers when given files. + + Unlike config.status, the FILE:TEMPLATE syntax is not supported for + files and headers. The template is always the filename suffixed with + '.in', in the corresponding directory under the top source directory. + ''' + + if 'CONFIG_FILES' in os.environ: + raise Exception, 'Using the CONFIG_FILES environment variable is not supported. Use --file instead.' + if 'CONFIG_HEADERS' in os.environ: + raise Exception, 'Using the CONFIG_HEADERS environment variable is not supported. Use --header instead.' + + parser = OptionParser() + parser.add_option('--recheck', dest='recheck', action='store_true', + help='update config.status by reconfiguring in the same conditions') + parser.add_option('--file', dest='files', metavar='FILE', action='append', + help='instantiate the configuration file FILE') + parser.add_option('--header', dest='headers', metavar='FILE', action='append', + help='instantiate the configuration header FILE') + parser.add_option('-v', '--verbose', dest='verbose', action='store_true', + help='display verbose output') + parser.add_option('-n', dest='not_topobjdir', action='store_true', + help='do not consider current directory as top object directory') + (options, args) = parser.parse_args() + + # Without -n, the current directory is meant to be the top object directory + if not options.not_topobjdir: + topobjdir = '.' + + env = ConfigEnvironment(topobjdir = topobjdir, topsrcdir = topsrcdir, + defines = defines, non_global_defines = non_global_defines, + substs = substs) + + if options.recheck: + # Execute configure from the top object directory + if not os.path.isabs(topsrcdir): + topsrcdir = relpath(topsrcdir, topobjdir) + os.chdir(topobjdir) + os.execlp('sh', 'sh', '-c', ' '.join([os.path.join(topsrcdir, 'configure'), env.substs['ac_configure_args'], '--no-create', '--no-recursion'])) + + if options.files: + files = options.files + headers = [] + if options.headers: + headers = options.headers + if not options.files: + files = [] + # Default to display messages when giving --file or --headers on the + # command line. + if options.files or options.headers or options.verbose: + global verbose + verbose = True + if not options.files and not options.headers: + print >>sys.stderr, "creating config files and headers..." + files = [os.path.join(topobjdir, f) for f in files] + headers = [os.path.join(topobjdir, f) for f in headers] + + for file in files: + env.create_config_file(file) + for header in headers: + env.create_config_header(header) diff --git a/build/Makefile.in b/build/Makefile.in index 9ceb4c6a265c0..c01ffed2b22d1 100644 --- a/build/Makefile.in +++ b/build/Makefile.in @@ -263,4 +263,8 @@ libs:: $(topsrcdir)/tools/rb/fix-linux-stack.pl endif GARBAGE += $(srcdir)/automationutils.pyc + +# Test for ConfigStatus.py +check:: + $(PYTHON) $(srcdir)/tests/unit-ConfigStatus.py endif # ENABLE_TESTS diff --git a/build/autoconf/config.status.m4 b/build/autoconf/config.status.m4 new file mode 100644 index 0000000000000..dbc0d1e97d147 --- /dev/null +++ b/build/autoconf/config.status.m4 @@ -0,0 +1,167 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +dnl For use in AC_SUBST replacement +define([MOZ_DIVERSION_SUBST], 11) + +dnl Replace AC_SUBST to store values in a format suitable for python. +dnl The necessary comma after the tuple can't be put here because it +dnl can mess around with things like: +dnl AC_SOMETHING(foo,AC_SUBST(),bar) +define([AC_SUBST], +[ifdef([AC_SUBST_$1], , +[define([AC_SUBST_$1], )dnl +AC_DIVERT_PUSH(MOZ_DIVERSION_SUBST)dnl + (''' $1 ''', r''' [$]$1 ''') +AC_DIVERT_POP()dnl +])]) + +dnl Wrap AC_DEFINE to store values in a format suitable for python. +dnl autoconf's AC_DEFINE still needs to be used to fill confdefs.h, +dnl which is #included during some compile checks. +dnl The necessary comma after the tuple can't be put here because it +dnl can mess around with things like: +dnl AC_SOMETHING(foo,AC_DEFINE(),bar) +define([_MOZ_AC_DEFINE], defn([AC_DEFINE])) +define([AC_DEFINE], +[cat >> confdefs.pytmp <<\EOF + (''' $1 ''', ifelse($#, 2, [r''' $2 '''], $#, 3, [r''' $2 '''], ' 1 ')) +EOF +ifelse($#, 2, _MOZ_AC_DEFINE([$1], [$2]), $#, 3, _MOZ_AC_DEFINE([$1], [$2], [$3]),_MOZ_AC_DEFINE([$1]))dnl +]) + +dnl Wrap AC_DEFINE_UNQUOTED to store values in a format suitable for +dnl python. +define([_MOZ_AC_DEFINE_UNQUOTED], defn([AC_DEFINE_UNQUOTED])) +define([AC_DEFINE_UNQUOTED], +[cat >> confdefs.pytmp <>>)dnl +echo creating $CONFIG_STATUS + +cat > $CONFIG_STATUS <>>), topsrcdir)) +dnl Don't rely on virtualenv here. Standalone js doesn't use it. +sys.path.append(os.path.join(topsrcdir, 'build')) +from ConfigStatus import config_status + +args = { + 'topsrcdir': topsrcdir, + 'topobjdir': os.path.dirname(<<<__file__>>>), + +dnl All defines and substs are stored with an additional space at the beginning +dnl and at the end of the string, to avoid any problem with values starting or +dnl ending with quotes. + 'defines': [(name[1:-1], value[1:-1]) for name, value in [ +EOF + +dnl confdefs.pytmp contains AC_DEFINEs, in the expected format, but +dnl lacks the final comma (see above). +sed 's/$/,/' confdefs.pytmp >> $CONFIG_STATUS +rm confdefs.pytmp confdefs.h + +cat >> $CONFIG_STATUS <<\EOF + ] ], + + 'substs': [(name[1:-1], value[1:-1]) for name, value in [ +EOF + +dnl The MOZ_DIVERSION_SUBST output diversion contains AC_SUBSTs, in the +dnl expected format, but lacks the final comma (see above). +sed 's/$/,/' >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF + ] ], + +dnl List of files to apply AC_SUBSTs to. This is the list of files given +dnl as an argument to AC_OUTPUT ($1) + 'files': [ +EOF + +for out in $1; do + echo " '$out'," >> $CONFIG_STATUS +done + +cat >> $CONFIG_STATUS <<\EOF + ], + +dnl List of header files to apply AC_DEFINEs to. This is stored in the +dnl AC_LIST_HEADER m4 macro by AC_CONFIG_HEADER. + 'headers': [ +EOF + +ifdef(<<>>, <<< +HEADERS="AC_LIST_HEADER" +for header in $HEADERS; do + echo " '$header'," >> $CONFIG_STATUS +done +>>>)dnl + +cat >> $CONFIG_STATUS <<\EOF + ], + +dnl List of AC_DEFINEs that aren't to be exposed in ALLDEFINES + 'non_global_defines': [ +EOF + +if test -n "$_NON_GLOBAL_ACDEFINES"; then + for var in $_NON_GLOBAL_ACDEFINES; do + echo " '$var'," >> $CONFIG_STATUS + done +fi + +cat >> $CONFIG_STATUS <<\EOF + ] +} + +dnl Do the actual work +config_status(**args) +EOF +changequote([, ]) +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +dnl Execute config.status, unless --no-create was passed to configure. +test "$no_create" = yes || ${PYTHON} $CONFIG_STATUS || exit 1 +]) diff --git a/build/tests/unit-ConfigStatus.py b/build/tests/unit-ConfigStatus.py new file mode 100644 index 0000000000000..608f031df1664 --- /dev/null +++ b/build/tests/unit-ConfigStatus.py @@ -0,0 +1,282 @@ +from __future__ import with_statement +import os +from StringIO import StringIO +import unittest +from mozunit import main, MockedOpen +import ConfigStatus +from ConfigStatus import FileAvoidWrite + +class ConfigEnvironment(ConfigStatus.ConfigEnvironment): + def __init__(self, **args): + ConfigStatus.ConfigEnvironment.__init__(self, **args) + # Be helpful to unit tests + if not 'top_srcdir' in self.substs: + self.substs['top_srcdir'] = self.topsrcdir.replace(os.sep, '/') + +class TestFileAvoidWrite(unittest.TestCase): + def test_file_avoid_write(self): + '''Test the FileAvoidWrite class + ''' + with MockedOpen({'file': 'content'}): + # Overwriting an existing file replaces its content + with FileAvoidWrite('file') as file: + file.write('bazqux') + self.assertEqual(open('file', 'r').read(), 'bazqux') + + # Creating a new file (obviously) stores its content + with FileAvoidWrite('file2') as file: + file.write('content') + self.assertEqual(open('file2').read(), 'content') + + class MyMockedOpen(MockedOpen): + '''MockedOpen extension to raise an exception if something + attempts to write in an opened file. + ''' + def __call__(self, name, mode): + if 'w' in mode: + raise Exception, 'Unexpected open with write mode' + return MockedOpen.__call__(self, name, mode) + + with MyMockedOpen({'file': 'content'}): + # Validate that MyMockedOpen works as intended + file = FileAvoidWrite('file') + file.write('foobar') + self.assertRaises(Exception, file.close) + + # Check that no write actually happens when writing the + # same content as what already is in the file + with FileAvoidWrite('file') as file: + file.write('content') + + +class TestEnvironment(unittest.TestCase): + def test_auto_substs(self): + '''Test the automatically set values of ACDEFINES and ALLDEFINES. + ''' + env = ConfigEnvironment( + defines = [ ('foo', 'bar'), ('baz', 'qux 42'), + ('abc', 'def'), ('extra', 'foobar') ], + non_global_defines = ['extra', 'ignore']) + # non_global_defines should be filtered out in ACDEFINES and + # ALLDEFINES. + # Original order of the defines need to be respected in ACDEFINES + self.assertEqual(env.substs['ACDEFINES'], '-Dfoo=bar -Dbaz=qux\\ 42 -Dabc=def') + # ALLDEFINES, on the other hand, needs to be sorted + self.assertEqual(env.substs['ALLDEFINES'], '''#define abc def +#define baz qux 42 +#define foo bar''') + + def test_config_file(self): + '''Test the creation of config files. + ''' + with MockedOpen({'file.in': '''#ifdef foo +@foo@ +@bar@ +'''}): + env = ConfigEnvironment(substs = [ ('foo', 'bar baz') ]) + env.create_config_file('file') + self.assertEqual(open('file', 'r').read(), '''#ifdef foo +bar baz +@bar@ +''') + + def test_config_header(self): + '''Test the creation of config headers. + ''' + with MockedOpen({'file.in': ''' +/* Comment */ +#define foo +#define foo 42 +#undef foo +#define bar +#define bar 42 +#undef bar + +# undef baz + +#ifdef foo +# undef foo +# define foo 42 + # define foo 42 +#endif +'''}): + env = ConfigEnvironment(defines = [ ('foo', 'baz qux'), ('baz', 1) ]) + env.create_config_header('file') + self.assertEqual(open('file','r').read(), ''' +/* Comment */ +#define foo +#define foo baz qux +#define foo baz qux +#define bar +#define bar 42 +/* #undef bar */ + +# define baz 1 + +#ifdef foo +# define foo baz qux +# define foo baz qux + # define foo baz qux +#endif +''') + +# Tests for get_relative_srcdir, get_depth, get_input and get_file_srcdir, +# depending on various cases of top source directory and top build +# directory location. +class TestPaths(unittest.TestCase): + def setUp(self): + self.dir = os.path.basename(os.path.abspath(os.curdir)) + self.absolute = os.path.normpath('/absolute') + +class TestPathsLocalBuildDir(TestPaths): + def get_env(self, topsrcdir): + env = ConfigEnvironment(topsrcdir = topsrcdir, topobjdir = '.') + self.assertEqual(env.get_relative_srcdir('file'), '.') + self.assertEqual(env.get_relative_srcdir('dir/file'), 'dir') + self.assertEqual(env.get_relative_srcdir('deeply/nested/path/to/file'), 'deeply/nested/path/to') + self.assertEqual(env.get_depth('file'), '.') + self.assertEqual(env.get_depth('dir/file'), '..') + self.assertEqual(env.get_depth('deeply/nested/path/to/file'), '../../../..') + return env + + def test_paths_local_build_local_src(self): + env = self.get_env('.') + self.assertEqual(env.get_input('file'), 'file.in') + self.assertEqual(env.get_input('dir/file'), os.path.join('dir', 'file.in')) + self.assertEqual(env.get_file_srcdir('file'), '.') + self.assertEqual(env.get_file_srcdir('dir/file'), 'dir') + + def test_paths_local_build_parent_src(self): + env = self.get_env('..') + self.assertEqual(env.get_input('file'), os.path.join('..', 'file.in')) + self.assertEqual(env.get_input('dir/file'), os.path.join('..', 'dir', 'file.in')) + self.assertEqual(env.get_file_srcdir('file'), '..') + self.assertEqual(env.get_file_srcdir('dir/file'), '../dir') + + def test_paths_local_build_absolute_src(self): + env = self.get_env(self.absolute) + self.assertEqual(env.get_input('file'), os.path.join(self.absolute, 'file.in')) + self.assertEqual(env.get_input('dir/file'), os.path.join(self.absolute, 'dir', 'file.in')) + self.assertEqual(env.get_input('%s/file' % self.dir), os.path.join(self.absolute, self.dir, 'file.in')) + self.assertEqual(env.get_file_srcdir('file'), '/absolute') + self.assertEqual(env.get_file_srcdir('dir/file'), '/absolute/dir') + self.assertEqual(env.get_file_srcdir('%s/file' % dir), '/absolute/%s' % dir) + +class TestPathsParentBuildDir(TestPaths): + def get_env(self, topsrcdir): + env = ConfigEnvironment(topsrcdir = topsrcdir, topobjdir = '..') + self.assertEqual(env.get_relative_srcdir('..'), '.') + self.assertEqual(env.get_relative_srcdir('file'), self.dir) + self.assertEqual(env.get_relative_srcdir('dir/file'), '%s/dir' % self.dir) + self.assertEqual(env.get_relative_srcdir('deeply/nested/path/to/file'), '%s/deeply/nested/path/to' % self.dir) + self.assertEqual(env.get_depth('../file'), '.') + self.assertEqual(env.get_depth('file'), '..') + self.assertEqual(env.get_depth('dir/file'), '../..') + self.assertEqual(env.get_depth('deeply/nested/path/to/file'), '../../../../..') + return env + + def test_paths_parent_build_parent_src(self): + env = self.get_env('..') + self.assertEqual(env.get_input('../file'), os.path.join('..', 'file.in')) + self.assertEqual(env.get_input('file'), os.path.join('..', self.dir, 'file.in')) + self.assertEqual(env.get_input('dir/file'), os.path.join('..', self.dir, 'dir', 'file.in')) + self.assertEqual(env.get_file_srcdir('../file'), '..') + self.assertEqual(env.get_file_srcdir('file'), '../%s' % self.dir) + self.assertEqual(env.get_file_srcdir('dir/file'), '../%s/dir' % self.dir) + + def test_paths_parent_build_ancestor_src(self): + env = self.get_env('../..') + self.assertEqual(env.get_input('../file'), os.path.join('..', '..', 'file.in')) + self.assertEqual(env.get_input('file'), os.path.join('..', '..', self.dir, 'file.in')) + self.assertEqual(env.get_input('dir/file'), os.path.join('..', '..', self.dir, 'dir', 'file.in')) + self.assertEqual(env.get_file_srcdir('../file'), '../..') + self.assertEqual(env.get_file_srcdir('file'), '../../%s' % self.dir) + self.assertEqual(env.get_file_srcdir('dir/file'), '../../%s/dir' % self.dir) + + def test_paths_parent_build_absolute_src(self): + env = self.get_env(self.absolute) + self.assertEqual(env.get_input('../file'), os.path.join(self.absolute, 'file.in')) + self.assertEqual(env.get_input('file'), os.path.join(self.absolute, self.dir, 'file.in')) + self.assertEqual(env.get_input('dir/file'), os.path.join(self.absolute, self.dir, 'dir', 'file.in')) + self.assertEqual(env.get_file_srcdir('../file'), '/absolute') + self.assertEqual(env.get_file_srcdir('file'), '/absolute/%s' % self.dir) + self.assertEqual(env.get_file_srcdir('dir/file'), '/absolute/%s/dir' % self.dir) + +class TestPathsRelativeBuild(TestPaths): + def get_env(self, topsrcdir): + env = ConfigEnvironment(topsrcdir = topsrcdir, topobjdir = 'relative') + self.assertEqual(env.get_relative_srcdir('relative/file'), '.') + self.assertEqual(env.get_relative_srcdir('relative/dir/file'), 'dir') + self.assertEqual(env.get_relative_srcdir('relative/deeply/nested/path/to/file'), 'deeply/nested/path/to') + self.assertEqual(env.get_depth('relative/file'), '.') + self.assertEqual(env.get_depth('relative/dir/file'), '..') + self.assertEqual(env.get_depth('relative/deeply/nested/path/to/file'), '../../../..') + return env + + def test_paths_relative_build_relative_src(self): + env = self.get_env('relative') + self.assertEqual(env.get_input('relative/file'), os.path.join('relative', 'file.in')) + self.assertEqual(env.get_input('relative/dir/file'), os.path.join('relative', 'dir', 'file.in')) + self.assertEqual(env.get_file_srcdir('relative/file'), 'relative') + self.assertEqual(env.get_file_srcdir('relative/dir/file'), 'relative/dir') + + def test_paths_relative_build_local_src(self): + env = self.get_env('.') + self.assertEqual(env.get_input('relative/file'), 'file.in') + self.assertEqual(env.get_input('relative/dir/file'), os.path.join('dir', 'file.in')) + self.assertEqual(env.get_file_srcdir('relative/file'), '.') + self.assertEqual(env.get_file_srcdir('relative/dir/file'), 'dir') + + def test_paths_relative_build_parent_src(self): + env = self.get_env('..') + self.assertEqual(env.get_input('relative/file'), os.path.join('..', 'file.in')) + self.assertEqual(env.get_input('relative/dir/file'), os.path.join('..', 'dir', 'file.in')) + self.assertEqual(env.get_file_srcdir('relative/file'), '..') + self.assertEqual(env.get_file_srcdir('relative/dir/file'), '../dir') + + def test_paths_relative_build_absolute_src(self): + env = self.get_env(self.absolute) + self.assertEqual(env.get_input('relative/file'), os.path.join(self.absolute, 'file.in')) + self.assertEqual(env.get_input('relative/dir/file'), os.path.join(self.absolute, 'dir', 'file.in')) + self.assertEqual(env.get_file_srcdir('relative/file'), '/absolute') + self.assertEqual(env.get_file_srcdir('relative/dir/file'), '/absolute/dir') + +class TestPathsAbsoluteBuild(unittest.TestCase): + def setUp(self): + self.absolute_build = os.path.normpath('/absolute/build') + + def get_env(self, topsrcdir): + env = ConfigEnvironment(topsrcdir = topsrcdir, topobjdir = self.absolute_build) + self.assertEqual(env.get_relative_srcdir('/absolute/build/file'), '.') + self.assertEqual(env.get_relative_srcdir('/absolute/build/dir/file'), 'dir') + self.assertEqual(env.get_relative_srcdir('/absolute/build/deeply/nested/path/to/file'), 'deeply/nested/path/to') + self.assertEqual(env.get_depth('/absolute/build/file'), '.') + self.assertEqual(env.get_depth('/absolute/build/dir/file'), '..') + self.assertEqual(env.get_depth('/absolute/build/deeply/nested/path/to/file'), '../../../..') + return env + + def test_paths_absolute_build_same_src(self): + env = self.get_env(self.absolute_build) + self.assertEqual(env.get_input('/absolute/build/file'), os.path.join(self.absolute_build, 'file.in')) + self.assertEqual(env.get_input('/absolute/build/dir/file'), os.path.join(self.absolute_build, 'dir', 'file.in')) + self.assertEqual(env.get_file_srcdir('/absolute/build/file'), '/absolute/build') + self.assertEqual(env.get_file_srcdir('/absolute/build/dir/file'), '/absolute/build/dir') + + def test_paths_absolute_build_ancestor_src(self): + absolute = os.path.dirname(self.absolute_build) + env = self.get_env(absolute) + self.assertEqual(env.get_input('/absolute/build/file'), os.path.join(absolute, 'file.in')) + self.assertEqual(env.get_input('/absolute/build/dir/file'), os.path.join(absolute, 'dir', 'file.in')) + self.assertEqual(env.get_file_srcdir('/absolute/build/file'), '/absolute') + self.assertEqual(env.get_file_srcdir('/absolute/build/dir/file'), '/absolute/dir') + + def test_paths_absolute_build_different_src(self): + absolute = os.path.normpath('/some/path') + env = self.get_env(absolute) + self.assertEqual(env.get_input('/absolute/build/file'), os.path.join(absolute, 'file.in')) + self.assertEqual(env.get_input('/absolute/build/dir/file'), os.path.join(absolute, 'dir', 'file.in')) + self.assertEqual(env.get_file_srcdir('/absolute/build/file'), '/some/path') + self.assertEqual(env.get_file_srcdir('/absolute/build/dir/file'), '/some/path/dir') + +if __name__ == "__main__": + main() diff --git a/client.mk b/client.mk index 53b8c04e021ce..eba2a73e3948c 100644 --- a/client.mk +++ b/client.mk @@ -320,8 +320,7 @@ endif ifneq (,$(CONFIG_STATUS)) $(OBJDIR)/config/autoconf.mk: $(TOPSRCDIR)/config/autoconf.mk.in - cd $(OBJDIR); \ - CONFIG_FILES=config/autoconf.mk ./config.status + $(OBJDIR)/config.status -n --file=$(OBJDIR)/config/autoconf.mk endif diff --git a/config/rules.mk b/config/rules.mk index 640894f887d2b..fff5c5c21ef25 100644 --- a/config/rules.mk +++ b/config/rules.mk @@ -1153,27 +1153,26 @@ GARBAGE_DIRS += $(_JAVA_DIR) ############################################################################### ifndef NO_MAKEFILE_RULE -# Note: Passing depth to make-makefile is optional. -# It saves the script some work, though. Makefile: Makefile.in - @$(PERL) $(AUTOCONF_TOOLS)/make-makefile -t $(topsrcdir) -d $(DEPTH) + @$(DEPTH)/config.status -n --file=Makefile endif ifndef NO_SUBMAKEFILES_RULE ifdef SUBMAKEFILES # VPATH does not work on some machines in this case, so add $(srcdir) $(SUBMAKEFILES): % : $(srcdir)/%.in - $(if $(subsrcdir),cd $(subsrcdir) && )$(PERL) $(AUTOCONF_TOOLS)/make-makefile -t $(topsrcdir)$(addprefix /,$(subsrcdir)) -d $(DEPTH) $(@:$(subsrcdir)/%=%) + $(DEPTH)$(addprefix /,$(subsrcdir))/config.status -n --file=$@ endif endif ifdef AUTOUPDATE_CONFIGURE $(topsrcdir)/configure: $(topsrcdir)/configure.in - (cd $(topsrcdir) && $(AUTOCONF)) && (cd $(DEPTH) && ./config.status --recheck) + (cd $(topsrcdir) && $(AUTOCONF)) && $(DEPTH)/config.status -n --recheck) endif $(DEPTH)/config/autoconf.mk: $(topsrcdir)/config/autoconf.mk.in - cd $(DEPTH) && CONFIG_HEADERS= CONFIG_FILES=config/autoconf.mk ./config.status + $(DEPTH)/config.status -n --file=$(DEPTH)/config/autoconf.mk + $(TOUCH) $@ ############################################################################### # Bunch of things that extend the 'export' rule (in order): diff --git a/configure.in b/configure.in index 5b93a37993a77..d6c9f5021b289 100644 --- a/configure.in +++ b/configure.in @@ -1033,7 +1033,6 @@ ASM_SUFFIX=s IMPORT_LIB_SUFFIX= TARGET_MD_ARCH=unix DIRENT_INO=d_ino -WIN_TOP_SRC= MOZ_USER_DIR=".mozilla" MOZ_JPEG_CFLAGS= @@ -7987,8 +7986,6 @@ if test "$MOZ_TREE_CAIRO"; then fi CAIRO_FEATURES_H=gfx/cairo/cairo/src/cairo-features.h - mv -f $CAIRO_FEATURES_H "$CAIRO_FEATURES_H".orig 2> /dev/null - else PKG_CHECK_MODULES(CAIRO, cairo >= $CAIRO_VERSION) MOZ_CAIRO_CFLAGS="$CAIRO_CFLAGS" @@ -8671,14 +8668,6 @@ if test "$ACCESSIBILITY" -a "$MOZ_ENABLE_GTK2" ; then AC_DEFINE_UNQUOTED(ATK_REV_VERSION, $ATK_REV_VERSION) fi -case "$host_os" in -mingw*) - WIN_TOP_SRC=`cd $srcdir; pwd -W` - ;; -esac - -AC_SUBST(WIN_TOP_SRC) - AC_SUBST(MOZILLA_VERSION) AC_SUBST(ac_configure_args) @@ -8730,80 +8719,6 @@ case "$host" in ;; esac -# Save the defines header file before autoconf removes it. -# (Do not add AC_DEFINE calls after this line.) - _CONFIG_TMP=confdefs-tmp.h - _CONFIG_DEFS_H=mozilla-config.h - - cat > $_CONFIG_TMP <<\EOF -/* List of defines generated by configure. Included with preprocessor flag, - * -include, to avoid long list of -D defines on the compile command-line. - * Do not edit. - */ - -#ifndef _MOZILLA_CONFIG_H_ -#define _MOZILLA_CONFIG_H_ -EOF - -_EGREP_PATTERN='^#define (' -if test -n "$_NON_GLOBAL_ACDEFINES"; then - for f in $_NON_GLOBAL_ACDEFINES; do - _EGREP_PATTERN="${_EGREP_PATTERN}$f|" - done -fi -_EGREP_PATTERN="${_EGREP_PATTERN}dummy_never_defined)" - - sort confdefs.h | $FIXED_EGREP -v "$_EGREP_PATTERN" >> $_CONFIG_TMP - - if test "$?" != 0; then - AC_MSG_ERROR([Error outputting config definitions]) - fi - - cat >> $_CONFIG_TMP <<\EOF - -/* The c99 defining the limit macros (UINT32_MAX for example), says: - * C++ implementations should define these macros only when __STDC_LIMIT_MACROS - * is defined before is included. */ -#define __STDC_LIMIT_MACROS - -/* Force-include hunspell_alloc_hooks.h for hunspell, so that we don't need to - * modify it directly. - * - * HUNSPELL_STATIC is defined in extensions/spellcheck/hunspell/src/Makefile.in, - * unless --enable-system-hunspell is defined. - */ -#if defined(HUNSPELL_STATIC) -#include "hunspell_alloc_hooks.h" -#endif - -#endif /* _MOZILLA_CONFIG_H_ */ - -EOF - - # Only write mozilla-config.h when something changes (or it doesn't exist) - if cmp -s $_CONFIG_TMP $_CONFIG_DEFS_H; then - rm $_CONFIG_TMP - else - AC_MSG_RESULT("creating $_CONFIG_DEFS_H") - mv -f $_CONFIG_TMP $_CONFIG_DEFS_H - - echo ==== $_CONFIG_DEFS_H ================================= - cat $_CONFIG_DEFS_H - fi - -dnl Probably shouldn't call this manually but we always want the output of DEFS -rm -f confdefs.h.save -mv confdefs.h confdefs.h.save -$FIXED_EGREP -v "$_EGREP_PATTERN" confdefs.h.save > confdefs.h -if test "$?" != 0; then - AC_MSG_ERROR([Error outputting confdefs.h]) -fi -AC_OUTPUT_MAKE_DEFS() -ACDEFINES=$DEFS -AC_SUBST(ACDEFINES) -rm -f confdefs.h -mv confdefs.h.save confdefs.h - dnl Create a virtualenv where we can install local Python packages AC_MSG_RESULT([Creating Python virtualenv]) rm -rf _virtualenv @@ -8833,27 +8748,9 @@ dnl Load the list of Makefiles to generate. dnl To add new Makefiles, edit allmakefiles.sh. dnl allmakefiles.sh sets the variable, MAKEFILES. . ${srcdir}/allmakefiles.sh -dnl -dnl Run a perl script to quickly create the makefiles. -dnl If it succeeds, it outputs a shell command to set CONFIG_FILES -dnl for the files it cannot handle correctly. This way, config.status -dnl will handle these files. -dnl If it fails, nothing is set and config.status will run as usual. -dnl -dnl This does not change the $MAKEFILES variable. -dnl -echo $MAKEFILES | ${PERL} $srcdir/build/autoconf/acoutput-fast.pl > conftest.sh -res="$?" -if test "$res" != 0; then - exit $res -fi -. ./conftest.sh -rm conftest.sh echo $MAKEFILES > unallmakefiles -mv -f config/autoconf.mk config/autoconf.mk.orig 2> /dev/null - AC_OUTPUT($MAKEFILES) # Generate Makefiles for WebRTC directly from .gyp files @@ -8913,16 +8810,6 @@ else mv -f ./mozinfo.json.tmp ./mozinfo.json fi -dnl Prevent the regeneration of cairo-features.h forcing rebuilds of gfx stuff -if test "$CAIRO_FEATURES_H"; then - if cmp -s $CAIRO_FEATURES_H "$CAIRO_FEATURES_H".orig; then - echo "$CAIRO_FEATURES_H is unchanged" - mv -f "$CAIRO_FEATURES_H".orig "$CAIRO_FEATURES_H" 2> /dev/null - else - rm -f "$CAIRO_FEATURES_H".orig 2> /dev/null - fi -fi - # Run jemalloc configure script if test "$MOZ_JEMALLOC" -a "$MOZ_MEMORY"; then @@ -9131,13 +9018,3 @@ AC_OUTPUT_SUBDIRS(js/src) ac_configure_args="$_SUBDIR_CONFIG_ARGS" fi # COMPILE_ENVIRONMENT && !LIBXUL_SDK_DIR - -dnl Prevent the regeneration of autoconf.mk forcing rebuilds of the world -dnl Needs to be at the end to respect possible changes from NSPR configure -if cmp -s config/autoconf.mk config/autoconf.mk.orig; then - echo "config/autoconf.mk is unchanged" - mv -f config/autoconf.mk.orig config/autoconf.mk 2> /dev/null -else - rm -f config/autoconf.mk.orig 2> /dev/null -fi - diff --git a/gfx/cairo/cairo/src/Makefile.in b/gfx/cairo/cairo/src/Makefile.in index f057c514ef192..f38a2523b8aee 100644 --- a/gfx/cairo/cairo/src/Makefile.in +++ b/gfx/cairo/cairo/src/Makefile.in @@ -233,4 +233,4 @@ DEFINES += -DMOZ_TREE_PIXMAN endif cairo-features.h: $(srcdir)/cairo-features.h.in $(GLOBAL_DEPS) - $(PERL) $(AUTOCONF_TOOLS)/make-makefile -t $(topsrcdir) -d $(DEPTH) ./$@ + $(DEPTH)/config.status -n --file=$@ diff --git a/js/src/aclocal.m4 b/js/src/aclocal.m4 index 631621891721c..8c0cae85f4c0a 100644 --- a/js/src/aclocal.m4 +++ b/js/src/aclocal.m4 @@ -3,6 +3,7 @@ dnl Local autoconf macros used with mozilla dnl The contents of this file are under the Public Domain. dnl +builtin(include, build/autoconf/config.status.m4)dnl builtin(include, build/autoconf/toolchain.m4)dnl builtin(include, build/autoconf/ccache.m4)dnl builtin(include, build/autoconf/pkg.m4)dnl diff --git a/js/src/build/ConfigStatus.py b/js/src/build/ConfigStatus.py new file mode 100644 index 0000000000000..c0e873d6f7d84 --- /dev/null +++ b/js/src/build/ConfigStatus.py @@ -0,0 +1,313 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Combined with build/autoconf/config.status.m4, ConfigStatus is an almost +# drop-in replacement for autoconf 2.13's config.status, with features +# borrowed from autoconf > 2.5, and additional features. + +from __future__ import with_statement +from optparse import OptionParser +import sys, re, os, posixpath +from StringIO import StringIO +# Standalone js doesn't have virtualenv. +sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'config')) +from Preprocessor import Preprocessor + +# Basic logging facility +verbose = False +def log(string): + if verbose: + print >>sys.stderr, string + +# We need relpath, but it is introduced in python 2.6 +# http://docs.python.org/library/os.path.html +def my_relpath(path, start): + """ + Return a relative version of a path + from /usr/lib/python2.6/posixpath.py + """ + + if not path: + raise ValueError("no path specified") + + start_list = os.path.abspath(start).split(os.path.sep) + path_list = os.path.abspath(path).split(os.path.sep) + + # Work out how much of the filepath is shared by start and path. + i = len(os.path.commonprefix([start_list, path_list])) + + rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:] + if not rel_list: + return os.curdir + return os.path.join(*rel_list) + +relpath = getattr(os.path, "relpath", my_relpath) + +def ensureParentDir(file): + '''Ensures the directory parent to the given file exists''' + dir = os.path.dirname(file) + if dir and not os.path.exists(dir): + try: + os.makedirs(dir) + except OSError, error: + if error.errno != errno.EEXIST: + raise + +class FileAvoidWrite(StringIO): + '''file-like object that buffers its output and only writes it to disk + if the new contents are different from what the file may already contain. + ''' + def __init__(self, filename): + self.filename = filename + StringIO.__init__(self) + + def close(self): + buf = self.getvalue() + StringIO.close(self) + try: + file = open(self.filename, 'rU') + except IOError: + pass + else: + try: + if file.read() == buf: + log("%s is unchanged" % relpath(self.filename, os.curdir)) + return + except IOError: + pass + finally: + file.close() + + log("creating %s" % relpath(self.filename, os.curdir)) + ensureParentDir(self.filename) + with open(self.filename, 'w') as file: + file.write(buf) + + def __enter__(self): + return self + def __exit__(self, type, value, traceback): + self.close() + +def shell_escape(s): + '''Escape some characters with a backslash, and double dollar signs. + ''' + return re.sub('''([ \t`#$^&*(){}\\|;'"<>?\[\]])''', r'\\\1', str(s)).replace('$', '$$') + +class ConfigEnvironment(object): + '''A ConfigEnvironment is defined by a source directory and a build + directory. It preprocesses files from the source directory and stores + the result in the object directory. + + There are two types of files: config files and config headers, + each treated through a different member function. + + Creating a ConfigEnvironment requires a few arguments: + - topsrcdir and topobjdir are, respectively, the top source and + the top object directory. + - defines is a list of (name, value) tuples. In autoconf, these are + set with AC_DEFINE and AC_DEFINE_UNQUOTED + - non_global_defines are a list of names appearing in defines above + that are not meant to be exported in ACDEFINES and ALLDEFINES (see + below) + - substs is a list of (name, value) tuples. In autoconf, these are + set with AC_SUBST. + + ConfigEnvironment automatically defines two additional substs variables + from all the defines not appearing in non_global_defines: + - ACDEFINES contains the defines in the form -DNAME=VALUE, for use on + preprocessor command lines. The order in which defines were given + when creating the ConfigEnvironment is preserved. + - ALLDEFINES contains the defines in the form #define NAME VALUE, in + sorted order, for use in config files, for an automatic listing of + defines. + + ConfigEnvironment expects a "top_srcdir" subst to be set with the top + source directory, in msys format on windows. It is used to derive a + "srcdir" subst when treating config files. + ''' + + def __init__(self, topobjdir = '.', topsrcdir = '.', + defines = [], non_global_defines = [], substs = []): + self.defines = dict(defines) + self.substs = dict(substs) + self.topsrcdir = topsrcdir + self.topobjdir = topobjdir + global_defines = [name for name, value in defines if not name in non_global_defines] + self.substs['ACDEFINES'] = ' '.join(["-D%s=%s" % (name, shell_escape(self.defines[name])) for name in global_defines]) + self.substs['ALLDEFINES'] = '\n'.join(sorted(["#define %s %s" % (name, self.defines[name]) for name in global_defines])) + + def get_relative_srcdir(self, file): + '''Returns the relative source directory for the given file, always + using / as a path separator. + ''' + assert(isinstance(file, basestring)) + dir = posixpath.dirname(relpath(file, self.topobjdir).replace(os.sep, '/')) + if dir: + return dir + return '.' + + def get_file_srcdir(self, file): + '''Returns the srcdir for the given file, where srcdir is in msys + format on windows, thus derived from top_srcdir. + ''' + dir = self.get_relative_srcdir(file) + return posixpath.normpath(posixpath.join(self.substs['top_srcdir'], dir)) + + def get_depth(self, file): + '''Returns the DEPTH for the given file, that is, the path to the + object directory relative to the directory containing the given file. + Always uses / as a path separator. + ''' + return relpath(self.topobjdir, os.path.dirname(file)).replace(os.sep, '/') + + def get_input(self, file): + '''Returns the input file path in the source tree that can be used + to create the given config file or header. + ''' + assert(isinstance(file, basestring)) + return os.path.normpath(os.path.join(self.topsrcdir, "%s.in" % relpath(file, self.topobjdir))) + + def create_config_file(self, path): + '''Creates the given config file. A config file is generated by + taking the corresponding source file and replacing occurences of + "@VAR@" by the value corresponding to "VAR" in the substs dict. + + Additional substs are defined according to the file being treated: + "srcdir" for its the path to its source directory + "relativesrcdir" for its source directory relative to the top + "DEPTH" for the path to the top object directory + ''' + input = self.get_input(path) + pp = Preprocessor() + pp.context.update(self.substs) + pp.context.update(srcdir = self.get_file_srcdir(path)) + pp.context.update(relativesrcdir = self.get_relative_srcdir(path)) + pp.context.update(DEPTH = self.get_depth(path)) + pp.do_filter('attemptSubstitution') + pp.setMarker(None) + with FileAvoidWrite(path) as pp.out: + pp.do_include(input) + + def create_config_header(self, path): + '''Creates the given config header. A config header is generated by + taking the corresponding source file and replacing some #define/#undef + occurences: + "#undef NAME" is turned into "#define NAME VALUE" + "#define NAME" is unchanged + "#define NAME ORIGINAL_VALUE" is turned into "#define NAME VALUE" + "#undef UNKNOWN_NAME" is turned into "/* #undef UNKNOWN_NAME */" + Whitespaces are preserved. + ''' + with open(self.get_input(path), 'rU') as input: + ensureParentDir(path) + output = FileAvoidWrite(path) + r = re.compile('^\s*#\s*(?P[a-z]+)(?:\s+(?P\S+)(?:\s+(?P\S+))?)?', re.U) + for l in input: + m = r.match(l) + if m: + cmd = m.group('cmd') + name = m.group('name') + value = m.group('value') + if name: + if name in self.defines: + if cmd == 'define' and value: + l = l[:m.start('value')] + str(self.defines[name]) + l[m.end('value'):] + elif cmd == 'undef': + l = l[:m.start('cmd')] + 'define' + l[m.end('cmd'):m.end('name')] + ' ' + str(self.defines[name]) + l[m.end('name'):] + elif cmd == 'undef': + l = '/* ' + l[:m.end('name')] + ' */' + l[m.end('name'):] + + output.write(l) + output.close() + +def config_status(topobjdir = '.', topsrcdir = '.', + defines = [], non_global_defines = [], substs = [], + files = [], headers = []): + '''Main function, providing config.status functionality. + + Contrary to config.status, it doesn't use CONFIG_FILES or CONFIG_HEADERS + variables, but like config.status from autoconf 2.6, single files may be + generated with the --file and --header options. Several such options can + be given to generate several files at the same time. + + Without the -n option, this program acts as config.status and considers + the current directory as the top object directory, even when config.status + is in a different directory. It will, however, treat the directory + containing config.status as the top object directory with the -n option, + while files given to the --file and --header arguments are considered + relative to the current directory. + + The --recheck option, like with the original config.status, runs configure + again, with the options given in the "ac_configure_args" subst. + + The options to this function are passed when creating the + ConfigEnvironment, except for files and headers, which contain the list + of files and headers to be generated by default. These lists, as well as + the actual wrapper script around this function, are meant to be generated + by configure. See build/autoconf/config.status.m4. + + Unlike config.status behaviour with CONFIG_FILES and CONFIG_HEADERS, + but like config.status behaviour with --file and --header, providing + files or headers on the command line inhibits the default generation of + files when given headers and headers when given files. + + Unlike config.status, the FILE:TEMPLATE syntax is not supported for + files and headers. The template is always the filename suffixed with + '.in', in the corresponding directory under the top source directory. + ''' + + if 'CONFIG_FILES' in os.environ: + raise Exception, 'Using the CONFIG_FILES environment variable is not supported. Use --file instead.' + if 'CONFIG_HEADERS' in os.environ: + raise Exception, 'Using the CONFIG_HEADERS environment variable is not supported. Use --header instead.' + + parser = OptionParser() + parser.add_option('--recheck', dest='recheck', action='store_true', + help='update config.status by reconfiguring in the same conditions') + parser.add_option('--file', dest='files', metavar='FILE', action='append', + help='instantiate the configuration file FILE') + parser.add_option('--header', dest='headers', metavar='FILE', action='append', + help='instantiate the configuration header FILE') + parser.add_option('-v', '--verbose', dest='verbose', action='store_true', + help='display verbose output') + parser.add_option('-n', dest='not_topobjdir', action='store_true', + help='do not consider current directory as top object directory') + (options, args) = parser.parse_args() + + # Without -n, the current directory is meant to be the top object directory + if not options.not_topobjdir: + topobjdir = '.' + + env = ConfigEnvironment(topobjdir = topobjdir, topsrcdir = topsrcdir, + defines = defines, non_global_defines = non_global_defines, + substs = substs) + + if options.recheck: + # Execute configure from the top object directory + if not os.path.isabs(topsrcdir): + topsrcdir = relpath(topsrcdir, topobjdir) + os.chdir(topobjdir) + os.execlp('sh', 'sh', '-c', ' '.join([os.path.join(topsrcdir, 'configure'), env.substs['ac_configure_args'], '--no-create', '--no-recursion'])) + + if options.files: + files = options.files + headers = [] + if options.headers: + headers = options.headers + if not options.files: + files = [] + # Default to display messages when giving --file or --headers on the + # command line. + if options.files or options.headers or options.verbose: + global verbose + verbose = True + if not options.files and not options.headers: + print >>sys.stderr, "creating config files and headers..." + files = [os.path.join(topobjdir, f) for f in files] + headers = [os.path.join(topobjdir, f) for f in headers] + + for file in files: + env.create_config_file(file) + for header in headers: + env.create_config_header(header) diff --git a/js/src/build/autoconf/config.status.m4 b/js/src/build/autoconf/config.status.m4 new file mode 100644 index 0000000000000..dbc0d1e97d147 --- /dev/null +++ b/js/src/build/autoconf/config.status.m4 @@ -0,0 +1,167 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +dnl For use in AC_SUBST replacement +define([MOZ_DIVERSION_SUBST], 11) + +dnl Replace AC_SUBST to store values in a format suitable for python. +dnl The necessary comma after the tuple can't be put here because it +dnl can mess around with things like: +dnl AC_SOMETHING(foo,AC_SUBST(),bar) +define([AC_SUBST], +[ifdef([AC_SUBST_$1], , +[define([AC_SUBST_$1], )dnl +AC_DIVERT_PUSH(MOZ_DIVERSION_SUBST)dnl + (''' $1 ''', r''' [$]$1 ''') +AC_DIVERT_POP()dnl +])]) + +dnl Wrap AC_DEFINE to store values in a format suitable for python. +dnl autoconf's AC_DEFINE still needs to be used to fill confdefs.h, +dnl which is #included during some compile checks. +dnl The necessary comma after the tuple can't be put here because it +dnl can mess around with things like: +dnl AC_SOMETHING(foo,AC_DEFINE(),bar) +define([_MOZ_AC_DEFINE], defn([AC_DEFINE])) +define([AC_DEFINE], +[cat >> confdefs.pytmp <<\EOF + (''' $1 ''', ifelse($#, 2, [r''' $2 '''], $#, 3, [r''' $2 '''], ' 1 ')) +EOF +ifelse($#, 2, _MOZ_AC_DEFINE([$1], [$2]), $#, 3, _MOZ_AC_DEFINE([$1], [$2], [$3]),_MOZ_AC_DEFINE([$1]))dnl +]) + +dnl Wrap AC_DEFINE_UNQUOTED to store values in a format suitable for +dnl python. +define([_MOZ_AC_DEFINE_UNQUOTED], defn([AC_DEFINE_UNQUOTED])) +define([AC_DEFINE_UNQUOTED], +[cat >> confdefs.pytmp <>>)dnl +echo creating $CONFIG_STATUS + +cat > $CONFIG_STATUS <>>), topsrcdir)) +dnl Don't rely on virtualenv here. Standalone js doesn't use it. +sys.path.append(os.path.join(topsrcdir, 'build')) +from ConfigStatus import config_status + +args = { + 'topsrcdir': topsrcdir, + 'topobjdir': os.path.dirname(<<<__file__>>>), + +dnl All defines and substs are stored with an additional space at the beginning +dnl and at the end of the string, to avoid any problem with values starting or +dnl ending with quotes. + 'defines': [(name[1:-1], value[1:-1]) for name, value in [ +EOF + +dnl confdefs.pytmp contains AC_DEFINEs, in the expected format, but +dnl lacks the final comma (see above). +sed 's/$/,/' confdefs.pytmp >> $CONFIG_STATUS +rm confdefs.pytmp confdefs.h + +cat >> $CONFIG_STATUS <<\EOF + ] ], + + 'substs': [(name[1:-1], value[1:-1]) for name, value in [ +EOF + +dnl The MOZ_DIVERSION_SUBST output diversion contains AC_SUBSTs, in the +dnl expected format, but lacks the final comma (see above). +sed 's/$/,/' >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF + ] ], + +dnl List of files to apply AC_SUBSTs to. This is the list of files given +dnl as an argument to AC_OUTPUT ($1) + 'files': [ +EOF + +for out in $1; do + echo " '$out'," >> $CONFIG_STATUS +done + +cat >> $CONFIG_STATUS <<\EOF + ], + +dnl List of header files to apply AC_DEFINEs to. This is stored in the +dnl AC_LIST_HEADER m4 macro by AC_CONFIG_HEADER. + 'headers': [ +EOF + +ifdef(<<>>, <<< +HEADERS="AC_LIST_HEADER" +for header in $HEADERS; do + echo " '$header'," >> $CONFIG_STATUS +done +>>>)dnl + +cat >> $CONFIG_STATUS <<\EOF + ], + +dnl List of AC_DEFINEs that aren't to be exposed in ALLDEFINES + 'non_global_defines': [ +EOF + +if test -n "$_NON_GLOBAL_ACDEFINES"; then + for var in $_NON_GLOBAL_ACDEFINES; do + echo " '$var'," >> $CONFIG_STATUS + done +fi + +cat >> $CONFIG_STATUS <<\EOF + ] +} + +dnl Do the actual work +config_status(**args) +EOF +changequote([, ]) +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +dnl Execute config.status, unless --no-create was passed to configure. +test "$no_create" = yes || ${PYTHON} $CONFIG_STATUS || exit 1 +]) diff --git a/js/src/config/rules.mk b/js/src/config/rules.mk index 640894f887d2b..fff5c5c21ef25 100644 --- a/js/src/config/rules.mk +++ b/js/src/config/rules.mk @@ -1153,27 +1153,26 @@ GARBAGE_DIRS += $(_JAVA_DIR) ############################################################################### ifndef NO_MAKEFILE_RULE -# Note: Passing depth to make-makefile is optional. -# It saves the script some work, though. Makefile: Makefile.in - @$(PERL) $(AUTOCONF_TOOLS)/make-makefile -t $(topsrcdir) -d $(DEPTH) + @$(DEPTH)/config.status -n --file=Makefile endif ifndef NO_SUBMAKEFILES_RULE ifdef SUBMAKEFILES # VPATH does not work on some machines in this case, so add $(srcdir) $(SUBMAKEFILES): % : $(srcdir)/%.in - $(if $(subsrcdir),cd $(subsrcdir) && )$(PERL) $(AUTOCONF_TOOLS)/make-makefile -t $(topsrcdir)$(addprefix /,$(subsrcdir)) -d $(DEPTH) $(@:$(subsrcdir)/%=%) + $(DEPTH)$(addprefix /,$(subsrcdir))/config.status -n --file=$@ endif endif ifdef AUTOUPDATE_CONFIGURE $(topsrcdir)/configure: $(topsrcdir)/configure.in - (cd $(topsrcdir) && $(AUTOCONF)) && (cd $(DEPTH) && ./config.status --recheck) + (cd $(topsrcdir) && $(AUTOCONF)) && $(DEPTH)/config.status -n --recheck) endif $(DEPTH)/config/autoconf.mk: $(topsrcdir)/config/autoconf.mk.in - cd $(DEPTH) && CONFIG_HEADERS= CONFIG_FILES=config/autoconf.mk ./config.status + $(DEPTH)/config.status -n --file=$(DEPTH)/config/autoconf.mk + $(TOUCH) $@ ############################################################################### # Bunch of things that extend the 'export' rule (in order): diff --git a/js/src/configure.in b/js/src/configure.in index 2689e8b133cf7..1379125adab00 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -874,7 +874,6 @@ ASM_SUFFIX=s IMPORT_LIB_SUFFIX= TARGET_MD_ARCH=unix DIRENT_INO=d_ino -WIN_TOP_SRC= MOZ_USER_DIR=".mozilla" MOZ_JS_LIBS='-L$(libdir) -lmozjs' @@ -4480,14 +4479,6 @@ dnl ======================================================== AC_HAVE_FUNCS(setlocale) AC_HAVE_FUNCS(localeconv) -case "$host_os" in -mingw*) - WIN_TOP_SRC=`cd $srcdir; pwd -W` - ;; -esac - -AC_SUBST(WIN_TOP_SRC) - AC_SUBST(MOZILLA_VERSION) AC_SUBST(ac_configure_args) @@ -4495,71 +4486,8 @@ AC_SUBST(ac_configure_args) dnl Spit out some output dnl ======================================================== -# Save the defines header file before autoconf removes it. -# (Do not add AC_DEFINE calls after this line.) - _CONFIG_TMP=confdefs-tmp.h - _CONFIG_DEFS_H=js-confdefs.h - - cat > $_CONFIG_TMP <<\EOF -/* List of defines generated by configure. Included with preprocessor flag, - * -include, to avoid long list of -D defines on the compile command-line. - * Do not edit. - */ - -#ifndef _JS_CONFDEFS_H_ -#define _JS_CONFDEFS_H_ -EOF - -_EGREP_PATTERN='^#define (' -if test -n "$_NON_GLOBAL_ACDEFINES"; then - for f in $_NON_GLOBAL_ACDEFINES; do - _EGREP_PATTERN="${_EGREP_PATTERN}$f|" - done -fi -_EGREP_PATTERN="${_EGREP_PATTERN}dummy_never_defined)" - - sort confdefs.h | egrep -v "$_EGREP_PATTERN" >> $_CONFIG_TMP - - if test "$?" != 0; then - AC_MSG_ERROR([Error outputting config definitions]) - fi - - cat >> $_CONFIG_TMP <<\EOF - -/* The c99 defining the limit macros (UINT32_MAX for example), says: - * C++ implementations should define these macros only when __STDC_LIMIT_MACROS - * is defined before is included. */ -#define __STDC_LIMIT_MACROS - -#endif /* _JS_CONFDEFS_H_ */ - -EOF - - # Only write js-confdefs.h when something changes (or it doesn't exist) - if cmp -s $_CONFIG_TMP $_CONFIG_DEFS_H; then - rm $_CONFIG_TMP - else - AC_MSG_RESULT("creating $_CONFIG_DEFS_H") - mv -f $_CONFIG_TMP $_CONFIG_DEFS_H - - echo ==== $_CONFIG_DEFS_H ================================= - cat $_CONFIG_DEFS_H - fi - -dnl Probably shouldn't call this manually but we always want the output of DEFS -rm -f confdefs.h.save -mv confdefs.h confdefs.h.save -egrep -v "$_EGREP_PATTERN" confdefs.h.save > confdefs.h -if test "$?" != 0; then - AC_MSG_ERROR([Error outputting confdefs.h]) -fi -AC_OUTPUT_MAKE_DEFS() -ACDEFINES=$DEFS -AC_SUBST(ACDEFINES) -rm -f confdefs.h -mv confdefs.h.save confdefs.h - MAKEFILES=" + js-confdefs.h Makefile shell/Makefile config/Makefile @@ -4586,37 +4514,10 @@ if test "$ENABLE_TESTS"; then " fi -dnl -dnl Run a perl script to quickly create the makefiles. -dnl If it succeeds, it outputs a shell command to set CONFIG_FILES -dnl for the files it cannot handle correctly. This way, config.status -dnl will handle these files. -dnl If it fails, nothing is set and config.status will run as usual. -dnl -dnl This does not change the $MAKEFILES variable. -dnl -echo $MAKEFILES | ${PERL} $srcdir/build/autoconf/acoutput-fast.pl > conftest.sh -res="$?" -if test "$res" != 0; then - exit $res -fi -. ./conftest.sh -rm conftest.sh - echo $MAKEFILES > unallmakefiles -mv -f config/autoconf.mk config/autoconf.mk.orig 2> /dev/null - AC_OUTPUT($MAKEFILES) -dnl Prevent the regeneration of autoconf.mk forcing rebuilds of the world -if cmp -s config/autoconf.mk config/autoconf.mk.orig; then - echo "config/autoconf.mk is unchanged" - mv -f config/autoconf.mk.orig config/autoconf.mk 2> /dev/null -else - rm -f config/autoconf.mk.orig 2> /dev/null -fi - # Produce the js-config script at configure time; see the comments for # 'js-config' in Makefile.in. AC_MSG_RESULT(invoking $MAKE to create js-config script) diff --git a/js/src/js-confdefs.h.in b/js/src/js-confdefs.h.in new file mode 100644 index 0000000000000..77a0204f727ae --- /dev/null +++ b/js/src/js-confdefs.h.in @@ -0,0 +1,17 @@ +/* List of defines generated by configure. Included with preprocessor flag, + * -include, to avoid long list of -D defines on the compile command-line. + * Do not edit. + */ + +#ifndef _JS_CONFDEFS_H_ +#define _JS_CONFDEFS_H_ + +@ALLDEFINES@ + +/* The c99 defining the limit macros (UINT32_MAX for example), says: + * C++ implementations should define these macros only when __STDC_LIMIT_MACROS + * is defined before is included. */ +#define __STDC_LIMIT_MACROS + +#endif /* _JS_CONFDEFS_H_ */ + diff --git a/mozilla-config.h.in b/mozilla-config.h.in new file mode 100644 index 0000000000000..b821728cc7085 --- /dev/null +++ b/mozilla-config.h.in @@ -0,0 +1,27 @@ +/* List of defines generated by configure. Included with preprocessor flag, + * -include, to avoid long list of -D defines on the compile command-line. + * Do not edit. + */ + +#ifndef _MOZILLA_CONFIG_H_ +#define _MOZILLA_CONFIG_H_ + +@ALLDEFINES@ + +/* The c99 defining the limit macros (UINT32_MAX for example), says: + * C++ implementations should define these macros only when __STDC_LIMIT_MACROS + * is defined before is included. */ +#define __STDC_LIMIT_MACROS + +/* Force-include hunspell_alloc_hooks.h for hunspell, so that we don't need to + * modify it directly. + * + * HUNSPELL_STATIC is defined in extensions/spellcheck/hunspell/src/Makefile.in, + * unless --enable-system-hunspell is defined. + */ +#if defined(HUNSPELL_STATIC) +#include "hunspell_alloc_hooks.h" +#endif + +#endif /* _MOZILLA_CONFIG_H_ */ +