17db96d56Sopenharmony_ci"""distutils.command.build_py
27db96d56Sopenharmony_ci
37db96d56Sopenharmony_ciImplements the Distutils 'build_py' command."""
47db96d56Sopenharmony_ci
57db96d56Sopenharmony_ciimport os
67db96d56Sopenharmony_ciimport importlib.util
77db96d56Sopenharmony_ciimport sys
87db96d56Sopenharmony_ciimport glob
97db96d56Sopenharmony_ci
107db96d56Sopenharmony_cifrom distutils.core import Command
117db96d56Sopenharmony_cifrom distutils.errors import *
127db96d56Sopenharmony_cifrom distutils.util import convert_path, Mixin2to3
137db96d56Sopenharmony_cifrom distutils import log
147db96d56Sopenharmony_ci
157db96d56Sopenharmony_ciclass build_py (Command):
167db96d56Sopenharmony_ci
177db96d56Sopenharmony_ci    description = "\"build\" pure Python modules (copy to build directory)"
187db96d56Sopenharmony_ci
197db96d56Sopenharmony_ci    user_options = [
207db96d56Sopenharmony_ci        ('build-lib=', 'd', "directory to \"build\" (copy) to"),
217db96d56Sopenharmony_ci        ('compile', 'c', "compile .py to .pyc"),
227db96d56Sopenharmony_ci        ('no-compile', None, "don't compile .py files [default]"),
237db96d56Sopenharmony_ci        ('optimize=', 'O',
247db96d56Sopenharmony_ci         "also compile with optimization: -O1 for \"python -O\", "
257db96d56Sopenharmony_ci         "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
267db96d56Sopenharmony_ci        ('force', 'f', "forcibly build everything (ignore file timestamps)"),
277db96d56Sopenharmony_ci        ]
287db96d56Sopenharmony_ci
297db96d56Sopenharmony_ci    boolean_options = ['compile', 'force']
307db96d56Sopenharmony_ci    negative_opt = {'no-compile' : 'compile'}
317db96d56Sopenharmony_ci
327db96d56Sopenharmony_ci    def initialize_options(self):
337db96d56Sopenharmony_ci        self.build_lib = None
347db96d56Sopenharmony_ci        self.py_modules = None
357db96d56Sopenharmony_ci        self.package = None
367db96d56Sopenharmony_ci        self.package_data = None
377db96d56Sopenharmony_ci        self.package_dir = None
387db96d56Sopenharmony_ci        self.compile = 0
397db96d56Sopenharmony_ci        self.optimize = 0
407db96d56Sopenharmony_ci        self.force = None
417db96d56Sopenharmony_ci
427db96d56Sopenharmony_ci    def finalize_options(self):
437db96d56Sopenharmony_ci        self.set_undefined_options('build',
447db96d56Sopenharmony_ci                                   ('build_lib', 'build_lib'),
457db96d56Sopenharmony_ci                                   ('force', 'force'))
467db96d56Sopenharmony_ci
477db96d56Sopenharmony_ci        # Get the distribution options that are aliases for build_py
487db96d56Sopenharmony_ci        # options -- list of packages and list of modules.
497db96d56Sopenharmony_ci        self.packages = self.distribution.packages
507db96d56Sopenharmony_ci        self.py_modules = self.distribution.py_modules
517db96d56Sopenharmony_ci        self.package_data = self.distribution.package_data
527db96d56Sopenharmony_ci        self.package_dir = {}
537db96d56Sopenharmony_ci        if self.distribution.package_dir:
547db96d56Sopenharmony_ci            for name, path in self.distribution.package_dir.items():
557db96d56Sopenharmony_ci                self.package_dir[name] = convert_path(path)
567db96d56Sopenharmony_ci        self.data_files = self.get_data_files()
577db96d56Sopenharmony_ci
587db96d56Sopenharmony_ci        # Ick, copied straight from install_lib.py (fancy_getopt needs a
597db96d56Sopenharmony_ci        # type system!  Hell, *everything* needs a type system!!!)
607db96d56Sopenharmony_ci        if not isinstance(self.optimize, int):
617db96d56Sopenharmony_ci            try:
627db96d56Sopenharmony_ci                self.optimize = int(self.optimize)
637db96d56Sopenharmony_ci                assert 0 <= self.optimize <= 2
647db96d56Sopenharmony_ci            except (ValueError, AssertionError):
657db96d56Sopenharmony_ci                raise DistutilsOptionError("optimize must be 0, 1, or 2")
667db96d56Sopenharmony_ci
677db96d56Sopenharmony_ci    def run(self):
687db96d56Sopenharmony_ci        # XXX copy_file by default preserves atime and mtime.  IMHO this is
697db96d56Sopenharmony_ci        # the right thing to do, but perhaps it should be an option -- in
707db96d56Sopenharmony_ci        # particular, a site administrator might want installed files to
717db96d56Sopenharmony_ci        # reflect the time of installation rather than the last
727db96d56Sopenharmony_ci        # modification time before the installed release.
737db96d56Sopenharmony_ci
747db96d56Sopenharmony_ci        # XXX copy_file by default preserves mode, which appears to be the
757db96d56Sopenharmony_ci        # wrong thing to do: if a file is read-only in the working
767db96d56Sopenharmony_ci        # directory, we want it to be installed read/write so that the next
777db96d56Sopenharmony_ci        # installation of the same module distribution can overwrite it
787db96d56Sopenharmony_ci        # without problems.  (This might be a Unix-specific issue.)  Thus
797db96d56Sopenharmony_ci        # we turn off 'preserve_mode' when copying to the build directory,
807db96d56Sopenharmony_ci        # since the build directory is supposed to be exactly what the
817db96d56Sopenharmony_ci        # installation will look like (ie. we preserve mode when
827db96d56Sopenharmony_ci        # installing).
837db96d56Sopenharmony_ci
847db96d56Sopenharmony_ci        # Two options control which modules will be installed: 'packages'
857db96d56Sopenharmony_ci        # and 'py_modules'.  The former lets us work with whole packages, not
867db96d56Sopenharmony_ci        # specifying individual modules at all; the latter is for
877db96d56Sopenharmony_ci        # specifying modules one-at-a-time.
887db96d56Sopenharmony_ci
897db96d56Sopenharmony_ci        if self.py_modules:
907db96d56Sopenharmony_ci            self.build_modules()
917db96d56Sopenharmony_ci        if self.packages:
927db96d56Sopenharmony_ci            self.build_packages()
937db96d56Sopenharmony_ci            self.build_package_data()
947db96d56Sopenharmony_ci
957db96d56Sopenharmony_ci        self.byte_compile(self.get_outputs(include_bytecode=0))
967db96d56Sopenharmony_ci
977db96d56Sopenharmony_ci    def get_data_files(self):
987db96d56Sopenharmony_ci        """Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
997db96d56Sopenharmony_ci        data = []
1007db96d56Sopenharmony_ci        if not self.packages:
1017db96d56Sopenharmony_ci            return data
1027db96d56Sopenharmony_ci        for package in self.packages:
1037db96d56Sopenharmony_ci            # Locate package source directory
1047db96d56Sopenharmony_ci            src_dir = self.get_package_dir(package)
1057db96d56Sopenharmony_ci
1067db96d56Sopenharmony_ci            # Compute package build directory
1077db96d56Sopenharmony_ci            build_dir = os.path.join(*([self.build_lib] + package.split('.')))
1087db96d56Sopenharmony_ci
1097db96d56Sopenharmony_ci            # Length of path to strip from found files
1107db96d56Sopenharmony_ci            plen = 0
1117db96d56Sopenharmony_ci            if src_dir:
1127db96d56Sopenharmony_ci                plen = len(src_dir)+1
1137db96d56Sopenharmony_ci
1147db96d56Sopenharmony_ci            # Strip directory from globbed filenames
1157db96d56Sopenharmony_ci            filenames = [
1167db96d56Sopenharmony_ci                file[plen:] for file in self.find_data_files(package, src_dir)
1177db96d56Sopenharmony_ci                ]
1187db96d56Sopenharmony_ci            data.append((package, src_dir, build_dir, filenames))
1197db96d56Sopenharmony_ci        return data
1207db96d56Sopenharmony_ci
1217db96d56Sopenharmony_ci    def find_data_files(self, package, src_dir):
1227db96d56Sopenharmony_ci        """Return filenames for package's data files in 'src_dir'"""
1237db96d56Sopenharmony_ci        globs = (self.package_data.get('', [])
1247db96d56Sopenharmony_ci                 + self.package_data.get(package, []))
1257db96d56Sopenharmony_ci        files = []
1267db96d56Sopenharmony_ci        for pattern in globs:
1277db96d56Sopenharmony_ci            # Each pattern has to be converted to a platform-specific path
1287db96d56Sopenharmony_ci            filelist = glob.glob(os.path.join(glob.escape(src_dir), convert_path(pattern)))
1297db96d56Sopenharmony_ci            # Files that match more than one pattern are only added once
1307db96d56Sopenharmony_ci            files.extend([fn for fn in filelist if fn not in files
1317db96d56Sopenharmony_ci                and os.path.isfile(fn)])
1327db96d56Sopenharmony_ci        return files
1337db96d56Sopenharmony_ci
1347db96d56Sopenharmony_ci    def build_package_data(self):
1357db96d56Sopenharmony_ci        """Copy data files into build directory"""
1367db96d56Sopenharmony_ci        lastdir = None
1377db96d56Sopenharmony_ci        for package, src_dir, build_dir, filenames in self.data_files:
1387db96d56Sopenharmony_ci            for filename in filenames:
1397db96d56Sopenharmony_ci                target = os.path.join(build_dir, filename)
1407db96d56Sopenharmony_ci                self.mkpath(os.path.dirname(target))
1417db96d56Sopenharmony_ci                self.copy_file(os.path.join(src_dir, filename), target,
1427db96d56Sopenharmony_ci                               preserve_mode=False)
1437db96d56Sopenharmony_ci
1447db96d56Sopenharmony_ci    def get_package_dir(self, package):
1457db96d56Sopenharmony_ci        """Return the directory, relative to the top of the source
1467db96d56Sopenharmony_ci           distribution, where package 'package' should be found
1477db96d56Sopenharmony_ci           (at least according to the 'package_dir' option, if any)."""
1487db96d56Sopenharmony_ci        path = package.split('.')
1497db96d56Sopenharmony_ci
1507db96d56Sopenharmony_ci        if not self.package_dir:
1517db96d56Sopenharmony_ci            if path:
1527db96d56Sopenharmony_ci                return os.path.join(*path)
1537db96d56Sopenharmony_ci            else:
1547db96d56Sopenharmony_ci                return ''
1557db96d56Sopenharmony_ci        else:
1567db96d56Sopenharmony_ci            tail = []
1577db96d56Sopenharmony_ci            while path:
1587db96d56Sopenharmony_ci                try:
1597db96d56Sopenharmony_ci                    pdir = self.package_dir['.'.join(path)]
1607db96d56Sopenharmony_ci                except KeyError:
1617db96d56Sopenharmony_ci                    tail.insert(0, path[-1])
1627db96d56Sopenharmony_ci                    del path[-1]
1637db96d56Sopenharmony_ci                else:
1647db96d56Sopenharmony_ci                    tail.insert(0, pdir)
1657db96d56Sopenharmony_ci                    return os.path.join(*tail)
1667db96d56Sopenharmony_ci            else:
1677db96d56Sopenharmony_ci                # Oops, got all the way through 'path' without finding a
1687db96d56Sopenharmony_ci                # match in package_dir.  If package_dir defines a directory
1697db96d56Sopenharmony_ci                # for the root (nameless) package, then fallback on it;
1707db96d56Sopenharmony_ci                # otherwise, we might as well have not consulted
1717db96d56Sopenharmony_ci                # package_dir at all, as we just use the directory implied
1727db96d56Sopenharmony_ci                # by 'tail' (which should be the same as the original value
1737db96d56Sopenharmony_ci                # of 'path' at this point).
1747db96d56Sopenharmony_ci                pdir = self.package_dir.get('')
1757db96d56Sopenharmony_ci                if pdir is not None:
1767db96d56Sopenharmony_ci                    tail.insert(0, pdir)
1777db96d56Sopenharmony_ci
1787db96d56Sopenharmony_ci                if tail:
1797db96d56Sopenharmony_ci                    return os.path.join(*tail)
1807db96d56Sopenharmony_ci                else:
1817db96d56Sopenharmony_ci                    return ''
1827db96d56Sopenharmony_ci
1837db96d56Sopenharmony_ci    def check_package(self, package, package_dir):
1847db96d56Sopenharmony_ci        # Empty dir name means current directory, which we can probably
1857db96d56Sopenharmony_ci        # assume exists.  Also, os.path.exists and isdir don't know about
1867db96d56Sopenharmony_ci        # my "empty string means current dir" convention, so we have to
1877db96d56Sopenharmony_ci        # circumvent them.
1887db96d56Sopenharmony_ci        if package_dir != "":
1897db96d56Sopenharmony_ci            if not os.path.exists(package_dir):
1907db96d56Sopenharmony_ci                raise DistutilsFileError(
1917db96d56Sopenharmony_ci                      "package directory '%s' does not exist" % package_dir)
1927db96d56Sopenharmony_ci            if not os.path.isdir(package_dir):
1937db96d56Sopenharmony_ci                raise DistutilsFileError(
1947db96d56Sopenharmony_ci                       "supposed package directory '%s' exists, "
1957db96d56Sopenharmony_ci                       "but is not a directory" % package_dir)
1967db96d56Sopenharmony_ci
1977db96d56Sopenharmony_ci        # Require __init__.py for all but the "root package"
1987db96d56Sopenharmony_ci        if package:
1997db96d56Sopenharmony_ci            init_py = os.path.join(package_dir, "__init__.py")
2007db96d56Sopenharmony_ci            if os.path.isfile(init_py):
2017db96d56Sopenharmony_ci                return init_py
2027db96d56Sopenharmony_ci            else:
2037db96d56Sopenharmony_ci                log.warn(("package init file '%s' not found " +
2047db96d56Sopenharmony_ci                          "(or not a regular file)"), init_py)
2057db96d56Sopenharmony_ci
2067db96d56Sopenharmony_ci        # Either not in a package at all (__init__.py not expected), or
2077db96d56Sopenharmony_ci        # __init__.py doesn't exist -- so don't return the filename.
2087db96d56Sopenharmony_ci        return None
2097db96d56Sopenharmony_ci
2107db96d56Sopenharmony_ci    def check_module(self, module, module_file):
2117db96d56Sopenharmony_ci        if not os.path.isfile(module_file):
2127db96d56Sopenharmony_ci            log.warn("file %s (for module %s) not found", module_file, module)
2137db96d56Sopenharmony_ci            return False
2147db96d56Sopenharmony_ci        else:
2157db96d56Sopenharmony_ci            return True
2167db96d56Sopenharmony_ci
2177db96d56Sopenharmony_ci    def find_package_modules(self, package, package_dir):
2187db96d56Sopenharmony_ci        self.check_package(package, package_dir)
2197db96d56Sopenharmony_ci        module_files = glob.glob(os.path.join(glob.escape(package_dir), "*.py"))
2207db96d56Sopenharmony_ci        modules = []
2217db96d56Sopenharmony_ci        setup_script = os.path.abspath(self.distribution.script_name)
2227db96d56Sopenharmony_ci
2237db96d56Sopenharmony_ci        for f in module_files:
2247db96d56Sopenharmony_ci            abs_f = os.path.abspath(f)
2257db96d56Sopenharmony_ci            if abs_f != setup_script:
2267db96d56Sopenharmony_ci                module = os.path.splitext(os.path.basename(f))[0]
2277db96d56Sopenharmony_ci                modules.append((package, module, f))
2287db96d56Sopenharmony_ci            else:
2297db96d56Sopenharmony_ci                self.debug_print("excluding %s" % setup_script)
2307db96d56Sopenharmony_ci        return modules
2317db96d56Sopenharmony_ci
2327db96d56Sopenharmony_ci    def find_modules(self):
2337db96d56Sopenharmony_ci        """Finds individually-specified Python modules, ie. those listed by
2347db96d56Sopenharmony_ci        module name in 'self.py_modules'.  Returns a list of tuples (package,
2357db96d56Sopenharmony_ci        module_base, filename): 'package' is a tuple of the path through
2367db96d56Sopenharmony_ci        package-space to the module; 'module_base' is the bare (no
2377db96d56Sopenharmony_ci        packages, no dots) module name, and 'filename' is the path to the
2387db96d56Sopenharmony_ci        ".py" file (relative to the distribution root) that implements the
2397db96d56Sopenharmony_ci        module.
2407db96d56Sopenharmony_ci        """
2417db96d56Sopenharmony_ci        # Map package names to tuples of useful info about the package:
2427db96d56Sopenharmony_ci        #    (package_dir, checked)
2437db96d56Sopenharmony_ci        # package_dir - the directory where we'll find source files for
2447db96d56Sopenharmony_ci        #   this package
2457db96d56Sopenharmony_ci        # checked - true if we have checked that the package directory
2467db96d56Sopenharmony_ci        #   is valid (exists, contains __init__.py, ... ?)
2477db96d56Sopenharmony_ci        packages = {}
2487db96d56Sopenharmony_ci
2497db96d56Sopenharmony_ci        # List of (package, module, filename) tuples to return
2507db96d56Sopenharmony_ci        modules = []
2517db96d56Sopenharmony_ci
2527db96d56Sopenharmony_ci        # We treat modules-in-packages almost the same as toplevel modules,
2537db96d56Sopenharmony_ci        # just the "package" for a toplevel is empty (either an empty
2547db96d56Sopenharmony_ci        # string or empty list, depending on context).  Differences:
2557db96d56Sopenharmony_ci        #   - don't check for __init__.py in directory for empty package
2567db96d56Sopenharmony_ci        for module in self.py_modules:
2577db96d56Sopenharmony_ci            path = module.split('.')
2587db96d56Sopenharmony_ci            package = '.'.join(path[0:-1])
2597db96d56Sopenharmony_ci            module_base = path[-1]
2607db96d56Sopenharmony_ci
2617db96d56Sopenharmony_ci            try:
2627db96d56Sopenharmony_ci                (package_dir, checked) = packages[package]
2637db96d56Sopenharmony_ci            except KeyError:
2647db96d56Sopenharmony_ci                package_dir = self.get_package_dir(package)
2657db96d56Sopenharmony_ci                checked = 0
2667db96d56Sopenharmony_ci
2677db96d56Sopenharmony_ci            if not checked:
2687db96d56Sopenharmony_ci                init_py = self.check_package(package, package_dir)
2697db96d56Sopenharmony_ci                packages[package] = (package_dir, 1)
2707db96d56Sopenharmony_ci                if init_py:
2717db96d56Sopenharmony_ci                    modules.append((package, "__init__", init_py))
2727db96d56Sopenharmony_ci
2737db96d56Sopenharmony_ci            # XXX perhaps we should also check for just .pyc files
2747db96d56Sopenharmony_ci            # (so greedy closed-source bastards can distribute Python
2757db96d56Sopenharmony_ci            # modules too)
2767db96d56Sopenharmony_ci            module_file = os.path.join(package_dir, module_base + ".py")
2777db96d56Sopenharmony_ci            if not self.check_module(module, module_file):
2787db96d56Sopenharmony_ci                continue
2797db96d56Sopenharmony_ci
2807db96d56Sopenharmony_ci            modules.append((package, module_base, module_file))
2817db96d56Sopenharmony_ci
2827db96d56Sopenharmony_ci        return modules
2837db96d56Sopenharmony_ci
2847db96d56Sopenharmony_ci    def find_all_modules(self):
2857db96d56Sopenharmony_ci        """Compute the list of all modules that will be built, whether
2867db96d56Sopenharmony_ci        they are specified one-module-at-a-time ('self.py_modules') or
2877db96d56Sopenharmony_ci        by whole packages ('self.packages').  Return a list of tuples
2887db96d56Sopenharmony_ci        (package, module, module_file), just like 'find_modules()' and
2897db96d56Sopenharmony_ci        'find_package_modules()' do."""
2907db96d56Sopenharmony_ci        modules = []
2917db96d56Sopenharmony_ci        if self.py_modules:
2927db96d56Sopenharmony_ci            modules.extend(self.find_modules())
2937db96d56Sopenharmony_ci        if self.packages:
2947db96d56Sopenharmony_ci            for package in self.packages:
2957db96d56Sopenharmony_ci                package_dir = self.get_package_dir(package)
2967db96d56Sopenharmony_ci                m = self.find_package_modules(package, package_dir)
2977db96d56Sopenharmony_ci                modules.extend(m)
2987db96d56Sopenharmony_ci        return modules
2997db96d56Sopenharmony_ci
3007db96d56Sopenharmony_ci    def get_source_files(self):
3017db96d56Sopenharmony_ci        return [module[-1] for module in self.find_all_modules()]
3027db96d56Sopenharmony_ci
3037db96d56Sopenharmony_ci    def get_module_outfile(self, build_dir, package, module):
3047db96d56Sopenharmony_ci        outfile_path = [build_dir] + list(package) + [module + ".py"]
3057db96d56Sopenharmony_ci        return os.path.join(*outfile_path)
3067db96d56Sopenharmony_ci
3077db96d56Sopenharmony_ci    def get_outputs(self, include_bytecode=1):
3087db96d56Sopenharmony_ci        modules = self.find_all_modules()
3097db96d56Sopenharmony_ci        outputs = []
3107db96d56Sopenharmony_ci        for (package, module, module_file) in modules:
3117db96d56Sopenharmony_ci            package = package.split('.')
3127db96d56Sopenharmony_ci            filename = self.get_module_outfile(self.build_lib, package, module)
3137db96d56Sopenharmony_ci            outputs.append(filename)
3147db96d56Sopenharmony_ci            if include_bytecode:
3157db96d56Sopenharmony_ci                if self.compile:
3167db96d56Sopenharmony_ci                    outputs.append(importlib.util.cache_from_source(
3177db96d56Sopenharmony_ci                        filename, optimization=''))
3187db96d56Sopenharmony_ci                if self.optimize > 0:
3197db96d56Sopenharmony_ci                    outputs.append(importlib.util.cache_from_source(
3207db96d56Sopenharmony_ci                        filename, optimization=self.optimize))
3217db96d56Sopenharmony_ci
3227db96d56Sopenharmony_ci        outputs += [
3237db96d56Sopenharmony_ci            os.path.join(build_dir, filename)
3247db96d56Sopenharmony_ci            for package, src_dir, build_dir, filenames in self.data_files
3257db96d56Sopenharmony_ci            for filename in filenames
3267db96d56Sopenharmony_ci            ]
3277db96d56Sopenharmony_ci
3287db96d56Sopenharmony_ci        return outputs
3297db96d56Sopenharmony_ci
3307db96d56Sopenharmony_ci    def build_module(self, module, module_file, package):
3317db96d56Sopenharmony_ci        if isinstance(package, str):
3327db96d56Sopenharmony_ci            package = package.split('.')
3337db96d56Sopenharmony_ci        elif not isinstance(package, (list, tuple)):
3347db96d56Sopenharmony_ci            raise TypeError(
3357db96d56Sopenharmony_ci                  "'package' must be a string (dot-separated), list, or tuple")
3367db96d56Sopenharmony_ci
3377db96d56Sopenharmony_ci        # Now put the module source file into the "build" area -- this is
3387db96d56Sopenharmony_ci        # easy, we just copy it somewhere under self.build_lib (the build
3397db96d56Sopenharmony_ci        # directory for Python source).
3407db96d56Sopenharmony_ci        outfile = self.get_module_outfile(self.build_lib, package, module)
3417db96d56Sopenharmony_ci        dir = os.path.dirname(outfile)
3427db96d56Sopenharmony_ci        self.mkpath(dir)
3437db96d56Sopenharmony_ci        return self.copy_file(module_file, outfile, preserve_mode=0)
3447db96d56Sopenharmony_ci
3457db96d56Sopenharmony_ci    def build_modules(self):
3467db96d56Sopenharmony_ci        modules = self.find_modules()
3477db96d56Sopenharmony_ci        for (package, module, module_file) in modules:
3487db96d56Sopenharmony_ci            # Now "build" the module -- ie. copy the source file to
3497db96d56Sopenharmony_ci            # self.build_lib (the build directory for Python source).
3507db96d56Sopenharmony_ci            # (Actually, it gets copied to the directory for this package
3517db96d56Sopenharmony_ci            # under self.build_lib.)
3527db96d56Sopenharmony_ci            self.build_module(module, module_file, package)
3537db96d56Sopenharmony_ci
3547db96d56Sopenharmony_ci    def build_packages(self):
3557db96d56Sopenharmony_ci        for package in self.packages:
3567db96d56Sopenharmony_ci            # Get list of (package, module, module_file) tuples based on
3577db96d56Sopenharmony_ci            # scanning the package directory.  'package' is only included
3587db96d56Sopenharmony_ci            # in the tuple so that 'find_modules()' and
3597db96d56Sopenharmony_ci            # 'find_package_tuples()' have a consistent interface; it's
3607db96d56Sopenharmony_ci            # ignored here (apart from a sanity check).  Also, 'module' is
3617db96d56Sopenharmony_ci            # the *unqualified* module name (ie. no dots, no package -- we
3627db96d56Sopenharmony_ci            # already know its package!), and 'module_file' is the path to
3637db96d56Sopenharmony_ci            # the .py file, relative to the current directory
3647db96d56Sopenharmony_ci            # (ie. including 'package_dir').
3657db96d56Sopenharmony_ci            package_dir = self.get_package_dir(package)
3667db96d56Sopenharmony_ci            modules = self.find_package_modules(package, package_dir)
3677db96d56Sopenharmony_ci
3687db96d56Sopenharmony_ci            # Now loop over the modules we found, "building" each one (just
3697db96d56Sopenharmony_ci            # copy it to self.build_lib).
3707db96d56Sopenharmony_ci            for (package_, module, module_file) in modules:
3717db96d56Sopenharmony_ci                assert package == package_
3727db96d56Sopenharmony_ci                self.build_module(module, module_file, package)
3737db96d56Sopenharmony_ci
3747db96d56Sopenharmony_ci    def byte_compile(self, files):
3757db96d56Sopenharmony_ci        if sys.dont_write_bytecode:
3767db96d56Sopenharmony_ci            self.warn('byte-compiling is disabled, skipping.')
3777db96d56Sopenharmony_ci            return
3787db96d56Sopenharmony_ci
3797db96d56Sopenharmony_ci        from distutils.util import byte_compile
3807db96d56Sopenharmony_ci        prefix = self.build_lib
3817db96d56Sopenharmony_ci        if prefix[-1] != os.sep:
3827db96d56Sopenharmony_ci            prefix = prefix + os.sep
3837db96d56Sopenharmony_ci
3847db96d56Sopenharmony_ci        # XXX this code is essentially the same as the 'byte_compile()
3857db96d56Sopenharmony_ci        # method of the "install_lib" command, except for the determination
3867db96d56Sopenharmony_ci        # of the 'prefix' string.  Hmmm.
3877db96d56Sopenharmony_ci        if self.compile:
3887db96d56Sopenharmony_ci            byte_compile(files, optimize=0,
3897db96d56Sopenharmony_ci                         force=self.force, prefix=prefix, dry_run=self.dry_run)
3907db96d56Sopenharmony_ci        if self.optimize > 0:
3917db96d56Sopenharmony_ci            byte_compile(files, optimize=self.optimize,
3927db96d56Sopenharmony_ci                         force=self.force, prefix=prefix, dry_run=self.dry_run)
3937db96d56Sopenharmony_ci
3947db96d56Sopenharmony_ciclass build_py_2to3(build_py, Mixin2to3):
3957db96d56Sopenharmony_ci    def run(self):
3967db96d56Sopenharmony_ci        self.updated_files = []
3977db96d56Sopenharmony_ci
3987db96d56Sopenharmony_ci        # Base class code
3997db96d56Sopenharmony_ci        if self.py_modules:
4007db96d56Sopenharmony_ci            self.build_modules()
4017db96d56Sopenharmony_ci        if self.packages:
4027db96d56Sopenharmony_ci            self.build_packages()
4037db96d56Sopenharmony_ci            self.build_package_data()
4047db96d56Sopenharmony_ci
4057db96d56Sopenharmony_ci        # 2to3
4067db96d56Sopenharmony_ci        self.run_2to3(self.updated_files)
4077db96d56Sopenharmony_ci
4087db96d56Sopenharmony_ci        # Remaining base class code
4097db96d56Sopenharmony_ci        self.byte_compile(self.get_outputs(include_bytecode=0))
4107db96d56Sopenharmony_ci
4117db96d56Sopenharmony_ci    def build_module(self, module, module_file, package):
4127db96d56Sopenharmony_ci        res = build_py.build_module(self, module, module_file, package)
4137db96d56Sopenharmony_ci        if res[1]:
4147db96d56Sopenharmony_ci            # file was copied
4157db96d56Sopenharmony_ci            self.updated_files.append(res[0])
4167db96d56Sopenharmony_ci        return res
417