Run test build: Inline variables

Remove the upper-case variables by inlining their values.

Rename the first argument from 'ctx' to idiomatic 'self'.

Test: Generated artifacts are identical
Change-Id: I6081e828db03423c088e5685812325c5ed96fb00
diff --git a/test/2034-spaces-in-SimpleName/build.py b/test/2034-spaces-in-SimpleName/build.py
index 4ccb848..c392e24 100644
--- a/test/2034-spaces-in-SimpleName/build.py
+++ b/test/2034-spaces-in-SimpleName/build.py
@@ -17,4 +17,4 @@
 # Use API level 10000 for spaces in SimpleName
 def build(ctx):
   ctx.bash("./generate-sources")
-  ctx.default_build(use_desugar=False, api_level="10000")
+  ctx.default_build(use_desugar=False, api_level=10000)
diff --git a/test/run_test_build.py b/test/run_test_build.py
index 34d569d..689b193 100755
--- a/test/run_test_build.py
+++ b/test/run_test_build.py
@@ -97,8 +97,17 @@
                           env=self.bash_env,
                           check=True)
 
+  def build(self) -> None:
+    script = self.test_dir / "build.py"
+    if script.exists():
+      module = SourceFileLoader("build_" + self.test_name,
+                                str(script)).load_module()
+      module.build(self)
+    else:
+      self.default_build()
+
   def default_build(
-      ctx,
+      self,
       use_desugar=True,
       use_hiddenapi=True,
       need_dex=None,
@@ -114,62 +123,32 @@
     ):
 
     # Wrap "pathlib.Path" with our own version that ensures all paths are absolute.
-    # Plain filenames are assumed to be relative to ctx.test_dir and made absolute.
+    # Plain filenames are assumed to be relative to self.test_dir and made absolute.
     class Path(pathlib.Path):
       def __new__(cls, filename: str):
         path = pathlib.Path(filename)
-        return path if path.is_absolute() else (ctx.test_dir / path)
+        return path if path.is_absolute() else (self.test_dir / path)
 
-    ANDROID_BUILD_TOP = ctx.android_build_top
-    TEST_NAME = ctx.test_name
-    need_dex = (ctx.host or ctx.target) if need_dex is None else need_dex
+    need_dex = (self.host or self.target) if need_dex is None else need_dex
 
-    RBE_exec_root = os.environ.get("RBE_exec_root")
-    RBE_rewrapper = ctx.android_build_top / "prebuilts/remoteexecution-client/live/rewrapper"
-
-    # Set default values for directories.
-    HAS_SRC = Path("src").exists()
-    HAS_SRC_ART = Path("src-art").exists()
-    HAS_SRC2 = Path("src2").exists()
-    HAS_SRC_MULTIDEX = Path("src-multidex").exists()
-    HAS_SMALI_MULTIDEX = Path("smali-multidex").exists()
-    HAS_JASMIN_MULTIDEX = Path("jasmin-multidex").exists()
-    HAS_SMALI_EX = Path("smali-ex").exists()
-    HAS_SRC_EX = Path("src-ex").exists()
-    HAS_SRC_EX2 = Path("src-ex2").exists()
-    HAS_SRC_AOTEX = Path("src-aotex").exists()
-    HAS_SRC_BCPEX = Path("src-bcpex").exists()
-    HAS_HIDDENAPI_SPEC = Path("hiddenapi-flags.csv").exists()
-
-    JAVAC_ARGS = shlex.split(ctx.javac_args) + javac_args
-    SMALI_ARGS = smali_args.copy()
-    D8_FLAGS = d8_flags.copy()
-
-    BUILD_MODE = ctx.mode
-
-    # Setup experimental API level mappings in a bash associative array.
-    EXPERIMENTAL_API_LEVEL = {}
-    EXPERIMENTAL_API_LEVEL["no-experiment"] = "26"
-    EXPERIMENTAL_API_LEVEL["default-methods"] = "24"
-    EXPERIMENTAL_API_LEVEL["parameter-annotations"] = "25"
-    EXPERIMENTAL_API_LEVEL["agents"] = "26"
-    EXPERIMENTAL_API_LEVEL["method-handles"] = "26"
-    EXPERIMENTAL_API_LEVEL["var-handles"] = "28"
-
-    if BUILD_MODE == "jvm":
+    if self.jvm:
       # No desugaring on jvm because it supports the latest functionality.
       use_desugar = False
-      # Do not attempt to build src-art directories on jvm,
-      # since it would fail without libcore.
-      HAS_SRC_ART = False
 
     # Set API level for smali and d8.
-    if not api_level:
-      api_level = EXPERIMENTAL_API_LEVEL[experimental]
-
-    # Add API level arguments to smali and dx
-    SMALI_ARGS.extend(["--api", str(api_level)])
-    D8_FLAGS.extend(["--min-api", str(api_level)])
+    if api_level:
+      assert isinstance(api_level, int), api_level
+      assert experimental == "no-experiment", experimental
+    else:
+      experiment2api_level = {
+        "no-experiment": 26,
+        "default-methods": 24,
+        "parameter-annotations": 25,
+        "agents": 26,
+        "method-handles": 26,
+        "var-handles": 28,
+      }
+      api_level = experiment2api_level[experimental]
 
     def run(executable: pathlib.Path, args: List[str]):
       assert isinstance(executable, pathlib.Path), executable
@@ -178,22 +157,22 @@
         cmd += ["/bin/bash"]
       cmd += [executable]
       cmd += args
-      env = ctx.bash_env
+      env = self.bash_env
       env.update({k: v for k, v in os.environ.items() if k.startswith("RBE_")})
       # Make paths relative as otherwise we could create too long command line.
       for i, arg in enumerate(cmd):
         if isinstance(arg, pathlib.Path):
           assert arg.absolute(), arg
-          cmd[i] = relpath(arg, ctx.test_dir)
+          cmd[i] = relpath(arg, self.test_dir)
         elif isinstance(arg, list):
           assert all(p.absolute() for p in arg), arg
-          cmd[i] = ":".join(relpath(p, ctx.test_dir) for p in arg)
+          cmd[i] = ":".join(relpath(p, self.test_dir) for p in arg)
         else:
           assert isinstance(arg, str), arg
       p = subprocess.run(cmd,
                          encoding=sys.stdout.encoding,
-                         cwd=ctx.test_dir,
-                         env=ctx.bash_env,
+                         cwd=self.test_dir,
+                         env=self.bash_env,
                          stderr=subprocess.STDOUT,
                          stdout=subprocess.PIPE)
       if p.returncode != 0:
@@ -203,18 +182,21 @@
 
 
     # Helper functions to execute tools.
-    soong_zip = functools.partial(run, ctx.soong_zip)
-    zipalign = functools.partial(run, ctx.zipalign)
-    javac = functools.partial(run, ctx.javac)
-    jasmin = functools.partial(run, ctx.jasmin)
-    smali = functools.partial(run, ctx.smali)
-    d8 = functools.partial(run, ctx.d8)
-    hiddenapi = functools.partial(run, ctx.hiddenapi)
+    soong_zip = functools.partial(run, self.soong_zip)
+    zipalign = functools.partial(run, self.zipalign)
+    javac = functools.partial(run, self.javac)
+    jasmin = functools.partial(run, self.jasmin)
+    smali = functools.partial(run, self.smali)
+    d8 = functools.partial(run, self.d8)
+    hiddenapi = functools.partial(run, self.hiddenapi)
 
     if "RBE_server_address" in os.environ:
-      version = match(r"Version: (\d*)\.(\d*)\.(\d*)", run(RBE_rewrapper, ["--version"]).stdout)
+      rbe_exec_root = os.environ.get("RBE_exec_root")
+      rbe_rewrapper = self.android_build_top / "prebuilts/remoteexecution-client/live/rewrapper"
+
+      version = match(r"Version: (\d*)\.(\d*)\.(\d*)", run(rbe_rewrapper, ["--version"]).stdout)
       assert version, "Could not parse RBE version"
-      assert tuple(map(int, version.groups())) >= (0, 76, 0), "Please update " + RBE_rewrapper
+      assert tuple(map(int, version.groups())) >= (0, 76, 0), "Please update " + rbe_rewrapper
 
       def rbe_wrap(args, inputs: Set[pathlib.Path]=None):
         with NamedTemporaryFile(mode="w+t") as input_list:
@@ -226,26 +208,26 @@
             elif isinstance(arg, list):
               assert all(p.absolute() for p in arg), arg
               inputs.update(arg)
-          input_list.writelines([relpath(i, RBE_exec_root)+"\n" for i in inputs])
+          input_list.writelines([relpath(i, rbe_exec_root)+"\n" for i in inputs])
           input_list.flush()
-          return run(RBE_rewrapper, [
+          return run(rbe_rewrapper, [
             "--platform=" + os.environ["RBE_platform"],
             "--input_list_paths=" + input_list.name,
           ] + args)
 
-      if USE_RBE_FOR_JAVAC > (hash(TEST_NAME) % 100):  # Use for given percentage of tests.
+      if USE_RBE_FOR_JAVAC > (hash(self.test_name) % 100):  # Use for given percentage of tests.
         def javac(args):
-          output = relpath(Path(args[args.index("-d") + 1]), RBE_exec_root)
-          return rbe_wrap(["--output_directories", output, ctx.javac] + args)
+          output = relpath(Path(args[args.index("-d") + 1]), rbe_exec_root)
+          return rbe_wrap(["--output_directories", output, self.javac] + args)
 
-      if USE_RBE_FOR_D8 > (hash(TEST_NAME) % 100):  # Use for given percentage of tests.
+      if USE_RBE_FOR_D8 > (hash(self.test_name) % 100):  # Use for given percentage of tests.
         def d8(args):
-          inputs = set([ctx.d8.parent.parent / "framework/d8.jar"])
-          output = relpath(Path(args[args.index("--output") + 1]), RBE_exec_root)
+          inputs = set([self.d8.parent.parent / "framework/d8.jar"])
+          output = relpath(Path(args[args.index("--output") + 1]), rbe_exec_root)
           return rbe_wrap([
             "--output_files" if output.endswith(".jar") else "--output_directories", output,
             "--toolchain_inputs=prebuilts/jdk/jdk11/linux-x86/bin/java",
-            ctx.d8] + args, inputs)
+            self.d8] + args, inputs)
 
     # If wrapper script exists, use it instead of the default javac.
     javac_wrapper = Path("javac_wrapper.sh")
@@ -279,7 +261,7 @@
     def make_smali(dst_dex: Path, src_dir: Path) -> Optional[Path]:
       if not use_smali or not src_dir.exists():
         return None  # No sources to compile.
-      smali(["-JXmx512m", "assemble"] + SMALI_ARGS +
+      smali(["-JXmx512m", "assemble"] + smali_args + ["--api", str(api_level)] +
             ["--output", dst_dex] + sorted(src_dir.glob("**/*.smali")))
       return dst_dex
 
@@ -290,9 +272,10 @@
       if not any(src_dir.exists() for src_dir in src_dirs):
         return None  # No sources to compile.
       dst_dir.mkdir(exist_ok=True)
-      args = JAVAC_ARGS + ["-implicit:none", "-encoding", "utf8", "-d", dst_dir]
-      if not ctx.jvm:
-        args += ["-bootclasspath", ctx.bootclasspath]
+      args = self.javac_args.split(" ") + javac_args
+      args += ["-implicit:none", "-encoding", "utf8", "-d", dst_dir]
+      if not self.jvm:
+        args += ["-bootclasspath", self.bootclasspath]
       if java_classpath:
         args += ["-classpath", java_classpath]
       for src_dir in src_dirs:
@@ -305,8 +288,8 @@
     # packaged in a jar file.
     def make_dex(src_dir: Path):
       dst_jar = Path(src_dir.name + ".jar")
-      args = D8_FLAGS + ["--output", dst_jar]
-      args += ["--lib", ctx.bootclasspath] if use_desugar else ["--no-desugaring"]
+      args = d8_flags + ["--min-api", str(api_level), "--output", dst_jar]
+      args += ["--lib", self.bootclasspath] if use_desugar else ["--no-desugaring"]
       args += sorted(src_dir.glob("**/*.class"))
       d8(args)
 
@@ -326,9 +309,9 @@
 
       # NB: We merge even if there is just single input.
       # It is useful to normalize non-deterministic smali output.
-      tmp_dir = ctx.test_dir / "dexmerge"
+      tmp_dir = self.test_dir / "dexmerge"
       tmp_dir.mkdir()
-      d8(["--min-api", api_level, "--output", tmp_dir] + srcs)
+      d8(["--min-api", str(api_level), "--output", tmp_dir] + srcs)
       assert not (tmp_dir / "classes2.dex").exists()
       for src_file in srcs:
         src_file.unlink()
@@ -337,6 +320,8 @@
 
 
     def make_hiddenapi(*dex_files: Path):
+      if not use_hiddenapi or not Path("hiddenapi-flags.csv").exists():
+        return  # Nothing to do.
       args: List[Union[str, Path]] = ["encode"]
       for dex_file in dex_files:
         args.extend(["--input-dex=" + str(dex_file), "--output-dex=" + str(dex_file)])
@@ -346,54 +331,46 @@
 
 
     if Path("classes.dex").exists():
-      zip(Path(TEST_NAME + ".jar"), Path("classes.dex"))
+      zip(Path(self.test_name + ".jar"), Path("classes.dex"))
       return
 
     if Path("classes.dm").exists():
-      zip(Path(TEST_NAME + ".jar"), Path("classes.dm"))
+      zip(Path(self.test_name + ".jar"), Path("classes.dm"))
       return
 
-
-    def has_multidex():
-      return HAS_SRC_MULTIDEX or HAS_JASMIN_MULTIDEX or HAS_SMALI_MULTIDEX
-
-
     if make_jasmin(Path("jasmin_classes"), Path("jasmin")):
       java_classpath.append(Path("jasmin_classes"))
 
     if make_jasmin(Path("jasmin_classes2"), Path("jasmin-multidex")):
       java_classpath.append(Path("jasmin_classes2"))
 
-    if HAS_SRC and (HAS_SRC_MULTIDEX or HAS_SRC_AOTEX or HAS_SRC_BCPEX or
-                    HAS_SRC_EX or HAS_SRC_ART or HAS_SRC2 or HAS_SRC_EX2):
-      # To allow circular references, compile src/, src-multidex/, src-aotex/,
-      # src-bcpex/, src-ex/ together and pass the output as class path argument.
-      # Replacement sources in src-art/, src2/ and src-ex2/ can replace symbols
-      # used by the other src-* sources we compile here but everything needed to
-      # compile the other src-* sources should be present in src/ (and jasmin*/).
-      make_java(Path("classes-tmp-all"),
-                Path("src"),
-                Path("src-multidex"),
-                Path("src-aotex"),
-                Path("src-bcpex"),
-                Path("src-ex"))
+    # To allow circular references, compile src/, src-multidex/, src-aotex/,
+    # src-bcpex/, src-ex/ together and pass the output as class path argument.
+    # Replacement sources in src-art/, src2/ and src-ex2/ can replace symbols
+    # used by the other src-* sources we compile here but everything needed to
+    # compile the other src-* sources should be present in src/ (and jasmin*/).
+    extra_srcs = ["src-multidex", "src-aotex", "src-bcpex", "src-ex"]
+    replacement_srcs = ["src2", "src-ex2"] + ([] if self.jvm else ["src-art"])
+    if (Path("src").exists() and
+        any(Path(p).exists() for p in extra_srcs + replacement_srcs)):
+      make_java(Path("classes-tmp-all"), Path("src"), *map(Path, extra_srcs))
       java_classpath.append(Path("classes-tmp-all"))
 
     if make_java(Path("classes-aotex"), Path("src-aotex")) and need_dex:
       make_dex(Path("classes-aotex"))
       # rename it so it shows up as "classes.dex" in the zip file.
       Path("classes-aotex.dex").rename(Path("classes.dex"))
-      zip(Path(TEST_NAME + "-aotex.jar"), Path("classes.dex"))
+      zip(Path(self.test_name + "-aotex.jar"), Path("classes.dex"))
 
     if make_java(Path("classes-bcpex"), Path("src-bcpex")) and need_dex:
       make_dex(Path("classes-bcpex"))
       # rename it so it shows up as "classes.dex" in the zip file.
       Path("classes-bcpex.dex").rename(Path("classes.dex"))
-      zip(Path(TEST_NAME + "-bcpex.jar"), Path("classes.dex"))
+      zip(Path(self.test_name + "-bcpex.jar"), Path("classes.dex"))
 
     make_java(Path("classes"), Path("src"))
 
-    if not ctx.jvm:
+    if not self.jvm:
       # Do not attempt to build src-art directories on jvm,
       # since it would fail without libcore.
       make_java(Path("classes"), Path("src-art"))
@@ -426,7 +403,7 @@
 
     # Compile Jasmin classes in jasmin-multidex as if they were part of
     # the classes2.jar
-    if HAS_JASMIN_MULTIDEX:
+    if Path("jasmin-multidex").exists():
       if need_dex:
         make_dex(Path("jasmin_classes2"))
         make_dexmerge(Path("classes2.dex"), Path("jasmin_classes2.dex"))
@@ -454,19 +431,18 @@
 
     if Path("classes-ex.dex").exists():
       # Apply hiddenapi on the dex files if the test has API list file(s).
-      if use_hiddenapi and HAS_HIDDENAPI_SPEC:
-        make_hiddenapi(Path("classes-ex.dex"))
+      make_hiddenapi(Path("classes-ex.dex"))
 
       # quick shuffle so that the stored name is "classes.dex"
       Path("classes.dex").rename(Path("classes-1.dex"))
       Path("classes-ex.dex").rename(Path("classes.dex"))
-      zip(Path(TEST_NAME + "-ex.jar"), Path("classes.dex"))
+      zip(Path(self.test_name + "-ex.jar"), Path("classes.dex"))
       Path("classes.dex").rename(Path("classes-ex.dex"))
       Path("classes-1.dex").rename(Path("classes.dex"))
 
     # Apply hiddenapi on the dex files if the test has API list file(s).
-    if need_dex and use_hiddenapi and HAS_HIDDENAPI_SPEC:
-      if has_multidex():
+    if need_dex:
+      if any(Path(".").glob("*-multidex")):
         make_hiddenapi(Path("classes.dex"), Path("classes2.dex"))
       else:
         make_hiddenapi(Path("classes.dex"))
@@ -474,21 +450,9 @@
     # Create a single dex jar with two dex files for multidex.
     if need_dex:
       if Path("classes2.dex").exists():
-        zip(Path(TEST_NAME + ".jar"), Path("classes.dex"), Path("classes2.dex"))
+        zip(Path(self.test_name + ".jar"), Path("classes.dex"), Path("classes2.dex"))
       else:
-        zip(Path(TEST_NAME + ".jar"), Path("classes.dex"))
-
-
-  def build_test(ctx) -> None:
-    """Run the build script for single run-test"""
-
-    script = ctx.test_dir / "build.py"
-    if script.exists():
-      module = SourceFileLoader("build_" + ctx.test_name,
-                                str(script)).load_module()
-      module.build(ctx)
-    else:
-      ctx.default_build()
+        zip(Path(self.test_name + ".jar"), Path("classes.dex"))
 
 
 # If we build just individual shard, we want to split the work among all the cores,
@@ -540,7 +504,7 @@
     with ThreadPoolExecutor(cpu_count() if use_multiprocessing(args.mode) else 1) as pool:
       jobs = {}
       for ctx in tests:
-        jobs[ctx.test_name] = pool.submit(ctx.build_test)
+        jobs[ctx.test_name] = pool.submit(ctx.build)
       for test_name, job in jobs.items():
         try:
           job.result()