Repository: pantsbuild/pants

Issue:
Use JvmToolTaskTestBase for ScroogeGenTest
SEE RB ID
https://rbcommons.com/s/twitter/r/538/



Discussion:

Resolving patch:
diff --git a/src/python/pants/option/parser.py b/src/python/pants/option/parser.py
--- a/src/python/pants/option/parser.py
+++ b/src/python/pants/option/parser.py
@@ -148,7 +148,7 @@ def parse_args(self, flags, namespace):
     mutex_map = defaultdict(list)
     for args, kwargs in self._unnormalized_option_registrations_iter():
       self._validate(args, kwargs)
-      dest = kwargs.get('dest') or self._select_dest(args)
+      dest = self.parse_dest(*args, **kwargs)
 
       # Compute the values provided on the command line for this option.  Note that there may be
       # multiple values, for any combination of the following reasons:
@@ -242,7 +242,7 @@ def option_registrations_iter(self):
     """
     def normalize_kwargs(args, orig_kwargs):
       nkwargs = copy.copy(orig_kwargs)
-      dest = nkwargs.get('dest') or self._select_dest(args)
+      dest = self.parse_dest(*args, **nkwargs)
       nkwargs['dest'] = dest
       if not ('default' in nkwargs and isinstance(nkwargs['default'], RankedValue)):
         nkwargs['default'] = self._compute_value(dest, nkwargs, [])
@@ -395,11 +395,17 @@ def _existing_scope(self, arg):
 
   _ENV_SANITIZER_RE = re.compile(r'[.-]')
 
-  def _select_dest(self, args):
-    """Select the dest name for the option.
+  @staticmethod
+  def parse_dest(*args, **kwargs):
+    """Select the dest name for an option registration.
 
-    '--foo-bar' -> 'foo_bar' and '-x' -> 'x'.
+    If an explicit `dest` is specified, returns that and otherwise derives a default from the
+    option flags where '--foo-bar' -> 'foo_bar' and '-x' -> 'x'.
     """
+    explicit_dest = kwargs.get('dest')
+    if explicit_dest:
+      return explicit_dest
+
     arg = next((a for a in args if a.startswith('--')), args[0])
     return arg.lstrip('-').replace('-', '_')
 


Test patch:
diff --git a/contrib/android/tests/python/pants_test/contrib/android/tasks/test_dx_compile.py b/contrib/android/tests/python/pants_test/contrib/android/tasks/test_dx_compile.py
--- a/contrib/android/tests/python/pants_test/contrib/android/tasks/test_dx_compile.py
+++ b/contrib/android/tests/python/pants_test/contrib/android/tasks/test_dx_compile.py
@@ -188,7 +188,7 @@ def test_no_matching_classes(self):
           # Initialize the classpath, but not for the relevant target.
           self._mock_products(context, other, [])
           # Then run for the empty target.
-          dx_task = self.create_task(context)
+          dx_task = self.prepare_execute(context)
 
           with self.assertRaises(DxCompile.EmptyDexError):
             dx_task.execute()
diff --git a/contrib/errorprone/tests/python/pants_test/contrib/errorprone/tasks/test_errorprone.py b/contrib/errorprone/tests/python/pants_test/contrib/errorprone/tasks/test_errorprone.py
--- a/contrib/errorprone/tests/python/pants_test/contrib/errorprone/tasks/test_errorprone.py
+++ b/contrib/errorprone/tests/python/pants_test/contrib/errorprone/tasks/test_errorprone.py
@@ -16,5 +16,5 @@ def task_type(cls):
     return ErrorProne
 
   def test_no_sources(self):
-    task = self.create_task(self.context())
+    task = self.prepare_execute(self.context())
     self.assertEqual(None, task.execute())
diff --git a/contrib/findbugs/tests/python/pants_test/contrib/findbugs/tasks/test_findbugs.py b/contrib/findbugs/tests/python/pants_test/contrib/findbugs/tasks/test_findbugs.py
--- a/contrib/findbugs/tests/python/pants_test/contrib/findbugs/tasks/test_findbugs.py
+++ b/contrib/findbugs/tests/python/pants_test/contrib/findbugs/tasks/test_findbugs.py
@@ -16,5 +16,5 @@ def task_type(cls):
     return FindBugs
 
   def test_no_sources(self):
-    task = self.create_task(self.context())
+    task = self.prepare_execute(self.context())
     self.assertEqual(None, task.execute())
diff --git a/contrib/jax_ws/tests/python/pants_test/contrib/jax_ws/tasks/test_jax_ws_gen.py b/contrib/jax_ws/tests/python/pants_test/contrib/jax_ws/tasks/test_jax_ws_gen.py
--- a/contrib/jax_ws/tests/python/pants_test/contrib/jax_ws/tasks/test_jax_ws_gen.py
+++ b/contrib/jax_ws/tests/python/pants_test/contrib/jax_ws/tasks/test_jax_ws_gen.py
@@ -16,5 +16,5 @@ def task_type(cls):
     return JaxWsGen
 
   def test_no_sources(self):
-    task = self.create_task(self.context())
+    task = self.prepare_execute(self.context())
     self.assertEqual(None, task.execute())
diff --git a/contrib/scrooge/tests/python/pants_test/contrib/scrooge/tasks/BUILD b/contrib/scrooge/tests/python/pants_test/contrib/scrooge/tasks/BUILD
--- a/contrib/scrooge/tests/python/pants_test/contrib/scrooge/tasks/BUILD
+++ b/contrib/scrooge/tests/python/pants_test/contrib/scrooge/tasks/BUILD
@@ -24,7 +24,7 @@ python_tests(
     'src/python/pants/build_graph',
     'src/python/pants/goal:context',
     'src/python/pants/util:dirutil',
-    'tests/python/pants_test/tasks:task_test_base',
+    'tests/python/pants_test/jvm:nailgun_task_test_base'
   ],
 )
 
diff --git a/contrib/scrooge/tests/python/pants_test/contrib/scrooge/tasks/test_scrooge_gen.py b/contrib/scrooge/tests/python/pants_test/contrib/scrooge/tasks/test_scrooge_gen.py
--- a/contrib/scrooge/tests/python/pants_test/contrib/scrooge/tasks/test_scrooge_gen.py
+++ b/contrib/scrooge/tests/python/pants_test/contrib/scrooge/tasks/test_scrooge_gen.py
@@ -15,32 +15,24 @@
 from pants.base.exceptions import TargetDefinitionException, TaskError
 from pants.build_graph.build_file_aliases import BuildFileAliases
 from pants.goal.context import Context
-from pants.util.dirutil import safe_rmtree
-from pants_test.tasks.task_test_base import TaskTestBase
+
+from pants_test.jvm.nailgun_task_test_base import NailgunTaskTestBase
 from twitter.common.collections import OrderedSet
 
 from pants.contrib.scrooge.tasks.scrooge_gen import ScroogeGen
 
 
-# TODO (tdesai) Issue-240: Use JvmToolTaskTestBase for ScroogeGenTest
-class ScroogeGenTest(TaskTestBase):
+class ScroogeGenTest(NailgunTaskTestBase):
   @classmethod
   def task_type(cls):
     return ScroogeGen
 
   @property
   def alias_groups(self):
-    return BuildFileAliases(targets={'java_thrift_library': JavaThriftLibrary,
-                                     'java_library': JavaLibrary,
-                                     'scala_library': ScalaLibrary})
-
-  def setUp(self):
-    super(ScroogeGenTest, self).setUp()
-    self.task_outdir = os.path.join(self.build_root, 'scrooge', 'gen-java')
-
-  def tearDown(self):
-    super(ScroogeGenTest, self).tearDown()
-    safe_rmtree(self.task_outdir)
+    return super(ScroogeGenTest, self).alias_groups.merge(
+      BuildFileAliases(targets={'java_thrift_library': JavaThriftLibrary,
+                                'java_library': JavaLibrary,
+                                'scala_library': ScalaLibrary}))
 
   def test_validate_compiler_configs(self):
     # Set synthetic defaults for the global scope.
@@ -73,7 +65,7 @@ def test_validate_compiler_configs(self):
 
     target = self.target('test_validate:one')
     context = self.context(target_roots=[target])
-    task = self.create_task(context)
+    task = self.prepare_execute(context)
     task._validate_compiler_configs([self.target('test_validate:one')])
     task._validate_compiler_configs([self.target('test_validate:two')])
 
@@ -81,11 +73,11 @@ def test_validate_compiler_configs(self):
       task._validate_compiler_configs([self.target('test_validate:three')])
 
   def test_scala(self):
-    sources = [os.path.join(self.task_outdir, 'org/pantsbuild/example/Example.scala')]
+    sources = [os.path.join(self.test_workdir, 'org/pantsbuild/example/Example.scala')]
     self._test_help('scala', 'finagle', ScalaLibrary, sources)
 
   def test_android(self):
-    sources = [os.path.join(self.task_outdir, 'org/pantsbuild/android_example/Example.java')]
+    sources = [os.path.join(self.test_workdir, 'org/pantsbuild/android_example/Example.java')]
     self._test_help('android', 'finagle', JavaLibrary, sources)
 
   def test_invalid_lang(self):
@@ -120,11 +112,9 @@ def _test_help(self, language, rpc_style, library_type, sources):
 
     target = self.target('test_smoke:a')
     context = self.context(target_roots=[target])
-    task = self.create_task(context)
+    task = self.prepare_execute(context)
 
     task._declares_service = lambda source: False
-    task._outdir = MagicMock()
-    task._outdir.return_value = self.task_outdir
 
     task.gen = MagicMock()
     task.gen.return_value = {'test_smoke/a.thrift': sources}
diff --git a/tests/python/pants_test/backend/codegen/jaxb/BUILD b/tests/python/pants_test/backend/codegen/jaxb/BUILD
--- a/tests/python/pants_test/backend/codegen/jaxb/BUILD
+++ b/tests/python/pants_test/backend/codegen/jaxb/BUILD
@@ -7,6 +7,6 @@ python_tests(
     'src/python/pants/backend/codegen/jaxb',
     'src/python/pants/build_graph',
     'src/python/pants/util:contextutil',
-    'tests/python/pants_test/tasks:task_test_base',
+    'tests/python/pants_test/jvm:nailgun_task_test_base'
   ],
 )
diff --git a/tests/python/pants_test/backend/codegen/jaxb/test_jaxb_gen.py b/tests/python/pants_test/backend/codegen/jaxb/test_jaxb_gen.py
--- a/tests/python/pants_test/backend/codegen/jaxb/test_jaxb_gen.py
+++ b/tests/python/pants_test/backend/codegen/jaxb/test_jaxb_gen.py
@@ -11,10 +11,11 @@
 from pants.backend.codegen.jaxb.jaxb_library import JaxbLibrary
 from pants.backend.codegen.jaxb.register import build_file_aliases as register_codegen
 from pants.build_graph.register import build_file_aliases as register_core
-from pants_test.tasks.task_test_base import TaskTestBase
 
+from pants_test.jvm.nailgun_task_test_base import NailgunTaskTestBase
 
-class JaxbGenJavaTest(TaskTestBase):
+
+class JaxbGenJavaTest(NailgunTaskTestBase):
   @classmethod
   def task_type(cls):
     return JaxbGen
@@ -79,12 +80,10 @@ def guess(path):
                      'Failed with no prefix: {0}'.format(guess_history[-1]))
 
   def test_simple(self):
-    self.set_options(use_nailgun=False)
     self.create_file('foo/vegetable.xml', self.create_schema('Vegetable'))
     jaxblib = self.make_target('foo:jaxblib', JaxbLibrary, sources=['vegetable.xml'])
     context = self.context(target_roots=[jaxblib])
-    task = self.create_task(context)
-    task.execute()
+    task = self.execute(context)
     files = []
     for (dirpath, dirnames, filenames) in os.walk(task.workdir):
       for filename in filenames:
diff --git a/tests/python/pants_test/backend/jvm/tasks/test_ivy_imports.py b/tests/python/pants_test/backend/jvm/tasks/test_ivy_imports.py
--- a/tests/python/pants_test/backend/jvm/tasks/test_ivy_imports.py
+++ b/tests/python/pants_test/backend/jvm/tasks/test_ivy_imports.py
@@ -54,8 +54,7 @@ def test_products(self):
                                     include_patterns=['a/b/c/*.proto'])
 
       self.set_options(use_nailgun=False)
-      ivy_imports_task = self.create_task(self.context(target_roots=[foo_target]))
-      ivy_imports_task.execute()
+      ivy_imports_task = self.execute(self.context(target_roots=[foo_target]))
 
       # Make sure the product is properly populated
       jar_import_products = ivy_imports_task.context.products.get_data(JarImportProducts)
diff --git a/tests/python/pants_test/backend/jvm/tasks/test_ivy_resolve.py b/tests/python/pants_test/backend/jvm/tasks/test_ivy_resolve.py
--- a/tests/python/pants_test/backend/jvm/tasks/test_ivy_resolve.py
+++ b/tests/python/pants_test/backend/jvm/tasks/test_ivy_resolve.py
@@ -51,7 +51,7 @@ def setUp(self):
   def resolve(self, targets):
     """Given some targets, execute a resolve, and return the resulting compile_classpath."""
     context = self.context(target_roots=targets)
-    self.create_task(context).execute()
+    self.execute(context)
     return context.products.get_data('compile_classpath')
 
   #
@@ -220,7 +220,8 @@ def test_ivy_classpath(self):
 
     junit_jar_lib = self._make_junit_target()
 
-    classpath = self.create_task(self.context()).ivy_classpath([junit_jar_lib])
+    task = self.prepare_execute(self.context())
+    classpath = task.ivy_classpath([junit_jar_lib])
 
     self.assertEquals(2, len(classpath))
 
@@ -229,11 +230,11 @@ def test_second_resolve_reuses_existing_resolution_files(self):
     with self._temp_workdir():
       # Initial resolve does a resolve and populates elements.
       initial_context = self.context(target_roots=[junit_jar_lib])
-      self.create_task(initial_context).execute()
+      self.execute(initial_context)
 
       # Second resolve should check files and do no ivy call.
       load_context = self.context(target_roots=[junit_jar_lib])
-      task = self.create_task(load_context)
+      task = self.prepare_execute(load_context)
 
       def fail(*arg, **kwargs):
         self.fail("Unexpected call to ivy.")
@@ -282,11 +283,11 @@ def test_loading_valid_resolve(self):
 
     # Initial resolve does a resolve and populates elements.
     context1 = self.context(target_roots=[junit_jar_lib])
-    self.create_task(context1).execute()
+    self.execute(context1)
 
     # Second resolve should check files and do no ivy call.
     context2 = self.context(target_roots=[junit_jar_lib])
-    task = self.create_task(context2)
+    task = self.prepare_execute(context2)
     task._do_resolve = self.fail
     task.execute()
 
diff --git a/tests/python/pants_test/base/context_utils.py b/tests/python/pants_test/base/context_utils.py
--- a/tests/python/pants_test/base/context_utils.py
+++ b/tests/python/pants_test/base/context_utils.py
@@ -103,6 +103,25 @@ def create_context(options=None, passthru_args=None, target_roots=None, build_gr
   Other params are as for ``Context``.
   """
   options = create_options(options or {}, passthru_args=passthru_args)
+  return create_context_from_options(options,
+                                     passthru_args=passthru_args,
+                                     target_roots=target_roots,
+                                     build_graph=build_graph,
+                                     build_file_parser=build_file_parser,
+                                     address_mapper=address_mapper,
+                                     console_outstream=console_outstream,
+                                     workspace=workspace)
+
+
+def create_context_from_options(options, target_roots=None, build_graph=None,
+                                build_file_parser=None, address_mapper=None, console_outstream=None,
+                                workspace=None):
+  """Creates a ``Context`` with the given options and no targets by default.
+
+  :param options: An :class:`pants.option.options.Option`-alike object that supports read methods.
+
+  Other params are as for ``Context``.
+  """
   run_tracker = TestContext.DummyRunTracker()
   target_roots = maybe_list(target_roots, Target) if target_roots else []
   return TestContext(options=options, run_tracker=run_tracker, target_roots=target_roots,
diff --git a/tests/python/pants_test/base_test.py b/tests/python/pants_test/base_test.py
--- a/tests/python/pants_test/base_test.py
+++ b/tests/python/pants_test/base_test.py
@@ -28,7 +28,7 @@
 from pants.source.source_root import SourceRootConfig
 from pants.subsystem.subsystem import Subsystem
 from pants.util.dirutil import safe_mkdir, safe_open, safe_rmtree
-from pants_test.base.context_utils import create_context
+from pants_test.base.context_utils import create_context_from_options
 from pants_test.option.util.fakes import create_options_for_optionables
 
 
@@ -310,19 +310,19 @@ def context(self, for_task_types=None, options=None, passthru_args=None, target_
 
     options = create_options_for_optionables(optionables,
                                              extra_scopes=extra_scopes,
-                                             options=options)
+                                             options=options,
+                                             passthru_args=passthru_args)
 
     Subsystem.reset(reset_options=True)
     Subsystem.set_options(options)
 
-    context = create_context(options=options,
-                             passthru_args=passthru_args,
-                             target_roots=target_roots,
-                             build_graph=self.build_graph,
-                             build_file_parser=self.build_file_parser,
-                             address_mapper=self.address_mapper,
-                             console_outstream=console_outstream,
-                             workspace=workspace)
+    context = create_context_from_options(options,
+                                          target_roots=target_roots,
+                                          build_graph=self.build_graph,
+                                          build_file_parser=self.build_file_parser,
+                                          address_mapper=self.address_mapper,
+                                          console_outstream=console_outstream,
+                                          workspace=workspace)
     return context
 
   def tearDown(self):
diff --git a/tests/python/pants_test/jvm/jvm_tool_task_test_base.py b/tests/python/pants_test/jvm/jvm_tool_task_test_base.py
--- a/tests/python/pants_test/jvm/jvm_tool_task_test_base.py
+++ b/tests/python/pants_test/jvm/jvm_tool_task_test_base.py
@@ -18,6 +18,8 @@
 from pants.ivy.bootstrapper import Bootstrapper
 from pants.java.jar.exclude import Exclude
 from pants.java.jar.jar_dependency import JarDependency
+from pants.util.dirutil import safe_mkdir
+
 from pants_test.jvm.jvm_task_test_base import JvmTaskTestBase
 
 
@@ -78,9 +80,12 @@ def setUp(self):
                                read_from=artifact_caches,
                                write_to=artifact_caches)
 
-    # Tool option defaults currently point to targets in the real BUILD.tools, so we copy it
-    # into our test workspace.
+    # Tool option defaults currently point to targets in the real BUILD.tools, so we copy it and
+    # its dependency BUILD files into our test workspace.
     shutil.copy(os.path.join(self.real_build_root, 'BUILD.tools'), self.build_root)
+    third_party = os.path.join(self.build_root, '3rdparty')
+    safe_mkdir(third_party)
+    shutil.copy(os.path.join(self.real_build_root, '3rdparty', 'BUILD'), third_party)
 
     Bootstrapper.reset_instance()
 
diff --git a/tests/python/pants_test/option/util/fakes.py b/tests/python/pants_test/option/util/fakes.py
--- a/tests/python/pants_test/option/util/fakes.py
+++ b/tests/python/pants_test/option/util/fakes.py
@@ -9,7 +9,8 @@
 
 from pants.option.arg_splitter import GLOBAL_SCOPE
 from pants.option.global_options import GlobalOptionsRegistrar
-from pants.option.optionable import Optionable
+from pants.option.option_util import is_list_option
+from pants.option.parser import Parser
 from pants.option.parser_hierarchy import enclosing_scope
 from pants.option.ranked_value import RankedValue
 
@@ -45,48 +46,30 @@ def is_default(self, key):
     return self.get_rank(key) in (RankedValue.NONE, RankedValue.HARDCODED)
 
 
-def create_option_values(option_values):
-  """Create a fake OptionValueContainer object for testing.
-
-  :param **options: Keyword args representing option values explicitly set via the command line.
-  :returns: A fake `OptionValueContainer` encapsulating the given option values.
-  """
-  return _FakeOptionValues(option_values)
-
-
-def _options_registration_function(defaults):
+def _options_registration_function(defaults, fingerprintables):
   def register(*args, **kwargs):
+    option_name = Parser.parse_dest(*args, **kwargs)
+
     default = kwargs.get('default')
     if default is None:
       if kwargs.get('type') == bool:
         default = False
       if kwargs.get('type') == list:
         default = []
-    for flag_name in args:
-      normalized_flag_name = flag_name.lstrip('-').replace('-', '_')
-      defaults[normalized_flag_name] = RankedValue(RankedValue.HARDCODED, default)
-  return register
+    defaults[option_name] = RankedValue(RankedValue.HARDCODED, default)
 
+    fingerprint = kwargs.get('fingerprint', False)
+    if fingerprint:
+      if is_list_option(kwargs):
+        val_type = kwargs.get('member_type', str)
+      else:
+        val_type = kwargs.get('type', str)
+      fingerprintables[option_name] = val_type
 
-def create_option_values_for_optionable(optionable_type, **options):
-  """Create a fake OptionValueContainer with appropriate defaults for the given `Optionable` type."
-
-  :param type optionable_type: An :class:`pants.option.optionable.Optionable` subclass.
-  :param **options: Keyword args representing option values explicitly set via the command line.
-  :returns: A fake `OptionValueContainer`, ie: the value returned from `get_options()`.
-  """
-  if not issubclass(optionable_type, Optionable):
-    raise TypeError('The given `optionable_type` was not a subclass of `Optionable`: {}'
-                    .format(optionable_type))
-
-  option_values = {}
-  registration_function = _options_registration_function(option_values)
-  optionable_type.register_options(registration_function)
-  option_values.update(**options)
-  return create_option_values(option_values)
+  return register
 
 
-def create_options(options, passthru_args=None):
+def create_options(options, passthru_args=None, fingerprintable_options=None):
   """Create a fake Options object for testing.
 
   Note that the returned object only provides access to the provided options values. There is
@@ -94,8 +77,14 @@ def create_options(options, passthru_args=None):
   cmd-line flags vs. config vs. env vars etc. etc.
 
   :param dict options: A dict of scope -> (dict of option name -> value).
+  :param list passthru_args: A list of passthrough command line argument values.
+  :param dict fingerprintable_options: A dict of scope -> (dict of option name -> option type).
+                                       This registry should contain entries for any of the
+                                       `options` that are expected to contribute to fingerprinting.
   :returns: An fake `Options` object encapsulating the given scoped options.
   """
+  fingerprintable = fingerprintable_options or defaultdict(dict)
+
   class FakeOptions(object):
     def for_scope(self, scope):
       scoped_options = options[scope]
@@ -105,7 +94,7 @@ def for_scope(self, scope):
       if isinstance(scoped_options, _FakeOptionValues):
         return scoped_options
       else:
-        return create_option_values(scoped_options)
+        return _FakeOptionValues(scoped_options)
 
     def for_global_scope(self):
       return self.for_scope('')
@@ -121,14 +110,19 @@ def scope_to_flags(self):
       return {}
 
     def get_fingerprintable_for_scope(self, scope):
-      return []
+      option_values = self.for_scope(scope)
+      return [(option_type, option_values[option_name])
+              for option_name, option_type in fingerprintable[scope].items()]
 
     def __getitem__(self, key):
       return self.for_scope(key)
   return FakeOptions()
 
 
-def create_options_for_optionables(optionables, extra_scopes=None, options=None):
+def create_options_for_optionables(optionables,
+                                   extra_scopes=None,
+                                   options=None,
+                                   passthru_args=None):
   """Create a fake Options object for testing with appropriate defaults for the given optionables.
 
   Any scoped `options` provided will override defaults, behaving as-if set on the command line.
@@ -137,10 +131,12 @@ def create_options_for_optionables(optionables, extra_scopes=None, options=None)
   :param iterable extra_scopes: An optional series of extra known scopes in play.
   :param dict options: A dict of scope -> (dict of option name -> value) representing option values
                        explicitly set via the command line.
+  :param list passthru_args: A list of passthrough args (specified after `--` on the command line).
   :returns: A fake `Options` object with defaults populated for the given `optionables` and any
             explicitly set `options` overlayed.
   """
   all_options = defaultdict(dict)
+  fingerprintable_options = defaultdict(dict)
   bootstrap_option_values = None
 
   def complete_scopes(scopes):
@@ -159,7 +155,8 @@ def complete_scopes(scopes):
 
   def register_func(on_scope):
     scoped_options = all_options[on_scope]
-    register = _options_registration_function(scoped_options)
+    scoped_fingerprintables = fingerprintable_options[on_scope]
+    register = _options_registration_function(scoped_options, scoped_fingerprintables)
     register.bootstrap = bootstrap_option_values
     register.scope = on_scope
     return register
@@ -168,7 +165,7 @@ def register_func(on_scope):
 
   # Register bootstrap options and grab their default values for use in subsequent registration.
   GlobalOptionsRegistrar.register_bootstrap_options(register_func(GLOBAL_SCOPE))
-  bootstrap_option_values = create_option_values(all_options[GLOBAL_SCOPE].copy())
+  bootstrap_option_values = _FakeOptionValues(all_options[GLOBAL_SCOPE].copy())
 
   # Now register the full global scope options.
   GlobalOptionsRegistrar.register_options(register_func(GLOBAL_SCOPE))
@@ -204,4 +201,6 @@ def register_func(on_scope):
         if key not in opts:  # Inner scope values override the inherited ones.
           opts[key] = val
 
-  return create_options(all_options)
+  return create_options(all_options,
+                        passthru_args=passthru_args,
+                        fingerprintable_options=fingerprintable_options)