Bug 808357 - Part 1: Split mozconfig code into own Python module; r=glandium

--HG--
rename : python/mozbuild/mozbuild/base.py => python/mozbuild/mozbuild/mozconfig.py
This commit is contained in:
Gregory Szorc 2012-12-04 22:02:03 -08:00
parent aa43825f66
commit f49dc5effc
4 changed files with 140 additions and 85 deletions

View File

@ -1,22 +1,20 @@
# This Source Code Form is subject to the terms of the Mozilla Public # 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, # License, v. 2.0. If a copy of the MPL was not distributed with this
# You can obtain one at http://mozilla.org/MPL/2.0/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
from __future__ import unicode_literals from __future__ import unicode_literals
import logging import logging
import os import os
import pymake.parser
import subprocess import subprocess
import sys import sys
import which import which
from pymake.data import Makefile
from mach.mixin.logging import LoggingMixin from mach.mixin.logging import LoggingMixin
from mach.mixin.process import ProcessExecutionMixin from mach.mixin.process import ProcessExecutionMixin
from .config import BuildConfig from .config import BuildConfig
from .mozconfig import MozconfigLoader
class MozbuildObject(ProcessExecutionMixin): class MozbuildObject(ProcessExecutionMixin):
@ -41,20 +39,33 @@ class MozbuildObject(ProcessExecutionMixin):
self.populate_logger() self.populate_logger()
self.log_manager = log_manager self.log_manager = log_manager
self._config_guess_output = None
self._make = None self._make = None
self._topobjdir = topobjdir self._topobjdir = topobjdir
self._mozconfig = None
self._config_guess_output = None
@property @property
def topobjdir(self): def topobjdir(self):
if self._topobjdir is None: if self._topobjdir is None:
self._load_mozconfig() if self.mozconfig['topobjdir'] is None:
self._topobjdir = 'obj-%s' % self._config_guess
if self._topobjdir is None: else:
self._topobjdir = 'obj-%s' % self._config_guess self._topobjdir = self.mozconfig['topobjdir']
return self._topobjdir return self._topobjdir
@property
def mozconfig(self):
"""Returns information about the current mozconfig file.
This a dict as returned by MozconfigLoader.read_mozconfig()
"""
if self._mozconfig is None:
loader = MozconfigLoader(self.topsrcdir)
self._mozconfig = loader.read_mozconfig()
return self._mozconfig
@property @property
def distdir(self): def distdir(self):
return os.path.join(self.topobjdir, 'dist') return os.path.join(self.topobjdir, 'dist')
@ -67,63 +78,6 @@ class MozbuildObject(ProcessExecutionMixin):
def statedir(self): def statedir(self):
return os.path.join(self.topobjdir, '.mozbuild') return os.path.join(self.topobjdir, '.mozbuild')
def _load_mozconfig(self, path=None):
# The mozconfig loader outputs a make file. We parse and load this make
# file with pymake and evaluate it in a context similar to client.mk.
loader = os.path.join(self.topsrcdir, 'build', 'autoconf',
'mozconfig2client-mk')
# os.environ from a library function is somewhat evil. But, mozconfig
# files are tightly coupled with the environment by definition. In the
# future, perhaps we'll have a more sanitized environment for mozconfig
# execution.
#
# The force of str is required because subprocess on Python <2.7.3
# does not like unicode in environment keys or values. At the time this
# was written, Mozilla shipped Python 2.7.2 with MozillaBuild.
env = dict(os.environ)
if path is not None:
env[str('MOZCONFIG')] = path
env[str('CONFIG_GUESS')] = self._config_guess
args = self._normalize_command([loader, self.topsrcdir], True)
output = subprocess.check_output(args, stderr=subprocess.PIPE,
cwd=self.topsrcdir, env=env)
# The output is make syntax. We parse this in a specialized make
# context.
statements = pymake.parser.parsestring(output, 'mozconfig')
makefile = Makefile(workdir=self.topsrcdir, env={
'TOPSRCDIR': self.topsrcdir,
'CONFIG_GUESS': self._config_guess})
statements.execute(makefile)
def get_value(name):
exp = makefile.variables.get(name)[2]
return exp.resolvestr(makefile, makefile.variables)
for name, flavor, source, value in makefile.variables:
# We only care about variables that came from the parsed mozconfig.
if source != pymake.data.Variables.SOURCE_MAKEFILE:
continue
# Ignore some pymake built-ins.
if name in ('.PYMAKE', 'MAKELEVEL', 'MAKEFLAGS'):
continue
if name == 'MOZ_OBJDIR':
self._topobjdir = get_value(name)
# If we want to extract other variables defined by mozconfig, here
# is where we'd do it.
@property @property
def _config_guess(self): def _config_guess(self):
if self._config_guess_output is None: if self._config_guess_output is None:

View File

@ -0,0 +1,72 @@
# 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/.
from __future__ import unicode_literals
import os
import subprocess
import pymake.parser
from pymake.data import Makefile
from mach.mixin.process import ProcessExecutionMixin
class MozconfigLoader(ProcessExecutionMixin):
"""Handles loading and parsing of mozconfig files."""
def __init__(self, topsrcdir):
self.topsrcdir = topsrcdir
@property
def _client_mk_loader_path(self):
return os.path.join(self.topsrcdir, 'build', 'autoconf',
'mozconfig2client-mk')
def read_mozconfig(self, path=None):
env = dict(os.environ)
if path is not None:
# Python < 2.7.3 doesn't like unicode strings in env keys.
env[b'MOZCONFIG'] = path
args = self._normalize_command([self._client_mk_loader_path,
self.topsrcdir], True)
output = subprocess.check_output(args, stderr=subprocess.PIPE,
cwd=self.topsrcdir, env=env)
# The output is make syntax. We parse this in a specialized make
# context.
statements = pymake.parser.parsestring(output, 'mozconfig')
makefile = Makefile(workdir=self.topsrcdir, env={
'TOPSRCDIR': self.topsrcdir})
statements.execute(makefile)
result = {
'topobjdir': None,
}
def get_value(name):
exp = makefile.variables.get(name)[2]
return exp.resolvestr(makefile, makefile.variables)
for name, flavor, source, value in makefile.variables:
# We only care about variables that came from the parsed mozconfig.
if source != pymake.data.Variables.SOURCE_MAKEFILE:
continue
# Ignore some pymake built-ins.
if name in ('.PYMAKE', 'MAKELEVEL', 'MAKEFLAGS'):
continue
if name == 'MOZ_OBJDIR':
result['topobjdir'] = get_value(name)
# If we want to extract other variables defined by mozconfig, here
# is where we'd do it.
return result

View File

@ -1,6 +1,6 @@
# This Source Code Form is subject to the terms of the Mozilla Public # 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, # License, v. 2.0. If a copy of the MPL was not distributed with this
# You can obtain one at http://mozilla.org/MPL/2.0/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
from __future__ import unicode_literals from __future__ import unicode_literals
@ -26,27 +26,24 @@ class TestMozbuildObject(unittest.TestCase):
def get_base(self): def get_base(self):
return MozbuildObject(topsrcdir, None, log_manager) return MozbuildObject(topsrcdir, None, log_manager)
def test_mozconfig_parsing(self):
with NamedTemporaryFile(mode='wt') as mozconfig:
mozconfig.write('mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/some-objdir')
mozconfig.flush()
os.environ['MOZCONFIG'] = mozconfig.name
base = self.get_base()
base._load_mozconfig()
self.assertEqual(base.topobjdir, '%s/some-objdir' % topsrcdir)
del os.environ['MOZCONFIG']
def test_objdir_config_guess(self): def test_objdir_config_guess(self):
base = self.get_base() base = self.get_base()
with NamedTemporaryFile() as mozconfig: with NamedTemporaryFile() as mozconfig:
os.environ['MOZCONFIG'] = mozconfig.name os.environ[b'MOZCONFIG'] = mozconfig.name
self.assertIsNotNone(base.topobjdir) self.assertIsNotNone(base.topobjdir)
self.assertEqual(len(base.topobjdir.split()), 1) self.assertEqual(len(base.topobjdir.split()), 1)
self.assertTrue(base.topobjdir.endswith(base._config_guess))
del os.environ[b'MOZCONFIG']
def test_config_guess(self):
# It's difficult to test for exact values from the output of
# config.guess because they vary depending on platform.
base = self.get_base()
result = base._config_guess
self.assertIsNotNone(result)
self.assertGreater(len(result), 0)
del os.environ['MOZCONFIG']

View File

@ -0,0 +1,32 @@
# 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/.
from __future__ import unicode_literals
import os
import unittest
from tempfile import NamedTemporaryFile
from mozbuild.mozconfig import MozconfigLoader
curdir = os.path.dirname(__file__)
topsrcdir = os.path.normpath(os.path.join(curdir, '..', '..', '..', '..'))
class TestMozconfigLoader(unittest.TestCase):
def get_loader(self):
return MozconfigLoader(topsrcdir)
def test_mozconfig_reading_moz_objdir(self):
with NamedTemporaryFile(mode='wt') as mozconfig:
mozconfig.write('mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/some-objdir')
mozconfig.flush()
loader = self.get_loader()
result = loader.read_mozconfig(mozconfig.name)
self.assertEqual(result['topobjdir'], '%s/some-objdir' % topsrcdir)