2013-03-02 03:51:11 +04:00
|
|
|
# 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/.
|
|
|
|
|
2020-09-10 02:51:57 +03:00
|
|
|
from __future__ import division, print_function, unicode_literals
|
2013-03-02 03:51:11 +04:00
|
|
|
|
2020-09-10 02:51:57 +03:00
|
|
|
import math
|
2013-03-02 03:51:11 +04:00
|
|
|
import os
|
|
|
|
import platform
|
2020-11-27 19:21:07 +03:00
|
|
|
import shutil
|
2021-04-19 19:37:35 +03:00
|
|
|
import site
|
2013-03-02 03:51:11 +04:00
|
|
|
import sys
|
2020-09-10 02:51:57 +03:00
|
|
|
|
2019-05-23 06:57:17 +03:00
|
|
|
if sys.version_info[0] < 3:
|
|
|
|
import __builtin__ as builtins
|
2021-05-04 23:39:58 +03:00
|
|
|
|
|
|
|
class MetaPathFinder(object):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2019-05-23 06:57:17 +03:00
|
|
|
else:
|
2021-05-04 23:39:58 +03:00
|
|
|
from importlib.abc import MetaPathFinder
|
|
|
|
|
2015-08-05 09:43:11 +03:00
|
|
|
|
|
|
|
from types import ModuleType
|
2013-05-21 05:07:48 +04:00
|
|
|
|
|
|
|
|
|
|
|
STATE_DIR_FIRST_RUN = """
|
|
|
|
mach and the build system store shared state in a common directory on the
|
|
|
|
filesystem. The following directory will be created:
|
|
|
|
|
|
|
|
{userdir}
|
|
|
|
|
|
|
|
If you would like to use a different directory, hit CTRL+c and set the
|
|
|
|
MOZBUILD_STATE_PATH environment variable to the directory you would like to
|
|
|
|
use and re-run mach. For this change to take effect forever, you'll likely
|
|
|
|
want to export this environment variable from your shell's init scripts.
|
2016-02-24 04:29:38 +03:00
|
|
|
|
|
|
|
Press ENTER/RETURN to continue or CTRL+c to abort.
|
2013-05-21 05:07:48 +04:00
|
|
|
""".lstrip()
|
|
|
|
|
2013-03-02 03:51:11 +04:00
|
|
|
|
|
|
|
# Individual files providing mach commands.
|
|
|
|
MACH_MODULES = [
|
2013-12-19 05:36:08 +04:00
|
|
|
"build/valgrind/mach_commands.py",
|
2016-09-14 18:00:27 +03:00
|
|
|
"devtools/shared/css/generated/mach_commands.py",
|
2013-11-15 02:34:50 +04:00
|
|
|
"dom/bindings/mach_commands.py",
|
2020-02-20 21:27:50 +03:00
|
|
|
"js/src/devtools/rootAnalysis/mach_commands.py",
|
2013-03-02 03:51:11 +04:00
|
|
|
"layout/tools/reftest/mach_commands.py",
|
2019-08-16 15:58:05 +03:00
|
|
|
"mobile/android/mach_commands.py",
|
2013-03-04 22:18:48 +04:00
|
|
|
"python/mach/mach/commands/commandinfo.py",
|
2016-03-28 18:18:24 +03:00
|
|
|
"python/mach/mach/commands/settings.py",
|
2019-08-16 15:58:05 +03:00
|
|
|
"python/mach_commands.py",
|
2013-03-02 03:51:11 +04:00
|
|
|
"python/mozboot/mozboot/mach_commands.py",
|
2019-07-01 17:02:20 +03:00
|
|
|
"python/mozbuild/mozbuild/artifact_commands.py",
|
2019-08-14 18:53:36 +03:00
|
|
|
"python/mozbuild/mozbuild/backend/mach_commands.py",
|
2019-08-16 15:58:05 +03:00
|
|
|
"python/mozbuild/mozbuild/build_commands.py",
|
2020-08-06 09:51:16 +03:00
|
|
|
"python/mozbuild/mozbuild/code_analysis/mach_commands.py",
|
2015-04-24 22:12:50 +03:00
|
|
|
"python/mozbuild/mozbuild/compilation/codecomplete.py",
|
2013-03-02 03:51:11 +04:00
|
|
|
"python/mozbuild/mozbuild/frontend/mach_commands.py",
|
2020-06-10 17:38:57 +03:00
|
|
|
"python/mozbuild/mozbuild/vendor/mach_commands.py",
|
2019-08-16 15:58:05 +03:00
|
|
|
"python/mozbuild/mozbuild/mach_commands.py",
|
2020-04-02 16:04:41 +03:00
|
|
|
"python/mozperftest/mozperftest/mach_commands.py",
|
2018-10-30 21:21:40 +03:00
|
|
|
"python/mozrelease/mozrelease/mach_commands.py",
|
2019-08-16 15:58:06 +03:00
|
|
|
"remote/mach_commands.py",
|
2021-04-15 01:24:11 +03:00
|
|
|
"security/manager/tools/mach_commands.py",
|
2016-05-17 01:53:22 +03:00
|
|
|
"taskcluster/mach_commands.py",
|
2017-03-29 11:47:46 +03:00
|
|
|
"testing/awsy/mach_commands.py",
|
2020-03-05 13:27:05 +03:00
|
|
|
"testing/condprofile/mach_commands.py",
|
2016-01-15 18:35:51 +03:00
|
|
|
"testing/firefox-ui/mach_commands.py",
|
2017-06-15 14:47:20 +03:00
|
|
|
"testing/geckodriver/mach_commands.py",
|
2013-09-09 23:37:38 +04:00
|
|
|
"testing/mach_commands.py",
|
2013-04-19 16:19:54 +04:00
|
|
|
"testing/marionette/mach_commands.py",
|
2013-03-02 03:51:11 +04:00
|
|
|
"testing/mochitest/mach_commands.py",
|
2015-12-07 15:28:40 +03:00
|
|
|
"testing/mozharness/mach_commands.py",
|
2018-04-23 23:43:30 +03:00
|
|
|
"testing/raptor/mach_commands.py",
|
2019-08-14 18:53:36 +03:00
|
|
|
"testing/talos/mach_commands.py",
|
2019-08-16 15:58:05 +03:00
|
|
|
"testing/tps/mach_commands.py",
|
2014-09-04 15:52:43 +04:00
|
|
|
"testing/web-platform/mach_commands.py",
|
2013-07-30 03:58:40 +04:00
|
|
|
"testing/xpcshell/mach_commands.py",
|
2019-02-20 22:18:06 +03:00
|
|
|
"toolkit/components/telemetry/tests/marionette/mach_commands.py",
|
Bug 1543247 - Part 1: Add `mach browsertime` command that installs and invokes browsertime. r=Standard8,ahal
[browsertime](https://github.com/sitespeedio/browsertime) is a harness
for running performance tests, similar to Mozilla's Raptor testing
framework. The Performance Team is using it locally with some
success, but we're running a heavily modified toolchain that is
challenging to install. This mach command is intended to be leverage
for getting more folks able to use browsertime easily.
In particular, the version of browsertime that this installs has
nalexander's changes to support testing GeckoView-based vehicles. If
this approach meets with approval, I'll continue to follow-up with
additional configuration and tooling layers to make it even easier to
drive GeckoView-based vehicles.
I elected to piggy-back install on the eslint installation process,
since this is very similar. To that end, I generalized what was there
very slightly. I elected not to try to move the existing code into a
more obvious shared location, although it might be possible, because
it wasn't clear what contexts the existing code would be invoked
from. In particular I wasn't certain the code could rely on a
complete mozbuild checkout.
I did need to ensure the local Node.js binary is early on the PATH;
this was an issue I ran into with my initial Node/Yarn prototyping
many months ago. At heart the issue is that package scripts in the
wild invoke a bare `node` or `npm` command; if there was a culture of
invoking $NODE or $NPM, this wouldn't be necessary. There's no harm
doing it for ESlint, and it will help the next person who wants to
install an NPM package for tooling in this manner.
Differential Revision: https://phabricator.services.mozilla.com/D26820
--HG--
extra : moz-landing-system : lando
2019-05-07 02:56:49 +03:00
|
|
|
"tools/browsertime/mach_commands.py",
|
2017-06-06 17:00:51 +03:00
|
|
|
"tools/compare-locales/mach_commands.py",
|
2016-03-18 20:54:07 +03:00
|
|
|
"tools/lint/mach_commands.py",
|
2013-03-06 21:58:40 +04:00
|
|
|
"tools/mach_commands.py",
|
2020-01-06 19:29:21 +03:00
|
|
|
"tools/moztreedocs/mach_commands.py",
|
2020-02-19 08:28:32 +03:00
|
|
|
"tools/phabricator/mach_commands.py",
|
2015-09-15 09:23:45 +03:00
|
|
|
"tools/power/mach_commands.py",
|
2017-07-18 04:36:23 +03:00
|
|
|
"tools/tryselect/mach_commands.py",
|
2019-07-19 17:18:56 +03:00
|
|
|
"tools/vcs/mach_commands.py",
|
2013-03-02 03:51:11 +04:00
|
|
|
]
|
|
|
|
|
2013-05-09 04:56:30 +04:00
|
|
|
|
|
|
|
CATEGORIES = {
|
|
|
|
"build": {
|
|
|
|
"short": "Build Commands",
|
|
|
|
"long": "Interact with the build system",
|
|
|
|
"priority": 80,
|
|
|
|
},
|
|
|
|
"post-build": {
|
|
|
|
"short": "Post-build Commands",
|
|
|
|
"long": "Common actions performed after completing a build.",
|
|
|
|
"priority": 70,
|
|
|
|
},
|
|
|
|
"testing": {
|
|
|
|
"short": "Testing",
|
|
|
|
"long": "Run tests.",
|
|
|
|
"priority": 60,
|
|
|
|
},
|
2014-11-26 21:11:28 +03:00
|
|
|
"ci": {
|
|
|
|
"short": "CI",
|
|
|
|
"long": "Taskcluster commands",
|
2018-10-30 21:21:40 +03:00
|
|
|
"priority": 59,
|
2014-11-26 21:11:28 +03:00
|
|
|
},
|
2013-05-09 04:56:30 +04:00
|
|
|
"devenv": {
|
|
|
|
"short": "Development Environment",
|
|
|
|
"long": "Set up and configure your development environment.",
|
|
|
|
"priority": 50,
|
|
|
|
},
|
|
|
|
"build-dev": {
|
|
|
|
"short": "Low-level Build System Interaction",
|
|
|
|
"long": "Interact with specific parts of the build system.",
|
|
|
|
"priority": 20,
|
|
|
|
},
|
|
|
|
"misc": {
|
|
|
|
"short": "Potpourri",
|
|
|
|
"long": "Potent potables and assorted snacks.",
|
|
|
|
"priority": 10,
|
2013-10-11 21:41:25 +04:00
|
|
|
},
|
2018-10-30 21:21:40 +03:00
|
|
|
"release": {
|
|
|
|
"short": "Release automation",
|
|
|
|
"long": "Commands for used in release automation.",
|
|
|
|
"priority": 5,
|
|
|
|
},
|
2013-10-11 21:41:25 +04:00
|
|
|
"disabled": {
|
|
|
|
"short": "Disabled",
|
2017-10-07 17:45:22 +03:00
|
|
|
"long": "The disabled commands are hidden by default. Use -v to display them. "
|
|
|
|
"These commands are unavailable for your current context, "
|
|
|
|
'run "mach <command>" to see why.',
|
2013-10-11 21:41:25 +04:00
|
|
|
"priority": 0,
|
2018-10-30 21:21:40 +03:00
|
|
|
},
|
2013-05-09 04:56:30 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
Bug 1356101 - Derive the mach_bootstrap search path from build/virtualenv_packages.txt. r=gps
Most entries in virtualenv_packages.txt that are .pth or packages.txt
are currently in SEARCH_PATHS in mach_bootstrap. The ones that are
missing would make sense in SEARCH_PATHS. None of non-.pth or
packages.txt entries, however, are in SEARCH_PATHS and don't make sense
there.
On the other hand, virtualenv_packages.txt misses a lot of things that
are in SEARCH_PATHS, all of which should be there. One exception:
xpcom/idl-parser, which causes problems due to the xpidl package
containing an xpidl module, which causes problems with the in-tree
scripts using it. Plus, it needs a cache directory, which is messy, so
it's preferable to keep it away from the virtualenv. It turns out it was
added to mach_bootstrap.py in bug 893976 for a command that was since
then removed (bug 1244736), so we can get away with removing it.
So instead of keeping those two separate lists out of sync, we replace
the SEARCH_PATHS list from mach_bootstrap with one that is derived at
runtime from the contents of virtualenv_packages.txt.
And since a .pth can't fail to install in the virtualenv, it makes no
sense to have psutil.pth defined as optional, which allows it to end up
in the mach_bootstrap search path automatically.
Finally, because we do have overlapping module names in the tree (e.g.
runtests), and mach_bootstrap's SEARCH_PATHS had a guaranteed order,
we change the order of the virtualenv_packages.txt file to match what
used to be in mach_bootstrap, and make all the pth entries use the same
file name so that the order is more guaranteed in the virtualenv too.
--HG--
extra : rebase_source : 5bd09f2f984d6f78a76b38e768d8a67806af5954
2017-04-14 02:19:08 +03:00
|
|
|
def search_path(mozilla_dir, packages_txt):
|
|
|
|
with open(os.path.join(mozilla_dir, packages_txt)) as f:
|
Bug 1717051: Move global mach dependencies to requirements definition r=ahal
After removing `optional` in Bug 1712804, we need to add a variant back
here because there's fallible dependencies. However, I've tweaked the
re-introduction of the feature to require a specific repercussion
message as well. This seemed like a decent tradeoff - the developer
becomes aware that the failure is bad, it has repercussions, but it's
not a blocking issue. Additionally, since we're printing pip's output,
the developer will be able to see the underlying error causing the
warning.
I also added comment functionality to requirements definitions to allow
adjacent documentation of why some requirements are fallible. (Related:
I'm looking forward to `mach_bootstrap` not needing to parse
requirements definitions. Almost there!)
Note that we'll temporarily lose the "pinned" nature of the
three moved dependencies until dependency locking is implemented
for Mach requirements definitions. Also note that the pinned
`zstandard_requirements.txt` can't be removed like the other
files because it still has a dangling usage.
Finally, in preparation for review: I didn't make
`PypiOptionalSpecifier` extend `PypiSpecifier` because I figured that
the benefit of flexibility (easier to allow implementations to diverge
without needing to untangle an inheritance relationship) was larger than
the cost of needing to add properties to both specifiers.
If we wanted re-use, I'd probably have `PypiOptionalSpecifier` _contain_
a `PypiSpecifier`, but then you have to reach deeper into the object to
get data, so *shrug*.
Differential Revision: https://phabricator.services.mozilla.com/D119835
2021-08-05 18:14:20 +03:00
|
|
|
packages = [
|
|
|
|
line.strip().split(":", maxsplit=1)
|
|
|
|
for line in f
|
|
|
|
if not line.lstrip().startswith("#")
|
|
|
|
]
|
Bug 1356101 - Derive the mach_bootstrap search path from build/virtualenv_packages.txt. r=gps
Most entries in virtualenv_packages.txt that are .pth or packages.txt
are currently in SEARCH_PATHS in mach_bootstrap. The ones that are
missing would make sense in SEARCH_PATHS. None of non-.pth or
packages.txt entries, however, are in SEARCH_PATHS and don't make sense
there.
On the other hand, virtualenv_packages.txt misses a lot of things that
are in SEARCH_PATHS, all of which should be there. One exception:
xpcom/idl-parser, which causes problems due to the xpidl package
containing an xpidl module, which causes problems with the in-tree
scripts using it. Plus, it needs a cache directory, which is messy, so
it's preferable to keep it away from the virtualenv. It turns out it was
added to mach_bootstrap.py in bug 893976 for a command that was since
then removed (bug 1244736), so we can get away with removing it.
So instead of keeping those two separate lists out of sync, we replace
the SEARCH_PATHS list from mach_bootstrap with one that is derived at
runtime from the contents of virtualenv_packages.txt.
And since a .pth can't fail to install in the virtualenv, it makes no
sense to have psutil.pth defined as optional, which allows it to end up
in the mach_bootstrap search path automatically.
Finally, because we do have overlapping module names in the tree (e.g.
runtests), and mach_bootstrap's SEARCH_PATHS had a guaranteed order,
we change the order of the virtualenv_packages.txt file to match what
used to be in mach_bootstrap, and make all the pth entries use the same
file name so that the order is more guaranteed in the virtualenv too.
--HG--
extra : rebase_source : 5bd09f2f984d6f78a76b38e768d8a67806af5954
2017-04-14 02:19:08 +03:00
|
|
|
|
2021-06-17 17:58:16 +03:00
|
|
|
def handle_package(action, package):
|
|
|
|
if action == "packages.txt":
|
|
|
|
for p in search_path(mozilla_dir, package):
|
Bug 1356101 - Derive the mach_bootstrap search path from build/virtualenv_packages.txt. r=gps
Most entries in virtualenv_packages.txt that are .pth or packages.txt
are currently in SEARCH_PATHS in mach_bootstrap. The ones that are
missing would make sense in SEARCH_PATHS. None of non-.pth or
packages.txt entries, however, are in SEARCH_PATHS and don't make sense
there.
On the other hand, virtualenv_packages.txt misses a lot of things that
are in SEARCH_PATHS, all of which should be there. One exception:
xpcom/idl-parser, which causes problems due to the xpidl package
containing an xpidl module, which causes problems with the in-tree
scripts using it. Plus, it needs a cache directory, which is messy, so
it's preferable to keep it away from the virtualenv. It turns out it was
added to mach_bootstrap.py in bug 893976 for a command that was since
then removed (bug 1244736), so we can get away with removing it.
So instead of keeping those two separate lists out of sync, we replace
the SEARCH_PATHS list from mach_bootstrap with one that is derived at
runtime from the contents of virtualenv_packages.txt.
And since a .pth can't fail to install in the virtualenv, it makes no
sense to have psutil.pth defined as optional, which allows it to end up
in the mach_bootstrap search path automatically.
Finally, because we do have overlapping module names in the tree (e.g.
runtests), and mach_bootstrap's SEARCH_PATHS had a guaranteed order,
we change the order of the virtualenv_packages.txt file to match what
used to be in mach_bootstrap, and make all the pth entries use the same
file name so that the order is more guaranteed in the virtualenv too.
--HG--
extra : rebase_source : 5bd09f2f984d6f78a76b38e768d8a67806af5954
2017-04-14 02:19:08 +03:00
|
|
|
yield os.path.join(mozilla_dir, p)
|
|
|
|
|
2021-06-17 17:58:16 +03:00
|
|
|
if action == "pth":
|
2021-06-17 17:58:16 +03:00
|
|
|
yield os.path.join(mozilla_dir, package)
|
Bug 1356101 - Derive the mach_bootstrap search path from build/virtualenv_packages.txt. r=gps
Most entries in virtualenv_packages.txt that are .pth or packages.txt
are currently in SEARCH_PATHS in mach_bootstrap. The ones that are
missing would make sense in SEARCH_PATHS. None of non-.pth or
packages.txt entries, however, are in SEARCH_PATHS and don't make sense
there.
On the other hand, virtualenv_packages.txt misses a lot of things that
are in SEARCH_PATHS, all of which should be there. One exception:
xpcom/idl-parser, which causes problems due to the xpidl package
containing an xpidl module, which causes problems with the in-tree
scripts using it. Plus, it needs a cache directory, which is messy, so
it's preferable to keep it away from the virtualenv. It turns out it was
added to mach_bootstrap.py in bug 893976 for a command that was since
then removed (bug 1244736), so we can get away with removing it.
So instead of keeping those two separate lists out of sync, we replace
the SEARCH_PATHS list from mach_bootstrap with one that is derived at
runtime from the contents of virtualenv_packages.txt.
And since a .pth can't fail to install in the virtualenv, it makes no
sense to have psutil.pth defined as optional, which allows it to end up
in the mach_bootstrap search path automatically.
Finally, because we do have overlapping module names in the tree (e.g.
runtests), and mach_bootstrap's SEARCH_PATHS had a guaranteed order,
we change the order of the virtualenv_packages.txt file to match what
used to be in mach_bootstrap, and make all the pth entries use the same
file name so that the order is more guaranteed in the virtualenv too.
--HG--
extra : rebase_source : 5bd09f2f984d6f78a76b38e768d8a67806af5954
2017-04-14 02:19:08 +03:00
|
|
|
|
2021-06-17 17:58:16 +03:00
|
|
|
for current_action, current_package in packages:
|
|
|
|
for path in handle_package(current_action, current_package):
|
2017-10-22 22:40:20 +03:00
|
|
|
yield path
|
|
|
|
|
Bug 1356101 - Derive the mach_bootstrap search path from build/virtualenv_packages.txt. r=gps
Most entries in virtualenv_packages.txt that are .pth or packages.txt
are currently in SEARCH_PATHS in mach_bootstrap. The ones that are
missing would make sense in SEARCH_PATHS. None of non-.pth or
packages.txt entries, however, are in SEARCH_PATHS and don't make sense
there.
On the other hand, virtualenv_packages.txt misses a lot of things that
are in SEARCH_PATHS, all of which should be there. One exception:
xpcom/idl-parser, which causes problems due to the xpidl package
containing an xpidl module, which causes problems with the in-tree
scripts using it. Plus, it needs a cache directory, which is messy, so
it's preferable to keep it away from the virtualenv. It turns out it was
added to mach_bootstrap.py in bug 893976 for a command that was since
then removed (bug 1244736), so we can get away with removing it.
So instead of keeping those two separate lists out of sync, we replace
the SEARCH_PATHS list from mach_bootstrap with one that is derived at
runtime from the contents of virtualenv_packages.txt.
And since a .pth can't fail to install in the virtualenv, it makes no
sense to have psutil.pth defined as optional, which allows it to end up
in the mach_bootstrap search path automatically.
Finally, because we do have overlapping module names in the tree (e.g.
runtests), and mach_bootstrap's SEARCH_PATHS had a guaranteed order,
we change the order of the virtualenv_packages.txt file to match what
used to be in mach_bootstrap, and make all the pth entries use the same
file name so that the order is more guaranteed in the virtualenv too.
--HG--
extra : rebase_source : 5bd09f2f984d6f78a76b38e768d8a67806af5954
2017-04-14 02:19:08 +03:00
|
|
|
|
2020-11-06 20:30:58 +03:00
|
|
|
def mach_sys_path(mozilla_dir):
|
|
|
|
return [
|
|
|
|
os.path.join(mozilla_dir, path)
|
|
|
|
for path in search_path(mozilla_dir, "build/mach_virtualenv_packages.txt")
|
|
|
|
]
|
|
|
|
|
|
|
|
|
2021-07-17 00:47:52 +03:00
|
|
|
INSTALL_PYTHON_GUIDANCE_LINUX = """
|
|
|
|
See https://firefox-source-docs.mozilla.org/setup/linux_build.html#installingpython
|
|
|
|
for guidance on how to install Python on your system.
|
|
|
|
""".strip()
|
|
|
|
|
|
|
|
INSTALL_PYTHON_GUIDANCE_OSX = """
|
|
|
|
See https://firefox-source-docs.mozilla.org/setup/macos_build.html
|
|
|
|
for guidance on how to prepare your system to build Firefox. Perhaps
|
|
|
|
you need to update Xcode, or install Python using brew?
|
|
|
|
""".strip()
|
|
|
|
|
|
|
|
INSTALL_PYTHON_GUIDANCE_MOZILLABUILD = """
|
|
|
|
Python is provided by MozillaBuild; ensure your MozillaBuild
|
|
|
|
installation is up to date.
|
|
|
|
See https://firefox-source-docs.mozilla.org/setup/windows_build.html#install-mozillabuild
|
|
|
|
for details.
|
|
|
|
""".strip()
|
|
|
|
|
|
|
|
INSTALL_PYTHON_GUIDANCE_OTHER = """
|
|
|
|
We do not have specific instructions for your platform on how to
|
|
|
|
install Python. You may find Pyenv (https://github.com/pyenv/pyenv)
|
|
|
|
helpful, if your system package manager does not provide a way to
|
|
|
|
install a recent enough Python 3.
|
|
|
|
""".strip()
|
|
|
|
|
|
|
|
|
2021-04-06 20:03:41 +03:00
|
|
|
def bootstrap(topsrcdir):
|
2021-07-17 00:47:52 +03:00
|
|
|
# Ensure we are running Python 3.6+. We run this check as soon as
|
|
|
|
# possible to avoid a cryptic import/usage error.
|
2021-06-07 22:46:17 +03:00
|
|
|
if sys.version_info < (3, 6):
|
|
|
|
print("Python 3.6+ is required to run mach.")
|
2013-03-02 03:51:11 +04:00
|
|
|
print("You are running Python", platform.python_version())
|
2021-07-17 00:47:52 +03:00
|
|
|
if sys.platform.startswith("linux"):
|
|
|
|
print(INSTALL_PYTHON_GUIDANCE_LINUX)
|
|
|
|
elif sys.platform.startswith("darwin"):
|
|
|
|
print(INSTALL_PYTHON_GUIDANCE_OSX)
|
|
|
|
elif "MOZILLABUILD" in os.environ:
|
|
|
|
print(INSTALL_PYTHON_GUIDANCE_MOZILLABUILD)
|
|
|
|
else:
|
|
|
|
print(INSTALL_PYTHON_GUIDANCE_OTHER)
|
2013-03-02 03:51:11 +04:00
|
|
|
sys.exit(1)
|
|
|
|
|
2020-11-27 19:21:07 +03:00
|
|
|
# This directory was deleted in bug 1666345, but there may be some ignored
|
|
|
|
# files here. We can safely just delete it for the user so they don't have
|
|
|
|
# to clean the repo themselves.
|
|
|
|
deleted_dir = os.path.join(topsrcdir, "third_party", "python", "psutil")
|
|
|
|
if os.path.exists(deleted_dir):
|
2020-12-01 20:40:10 +03:00
|
|
|
shutil.rmtree(deleted_dir, ignore_errors=True)
|
2020-11-27 19:21:07 +03:00
|
|
|
|
2021-07-17 00:47:54 +03:00
|
|
|
if sys.prefix == sys.base_prefix:
|
2021-04-19 19:37:35 +03:00
|
|
|
# We are not in a virtualenv. Remove global site packages
|
|
|
|
# from sys.path.
|
2021-04-19 19:37:36 +03:00
|
|
|
site_paths = set(site.getsitepackages() + [site.getusersitepackages()])
|
2021-04-19 19:37:35 +03:00
|
|
|
sys.path = [path for path in sys.path if path not in site_paths]
|
|
|
|
|
2021-04-06 20:03:41 +03:00
|
|
|
sys.path[0:0] = mach_sys_path(topsrcdir)
|
2017-08-23 18:41:01 +03:00
|
|
|
import mach.base
|
2016-06-01 01:37:56 +03:00
|
|
|
import mach.main
|
2019-07-31 00:35:53 +03:00
|
|
|
from mach.util import setenv
|
2016-06-10 19:41:59 +03:00
|
|
|
from mozboot.util import get_state_dir
|
2013-03-02 03:51:11 +04:00
|
|
|
|
2020-05-07 18:08:43 +03:00
|
|
|
# Set a reasonable limit to the number of open files.
|
|
|
|
#
|
|
|
|
# Some linux systems set `ulimit -n` to a very high number, which works
|
|
|
|
# well for systems that run servers, but this setting causes performance
|
|
|
|
# problems when programs close file descriptors before forking, like
|
|
|
|
# Python's `subprocess.Popen(..., close_fds=True)` (close_fds=True is the
|
|
|
|
# default in Python 3), or Rust's stdlib. In some cases, Firefox does the
|
|
|
|
# same thing when spawning processes. We would prefer to lower this limit
|
|
|
|
# to avoid such performance problems; processes spawned by `mach` will
|
|
|
|
# inherit the limit set here.
|
|
|
|
#
|
|
|
|
# The Firefox build defaults the soft limit to 1024, except for builds that
|
|
|
|
# do LTO, where the soft limit is 8192. We're going to default to the
|
|
|
|
# latter, since people do occasionally do LTO builds on their local
|
|
|
|
# machines, and requiring them to discover another magical setting after
|
|
|
|
# setting up an LTO build in the first place doesn't seem good.
|
|
|
|
#
|
|
|
|
# This code mimics the code in taskcluster/scripts/run-task.
|
|
|
|
try:
|
|
|
|
import resource
|
2020-10-26 21:34:53 +03:00
|
|
|
|
2020-05-07 18:08:43 +03:00
|
|
|
# Keep the hard limit the same, though, allowing processes to change
|
|
|
|
# their soft limit if they need to (Firefox does, for instance).
|
|
|
|
(soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE)
|
|
|
|
# Permit people to override our default limit if necessary via
|
|
|
|
# MOZ_LIMIT_NOFILE, which is the same variable `run-task` uses.
|
|
|
|
limit = os.environ.get("MOZ_LIMIT_NOFILE")
|
|
|
|
if limit:
|
|
|
|
limit = int(limit)
|
|
|
|
else:
|
|
|
|
# If no explicit limit is given, use our default if it's less than
|
|
|
|
# the current soft limit. For instance, the default on macOS is
|
|
|
|
# 256, so we'd pick that rather than our default.
|
|
|
|
limit = min(soft, 8192)
|
|
|
|
# Now apply the limit, if it's different from the original one.
|
|
|
|
if limit != soft:
|
|
|
|
resource.setrlimit(resource.RLIMIT_NOFILE, (limit, hard))
|
|
|
|
except ImportError:
|
|
|
|
# The resource module is UNIX only.
|
|
|
|
pass
|
|
|
|
|
2017-08-23 18:41:01 +03:00
|
|
|
def resolve_repository():
|
|
|
|
import mozversioncontrol
|
|
|
|
|
|
|
|
try:
|
|
|
|
# This API doesn't respect the vcs binary choices from configure.
|
|
|
|
# If we ever need to use the VCS binary here, consider something
|
|
|
|
# more robust.
|
2021-04-06 20:03:41 +03:00
|
|
|
return mozversioncontrol.get_repository_object(path=topsrcdir)
|
2017-08-25 03:09:09 +03:00
|
|
|
except (mozversioncontrol.InvalidRepoPath, mozversioncontrol.MissingVCSTool):
|
2017-08-23 18:41:01 +03:00
|
|
|
return None
|
|
|
|
|
2019-10-02 01:31:23 +03:00
|
|
|
def pre_dispatch_handler(context, handler, args):
|
|
|
|
# If --disable-tests flag was enabled in the mozconfig used to compile
|
|
|
|
# the build, tests will be disabled. Instead of trying to run
|
|
|
|
# nonexistent tests then reporting a failure, this will prevent mach
|
|
|
|
# from progressing beyond this point.
|
2020-08-26 17:45:52 +03:00
|
|
|
if handler.category == "testing" and not handler.ok_if_tests_disabled:
|
2019-10-02 01:31:23 +03:00
|
|
|
from mozbuild.base import BuildEnvironmentNotFoundException
|
2020-10-26 21:34:53 +03:00
|
|
|
|
2019-10-02 01:31:23 +03:00
|
|
|
try:
|
|
|
|
from mozbuild.base import MozbuildObject
|
2020-10-26 21:34:53 +03:00
|
|
|
|
2019-10-02 01:31:23 +03:00
|
|
|
# all environments should have an instance of build object.
|
|
|
|
build = MozbuildObject.from_environment()
|
|
|
|
if build is not None and hasattr(build, "mozconfig"):
|
|
|
|
ac_options = build.mozconfig["configure_args"]
|
|
|
|
if ac_options and "--disable-tests" in ac_options:
|
|
|
|
print(
|
|
|
|
"Tests have been disabled by mozconfig with the flag "
|
|
|
|
+ '"ac_add_options --disable-tests".\n'
|
|
|
|
+ "Remove the flag, and re-compile to enable tests."
|
|
|
|
)
|
|
|
|
sys.exit(1)
|
|
|
|
except BuildEnvironmentNotFoundException:
|
|
|
|
# likely automation environment, so do nothing.
|
|
|
|
pass
|
|
|
|
|
2020-09-10 02:51:57 +03:00
|
|
|
def post_dispatch_handler(
|
|
|
|
context, handler, instance, success, start_time, end_time, depth, args
|
|
|
|
):
|
2016-02-09 18:09:17 +03:00
|
|
|
"""Perform global operations after command dispatch.
|
|
|
|
|
|
|
|
|
|
|
|
For now, we will use this to handle build system telemetry.
|
|
|
|
"""
|
|
|
|
|
2020-09-10 02:51:57 +03:00
|
|
|
# Don't finalize telemetry data if this mach command was invoked as part of
|
|
|
|
# another mach command.
|
|
|
|
if depth != 1:
|
2016-02-09 18:09:17 +03:00
|
|
|
return
|
|
|
|
|
2020-09-10 02:51:57 +03:00
|
|
|
_finalize_telemetry_glean(
|
|
|
|
context.telemetry, handler.name == "bootstrap", success
|
|
|
|
)
|
2016-02-09 18:09:17 +03:00
|
|
|
|
2020-06-12 00:35:05 +03:00
|
|
|
def populate_context(key=None):
|
2014-07-03 02:15:31 +04:00
|
|
|
if key is None:
|
|
|
|
return
|
|
|
|
if key == "state_dir":
|
2019-02-04 23:52:31 +03:00
|
|
|
state_dir = get_state_dir()
|
|
|
|
if state_dir == os.environ.get("MOZBUILD_STATE_PATH"):
|
2015-07-14 23:44:59 +03:00
|
|
|
if not os.path.exists(state_dir):
|
2014-07-03 02:15:31 +04:00
|
|
|
print(
|
|
|
|
"Creating global state directory from environment variable: %s"
|
2016-02-24 04:29:38 +03:00
|
|
|
% state_dir
|
|
|
|
)
|
2015-07-14 23:44:59 +03:00
|
|
|
os.makedirs(state_dir, mode=0o770)
|
2014-07-03 02:15:31 +04:00
|
|
|
else:
|
2015-07-14 23:44:59 +03:00
|
|
|
if not os.path.exists(state_dir):
|
2017-04-14 14:20:52 +03:00
|
|
|
if not os.environ.get("MOZ_AUTOMATION"):
|
|
|
|
print(STATE_DIR_FIRST_RUN.format(userdir=state_dir))
|
|
|
|
try:
|
|
|
|
sys.stdin.readline()
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
sys.exit(1)
|
2014-07-03 02:15:31 +04:00
|
|
|
|
2015-07-14 23:44:59 +03:00
|
|
|
print("\nCreating default state directory: %s" % state_dir)
|
2016-02-24 04:29:38 +03:00
|
|
|
os.makedirs(state_dir, mode=0o770)
|
2014-07-03 02:15:31 +04:00
|
|
|
|
|
|
|
return state_dir
|
2015-07-14 23:44:59 +03:00
|
|
|
|
2019-02-05 00:07:25 +03:00
|
|
|
if key == "local_state_dir":
|
|
|
|
return get_state_dir(srcdir=True)
|
|
|
|
|
2014-07-03 02:15:31 +04:00
|
|
|
if key == "topdir":
|
|
|
|
return topsrcdir
|
2015-07-14 23:44:59 +03:00
|
|
|
|
2019-10-02 01:31:23 +03:00
|
|
|
if key == "pre_dispatch_handler":
|
|
|
|
return pre_dispatch_handler
|
|
|
|
|
2016-02-09 18:09:17 +03:00
|
|
|
if key == "post_dispatch_handler":
|
|
|
|
return post_dispatch_handler
|
|
|
|
|
2017-08-23 18:41:01 +03:00
|
|
|
if key == "repository":
|
|
|
|
return resolve_repository()
|
|
|
|
|
2014-07-03 02:15:31 +04:00
|
|
|
raise AttributeError(key)
|
2013-07-16 06:56:15 +04:00
|
|
|
|
2018-11-10 22:04:30 +03:00
|
|
|
# Note which process is top-level so that recursive mach invocations can avoid writing
|
|
|
|
# telemetry data.
|
|
|
|
if "MACH_MAIN_PID" not in os.environ:
|
2019-07-31 00:35:53 +03:00
|
|
|
setenv("MACH_MAIN_PID", str(os.getpid()))
|
2018-11-10 22:04:30 +03:00
|
|
|
|
2017-08-23 06:29:37 +03:00
|
|
|
driver = mach.main.Mach(os.getcwd())
|
|
|
|
driver.populate_context_handler = populate_context
|
2013-07-16 06:56:15 +04:00
|
|
|
|
2017-08-23 06:29:37 +03:00
|
|
|
if not driver.settings_paths:
|
2016-03-28 18:18:24 +03:00
|
|
|
# default global machrc location
|
2019-02-04 23:52:31 +03:00
|
|
|
driver.settings_paths.append(get_state_dir())
|
2016-03-28 18:18:24 +03:00
|
|
|
# always load local repository configuration
|
2021-04-06 20:03:41 +03:00
|
|
|
driver.settings_paths.append(topsrcdir)
|
2016-03-28 18:18:24 +03:00
|
|
|
|
2013-05-09 04:56:30 +04:00
|
|
|
for category, meta in CATEGORIES.items():
|
2017-08-23 06:29:37 +03:00
|
|
|
driver.define_category(category, meta["short"], meta["long"], meta["priority"])
|
2013-05-09 04:56:30 +04:00
|
|
|
|
2021-04-19 17:21:42 +03:00
|
|
|
# Sparse checkouts may not have all mach_commands.py files. Ignore
|
|
|
|
# errors from missing files. Same for spidermonkey tarballs.
|
2017-08-23 18:41:01 +03:00
|
|
|
repo = resolve_repository()
|
2021-04-19 17:21:42 +03:00
|
|
|
missing_ok = (
|
|
|
|
repo is not None and repo.sparse_checkout_present()
|
|
|
|
) or os.path.exists(os.path.join(topsrcdir, "INSTALL"))
|
2017-08-23 18:41:01 +03:00
|
|
|
|
2013-03-02 03:51:11 +04:00
|
|
|
for path in MACH_MODULES:
|
2017-08-23 18:41:01 +03:00
|
|
|
try:
|
2021-04-06 20:03:41 +03:00
|
|
|
driver.load_commands_from_file(os.path.join(topsrcdir, path))
|
2017-08-23 18:41:01 +03:00
|
|
|
except mach.base.MissingFileError:
|
2021-04-19 17:21:42 +03:00
|
|
|
if not missing_ok:
|
2017-08-23 18:41:01 +03:00
|
|
|
raise
|
2013-05-09 04:56:30 +04:00
|
|
|
|
2017-08-23 06:29:37 +03:00
|
|
|
return driver
|
2015-08-05 09:43:11 +03:00
|
|
|
|
|
|
|
|
2020-09-10 02:51:57 +03:00
|
|
|
def _finalize_telemetry_glean(telemetry, is_bootstrap, success):
|
|
|
|
"""Submit telemetry collected by Glean.
|
|
|
|
|
|
|
|
Finalizes some metrics (command success state and duration, system information) and
|
|
|
|
requests Glean to send the collected data.
|
|
|
|
"""
|
|
|
|
|
2020-09-16 00:15:20 +03:00
|
|
|
from mach.telemetry import MACH_METRICS_PATH
|
2020-10-27 23:35:17 +03:00
|
|
|
from mozbuild.telemetry import (
|
|
|
|
get_cpu_brand,
|
|
|
|
get_distro_and_version,
|
|
|
|
get_psutil_stats,
|
2021-05-05 17:56:32 +03:00
|
|
|
get_shell_info,
|
2020-10-27 23:35:17 +03:00
|
|
|
)
|
2020-09-10 02:51:57 +03:00
|
|
|
|
2020-09-16 00:15:20 +03:00
|
|
|
mach_metrics = telemetry.metrics(MACH_METRICS_PATH)
|
|
|
|
mach_metrics.mach.duration.stop()
|
|
|
|
mach_metrics.mach.success.set(success)
|
|
|
|
system_metrics = mach_metrics.mach.system
|
2021-04-20 02:17:06 +03:00
|
|
|
cpu_brand = get_cpu_brand()
|
|
|
|
if cpu_brand:
|
|
|
|
system_metrics.cpu_brand.set(cpu_brand)
|
2020-10-27 23:35:17 +03:00
|
|
|
distro, version = get_distro_and_version()
|
|
|
|
system_metrics.distro.set(distro)
|
|
|
|
system_metrics.distro_version.set(version)
|
2020-09-10 02:51:57 +03:00
|
|
|
|
2021-05-05 17:56:32 +03:00
|
|
|
vscode_terminal, ssh_connection = get_shell_info()
|
|
|
|
system_metrics.vscode_terminal.set(vscode_terminal)
|
|
|
|
system_metrics.ssh_connection.set(ssh_connection)
|
|
|
|
|
2020-09-10 02:51:57 +03:00
|
|
|
has_psutil, logical_cores, physical_cores, memory_total = get_psutil_stats()
|
|
|
|
if has_psutil:
|
2020-11-27 19:21:07 +03:00
|
|
|
# psutil may not be available (we allow `mach create-mach-environment`
|
|
|
|
# to fail to install it).
|
2020-09-10 02:51:57 +03:00
|
|
|
system_metrics.logical_cores.add(logical_cores)
|
|
|
|
system_metrics.physical_cores.add(physical_cores)
|
|
|
|
if memory_total is not None:
|
|
|
|
system_metrics.memory.accumulate(
|
|
|
|
int(math.ceil(float(memory_total) / (1024 * 1024 * 1024)))
|
2020-10-26 21:34:53 +03:00
|
|
|
)
|
2020-09-10 02:51:57 +03:00
|
|
|
telemetry.submit(is_bootstrap)
|
|
|
|
|
|
|
|
|
2015-08-05 09:43:11 +03:00
|
|
|
# Hook import such that .pyc/.pyo files without a corresponding .py file in
|
|
|
|
# the source directory are essentially ignored. See further below for details
|
|
|
|
# and caveats.
|
|
|
|
# Objdirs outside the source directory are ignored because in most cases, if
|
|
|
|
# a .pyc/.pyo file exists there, a .py file will be next to it anyways.
|
|
|
|
class ImportHook(object):
|
|
|
|
def __init__(self, original_import):
|
|
|
|
self._original_import = original_import
|
|
|
|
# Assume the source directory is the parent directory of the one
|
|
|
|
# containing this file.
|
|
|
|
self._source_dir = (
|
|
|
|
os.path.normcase(
|
|
|
|
os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
2020-10-26 21:34:53 +03:00
|
|
|
)
|
2015-08-05 09:43:11 +03:00
|
|
|
+ os.sep
|
2020-10-26 21:34:53 +03:00
|
|
|
)
|
2015-08-05 09:43:11 +03:00
|
|
|
self._modules = set()
|
|
|
|
|
|
|
|
def __call__(self, name, globals=None, locals=None, fromlist=None, level=-1):
|
2019-05-29 10:58:19 +03:00
|
|
|
if sys.version_info[0] >= 3 and level < 0:
|
|
|
|
level = 0
|
|
|
|
|
2015-08-05 09:43:11 +03:00
|
|
|
# name might be a relative import. Instead of figuring out what that
|
|
|
|
# resolves to, which is complex, just rely on the real import.
|
|
|
|
# Since we don't know the full module name, we can't check sys.modules,
|
|
|
|
# so we need to keep track of which modules we've already seen to avoid
|
|
|
|
# to stat() them again when they are imported multiple times.
|
|
|
|
module = self._original_import(name, globals, locals, fromlist, level)
|
|
|
|
|
|
|
|
# Some tests replace modules in sys.modules with non-module instances.
|
|
|
|
if not isinstance(module, ModuleType):
|
|
|
|
return module
|
|
|
|
|
|
|
|
resolved_name = module.__name__
|
|
|
|
if resolved_name in self._modules:
|
|
|
|
return module
|
|
|
|
self._modules.add(resolved_name)
|
|
|
|
|
|
|
|
# Builtin modules don't have a __file__ attribute.
|
2019-10-02 20:54:11 +03:00
|
|
|
if not getattr(module, "__file__", None):
|
2015-08-05 09:43:11 +03:00
|
|
|
return module
|
|
|
|
|
|
|
|
# Note: module.__file__ is not always absolute.
|
|
|
|
path = os.path.normcase(os.path.abspath(module.__file__))
|
|
|
|
# Note: we could avoid normcase and abspath above for non pyc/pyo
|
|
|
|
# files, but those are actually rare, so it doesn't really matter.
|
|
|
|
if not path.endswith((".pyc", ".pyo")):
|
|
|
|
return module
|
|
|
|
|
|
|
|
# Ignore modules outside our source directory
|
|
|
|
if not path.startswith(self._source_dir):
|
|
|
|
return module
|
|
|
|
|
|
|
|
# If there is no .py corresponding to the .pyc/.pyo module we're
|
|
|
|
# loading, remove the .pyc/.pyo file, and reload the module.
|
|
|
|
# Since we already loaded the .pyc/.pyo module, if it had side
|
|
|
|
# effects, they will have happened already, and loading the module
|
|
|
|
# with the same name, from another directory may have the same side
|
|
|
|
# effects (or different ones). We assume it's not a problem for the
|
|
|
|
# python modules under our source directory (either because it
|
|
|
|
# doesn't happen or because it doesn't matter).
|
|
|
|
if not os.path.exists(module.__file__[:-1]):
|
2017-02-21 19:08:44 +03:00
|
|
|
if os.path.exists(module.__file__):
|
|
|
|
os.remove(module.__file__)
|
2015-08-05 09:43:11 +03:00
|
|
|
del sys.modules[module.__name__]
|
|
|
|
module = self(name, globals, locals, fromlist, level)
|
|
|
|
|
|
|
|
return module
|
|
|
|
|
|
|
|
|
2021-05-04 23:39:58 +03:00
|
|
|
# Hook import such that .pyc/.pyo files without a corresponding .py file in
|
|
|
|
# the source directory are essentially ignored. See further below for details
|
|
|
|
# and caveats.
|
|
|
|
# Objdirs outside the source directory are ignored because in most cases, if
|
|
|
|
# a .pyc/.pyo file exists there, a .py file will be next to it anyways.
|
|
|
|
class FinderHook(MetaPathFinder):
|
|
|
|
def __init__(self, klass):
|
|
|
|
# Assume the source directory is the parent directory of the one
|
|
|
|
# containing this file.
|
|
|
|
self._source_dir = (
|
|
|
|
os.path.normcase(
|
|
|
|
os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
|
|
|
)
|
|
|
|
+ os.sep
|
|
|
|
)
|
|
|
|
self.finder_class = klass
|
|
|
|
|
|
|
|
def find_spec(self, full_name, paths=None, target=None):
|
|
|
|
spec = self.finder_class.find_spec(full_name, paths, target)
|
|
|
|
|
|
|
|
# Some modules don't have an origin.
|
|
|
|
if spec is None or spec.origin is None:
|
|
|
|
return spec
|
|
|
|
|
|
|
|
# Normalize the origin path.
|
|
|
|
path = os.path.normcase(os.path.abspath(spec.origin))
|
|
|
|
# Note: we could avoid normcase and abspath above for non pyc/pyo
|
|
|
|
# files, but those are actually rare, so it doesn't really matter.
|
|
|
|
if not path.endswith((".pyc", ".pyo")):
|
|
|
|
return spec
|
|
|
|
|
|
|
|
# Ignore modules outside our source directory
|
|
|
|
if not path.startswith(self._source_dir):
|
|
|
|
return spec
|
|
|
|
|
|
|
|
# If there is no .py corresponding to the .pyc/.pyo module we're
|
|
|
|
# resolving, remove the .pyc/.pyo file, and try again.
|
|
|
|
if not os.path.exists(spec.origin[:-1]):
|
|
|
|
if os.path.exists(spec.origin):
|
|
|
|
os.remove(spec.origin)
|
|
|
|
spec = self.finder_class.find_spec(full_name, paths, target)
|
|
|
|
|
|
|
|
return spec
|
|
|
|
|
|
|
|
|
|
|
|
# Additional hook for python >= 3.8's importlib.metadata.
|
|
|
|
class MetadataHook(FinderHook):
|
|
|
|
def find_distributions(self, *args, **kwargs):
|
|
|
|
return self.finder_class.find_distributions(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
def hook(finder):
|
|
|
|
has_find_spec = hasattr(finder, "find_spec")
|
|
|
|
has_find_distributions = hasattr(finder, "find_distributions")
|
|
|
|
if has_find_spec and has_find_distributions:
|
|
|
|
return MetadataHook(finder)
|
|
|
|
elif has_find_spec:
|
|
|
|
return FinderHook(finder)
|
|
|
|
return finder
|
|
|
|
|
|
|
|
|
2020-10-05 20:05:05 +03:00
|
|
|
# Install our hook. This can be deleted when the Python 3 migration is complete.
|
|
|
|
if sys.version_info[0] < 3:
|
|
|
|
builtins.__import__ = ImportHook(builtins.__import__)
|
2021-05-04 23:39:58 +03:00
|
|
|
else:
|
|
|
|
sys.meta_path = [hook(c) for c in sys.meta_path]
|