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/.
|
|
|
|
|
|
|
|
from __future__ import print_function, unicode_literals
|
|
|
|
|
Bug 1182677 - Aggressively prompt to run `mach mercurial-setup`; r=smacleod
Having not configured or out-of-date tools benefits nobody. It slows
people down.
Version control tools are an integral part of working on Firefox. It is
important for version control tools to be configured optimally and to be
continuously updated so they stay optimal.
The `mach mercurial-setup` command exists to optimally configure
Mercurial for working on Firefox and other Mozilla projects.
This commit adds a pre-dispatch handler to mach that will verify
Mercurial is in a happy state. If `mach mercurial-setup` has never
executed, it will complain. If `mach mercurial-setup` hasn't been
executed in the past 31 days, it will complain.
Yes, aborting command execution and forcing people to context switch to
run `mach mercurial-setup` is annoying. First, we have carved out
several exceptions to this behavior, including detection for running in
automation, on the machines of curmudgeons, when Mercurial isn't being
used, and from non-interactive processes. Second, I argue that people
ignore optional notifications and that having persistently
poorly-configured tools is worse than a single context switch at most
every month. Therefore, the heavyhanded approach is justified.
In addition, if we did support a non-fatal notification, we would
introduce the problem of extra output from commands. If anyone was e.g.
parsing mach output, we could very likely break those systems. These
cases should be caught by the isatty() check or be running in a context
with MOZ_AUTOMATION set. But you never know.
--HG--
extra : commitid : 7f7JQpa953u
extra : rebase_source : 47b6304b6ac2c9d8136f2023a7d03df7d1f45e4f
extra : source : f06616ee7b2b54d63d20ee4795539514d1df8c7b
2015-07-15 00:20:03 +03:00
|
|
|
import errno
|
2016-01-29 21:06:36 +03:00
|
|
|
import json
|
2013-03-02 03:51:11 +04:00
|
|
|
import os
|
|
|
|
import platform
|
2016-02-25 02:11:58 +03:00
|
|
|
import subprocess
|
2013-03-02 03:51:11 +04:00
|
|
|
import sys
|
2016-01-29 21:06:36 +03:00
|
|
|
import uuid
|
2019-05-23 06:57:17 +03:00
|
|
|
if sys.version_info[0] < 3:
|
|
|
|
import __builtin__ as builtins
|
|
|
|
else:
|
|
|
|
import builtins
|
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',
|
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',
|
2019-07-01 16:57:18 +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',
|
2019-08-16 15:58:05 +03:00
|
|
|
'python/mozbuild/mozbuild/mach_commands.py',
|
2018-10-30 21:21:40 +03:00
|
|
|
'python/mozrelease/mozrelease/mach_commands.py',
|
2019-08-16 15:58:05 +03:00
|
|
|
'python/safety/mach_commands.py',
|
2019-08-16 15:58:06 +03:00
|
|
|
'remote/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',
|
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',
|
2013-11-21 00:37:22 +04:00
|
|
|
'tools/docs/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',
|
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:
|
|
|
|
packages = [line.rstrip().split(':') for line in f]
|
|
|
|
|
2017-10-22 22:40:20 +03:00
|
|
|
def handle_package(package):
|
|
|
|
if package[0] == 'optional':
|
|
|
|
try:
|
|
|
|
for path in handle_package(package[1:]):
|
|
|
|
yield path
|
2018-01-31 22:32:08 +03:00
|
|
|
except Exception:
|
2017-10-22 22:40:20 +03:00
|
|
|
pass
|
|
|
|
|
2018-10-10 22:53:47 +03:00
|
|
|
if package[0] in ('windows', '!windows'):
|
|
|
|
for_win = not package[0].startswith('!')
|
|
|
|
is_win = sys.platform == 'win32'
|
|
|
|
if is_win == for_win:
|
|
|
|
for path in handle_package(package[1:]):
|
|
|
|
yield path
|
|
|
|
|
2018-11-23 00:44:09 +03:00
|
|
|
if package[0] in ('python2', 'python3'):
|
|
|
|
for_python3 = package[0].endswith('3')
|
|
|
|
is_python3 = sys.version_info[0] > 2
|
|
|
|
if is_python3 == for_python3:
|
|
|
|
for path in handle_package(package[1:]):
|
|
|
|
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
|
|
|
if package[0] == 'packages.txt':
|
|
|
|
assert len(package) == 2
|
|
|
|
for p in search_path(mozilla_dir, package[1]):
|
|
|
|
yield os.path.join(mozilla_dir, p)
|
|
|
|
|
|
|
|
if package[0].endswith('.pth'):
|
|
|
|
assert len(package) == 2
|
|
|
|
yield os.path.join(mozilla_dir, package[1])
|
|
|
|
|
2017-10-22 22:40:20 +03:00
|
|
|
for package in packages:
|
|
|
|
for path in handle_package(package):
|
|
|
|
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
|
|
|
|
2013-03-08 01:19:27 +04:00
|
|
|
def bootstrap(topsrcdir, mozilla_dir=None):
|
|
|
|
if mozilla_dir is None:
|
|
|
|
mozilla_dir = topsrcdir
|
|
|
|
|
2019-08-27 06:08:08 +03:00
|
|
|
# Ensure we are running Python 2.7 or 3.5+. We put this check here so we
|
|
|
|
# generate a user-friendly error message rather than a cryptic stack trace
|
|
|
|
# on module import.
|
|
|
|
major, minor = sys.version_info[:2]
|
|
|
|
if (major == 2 and minor < 7) or (major == 3 and minor < 5):
|
|
|
|
print('Python 2.7 or Python 3.5+ is required to run mach.')
|
2013-03-02 03:51:11 +04:00
|
|
|
print('You are running Python', platform.python_version())
|
|
|
|
sys.exit(1)
|
|
|
|
|
2013-05-21 05:07:48 +04:00
|
|
|
# Global build system and mach state is stored in a central directory. By
|
|
|
|
# default, this is ~/.mozbuild. However, it can be defined via an
|
|
|
|
# environment variable. We detect first run (by lack of this directory
|
|
|
|
# existing) and notify the user that it will be created. The logic for
|
|
|
|
# creation is much simpler for the "advanced" environment variable use
|
|
|
|
# case. For default behavior, we educate users and give them an opportunity
|
|
|
|
# to react. We always exit after creating the directory because users don't
|
|
|
|
# like surprises.
|
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
|
|
|
sys.path[0:0] = [os.path.join(mozilla_dir, path)
|
|
|
|
for path in search_path(mozilla_dir,
|
|
|
|
'build/virtualenv_packages.txt')]
|
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
|
|
|
|
2016-12-22 03:28:28 +03:00
|
|
|
from mozbuild.util import patch_main
|
|
|
|
patch_main()
|
|
|
|
|
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.
|
|
|
|
return mozversioncontrol.get_repository_object(path=mozilla_dir)
|
2017-08-25 03:09:09 +03:00
|
|
|
except (mozversioncontrol.InvalidRepoPath,
|
|
|
|
mozversioncontrol.MissingVCSTool):
|
2017-08-23 18:41:01 +03:00
|
|
|
return None
|
|
|
|
|
2018-11-10 22:04:30 +03:00
|
|
|
def should_skip_telemetry_submission(handler):
|
2016-02-09 18:09:17 +03:00
|
|
|
# The user is performing a maintenance command.
|
2018-05-16 19:26:52 +03:00
|
|
|
if handler.name in ('bootstrap', 'doctor', 'mach-commands', 'vcs-setup',
|
|
|
|
# We call mach environment in client.mk which would cause the
|
|
|
|
# data submission to block the forward progress of make.
|
|
|
|
'environment'):
|
2016-02-09 18:09:17 +03:00
|
|
|
return True
|
|
|
|
|
2018-11-10 22:04:30 +03:00
|
|
|
# Never submit data when running in automation or when running tests.
|
|
|
|
if any(e in os.environ for e in ('MOZ_AUTOMATION', 'TASK_ID', 'MACH_TELEMETRY_NO_SUBMIT')):
|
|
|
|
return True
|
|
|
|
|
2016-02-09 18:09:17 +03:00
|
|
|
return False
|
|
|
|
|
2018-10-31 23:03:56 +03:00
|
|
|
def post_dispatch_handler(context, handler, instance, result,
|
2018-11-10 22:04:30 +03:00
|
|
|
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.
|
|
|
|
"""
|
2018-11-10 22:04:30 +03:00
|
|
|
# Don't write telemetry data if this mach command was invoked as part of another
|
|
|
|
# mach command.
|
|
|
|
if depth != 1 or os.environ.get('MACH_MAIN_PID') != str(os.getpid()):
|
2016-02-09 18:09:17 +03:00
|
|
|
return
|
|
|
|
|
2018-12-13 21:09:03 +03:00
|
|
|
# Don't write telemetry data for 'mach' when 'DISABLE_TELEMETRY' is set.
|
|
|
|
if os.environ.get('DISABLE_TELEMETRY') == '1':
|
|
|
|
return
|
|
|
|
|
2016-02-09 18:09:17 +03:00
|
|
|
# We have not opted-in to telemetry
|
2018-08-07 19:26:31 +03:00
|
|
|
if not context.settings.build.telemetry:
|
2016-02-09 18:09:17 +03:00
|
|
|
return
|
|
|
|
|
2018-10-31 23:03:56 +03:00
|
|
|
from mozbuild.telemetry import gather_telemetry
|
|
|
|
from mozbuild.base import MozbuildObject
|
2018-11-10 22:14:44 +03:00
|
|
|
import mozpack.path as mozpath
|
2018-10-31 23:03:56 +03:00
|
|
|
|
|
|
|
if not isinstance(instance, MozbuildObject):
|
|
|
|
instance = MozbuildObject.from_environment()
|
|
|
|
|
|
|
|
try:
|
|
|
|
substs = instance.substs
|
|
|
|
except Exception:
|
|
|
|
substs = {}
|
|
|
|
|
2019-03-28 01:42:48 +03:00
|
|
|
command_attrs = getattr(context, 'command_attrs', {})
|
|
|
|
|
2018-11-10 22:14:44 +03:00
|
|
|
# We gather telemetry for every operation.
|
|
|
|
paths = {
|
|
|
|
instance.topsrcdir: '$topsrcdir/',
|
|
|
|
instance.topobjdir: '$topobjdir/',
|
|
|
|
mozpath.normpath(os.path.expanduser('~')): '$HOME/',
|
|
|
|
}
|
|
|
|
# This might override one of the existing entries, that's OK.
|
|
|
|
# We don't use a sigil here because we treat all arguments as potentially relative
|
|
|
|
# paths, so we'd like to get them back as they were specified.
|
|
|
|
paths[mozpath.normpath(os.getcwd())] = ''
|
2018-11-10 22:04:06 +03:00
|
|
|
data = gather_telemetry(command=handler.name, success=(result == 0),
|
|
|
|
start_time=start_time, end_time=end_time,
|
|
|
|
mach_context=context, substs=substs,
|
2019-03-28 01:42:48 +03:00
|
|
|
command_attrs=command_attrs, paths=paths)
|
2018-11-10 22:04:06 +03:00
|
|
|
if data:
|
2019-02-04 23:52:31 +03:00
|
|
|
telemetry_dir = os.path.join(get_state_dir(), 'telemetry')
|
2018-11-10 22:04:06 +03:00
|
|
|
try:
|
|
|
|
os.mkdir(telemetry_dir)
|
|
|
|
except OSError as e:
|
|
|
|
if e.errno != errno.EEXIST:
|
|
|
|
raise
|
|
|
|
outgoing_dir = os.path.join(telemetry_dir, 'outgoing')
|
|
|
|
try:
|
|
|
|
os.mkdir(outgoing_dir)
|
|
|
|
except OSError as e:
|
|
|
|
if e.errno != errno.EEXIST:
|
|
|
|
raise
|
|
|
|
|
|
|
|
with open(os.path.join(outgoing_dir, str(uuid.uuid4()) + '.json'),
|
|
|
|
'w') as f:
|
|
|
|
json.dump(data, f, sort_keys=True)
|
2018-10-31 23:03:56 +03:00
|
|
|
|
2018-11-10 22:04:30 +03:00
|
|
|
if should_skip_telemetry_submission(handler):
|
2018-11-10 22:04:05 +03:00
|
|
|
return True
|
|
|
|
|
2019-02-04 23:52:31 +03:00
|
|
|
state_dir = get_state_dir()
|
2016-02-09 18:09:17 +03:00
|
|
|
|
Bug 1505086: use `mach python` to submit build telemetry r=ted
While attempting to improve the build telemetry submission
logic, I found a bug in the way telemetry submission
works. Essentially the submission script was failing to
import any of the required packages (specifically
`mozbuild.telemetry` in this case) as the method used to
modify path was incorrect and the script was running outside
of the virtualenv. The invocation is also sending stdout
and stderr to `/dev/null`, making this problem even less obvious.
When I fixed the path modifications, I realized that `mozbuild`
imports will require a long chain of other imports
(and transitively, more `sys.path` modifications)
such as `which`, `mach`, `mozautomation`, etc to complete.
When I tested the submission script, I did so by running
`mach python build/submit_telemetry_data.py`, which runs the
script in a virtualenv with all required packages installed.
That's likely part of the reasons I overlooked this issue in testing.
Rather than go through the process of importing every dependency
of `mozbuild`, this commit changes the invocation of the submission
script to go through `mach python`. Things seem to work as
expected with this change.
Differential Revision: https://phabricator.services.mozilla.com/D11278
--HG--
extra : moz-landing-system : lando
2018-11-12 17:57:57 +03:00
|
|
|
machpath = os.path.join(instance.topsrcdir, 'mach')
|
2016-02-25 02:11:58 +03:00
|
|
|
with open(os.devnull, 'wb') as devnull:
|
2018-11-13 16:34:17 +03:00
|
|
|
subprocess.Popen([sys.executable, machpath, 'python',
|
Bug 1505086: use `mach python` to submit build telemetry r=ted
While attempting to improve the build telemetry submission
logic, I found a bug in the way telemetry submission
works. Essentially the submission script was failing to
import any of the required packages (specifically
`mozbuild.telemetry` in this case) as the method used to
modify path was incorrect and the script was running outside
of the virtualenv. The invocation is also sending stdout
and stderr to `/dev/null`, making this problem even less obvious.
When I fixed the path modifications, I realized that `mozbuild`
imports will require a long chain of other imports
(and transitively, more `sys.path` modifications)
such as `which`, `mach`, `mozautomation`, etc to complete.
When I tested the submission script, I did so by running
`mach python build/submit_telemetry_data.py`, which runs the
script in a virtualenv with all required packages installed.
That's likely part of the reasons I overlooked this issue in testing.
Rather than go through the process of importing every dependency
of `mozbuild`, this commit changes the invocation of the submission
script to go through `mach python`. Things seem to work as
expected with this change.
Differential Revision: https://phabricator.services.mozilla.com/D11278
--HG--
extra : moz-landing-system : lando
2018-11-12 17:57:57 +03:00
|
|
|
'--no-virtualenv',
|
2016-02-25 02:11:58 +03:00
|
|
|
os.path.join(topsrcdir, 'build',
|
|
|
|
'submit_telemetry_data.py'),
|
2018-11-12 17:57:59 +03:00
|
|
|
state_dir],
|
2017-10-07 17:45:22 +03:00
|
|
|
stdout=devnull, stderr=devnull)
|
2016-02-09 18:09:17 +03:00
|
|
|
|
2014-07-03 02:15:31 +04:00
|
|
|
def populate_context(context, key=None):
|
|
|
|
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
|
|
|
|
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
|
2017-08-23 06:29:37 +03:00
|
|
|
driver.settings_paths.append(mozilla_dir)
|
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'],
|
2017-10-07 17:45:22 +03:00
|
|
|
meta['priority'])
|
2013-05-09 04:56:30 +04:00
|
|
|
|
2017-08-23 18:41:01 +03:00
|
|
|
repo = resolve_repository()
|
|
|
|
|
2013-03-02 03:51:11 +04:00
|
|
|
for path in MACH_MODULES:
|
2017-08-23 18:41:01 +03:00
|
|
|
# Sparse checkouts may not have all mach_commands.py files. Ignore
|
|
|
|
# errors from missing files.
|
|
|
|
try:
|
|
|
|
driver.load_commands_from_file(os.path.join(mozilla_dir, path))
|
|
|
|
except mach.base.MissingFileError:
|
|
|
|
if not repo or not repo.sparse_checkout_present():
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
# 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__)))) + os.sep
|
|
|
|
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.
|
|
|
|
if not hasattr(module, '__file__'):
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
# Install our hook
|
2019-05-23 06:57:17 +03:00
|
|
|
builtins.__import__ = ImportHook(builtins.__import__)
|