Merge pull request #4356 from xclaesse/wipe

Add --wipe command line option
pull/4466/head
Jussi Pakkanen 6 years ago committed by GitHub
commit 996f07c546
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 43
      mesonbuild/coredata.py
  2. 29
      mesonbuild/environment.py
  3. 1
      mesonbuild/mconf.py
  4. 42
      mesonbuild/msetup.py
  5. 36
      run_unittests.py
  6. 4
      test cases/unit/46 reconfigure/main.c
  7. 9
      test cases/unit/46 reconfigure/meson.build
  8. 4
      test cases/unit/46 reconfigure/meson_options.txt

@ -22,6 +22,7 @@ from .mesonlib import default_libdir, default_libexecdir, default_prefix
from .wrap import WrapMode
import ast
import argparse
import configparser
version = '0.48.999'
backendlist = ['ninja', 'vs', 'vs2010', 'vs2015', 'vs2017', 'xcode']
@ -459,6 +460,45 @@ class CoreData:
sub = 'In subproject {}: '.format(subproject) if subproject else ''
mlog.warning('{}Unknown options: "{}"'.format(sub, unknown_options))
def get_cmd_line_file(build_dir):
return os.path.join(build_dir, 'meson-private', 'cmd_line.txt')
def read_cmd_line_file(build_dir, options):
filename = get_cmd_line_file(build_dir)
config = configparser.ConfigParser()
config.read(filename)
# Do a copy because config is not really a dict. options.cmd_line_options
# overrides values from the file.
d = dict(config['options'])
d.update(options.cmd_line_options)
options.cmd_line_options = d
properties = config['properties']
if options.cross_file is None:
options.cross_file = properties.get('cross_file', None)
def write_cmd_line_file(build_dir, options):
filename = get_cmd_line_file(build_dir)
config = configparser.ConfigParser()
properties = {}
if options.cross_file is not None:
properties['cross_file'] = options.cross_file
config['options'] = options.cmd_line_options
config['properties'] = properties
with open(filename, 'w') as f:
config.write(f)
def update_cmd_line_file(build_dir, options):
filename = get_cmd_line_file(build_dir)
config = configparser.ConfigParser()
config.read(filename)
config['options'].update(options.cmd_line_options)
with open(filename, 'w') as f:
config.write(f)
def load(build_dir):
filename = os.path.join(build_dir, 'meson-private', 'coredata.dat')
load_fail_msg = 'Coredata file {!r} is corrupted. Try with a fresh build tree.'.format(filename)
@ -470,7 +510,8 @@ def load(build_dir):
if not isinstance(obj, CoreData):
raise MesonException(load_fail_msg)
if obj.version != version:
raise MesonException('Build directory has been generated with Meson version %s, which is incompatible with current version %s.\nPlease delete this build directory AND create a new one.' %
raise MesonException('Build directory has been generated with Meson version %s, '
'which is incompatible with current version %s.\n' %
(obj.version, version))
return obj

@ -17,7 +17,7 @@ import configparser, os, platform, re, sys, shlex, shutil, subprocess
from . import coredata
from .linkers import ArLinker, ArmarLinker, VisualStudioLinker, DLinker
from . import mesonlib
from .mesonlib import EnvironmentException, PerMachine, Popen_safe
from .mesonlib import MesonException, EnvironmentException, PerMachine, Popen_safe
from . import mlog
from . import compilers
@ -317,13 +317,17 @@ class Environment:
self.coredata = coredata.load(self.get_build_dir())
self.first_invocation = False
except FileNotFoundError:
# WARNING: Don't use any values from coredata in __init__. It gets
# re-initialized with project options by the interpreter during
# build file parsing.
self.coredata = coredata.CoreData(options)
# Used by the regenchecker script, which runs meson
self.coredata.meson_command = mesonlib.meson_command
self.first_invocation = True
self.create_new_coredata(options)
except MesonException as e:
# If we stored previous command line options, we can recover from
# a broken/outdated coredata.
if os.path.isfile(coredata.get_cmd_line_file(self.build_dir)):
mlog.warning('Regenerating configuration from scratch.')
mlog.log('Reason:', mlog.red(str(e)))
coredata.read_cmd_line_file(self.build_dir, options)
self.create_new_coredata(options)
else:
raise e
self.exe_wrapper = None
self.machines = MachineInfos()
@ -389,6 +393,15 @@ class Environment:
else:
self.native_strip_bin = ['strip']
def create_new_coredata(self, options):
# WARNING: Don't use any values from coredata in __init__. It gets
# re-initialized with project options by the interpreter during
# build file parsing.
self.coredata = coredata.CoreData(options)
# Used by the regenchecker script, which runs meson
self.coredata.meson_command = mesonlib.meson_command
self.first_invocation = True
def is_cross_build(self):
return self.cross_info is not None

@ -153,6 +153,7 @@ def run(options):
save = False
if len(options.cmd_line_options) > 0:
c.set_options(options.cmd_line_options)
coredata.update_cmd_line_file(builddir, options)
save = True
elif options.clearcache:
c.clear_cache()

@ -39,6 +39,10 @@ def add_arguments(parser):
help='Set options and reconfigure the project. Useful when new ' +
'options have been added to the project and the default value ' +
'is not working.')
parser.add_argument('--wipe', action='store_true',
help='Wipe build directory and reconfigure using previous command line options. ' +
'Userful when build directory got corrupted, or when rebuilding with a ' +
'newer version of meson.')
parser.add_argument('builddir', nargs='?', default=None)
parser.add_argument('sourcedir', nargs='?', default=None)
@ -46,7 +50,28 @@ class MesonApp:
def __init__(self, options):
(self.source_dir, self.build_dir) = self.validate_dirs(options.builddir,
options.sourcedir,
options.reconfigure)
options.reconfigure,
options.wipe)
if options.wipe:
# Make a copy of the cmd line file to make sure we can always
# restore that file if anything bad happens. For example if
# configuration fails we need to be able to wipe again.
filename = coredata.get_cmd_line_file(self.build_dir)
with open(filename, 'r') as f:
content = f.read()
coredata.read_cmd_line_file(self.build_dir, options)
try:
mesonlib.windows_proof_rmtree(self.build_dir)
finally:
# Restore the file
path = os.path.dirname(filename)
os.makedirs(path, exist_ok=True)
with open(filename, 'w') as f:
f.write(content)
self.options = options
def has_build_file(self, dirname):
@ -83,21 +108,22 @@ class MesonApp:
return ndir2, ndir1
raise MesonException('Neither directory contains a build file %s.' % environment.build_filename)
def validate_dirs(self, dir1, dir2, reconfigure):
def validate_dirs(self, dir1, dir2, reconfigure, wipe):
(src_dir, build_dir) = self.validate_core_dirs(dir1, dir2)
priv_dir = os.path.join(build_dir, 'meson-private/coredata.dat')
if os.path.exists(priv_dir):
if not reconfigure:
if not reconfigure and not wipe:
print('Directory already configured.\n'
'\nJust run your build command (e.g. ninja) and Meson will regenerate as necessary.\n'
'If ninja fails, run "ninja reconfigure" or "meson --reconfigure"\n'
'to force Meson to regenerate.\n'
'\nIf build failures persist, manually wipe your build directory to clear any\n'
'stored system data.\n'
'\nIf build failures persist, run "meson setup --wipe" to rebuild from scratch\n'
'using the same options as passed when configuring the build.'
'\nTo change option values, run "meson configure" instead.')
sys.exit(0)
else:
if reconfigure:
has_cmd_line_file = os.path.exists(coredata.get_cmd_line_file(build_dir))
if (wipe and not has_cmd_line_file) or (not wipe and reconfigure):
print('Directory does not contain a valid build tree:\n{}'.format(build_dir))
sys.exit(1)
return src_dir, build_dir
@ -168,6 +194,10 @@ class MesonApp:
build.save(b, dumpfile)
# Post-conf scripts must be run after writing coredata or else introspection fails.
intr.backend.run_postconf_scripts()
if env.first_invocation:
coredata.write_cmd_line_file(self.build_dir, self.options)
else:
coredata.update_cmd_line_file(self.build_dir, self.options)
except:
if 'cdf' in locals():
old_cdf = cdf + '.prev'

@ -25,6 +25,7 @@ import shutil
import sys
import unittest
import platform
import pickle
from itertools import chain
from unittest import mock
from configparser import ConfigParser
@ -2778,6 +2779,41 @@ recommended as it is not supported on some platforms''')
self.wipe()
self.init(testdir, extra_args=['-Dstart_native=true'])
def test_reconfigure(self):
testdir = os.path.join(self.unit_test_dir, '46 reconfigure')
self.init(testdir, extra_args=['-Dopt1=val1'])
self.setconf('-Dopt2=val2')
# Set an older version to force a reconfigure from scratch
filename = os.path.join(self.privatedir, 'coredata.dat')
with open(filename, 'rb') as f:
obj = pickle.load(f)
obj.version = '0.47.0'
with open(filename, 'wb') as f:
pickle.dump(obj, f)
out = self.init(testdir, extra_args=['--reconfigure', '-Dopt3=val3'])
self.assertRegex(out, 'WARNING:.*Regenerating configuration from scratch')
self.assertRegex(out, 'opt1 val1')
self.assertRegex(out, 'opt2 val2')
self.assertRegex(out, 'opt3 val3')
self.assertRegex(out, 'opt4 default4')
self.build()
self.run_tests()
# Create a file in builddir and verify wipe command removes it
filename = os.path.join(self.builddir, 'something')
open(filename, 'w').close()
self.assertTrue(os.path.exists(filename))
out = self.init(testdir, extra_args=['--wipe', '-Dopt4=val4'])
self.assertFalse(os.path.exists(filename))
self.assertRegex(out, 'opt1 val1')
self.assertRegex(out, 'opt2 val2')
self.assertRegex(out, 'opt3 val3')
self.assertRegex(out, 'opt4 val4')
self.build()
self.run_tests()
class FailureTests(BasePlatformTests):
'''
Tests that test failure conditions. Build files here should be dynamically

@ -0,0 +1,4 @@
int main(int argc, char *argv[])
{
return 0;
}

@ -0,0 +1,9 @@
project('test-reconfigure', 'c')
message('opt1 ' + get_option('opt1'))
message('opt2 ' + get_option('opt2'))
message('opt3 ' + get_option('opt3'))
message('opt4 ' + get_option('opt4'))
exe = executable('test1', 'main.c')
test('test1', exe)

@ -0,0 +1,4 @@
option('opt1', type : 'string', value : 'default1')
option('opt2', type : 'string', value : 'default2')
option('opt3', type : 'string', value : 'default3')
option('opt4', type : 'string', value : 'default4')
Loading…
Cancel
Save